From 2c165aba366d4b22e7be02c723187795b91cf3e3 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Fri, 16 Dec 2016 22:27:45 +0000 Subject: [PATCH 001/167] uart_intr_config should return ESP_OK on success --- components/driver/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index 556e97baac5..a2479f5db04 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -447,7 +447,7 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ } UART[uart_num]->int_ena.val = intr_conf->intr_enable_mask; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - return ESP_FAIL; + return ESP_OK; } //internal isr handler for default driver code. From c01dedcb06afb3dee3199435ddba2f83f9e0186f Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Sun, 11 Dec 2016 16:36:47 +0800 Subject: [PATCH 002/167] component/bt : change api for V2.0 1. change all gatt cb function to 3 args and function type. add gattc_if/gatts_if as second argument 2. delete gatt_if of "gatt cb param" 3. separate conn_id and gatt_if from conn_id 4. change the demo code as the gatt changed --- components/bt/bluedroid/api/esp_blufi_api.c | 2 +- components/bt/bluedroid/api/esp_gap_ble_api.c | 2 +- components/bt/bluedroid/api/esp_gattc_api.c | 117 +++++++------ components/bt/bluedroid/api/esp_gatts_api.c | 27 +-- .../bt/bluedroid/api/include/esp_blufi_api.h | 18 +- .../bt/bluedroid/api/include/esp_bt_defs.h | 7 - .../bluedroid/api/include/esp_gap_ble_api.h | 11 +- .../bt/bluedroid/api/include/esp_gatt_defs.h | 4 +- .../bt/bluedroid/api/include/esp_gattc_api.h | 129 ++++++++------ .../bt/bluedroid/api/include/esp_gatts_api.h | 45 +++-- components/bt/bluedroid/btc/core/btc_manage.c | 6 +- .../bt/bluedroid/btc/include/btc_manage.h | 4 +- .../btc/profile/esp/blufi/blufi_prf.c | 6 +- .../btc/profile/std/gap/btc_gap_ble.c | 2 +- .../btc/profile/std/gatt/btc_gatt_util.c | 5 +- .../btc/profile/std/gatt/btc_gattc.c | 139 +++++++++------ .../btc/profile/std/gatt/btc_gatts.c | 101 ++++++----- .../std/{gatt => }/include/btc_gatt_util.h | 6 +- .../btc/profile/std/include/btc_gattc.h | 8 +- .../btc/profile/std/include/btc_gatts.h | 6 +- examples/12_blufi/components/blufi/blufi.c | 9 +- examples/14_gatt_server/main/gatts_demo.c | 140 ++++++++++----- examples/15_gatt_client/main/gattc_demo.c | 161 ++++++++++++------ 23 files changed, 573 insertions(+), 382 deletions(-) rename components/bt/bluedroid/btc/profile/std/{gatt => }/include/btc_gatt_util.h (80%) diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index a74c9d04b58..2697f2cbf93 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -22,7 +22,7 @@ #include "btc_main.h" #include "future.h" -esp_err_t esp_blufi_register_callback(esp_profile_cb_t callback) +esp_err_t esp_blufi_register_callback(esp_blufi_cb_t callback) { return (btc_profile_cb_set(BTC_PID_BLUFI, callback) == 0 ? ESP_OK : ESP_FAIL); } diff --git a/components/bt/bluedroid/api/esp_gap_ble_api.c b/components/bt/bluedroid/api/esp_gap_ble_api.c index 0770fcfed50..6e18f65822b 100644 --- a/components/bt/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/bluedroid/api/esp_gap_ble_api.c @@ -21,7 +21,7 @@ #include "btc_gap_ble.h" -esp_err_t esp_ble_gap_register_callback(esp_profile_cb_t callback) +esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback) { return (btc_profile_cb_set(BTC_PID_GAP_BLE, callback) == 0 ? ESP_OK : ESP_FAIL); } diff --git a/components/bt/bluedroid/api/esp_gattc_api.c b/components/bt/bluedroid/api/esp_gattc_api.c index c856947f014..28fc4295758 100644 --- a/components/bt/bluedroid/api/esp_gattc_api.c +++ b/components/bt/bluedroid/api/esp_gattc_api.c @@ -17,8 +17,9 @@ #include "esp_gattc_api.h" #include "btc_manage.h" #include "btc_gattc.h" +#include "btc_gatt_util.h" -esp_err_t esp_ble_gattc_register_callback(esp_profile_cb_t callback) +esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback) { if (callback == NULL) { return ESP_FAIL; @@ -33,7 +34,6 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id) btc_msg_t msg; btc_ble_gattc_args_t arg; - //if (app_id < ESP_APP_ID_MIN || app_id > ESP_APP_ID_MAX) { if (app_id > ESP_APP_ID_MAX) { return ESP_ERR_INVALID_ARG; } @@ -46,7 +46,7 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id) return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gatt_if) +esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -54,12 +54,12 @@ esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gatt_if) msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_APP_UNREGISTER; - arg.app_unreg.gatt_if = gatt_if; + arg.app_unreg.gattc_if = gattc_if; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct) +esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, bool is_direct) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -67,14 +67,14 @@ esp_err_t esp_ble_gattc_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bo msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_OPEN; - arg.open.gatt_if = gatt_if; + arg.open.gattc_if = gattc_if; memcpy(arg.open.remote_bda, remote_bda, ESP_BD_ADDR_LEN); arg.open.is_direct = is_direct; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_close (uint16_t conn_id) +esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -82,12 +82,12 @@ esp_err_t esp_ble_gattc_close (uint16_t conn_id) msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CLOSE; - arg.close.conn_id = conn_id; + arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_config_mtu (uint16_t conn_id, uint16_t mtu) +esp_err_t esp_ble_gattc_config_mtu (esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t mtu) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -99,13 +99,13 @@ esp_err_t esp_ble_gattc_config_mtu (uint16_t conn_id, uint16_t mtu) msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CFG_MTU; - arg.cfg_mtu.conn_id = conn_id; + arg.cfg_mtu.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); arg.cfg_mtu.mtu = mtu; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_search_service(uint16_t conn_id, esp_bt_uuid_t *filter_uuid) +esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *filter_uuid) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -113,7 +113,8 @@ esp_err_t esp_ble_gattc_search_service(uint16_t conn_id, esp_bt_uuid_t *filter_u msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_SEARCH_SERVICE; - arg.search_srvc.conn_id = conn_id; + arg.search_srvc.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); + if (filter_uuid) { arg.search_srvc.filter_uuid_enable = true; memcpy(&arg.search_srvc.filter_uuid, filter_uuid, sizeof(esp_bt_uuid_t)); @@ -124,9 +125,10 @@ esp_err_t esp_ble_gattc_search_service(uint16_t conn_id, esp_bt_uuid_t *filter_u return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *start_char_id) +esp_err_t esp_ble_gattc_get_characteristic(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *start_char_id) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -134,12 +136,12 @@ esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; if (start_char_id) { - arg.get_next_char.conn_id = conn_id; + arg.get_next_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_next_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_next_char.char_id, start_char_id, sizeof(esp_gatt_id_t)); msg.act = BTC_GATTC_ACT_GET_NEXT_CHAR; } else { - arg.get_first_char.conn_id = conn_id; + arg.get_first_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_first_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); msg.act = BTC_GATTC_ACT_GET_FIRST_CHAR; } @@ -147,7 +149,8 @@ esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, +esp_err_t esp_ble_gattc_get_descriptor(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, esp_gatt_id_t *start_descr_id) @@ -159,13 +162,13 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, msg.pid = BTC_PID_GATTC; if (start_descr_id) { - arg.get_next_descr.conn_id = conn_id; + arg.get_next_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_next_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_next_descr.char_id, char_id, sizeof(esp_gatt_id_t)); memcpy(&arg.get_next_descr.descr_id, start_descr_id, sizeof(esp_gatt_id_t)); msg.act = BTC_GATTC_ACT_GET_NEXT_DESCR; } else { - arg.get_first_descr.conn_id = conn_id; + arg.get_first_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_first_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_first_descr.char_id, char_id, sizeof(esp_gatt_id_t)); msg.act = BTC_GATTC_ACT_GET_FIRST_DESCR; @@ -174,9 +177,10 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_srvc_id_t *start_incl_srvc_id) +esp_err_t esp_ble_gattc_get_included_service(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_srvc_id_t *start_incl_srvc_id) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -185,12 +189,12 @@ esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, msg.pid = BTC_PID_GATTC; if (start_incl_srvc_id) { - arg.get_next_incl_srvc.conn_id = conn_id; + arg.get_next_incl_srvc.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_next_incl_srvc.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.get_next_incl_srvc.start_service_id, start_incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); msg.act = BTC_GATTC_ACT_GET_NEXT_INCL_SERVICE; } else { - arg.get_first_incl_srvc.conn_id = conn_id; + arg.get_first_incl_srvc.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.get_first_incl_srvc.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); msg.act = BTC_GATTC_ACT_GET_FIRST_INCL_SERVICE; } @@ -198,8 +202,11 @@ esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, esp_gatt_auth_req_t auth_req) +esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -207,7 +214,7 @@ esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR; - arg.read_char.conn_id = conn_id; + arg.read_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.read_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.read_char.char_id, char_id, sizeof(esp_gatt_id_t)); arg.read_char.auth_req = auth_req; @@ -215,11 +222,12 @@ esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - esp_gatt_auth_req_t auth_req) +esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -227,7 +235,7 @@ esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR_DESCR; - arg.read_descr.conn_id = conn_id; + arg.read_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.read_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.read_descr.char_id, char_id, sizeof(esp_gatt_id_t)); memcpy(&arg.read_descr.descr_id, descr_id, sizeof(esp_gatt_id_t)); @@ -236,7 +244,8 @@ esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, +esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t value_len, @@ -250,7 +259,7 @@ esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR; - arg.write_char.conn_id = (uint16_t) conn_id; + arg.write_char.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.write_char.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.write_char.char_id, char_id, sizeof(esp_gatt_id_t)); arg.write_char.value_len = value_len > ESP_GATT_MAX_ATTR_LEN ? ESP_GATT_MAX_ATTR_LEN : value_len; @@ -261,14 +270,15 @@ esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - uint16_t value_len, - uint8_t *value, - esp_gatt_write_type_t write_type, - esp_gatt_auth_req_t auth_req) +esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -276,7 +286,7 @@ esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR_DESCR; - arg.write_descr.conn_id = (uint16_t) conn_id; + arg.write_descr.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.write_descr.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.write_descr.char_id, char_id, sizeof(esp_gatt_id_t)); memcpy(&arg.write_descr.descr_id, descr_id, sizeof(esp_gatt_id_t)); @@ -288,7 +298,8 @@ esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, +esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t offset, @@ -303,7 +314,7 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_PREPARE_WRITE; - arg.prep_write.conn_id = conn_id; + arg.prep_write.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); memcpy(&arg.prep_write.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.prep_write.char_id, char_id, sizeof(esp_gatt_id_t)); arg.prep_write.offset = offset; @@ -314,7 +325,7 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), btc_gattc_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gattc_execute_write (uint16_t conn_id, bool is_execute) +esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, bool is_execute) { btc_msg_t msg; btc_ble_gattc_args_t arg; @@ -322,13 +333,13 @@ esp_err_t esp_ble_gattc_execute_write (uint16_t conn_id, bool is_execute) msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_EXECUTE_WRITE; - arg.exec_write.conn_id = conn_id; + arg.exec_write.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); arg.exec_write.is_execute = is_execute; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, +esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id) @@ -339,7 +350,7 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_REG_FOR_NOTIFY; - arg.reg_for_notify.gatt_if = gatt_if; + arg.reg_for_notify.gattc_if = gattc_if; memcpy(arg.reg_for_notify.remote_bda, server_bda, sizeof(esp_bd_addr_t)); memcpy(&arg.reg_for_notify.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.reg_for_notify.char_id, char_id, sizeof(esp_gatt_id_t)); @@ -347,7 +358,7 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gatt_if, +esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, esp_bd_addr_t server_bda, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id) @@ -358,7 +369,7 @@ esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gatt_if, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_UNREG_FOR_NOTIFY; - arg.unreg_for_notify.gatt_if = gatt_if; + arg.unreg_for_notify.gattc_if = gattc_if; memcpy(arg.unreg_for_notify.remote_bda, server_bda, sizeof(esp_bd_addr_t)); memcpy(&arg.unreg_for_notify.service_id, srvc_id, sizeof(esp_gatt_srvc_id_t)); memcpy(&arg.unreg_for_notify.char_id, char_id, sizeof(esp_gatt_id_t)); diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index 803ff030dd9..2504e58f8f3 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -17,10 +17,11 @@ #include "esp_gatts_api.h" #include "btc_manage.h" #include "btc_gatts.h" +#include "btc_gatt_util.h" #define COPY_TO_GATTS_ARGS(_gatt_args, _arg, _arg_type) memcpy(_gatt_args, _arg, sizeof(_arg_type)) -esp_err_t esp_ble_gatts_register_callback(esp_profile_cb_t callback) +esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback) { return (btc_profile_cb_set(BTC_PID_GATTS, callback) == 0 ? ESP_OK : ESP_FAIL); } @@ -44,7 +45,7 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id) } -esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if) +esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if) { btc_msg_t msg; btc_ble_gatts_args_t arg; @@ -52,12 +53,12 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if) msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_APP_UNREGISTER; - arg.app_unreg.gatt_if = gatt_if; + arg.app_unreg.gatts_if = gatts_if; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatt_if, +esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, esp_gatt_srvc_id_t *service_id, uint16_t num_handle) { btc_msg_t msg; @@ -66,7 +67,7 @@ esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatt_if, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CREATE_SERVICE; - arg.create_srvc.gatt_if = gatt_if; + arg.create_srvc.gatts_if = gatts_if; arg.create_srvc.num_handle = num_handle; memcpy(&arg.create_srvc.service_id, service_id, sizeof(esp_gatt_srvc_id_t)); @@ -164,7 +165,7 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle) } -esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, +esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle, uint16_t value_len, uint8_t *value, bool need_confirm) { btc_msg_t msg; @@ -173,7 +174,7 @@ esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_SEND_INDICATE; - arg.send_ind.conn_id = conn_id; + arg.send_ind.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); arg.send_ind.attr_handle = attr_handle; arg.send_ind.need_confirm = need_confirm; arg.send_ind.value_len = value_len; @@ -182,7 +183,7 @@ esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, +esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id, esp_gatt_status_t status, esp_gatt_rsp_t *rsp) { btc_msg_t msg; @@ -191,7 +192,7 @@ esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_SEND_RESPONSE; - arg.send_rsp.conn_id = conn_id; + arg.send_rsp.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); arg.send_rsp.trans_id = trans_id; arg.send_rsp.status = status; arg.send_rsp.rsp = rsp; @@ -199,7 +200,7 @@ esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct) +esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct) { btc_msg_t msg; btc_ble_gatts_args_t arg; @@ -207,14 +208,14 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bo msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_OPEN; - arg.open.gatt_if = gatt_if; + arg.open.gatts_if = gatts_if; arg.open.is_direct = is_direct; memcpy(&arg.open.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_gatts_close(uint16_t conn_id) +esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id) { btc_msg_t msg; btc_ble_gatts_args_t arg; @@ -222,7 +223,7 @@ esp_err_t esp_ble_gatts_close(uint16_t conn_id) msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CLOSE; - arg.close.conn_id = conn_id; + arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } diff --git a/components/bt/bluedroid/api/include/esp_blufi_api.h b/components/bt/bluedroid/api/include/esp_blufi_api.h index 609d9c1c595..2644bd5244e 100644 --- a/components/bt/bluedroid/api/include/esp_blufi_api.h +++ b/components/bt/bluedroid/api/include/esp_blufi_api.h @@ -25,9 +25,11 @@ extern "C" { #define ESP_BLUFI_RECV_DATA_LEN_MAX (64+1) -#define ESP_BLUFI_EVENT_INIT_FINISH 0 -#define ESP_BLUFI_EVENT_DEINIT_FINISH 1 -#define ESP_BLUFI_EVENT_RECV_DATA 2 +typedef enum { + ESP_BLUFI_EVENT_INIT_FINISH = 0, + ESP_BLUFI_EVENT_DEINIT_FINISH = 1, + ESP_BLUFI_EVENT_RECV_DATA = 2, +} esp_blufi_cb_event_t; /// BLUFI config status typedef enum { @@ -74,6 +76,14 @@ typedef union { } recv_data; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_DATA */ } esp_blufi_cb_param_t; +/** + * @brief BLUFI callback function type + * @param event : Event type + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_blufi_cb_t)(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param); + + /** * * @brief This function is called to receive blufi callback event @@ -83,7 +93,7 @@ typedef union { * @return ESP_OK - success, other - failed * */ -esp_err_t esp_blufi_register_callback(esp_profile_cb_t callback); +esp_err_t esp_blufi_register_callback(esp_blufi_cb_t callback); /** * diff --git a/components/bt/bluedroid/api/include/esp_bt_defs.h b/components/bt/bluedroid/api/include/esp_bt_defs.h index 65de8df5eec..cba8fbe74f4 100644 --- a/components/bt/bluedroid/api/include/esp_bt_defs.h +++ b/components/bt/bluedroid/api/include/esp_bt_defs.h @@ -96,13 +96,6 @@ typedef enum { /// Maximum of the application id #define ESP_APP_ID_MAX 0x7fff -/** - * @brief Each profile callback function type - * @param event : Event type - * @param param : Point to callback parameter, currently is union type - */ -typedef void (* esp_profile_cb_t)(uint32_t event, void *param); - #ifdef __cplusplus } #endif 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 d01595d5954..f500f840314 100644 --- a/components/bt/bluedroid/api/include/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/esp_gap_ble_api.h @@ -31,7 +31,7 @@ typedef enum { ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT , /*!< When scan response data set complete, the event comes */ ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, /*!< When scan parameters set complete, the event comes */ ESP_GAP_BLE_SCAN_RESULT_EVT, /*!< When one scan result ready, the event comes each time */ -}esp_gap_ble_cb_event_t; +} esp_gap_ble_cb_event_t; /// Advertising data maximum length #define ESP_BLE_ADV_DATA_LEN_MAX 31 @@ -257,6 +257,13 @@ typedef union { } scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */ } esp_ble_gap_cb_param_t; +/** + * @brief GAP callback function type + * @param event : Event type + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gap_ble_cb_t)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); + /** * @brief This function is called to occur gap event, such as scan result * @@ -267,7 +274,7 @@ typedef union { * - other : failed * */ -esp_err_t esp_ble_gap_register_callback(esp_profile_cb_t callback); +esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback); /** diff --git a/components/bt/bluedroid/api/include/esp_gatt_defs.h b/components/bt/bluedroid/api/include/esp_gatt_defs.h index 931733e4020..7b119a62cd1 100644 --- a/components/bt/bluedroid/api/include/esp_gatt_defs.h +++ b/components/bt/bluedroid/api/include/esp_gatt_defs.h @@ -269,7 +269,9 @@ typedef enum { ESP_GATT_WRITE_TYPE_RSP, /*!< Gatt write attribute need remote response */ } esp_gatt_write_type_t; -typedef uint32_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ +#define ESP_GATT_IF_NONE 0xff /*!< If callback report gattc_if/gatts_if as this macro, means this event is not correspond to any app */ + +typedef uint8_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ #ifdef __cplusplus } diff --git a/components/bt/bluedroid/api/include/esp_gattc_api.h b/components/bt/bluedroid/api/include/esp_gattc_api.h index 4407975a0d0..b52dabbdaca 100644 --- a/components/bt/bluedroid/api/include/esp_gattc_api.h +++ b/components/bt/bluedroid/api/include/esp_gattc_api.h @@ -81,7 +81,6 @@ typedef union { */ struct gattc_reg_evt_param { esp_gatt_status_t status; /*!< Operation status */ - esp_gatt_if_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t app_id; /*!< Application id which input in register API */ } reg; /*!< Gatt client callback param of ESP_GATTC_REG_EVT */ @@ -91,7 +90,6 @@ typedef union { struct gattc_open_evt_param { esp_gatt_status_t status; /*!< Operation status */ uint16_t conn_id; /*!< Connection id */ - esp_gatt_if_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ uint16_t mtu; /*!< MTU size */ } open; /*!< Gatt client callback param of ESP_GATTC_OPEN_EVT */ @@ -102,7 +100,6 @@ typedef union { struct gattc_close_evt_param { esp_gatt_status_t status; /*!< Operation status */ uint16_t conn_id; /*!< Connection id */ - esp_gatt_if_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ esp_gatt_conn_reason_t reason; /*!< The reason of gatt connection close */ } close; /*!< Gatt client callback param of ESP_GATTC_CLOSE_EVT */ @@ -247,6 +244,14 @@ typedef union { } esp_ble_gattc_cb_param_t; /*!< GATT client callback parameter union type */ +/** + * @brief GATT Client callback function type + * @param event : Event type + * @param gatts_if : GATT client access interface, normally + * different gattc_if correspond to different profile + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gattc_cb_t)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); /** * @brief This function is called to register application callbacks @@ -259,7 +264,7 @@ typedef union { * - other: failed * */ -esp_err_t esp_ble_gattc_register_callback(esp_profile_cb_t callback); +esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback); /** @@ -280,20 +285,20 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id); * @brief This function is called to unregister an application * from GATTC module. * - * @param[in] gatt_if : app identifier. + * @param[in] gattc_if: Gatt client access interface. * * @return * - ESP_OK: success * - other: failed * */ -esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gatt_if); +esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if); /** * @brief Open a direct connection or add a background auto connection * - * @param[in] gatt_if: application identity. + * @param[in] gattc_if: Gatt client access interface. * @param[in] remote_bda: remote device bluetooth device address. * @param[in] is_direct: direct connection or background auto connection * @@ -302,12 +307,13 @@ esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gatt_if); * - other: failed * */ -esp_err_t esp_ble_gattc_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct); +esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, bool is_direct); /** * @brief Close a connection to a GATT server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID to be closed. * * @return @@ -315,13 +321,14 @@ esp_err_t esp_ble_gattc_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bo * - other: failed * */ -esp_err_t esp_ble_gattc_close(uint16_t conn_id); +esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id); /** * @brief Configure the MTU size in the GATT channel. This can be done * only once per connection. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID. * @param[in] mtu: desired MTU size to use. * @@ -330,7 +337,7 @@ esp_err_t esp_ble_gattc_close(uint16_t conn_id); * - other: failed * */ -esp_err_t esp_ble_gattc_config_mtu(uint16_t conn_id, uint16_t mtu); +esp_err_t esp_ble_gattc_config_mtu (esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t mtu); /** @@ -339,6 +346,7 @@ esp_err_t esp_ble_gattc_config_mtu(uint16_t conn_id, uint16_t mtu); * by a callback event, and followed by a service search complete * event. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID. * @param[in] filter_uuid: a UUID of the service application is interested in. * If Null, discover for all services. @@ -348,32 +356,32 @@ esp_err_t esp_ble_gattc_config_mtu(uint16_t conn_id, uint16_t mtu); * - other: failed * */ -esp_err_t esp_ble_gattc_search_service(uint16_t conn_id, esp_bt_uuid_t *filter_uuid); +esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *filter_uuid); /** * @brief This function is called to find the first characteristic of the * service on the given server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID which identify the server. - * * @param[in] srvc_id: service ID - * * @param[in] start_char_id: the start characteristic ID - * * @return * - ESP_OK: success * - other: failed * */ -esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *start_char_id); - +esp_err_t esp_ble_gattc_get_characteristic(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *start_char_id); /** * @brief This function is called to find the descriptor of the * service on the given server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID which identify the server. * @param[in] srvc_id: the service ID of which the characteristic is belonged to. * @param[in] char_id: Characteristic ID, if NULL find the first available @@ -385,8 +393,10 @@ esp_err_t esp_ble_gattc_get_characteristic(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, +esp_err_t esp_ble_gattc_get_descriptor(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, esp_gatt_id_t *start_descr_id); @@ -394,6 +404,7 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, * @brief This function is called to find the first characteristic of the * service on the given server. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID which identify the server. * @param[in] srvc_id: the service ID of which the characteristic is belonged to. * @param[in] start_incl_srvc_id: the start include service id @@ -403,14 +414,17 @@ esp_err_t esp_ble_gattc_get_descriptor(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, esp_gatt_srvc_id_t *start_incl_srvc_id); +esp_err_t esp_ble_gattc_get_included_service(esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_srvc_id_t *start_incl_srvc_id); /** * @brief This function is called to read a service's characteristics of - * the given characteriistic ID + * the given characteristic ID * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID to read. @@ -421,15 +435,17 @@ esp_err_t esp_ble_gattc_get_included_service(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_auth_req_t auth_req); +esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_auth_req_t auth_req); /** * @brief This function is called to read a characteristics descriptor. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID to read. @@ -441,16 +457,18 @@ esp_err_t esp_ble_gattc_read_char (uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - esp_gatt_auth_req_t auth_req); +esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + esp_gatt_auth_req_t auth_req); /** * @brief This function is called to write characteristic value. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID to write. @@ -464,7 +482,8 @@ esp_err_t esp_ble_gattc_read_char_descr (uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, +esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t value_len, @@ -476,6 +495,7 @@ esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, /** * @brief This function is called to write characteristic descriptor value. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID * @param[in] srvc_id : service ID. * @param[in] char_id : characteristic ID. @@ -490,19 +510,21 @@ esp_err_t esp_ble_gattc_write_char( uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id, - esp_gatt_id_t *descr_id, - uint16_t value_len, - uint8_t *value, - esp_gatt_write_type_t write_type, - esp_gatt_auth_req_t auth_req); +esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, + uint16_t conn_id, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id, + esp_gatt_id_t *descr_id, + uint16_t value_len, + uint8_t *value, + esp_gatt_write_type_t write_type, + esp_gatt_auth_req_t auth_req); /** * @brief This function is called to prepare write a characteristic value. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] srvc_id : service ID. * @param[in] char_id : GATT characteristic ID of the service. @@ -516,7 +538,8 @@ esp_err_t esp_ble_gattc_write_char_descr (uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, +esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, + uint16_t conn_id, esp_gatt_srvc_id_t *srvc_id, esp_gatt_id_t *char_id, uint16_t offset, @@ -524,9 +547,11 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, uint8_t *value, esp_gatt_auth_req_t auth_req); + /** * @brief This function is called to execute write a prepare write sequence. * + * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id : connection ID. * @param[in] is_execute : execute or cancel. * @@ -535,13 +560,13 @@ esp_err_t esp_ble_gattc_prepare_write(uint16_t conn_id, * - other: failed * */ -esp_err_t esp_ble_gattc_execute_write (uint16_t conn_id, bool is_execute); +esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, bool is_execute); /** * @brief This function is called to register for notification of a service. * - * @param[in] gatt_if : gatt interface id. + * @param[in] gattc_if: Gatt client access interface. * @param[in] server_bda : target GATT server. * @param[in] srvc_id : pointer to GATT service ID. * @param[in] char_id : pointer to GATT characteristic ID. @@ -551,16 +576,16 @@ esp_err_t esp_ble_gattc_execute_write (uint16_t conn_id, bool is_execute); * - other: failed * */ -esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, - esp_bd_addr_t server_bda, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id); +esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id); /** * @brief This function is called to de-register for notification of a service. * - * @param[in] gatt_if : gatt interface id. + * @param[in] gattc_if: Gatt client access interface. * @param[in] server_bda : target GATT server. * @param[in] srvc_id : pointer to GATT service ID. * @param[in] char_id : pointer to GATT characteristic ID. @@ -570,10 +595,10 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gatt_if, * - other: failed * */ -esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gatt_if, - esp_bd_addr_t server_bda, - esp_gatt_srvc_id_t *srvc_id, - esp_gatt_id_t *char_id); +esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, + esp_bd_addr_t server_bda, + esp_gatt_srvc_id_t *srvc_id, + esp_gatt_id_t *char_id); #ifdef __cplusplus } diff --git a/components/bt/bluedroid/api/include/esp_gatts_api.h b/components/bt/bluedroid/api/include/esp_gatts_api.h index 30aa3ecf566..d0fc055a7fd 100644 --- a/components/bt/bluedroid/api/include/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/esp_gatts_api.h @@ -59,7 +59,6 @@ typedef union { */ struct gatts_reg_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t app_id; /*!< Application id which input in register API */ } reg; /*!< Gatt server callback param of ESP_GATTS_REG_EVT */ @@ -127,7 +126,6 @@ typedef union { */ struct gatts_create_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t service_handle; /*!< Service attribute handle */ esp_gatt_srvc_id_t service_id; /*!< Service id, include service uuid and other information */ } create; /*!< Gatt server callback param of ESP_GATTS_CREATE_EVT */ @@ -137,7 +135,6 @@ typedef union { */ struct gatts_add_incl_srvc_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t attr_handle; /*!< Included service attribute handle */ uint16_t service_handle; /*!< Service attribute handle */ } add_incl_srvc; /*!< Gatt server callback param of ESP_GATTS_ADD_INCL_SRVC_EVT */ @@ -147,7 +144,6 @@ typedef union { */ struct gatts_add_char_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t attr_handle; /*!< Characteristic attribute handle */ uint16_t service_handle; /*!< Service attribute handle */ esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ @@ -158,7 +154,6 @@ typedef union { */ struct gatts_add_char_descr_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t attr_handle; /*!< Descriptor attribute handle */ uint16_t service_handle; /*!< Service attribute handle */ esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ @@ -169,7 +164,6 @@ typedef union { */ struct gatts_delete_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t service_handle; /*!< Service attribute handle */ } del; /*!< Gatt server callback param of ESP_GATTS_DELETE_EVT */ @@ -178,7 +172,6 @@ typedef union { */ struct gatts_start_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t service_handle; /*!< Service attribute handle */ } start; /*!< Gatt server callback param of ESP_GATTS_START_EVT */ @@ -187,7 +180,6 @@ typedef union { */ struct gatts_stop_evt_param { esp_gatt_status_t status; /*!< Operation status */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ uint16_t service_handle; /*!< Service attribute handle */ } stop; /*!< Gatt server callback param of ESP_GATTS_STOP_EVT */ @@ -196,7 +188,6 @@ typedef union { */ struct gatts_connect_evt_param { uint16_t conn_id; /*!< Connection id */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ 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 */ @@ -206,7 +197,6 @@ typedef union { */ struct gatts_disconnect_evt_param { uint16_t conn_id; /*!< Connection id */ - uint16_t gatt_if; /*!< Gatt interface id, different application on gatt client different gatt_if */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ bool is_connected; /*!< Indicate it is connected or not */ } disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */ @@ -240,6 +230,15 @@ typedef union { } rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */ } esp_ble_gatts_cb_param_t; +/** + * @brief GATT Server callback function type + * @param event : Event type + * @param gatts_if : GATT server access interface, normally + * different gatts_if correspond to different profile + * @param param : Point to callback parameter, currently is union type + */ +typedef void (* esp_gatts_cb_t)(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + /** * @brief This function is called to register application callbacks * with BTA GATTS module. @@ -249,9 +248,7 @@ typedef union { * - other : failed * */ -esp_err_t esp_ble_gatts_register_callback(esp_profile_cb_t callback); - - +esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback); /** * @brief This function is called to register application identifier @@ -268,14 +265,13 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id); /** * @brief unregister with GATT Server. * - * @param[in] gatt_if: gatt interface id. - * + * @param[in] gatts_if: GATT server access interface * @return * - ESP_OK : success * - other : failed * */ -esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if); +esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if); /** @@ -285,7 +281,7 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if); * the callback function needs to be used when adding included * service and characteristics/descriptors into the service. * - * @param[in] gatt_if: gatt interface ID + * @param[in] gatts_if: GATT server access interface * @param[in] service_id: service ID. * @param[in] num_handle: number of handle requested for this service. * @@ -294,7 +290,7 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatt_if); * - other : failed * */ -esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatt_if, +esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, esp_gatt_srvc_id_t *service_id, uint16_t num_handle); @@ -402,6 +398,7 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle); /** * @brief This function is called to read a characteristics descriptor. * + * @param[in] gatts_if: GATT server access interface * @param[in] conn_id - connection id to indicate. * @param[in] attr_handle - attribute handle to indicate. * @param[in] value_len - indicate value length. @@ -413,13 +410,14 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle); * - other : failed * */ -esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, +esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle, uint16_t value_len, uint8_t *value, bool need_confirm); /** * @brief This function is called to send a response to a request. * + * @param[in] gatts_if: GATT server access interface * @param[in] conn_id - connection identifier. * @param[in] trans_id - transfer id * @param[in] status - response status @@ -430,14 +428,14 @@ esp_err_t esp_ble_gatts_send_indicate(uint16_t conn_id, uint16_t attr_handle, * - other : failed * */ -esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, +esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id, esp_gatt_status_t status, esp_gatt_rsp_t *rsp); /** * @brief Open a direct open connection or add a background auto connection * - * @param[in] gatt_if: application ID. + * @param[in] gatts_if: GATT server access interface * @param[in] remote_bda: remote device bluetooth device address. * @param[in] is_direct: direct connection or background auto connection * @@ -446,11 +444,12 @@ esp_err_t esp_ble_gatts_send_response(uint16_t conn_id, uint32_t trans_id, * - other : failed * */ -esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bool is_direct); +esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct); /** * @brief Close a connection a remote device. * + * @param[in] gatts_if: GATT server access interface * @param[in] conn_id: connection ID to be closed. * * @return @@ -458,7 +457,7 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatt_if, esp_bd_addr_t remote_bda, bo * - other : failed * */ -esp_err_t esp_ble_gatts_close(uint16_t conn_id); +esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id); #ifdef __cplusplus } diff --git a/components/bt/bluedroid/btc/core/btc_manage.c b/components/bt/bluedroid/btc/core/btc_manage.c index 46d20dc2d8d..213c4939c1e 100644 --- a/components/bt/bluedroid/btc/core/btc_manage.c +++ b/components/bt/bluedroid/btc/core/btc_manage.c @@ -20,7 +20,7 @@ #include "esp_bt_defs.h" #include "esp_gatt_defs.h" -static esp_profile_cb_t btc_profile_cb_tab[BTC_PID_NUM] = {}; +static void *btc_profile_cb_tab[BTC_PID_NUM] = {}; void esp_profile_cb_reset(void) { @@ -31,7 +31,7 @@ void esp_profile_cb_reset(void) } } -int btc_profile_cb_set(btc_pid_t profile_id, esp_profile_cb_t cb) +int btc_profile_cb_set(btc_pid_t profile_id, void *cb) { if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return -1; @@ -42,7 +42,7 @@ int btc_profile_cb_set(btc_pid_t profile_id, esp_profile_cb_t cb) return 0; } -esp_profile_cb_t btc_profile_cb_get(btc_pid_t profile_id) +void *btc_profile_cb_get(btc_pid_t profile_id) { if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return NULL; diff --git a/components/bt/bluedroid/btc/include/btc_manage.h b/components/bt/bluedroid/btc/include/btc_manage.h index e8591caa8af..8789f543dd1 100644 --- a/components/bt/bluedroid/btc/include/btc_manage.h +++ b/components/bt/bluedroid/btc/include/btc_manage.h @@ -22,7 +22,7 @@ /* reset gatt callback table */ void esp_profile_cb_reset(void); -int btc_profile_cb_set(btc_pid_t profile_id, esp_profile_cb_t cb); -esp_profile_cb_t btc_profile_cb_get(btc_pid_t profile_id); +int btc_profile_cb_set(btc_pid_t profile_id, void *cb); +void *btc_profile_cb_get(btc_pid_t profile_id); #endif /* __BTC_MANAGE_H__ */ diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index 8acb9565c8a..d6e23e63a33 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -37,7 +37,7 @@ const char success_msg[] = "BLUFI_CONFIG_OK"; const char failed_msg[] = "BLUFI_CONFIG_FAILED"; -#define BTC_BLUFI_CB_TO_APP(_event, _param) ((esp_profile_cb_t)btc_profile_cb_get(BTC_PID_BLUFI))(_event, _param) +#define BTC_BLUFI_CB_TO_APP(event, param) ((esp_blufi_cb_t)btc_profile_cb_get(BTC_PID_BLUFI))((event), (param)) #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] @@ -147,6 +147,8 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) tBT_UUID uuid = {LEN_UUID_16, {SVC_BLUFI_UUID}}; UINT8 *p_rec_data = NULL; tBTA_GATT_STATUS status; + tBTA_DM_DISC disc_mode = BTA_DM_BLE_GENERAL_DISCOVERABLE; + tBTA_DM_CONN conn_mode = BTA_DM_BLE_CONNECTABLE; LOG_DEBUG("blufi profile cb event = %x\n", event); switch (event) { @@ -166,6 +168,8 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) event, status, blufi_cb_env.gatt_if); LOG_DEBUG("set advertising parameters\n"); + //set connectable,discoverable, pairable and paired only modes of local device + BTA_DmSetVisibility(disc_mode, conn_mode, (uint8_t)BTA_DM_NON_PAIRABLE, (uint8_t)BTA_DM_CONN_ALL); //set the advertising data to the btm layer BlufiBleConfigadvData(&esp32_adv_data[BLE_ADV_DATA_IDX], NULL); //set the adversting data to the btm layer diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index f458cb4ced2..39b392e62f7 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -26,7 +26,7 @@ static tBTA_BLE_ADV_DATA gl_bta_adv_data; static tBTA_BLE_ADV_DATA gl_bta_scan_rsp_data; -#define BTC_GAP_BLE_CB_TO_APP(_event, _param) ((esp_profile_cb_t )btc_profile_cb_get(BTC_PID_GAP_BLE))(_event, _param) +#define BTC_GAP_BLE_CB_TO_APP(event, param) ((esp_gap_ble_cb_t)btc_profile_cb_get(BTC_PID_GAP_BLE))((event), (param)) static void btc_gap_adv_point_cleanup(void **buf) diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c index 938d2dc409a..217ec9db4bd 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c @@ -169,13 +169,14 @@ uint16_t get_uuid16(tBT_UUID *p_uuid) return (UINT16) p_uuid->uu.uuid32; } } -uint16_t set_read_value(esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src) +uint16_t set_read_value(uint8_t *gattc_if, esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src) { uint16_t descr_type = 0; uint16_t len = 0; p_dest->read.status = p_src->status; - p_dest->read.conn_id = p_src->conn_id; + p_dest->read.conn_id = BTC_GATT_GET_CONN_ID(p_src->conn_id); + *gattc_if = BTC_GATT_GET_GATT_IF(p_src->conn_id); bta_to_btc_srvc_id(&p_dest->read.srvc_id, &p_src->srvc_id); bta_to_btc_gatt_id(&p_dest->read.char_id, &p_src->char_id); bta_to_btc_gatt_id(&p_dest->read.descr_id, &p_src->descr_type); 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 b5da7ed2490..b7df92fcfa8 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -22,7 +22,7 @@ #include "bt_trace.h" #include "esp_gattc_api.h" -#define BTC_GATTC_CB_TO_APP(_event, _param) ((esp_profile_cb_t )btc_profile_cb_get(BTC_PID_GATTC))(_event, _param) +#define BTC_GATTC_CB_TO_APP(event, gattc_if, param) ((esp_gattc_cb_t )btc_profile_cb_get(BTC_PID_GATTC))((event), (gattc_if), (param)) void btc_gattc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { @@ -141,13 +141,13 @@ static void btc_gattc_app_register(btc_ble_gattc_args_t *arg) static void btc_gattc_app_unregister(btc_ble_gattc_args_t *arg) { - BTA_GATTC_AppDeregister(arg->app_unreg.gatt_if); + BTA_GATTC_AppDeregister(arg->app_unreg.gattc_if); } static void btc_gattc_open(btc_ble_gattc_args_t *arg) { tBTA_GATT_TRANSPORT transport = BTA_GATT_TRANSPORT_LE; - BTA_GATTC_Open(arg->open.gatt_if, arg->open.remote_bda, arg->open.is_direct, transport); + BTA_GATTC_Open(arg->open.gattc_if, arg->open.remote_bda, arg->open.is_direct, transport); } static void btc_gattc_close(btc_ble_gattc_args_t *arg) @@ -181,6 +181,7 @@ static void btc_gattc_get_first_char(btc_ble_gattc_args_t *arg) tBTA_GATT_CHAR_PROP out_char_prop; tBTA_GATT_SRVC_ID srvc_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&srvc_id, &arg->get_first_char.service_id); status = BTA_GATTC_GetFirstChar(arg->get_first_char.conn_id, &srvc_id, NULL, @@ -189,13 +190,14 @@ static void btc_gattc_get_first_char(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&char_id, &out_char_id.char_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_first_char.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_char.conn_id = arg->get_first_char.conn_id; + param.get_char.conn_id = BTC_GATT_GET_CONN_ID(arg->get_first_char.conn_id); param.get_char.status = status; memcpy(¶m.get_char.srvc_id, &arg->get_first_char.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_char.char_id, &char_id, sizeof(esp_gatt_id_t)); param.get_char.char_prop = out_char_prop; - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) @@ -206,6 +208,8 @@ static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) tBTA_GATTC_CHAR_ID out_char_id; tBTA_GATT_CHAR_PROP out_char_prop; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; + btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->get_next_char.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->get_next_char.char_id); @@ -216,13 +220,14 @@ static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&char_id, &out_char_id.char_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_next_char.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_char.conn_id = arg->get_next_char.conn_id; + param.get_char.conn_id = BTC_GATT_GET_CONN_ID(arg->get_next_char.conn_id); param.get_char.status = status; memcpy(¶m.get_char.srvc_id, &arg->get_next_char.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_char.char_id, &char_id, sizeof(esp_gatt_id_t)); param.get_char.char_prop = out_char_prop; - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); } static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) @@ -232,6 +237,7 @@ static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) tBTA_GATTC_CHAR_ID in_char_id; tBTA_GATTC_CHAR_DESCR_ID out_char_descr_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->get_first_descr.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->get_first_descr.char_id); @@ -243,13 +249,14 @@ static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&descr_id, &out_char_descr_id.descr_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_first_descr.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_descr.conn_id = arg->get_first_descr.conn_id; + param.get_descr.conn_id = BTC_GATT_GET_CONN_ID(arg->get_first_descr.conn_id); param.get_descr.status = status; memcpy(¶m.get_descr.srvc_id, &arg->get_first_descr.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_descr.char_id, &arg->get_first_descr.char_id, sizeof(esp_gatt_id_t)); memcpy(¶m.get_descr.descr_id, &descr_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) @@ -259,6 +266,7 @@ static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) tBTA_GATTC_CHAR_DESCR_ID in_char_descr_id; tBTA_GATTC_CHAR_DESCR_ID out_char_descr_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&in_char_descr_id.char_id.srvc_id, &arg->get_next_descr.service_id); btc_to_bta_gatt_id(&in_char_descr_id.char_id.char_id, &arg->get_next_descr.char_id); @@ -270,13 +278,14 @@ static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) bta_to_btc_gatt_id(&descr_id, &out_char_descr_id.descr_id); } + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_next_descr.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_descr.conn_id = arg->get_next_descr.conn_id; + param.get_descr.conn_id = BTC_GATT_GET_CONN_ID(arg->get_next_descr.conn_id); param.get_descr.status = status; memcpy(¶m.get_descr.srvc_id, &arg->get_next_descr.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_descr.char_id, &arg->get_next_descr.char_id, sizeof(esp_gatt_id_t)); memcpy(¶m.get_descr.descr_id, &descr_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); } static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) @@ -286,6 +295,7 @@ static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) tBTA_GATT_SRVC_ID srvc_id; tBTA_GATTC_INCL_SVC_ID out_incl_svc_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&srvc_id, &arg->get_first_incl_srvc.service_id); @@ -294,12 +304,13 @@ static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) bta_to_btc_srvc_id(&incl_srvc_id, &out_incl_svc_id.incl_svc_id); + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_first_incl_srvc.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_incl_srvc.conn_id = arg->get_first_incl_srvc.conn_id; + param.get_incl_srvc.conn_id = BTC_GATT_GET_CONN_ID(arg->get_first_incl_srvc.conn_id); param.get_incl_srvc.status = status; memcpy(¶m.get_incl_srvc.srvc_id, &arg->get_first_incl_srvc.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_incl_srvc.incl_srvc_id, &incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) @@ -309,6 +320,7 @@ static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) tBTA_GATTC_INCL_SVC_ID in_incl_svc_id; tBTA_GATTC_INCL_SVC_ID out_incl_svc_id; esp_ble_gattc_cb_param_t param; + esp_gatt_if_t gattc_if; btc_to_bta_srvc_id(&in_incl_svc_id.srvc_id, &arg->get_next_incl_srvc.service_id); btc_to_bta_srvc_id(&in_incl_svc_id.incl_svc_id, &arg->get_next_incl_srvc.start_service_id); @@ -318,12 +330,13 @@ static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) bta_to_btc_srvc_id(&incl_srvc_id, &out_incl_svc_id.incl_svc_id); + gattc_if = BTC_GATT_GET_GATT_IF(arg->get_next_incl_srvc.conn_id); memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); - param.get_incl_srvc.conn_id = arg->get_next_incl_srvc.conn_id; + param.get_incl_srvc.conn_id = BTC_GATT_GET_CONN_ID(arg->get_next_incl_srvc.conn_id); param.get_incl_srvc.status = status; memcpy(¶m.get_incl_srvc.srvc_id, &arg->get_next_incl_srvc.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_incl_srvc.incl_srvc_id, &incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); } static void btc_gattc_read_char(btc_ble_gattc_args_t *arg) @@ -402,7 +415,7 @@ static void btc_gattc_reg_for_notify(btc_ble_gattc_args_t *arg) btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->reg_for_notify.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->reg_for_notify.char_id); - status = BTA_GATTC_RegisterForNotifications(arg->reg_for_notify.gatt_if, + status = BTA_GATTC_RegisterForNotifications(arg->reg_for_notify.gattc_if, arg->reg_for_notify.remote_bda, &in_char_id); @@ -410,7 +423,7 @@ static void btc_gattc_reg_for_notify(btc_ble_gattc_args_t *arg) param.reg_for_notify.status = status; memcpy(¶m.reg_for_notify.srvc_id, &arg->reg_for_notify.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.reg_for_notify.char_id, &arg->reg_for_notify.service_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_FOR_NOTIFY_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_FOR_NOTIFY_EVT, arg->reg_for_notify.gattc_if, ¶m); } static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) @@ -422,7 +435,7 @@ static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) btc_to_bta_srvc_id(&in_char_id.srvc_id, &arg->unreg_for_notify.service_id); btc_to_bta_gatt_id(&in_char_id.char_id, &arg->unreg_for_notify.char_id); - status = BTA_GATTC_DeregisterForNotifications(arg->unreg_for_notify.gatt_if, + status = BTA_GATTC_DeregisterForNotifications(arg->unreg_for_notify.gattc_if, arg->unreg_for_notify.remote_bda, &in_char_id); @@ -430,7 +443,7 @@ static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) param.unreg_for_notify.status = status; memcpy(¶m.unreg_for_notify.srvc_id, &arg->unreg_for_notify.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.unreg_for_notify.char_id, &arg->unreg_for_notify.service_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_FOR_NOTIFY_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_FOR_NOTIFY_EVT, arg->unreg_for_notify.gattc_if, ¶m); } void btc_gattc_call_handler(btc_msg_t *msg) @@ -508,6 +521,7 @@ void btc_gattc_call_handler(btc_msg_t *msg) void btc_gattc_cb_handler(btc_msg_t *msg) { tBTA_GATTC *arg = (tBTA_GATTC *)(msg->arg); + esp_gatt_if_t gattc_if; esp_ble_gattc_cb_param_t param; memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); @@ -515,75 +529,90 @@ void btc_gattc_cb_handler(btc_msg_t *msg) switch (msg->act) { case BTA_GATTC_REG_EVT: { tBTA_GATTC_REG *reg_oper = &arg->reg_oper; + + gattc_if = reg_oper->client_if; param.reg.status = reg_oper->status; - param.reg.gatt_if = reg_oper->client_if; param.reg.app_id = reg_oper->app_uuid.uu.uuid16; - BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_EVT, gattc_if, ¶m); break; } case BTA_GATTC_DEREG_EVT: { - BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_EVT, NULL); + tBTA_GATTC_REG *reg_oper = &arg->reg_oper; + + gattc_if = reg_oper->client_if; + BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_EVT, gattc_if, NULL); break; } case BTA_GATTC_READ_CHAR_EVT: { - set_read_value(¶m, &arg->read); - BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_CHAR_EVT, ¶m); + set_read_value(&gattc_if, ¶m, &arg->read); + BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_CHAR_EVT, gattc_if, ¶m); break; } - case BTA_GATTC_WRITE_CHAR_EVT: case BTA_GATTC_PREP_WRITE_EVT: { tBTA_GATTC_WRITE *write = &arg->write; uint32_t ret_evt = (msg->act == BTA_GATTC_WRITE_CHAR_EVT) ? ESP_GATTC_WRITE_CHAR_EVT : ESP_GATTC_PREP_WRITE_EVT; - param.write.conn_id = write->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(write->conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(write->conn_id); param.write.status = write->status; bta_to_btc_srvc_id(¶m.write.srvc_id, &write->srvc_id); bta_to_btc_gatt_id(¶m.write.char_id, &write->char_id); - BTC_GATTC_CB_TO_APP(ret_evt, ¶m); + BTC_GATTC_CB_TO_APP(ret_evt, gattc_if, ¶m); break; } case BTA_GATTC_EXEC_EVT: { tBTA_GATTC_EXEC_CMPL *exec_cmpl = &arg->exec_cmpl; - param.exec_cmpl.conn_id = exec_cmpl->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(exec_cmpl->conn_id); + param.exec_cmpl.conn_id = BTC_GATT_GET_CONN_ID(exec_cmpl->conn_id); param.exec_cmpl.status = exec_cmpl->status; - BTC_GATTC_CB_TO_APP(ESP_GATTC_EXEC_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_EXEC_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SEARCH_CMPL_EVT: { tBTA_GATTC_SEARCH_CMPL *search_cmpl = &arg->search_cmpl; - param.search_cmpl.conn_id = search_cmpl->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(search_cmpl->conn_id); + param.search_cmpl.conn_id = BTC_GATT_GET_CONN_ID(search_cmpl->conn_id); param.search_cmpl.status = search_cmpl->status; - BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_CMPL_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_CMPL_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SEARCH_RES_EVT: { tBTA_GATTC_SRVC_RES *srvc_res = &arg->srvc_res; - param.search_res.conn_id = srvc_res->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(srvc_res->conn_id); + param.search_res.conn_id = BTC_GATT_GET_CONN_ID(srvc_res->conn_id); bta_to_btc_srvc_id(¶m.search_res.srvc_id, &srvc_res->service_uuid); - BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_RES_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_RES_EVT, gattc_if, ¶m); break; } case BTA_GATTC_READ_DESCR_EVT: { - set_read_value(¶m, &arg->read); - BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_DESCR_EVT, ¶m); + set_read_value(&gattc_if, ¶m, &arg->read); + BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_DESCR_EVT, gattc_if, ¶m); break; } case BTA_GATTC_WRITE_DESCR_EVT: { tBTA_GATTC_WRITE *write = &arg->write; - param.write.conn_id = write->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(write->conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(write->conn_id); param.write.status = write->status; bta_to_btc_srvc_id(¶m.write.srvc_id, &write->srvc_id); bta_to_btc_gatt_id(¶m.write.char_id, &write->char_id); bta_to_btc_gatt_id(¶m.write.descr_id, &write->descr_type); - BTC_GATTC_CB_TO_APP(ESP_GATTC_WRITE_DESCR_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_WRITE_DESCR_EVT, gattc_if, ¶m); break; } case BTA_GATTC_NOTIF_EVT: { tBTA_GATTC_NOTIFY *notify = &arg->notify; - param.notify.conn_id = notify->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(notify->conn_id); + param.notify.conn_id = BTC_GATT_GET_CONN_ID(notify->conn_id); memcpy(param.notify.remote_bda, notify->bda, sizeof(esp_bd_addr_t)); bta_to_btc_srvc_id(¶m.notify.srvc_id, ¬ify->char_id.srvc_id); bta_to_btc_gatt_id(¶m.notify.char_id, ¬ify->char_id.char_id); @@ -597,57 +626,63 @@ void btc_gattc_cb_handler(btc_msg_t *msg) BTA_GATTC_SendIndConfirm(notify->conn_id, ¬ify->char_id); } - BTC_GATTC_CB_TO_APP(ESP_GATTC_NOTIFY_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_NOTIFY_EVT, gattc_if, ¶m); break; } case BTA_GATTC_OPEN_EVT: { tBTA_GATTC_OPEN *open = &arg->open; + + gattc_if = open->client_if; param.open.status = open->status; - param.open.conn_id = open->conn_id; - param.open.gatt_if = open->client_if; + param.open.conn_id = BTC_GATT_GET_CONN_ID(open->conn_id); memcpy(param.open.remote_bda, open->remote_bda, sizeof(esp_bd_addr_t)); param.open.mtu = open->mtu; - BTC_GATTC_CB_TO_APP(ESP_GATTC_OPEN_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_OPEN_EVT, gattc_if, ¶m); break; } case BTA_GATTC_CLOSE_EVT: { tBTA_GATTC_CLOSE *close = &arg->close; + + gattc_if = close->client_if; param.close.status = close->status; - param.close.conn_id = close->conn_id; - param.close.gatt_if = close->client_if; + param.close.conn_id = BTC_GATT_GET_CONN_ID(close->conn_id); memcpy(param.close.remote_bda, close->remote_bda, sizeof(esp_bd_addr_t)); param.close.reason = close->reason; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CLOSE_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_CLOSE_EVT, gattc_if, ¶m); break; } case BTA_GATTC_CFG_MTU_EVT: { tBTA_GATTC_CFG_MTU *cfg_mtu = &arg->cfg_mtu; - param.cfg_mtu.conn_id = cfg_mtu->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(cfg_mtu->conn_id); + param.cfg_mtu.conn_id = BTC_GATT_GET_CONN_ID(cfg_mtu->conn_id); param.cfg_mtu.status = cfg_mtu->status; param.cfg_mtu.mtu = cfg_mtu->mtu; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CFG_MTU_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_CFG_MTU_EVT, gattc_if, ¶m); break; } case BTA_GATTC_ACL_EVT: { - BTC_GATTC_CB_TO_APP(ESP_GATTC_ACL_EVT, NULL); + /* Currently, this event will never happen */ break; } case BTA_GATTC_CANCEL_OPEN_EVT: { - BTC_GATTC_CB_TO_APP(ESP_GATTC_CANCEL_OPEN_EVT, NULL); + /* Currently, this event will never happen */ break; } case BTA_GATTC_CONGEST_EVT: { tBTA_GATTC_CONGEST *congest = &arg->congest; - param.congest.conn_id = congest->conn_id; + + gattc_if = BTC_GATT_GET_GATT_IF(congest->conn_id); + param.congest.conn_id = BTC_GATT_GET_CONN_ID(congest->conn_id); param.congest.congested = (congest->congested == TRUE) ? true : false; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CONGEST_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_CONGEST_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SRVC_CHG_EVT: { memcpy(param.srvc_chg.remote_bda, arg->remote_bda, sizeof(esp_bd_addr_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_SRVC_CHG_EVT, ¶m); + BTC_GATTC_CB_TO_APP(ESP_GATTC_SRVC_CHG_EVT, ESP_GATT_IF_NONE, ¶m); break; } default: 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 8ef1e9aefd1..c7afabfa2a6 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -23,7 +23,7 @@ #include "esp_gatts_api.h" -#define BTC_GATTS_CB_TO_APP(_event, _param) ((esp_profile_cb_t)btc_profile_cb_get(BTC_PID_GATTS))(_event, _param) +#define BTC_GATTS_CB_TO_APP(event, gatts_if, param) ((esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS))((event), (gatts_if), (param)) #define A2C_GATTS_EVT(_bta_event) (_bta_event) //BTA TO BTC EVT #define C2A_GATTS_EVT(_btc_event) (_btc_event) //BTC TO BTA EVT @@ -171,12 +171,12 @@ void btc_gatts_call_handler(btc_msg_t *msg) break; } case BTC_GATTS_ACT_APP_UNREGISTER: - BTA_GATTS_AppDeregister(arg->app_unreg.gatt_if); + BTA_GATTS_AppDeregister(arg->app_unreg.gatts_if); break; case BTC_GATTS_ACT_CREATE_SERVICE: { tBTA_GATT_SRVC_ID srvc_id; btc_to_bta_srvc_id(&srvc_id, &arg->create_srvc.service_id); - BTA_GATTS_CreateService(arg->create_srvc.gatt_if, &srvc_id.id.uuid, + BTA_GATTS_CreateService(arg->create_srvc.gatts_if, &srvc_id.id.uuid, srvc_id.id.inst_id, arg->create_srvc.num_handle, srvc_id.is_primary); break; @@ -227,7 +227,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) } param.rsp.status = 0; - BTC_GATTS_CB_TO_APP(ESP_GATTS_RESPONSE_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m); break; } case BTC_GATTS_ACT_OPEN: { @@ -252,7 +252,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) transport = BTA_GATT_TRANSPORT_LE; // Connect! - BTA_GATTS_Open(arg->open.gatt_if, arg->open.remote_bda, + BTA_GATTS_Open(arg->open.gatts_if, arg->open.remote_bda, arg->open.is_direct, transport); break; } @@ -277,34 +277,38 @@ void btc_gatts_call_handler(btc_msg_t *msg) void btc_gatts_cb_handler(btc_msg_t *msg) { esp_ble_gatts_cb_param_t param; - tBTA_GATTS *p_data = (tBTA_GATTS *)msg->arg; + esp_gatt_if_t gatts_if; switch (msg->act) { case BTA_GATTS_REG_EVT: { + gatts_if = p_data->reg_oper.server_if; param.reg.status = p_data->reg_oper.status; - param.reg.gatt_if = p_data->reg_oper.server_if; param.reg.app_id = p_data->reg_oper.uuid.uu.uuid16; - BTC_GATTS_CB_TO_APP(ESP_GATTS_REG_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_REG_EVT, gatts_if, ¶m); break; } - case BTA_GATTS_DEREG_EVT: - // do nothing + case BTA_GATTS_DEREG_EVT: { + gatts_if = p_data->reg_oper.server_if; + BTC_GATTS_CB_TO_APP(ESP_GATTS_UNREG_EVT, gatts_if, NULL); break; + } case BTA_GATTS_READ_EVT: { - param.read.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.read.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.read.trans_id = p_data->req_data.trans_id; memcpy(param.read.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); param.read.handle = p_data->req_data.p_data->read_req.handle, - param.read.offset = p_data->req_data.p_data->read_req.offset, - param.read.is_long = p_data->req_data.p_data->read_req.is_long, + param.read.offset = p_data->req_data.p_data->read_req.offset, + param.read.is_long = p_data->req_data.p_data->read_req.is_long, - BTC_GATTS_CB_TO_APP(ESP_GATTS_READ_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_READ_EVT, gatts_if, ¶m); break; } case BTA_GATTS_WRITE_EVT: { - param.write.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.write.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.write.trans_id = p_data->req_data.trans_id; memcpy(param.write.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); param.write.handle = p_data->req_data.p_data->write_req.handle; @@ -314,103 +318,104 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.write.len = p_data->req_data.p_data->write_req.len; param.write.value = p_data->req_data.p_data->write_req.value; - BTC_GATTS_CB_TO_APP(ESP_GATTS_WRITE_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_WRITE_EVT, gatts_if, ¶m); break; } case BTA_GATTS_EXEC_WRITE_EVT: { - param.exec_write.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.exec_write.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.exec_write.trans_id = p_data->req_data.trans_id; memcpy(param.exec_write.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); param.exec_write.exec_write_flag = p_data->req_data.p_data->exec_write; - BTC_GATTS_CB_TO_APP(ESP_GATTS_EXEC_WRITE_EVT, ¶m); - + BTC_GATTS_CB_TO_APP(ESP_GATTS_EXEC_WRITE_EVT, gatts_if, ¶m); break; } case BTA_GATTS_MTU_EVT: - param.mtu.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.mtu.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.mtu.mtu = p_data->req_data.p_data->mtu; - BTC_GATTS_CB_TO_APP(ESP_GATTS_MTU_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_MTU_EVT, gatts_if, ¶m); break; case BTA_GATTS_CONF_EVT: - param.conf.conn_id = p_data->req_data.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); + param.conf.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.conf.status = p_data->req_data.status; - BTC_GATTS_CB_TO_APP(ESP_GATTS_CONF_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_CONF_EVT, gatts_if, ¶m); break; case BTA_GATTS_CREATE_EVT: + gatts_if = p_data->create.server_if; param.create.status = p_data->create.status; - param.create.gatt_if = p_data->create.server_if; param.create.service_handle = p_data->create.service_id; param.create.service_id.is_primary = p_data->create.is_primary; param.create.service_id.id.inst_id = p_data->create.svc_instance; bta_to_btc_uuid(¶m.create.service_id.id.uuid, &p_data->create.uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_CREATE_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_CREATE_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_INCL_SRVC_EVT: + gatts_if = p_data->add_result.server_if; param.add_incl_srvc.status = p_data->add_result.status; - param.add_incl_srvc.gatt_if = p_data->add_result.server_if; param.add_incl_srvc.attr_handle = p_data->add_result.attr_id; param.add_incl_srvc.service_handle = p_data->add_result.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_INCL_SRVC_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_INCL_SRVC_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_CHAR_EVT: + gatts_if = p_data->add_result.server_if; param.add_char.status = p_data->add_result.status; - param.add_char.gatt_if = p_data->add_result.server_if; param.add_char.attr_handle = p_data->add_result.attr_id; param.add_char.service_handle = p_data->add_result.service_id; bta_to_btc_uuid(¶m.add_char.char_uuid, &p_data->add_result.char_uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_CHAR_DESCR_EVT: + gatts_if = p_data->add_result.server_if; param.add_char_descr.status = p_data->add_result.status; - param.add_char_descr.gatt_if = p_data->add_result.server_if; param.add_char_descr.attr_handle = p_data->add_result.attr_id; param.add_char_descr.service_handle = p_data->add_result.service_id; bta_to_btc_uuid(¶m.add_char_descr.char_uuid, &p_data->add_result.char_uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_DESCR_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_DESCR_EVT, gatts_if, ¶m); break; case BTA_GATTS_DELELTE_EVT: + gatts_if = p_data->srvc_oper.server_if; param.del.status = p_data->srvc_oper.status; - param.del.gatt_if = p_data->srvc_oper.server_if; param.del.service_handle = p_data->srvc_oper.service_id; - - BTC_GATTS_CB_TO_APP(ESP_GATTS_DELETE_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_DELETE_EVT, gatts_if, ¶m); break; case BTA_GATTS_START_EVT: + gatts_if = p_data->srvc_oper.server_if; param.start.status = p_data->srvc_oper.status; - param.start.gatt_if = p_data->srvc_oper.server_if; param.start.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_START_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_START_EVT, gatts_if, ¶m); break; case BTA_GATTS_STOP_EVT: + gatts_if = p_data->srvc_oper.server_if; param.stop.status = p_data->srvc_oper.status; - param.stop.gatt_if = p_data->srvc_oper.server_if; param.stop.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_STOP_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_STOP_EVT, gatts_if, ¶m); break; case BTA_GATTS_CONNECT_EVT: - param.connect.conn_id = p_data->conn.conn_id; - param.connect.gatt_if = p_data->conn.server_if; + 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, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_CONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_DISCONNECT_EVT: - param.connect.conn_id = p_data->conn.conn_id; - param.connect.gatt_if = p_data->conn.server_if; - param.connect.is_connected = false; - memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); + 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; + memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); - BTC_GATTS_CB_TO_APP(ESP_GATTS_DISCONNECT_EVT, ¶m); + BTC_GATTS_CB_TO_APP(ESP_GATTS_DISCONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_OPEN_EVT: // do nothing @@ -422,8 +427,10 @@ void btc_gatts_cb_handler(btc_msg_t *msg) // do nothing break; case BTA_GATTS_CONGEST_EVT: - param.congest.conn_id = p_data->congest.conn_id; + gatts_if = BTC_GATT_GET_GATT_IF(p_data->congest.conn_id); + param.congest.conn_id = BTC_GATT_GET_CONN_ID(p_data->congest.conn_id); param.congest.congested = p_data->congest.congested; + BTC_GATTS_CB_TO_APP(ESP_GATTS_CONGEST_EVT, gatts_if, ¶m); break; default: // do nothing diff --git a/components/bt/bluedroid/btc/profile/std/gatt/include/btc_gatt_util.h b/components/bt/bluedroid/btc/profile/std/include/btc_gatt_util.h similarity index 80% rename from components/bt/bluedroid/btc/profile/std/gatt/include/btc_gatt_util.h rename to components/bt/bluedroid/btc/profile/std/include/btc_gatt_util.h index 540e118d594..99083f74f92 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/include/btc_gatt_util.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gatt_util.h @@ -21,6 +21,10 @@ #include "esp_gatt_defs.h" #include "esp_gattc_api.h" +#define BTC_GATT_CREATE_CONN_ID(gatt_if, conn_id) ((uint16_t) ((((uint8_t)(conn_id)) << 8) | ((uint8_t)(gatt_if)))) +#define BTC_GATT_GET_CONN_ID(conn_id) (((uint16_t)(conn_id)) >> 8) +#define BTC_GATT_GET_GATT_IF(conn_id) ((uint8_t)(conn_id)) + void btc128_to_bta_uuid(tBT_UUID *p_dest, uint8_t *p_src); void btc_to_bta_uuid(tBT_UUID *p_dest, esp_bt_uuid_t *p_src); void btc_to_bta_gatt_id(tBTA_GATT_ID *p_dest, esp_gatt_id_t *p_src); @@ -31,6 +35,6 @@ void bta_to_btc_uuid(esp_bt_uuid_t *p_dest, tBT_UUID *p_src); void bta_to_btc_gatt_id(esp_gatt_id_t *p_dest, tBTA_GATT_ID *p_src); void bta_to_btc_srvc_id(esp_gatt_srvc_id_t *p_dest, tBTA_GATT_SRVC_ID *p_src); -uint16_t set_read_value(esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src); +uint16_t set_read_value(uint8_t *gattc_if, esp_ble_gattc_cb_param_t *p_dest, tBTA_GATTC_READ *p_src); #endif /* __BTC_GATT_UTIL_H__*/ diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h b/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h index 4bca4ae5ab0..aef84189570 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gattc.h @@ -51,11 +51,11 @@ typedef union { } app_reg; //BTC_GATTC_ACT_APP_UNREGISTER, struct app_unreg_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; } app_unreg; //BTC_GATTC_ACT_OPEN, struct open_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; esp_bd_addr_t remote_bda; bool is_direct; } open; @@ -162,14 +162,14 @@ typedef union { } exec_write; //BTC_GATTC_ACT_REG_FOR_NOTIFY, struct reg_for_notify_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; esp_bd_addr_t remote_bda; esp_gatt_srvc_id_t service_id; esp_gatt_id_t char_id; } reg_for_notify; //BTC_GATTC_ACT_UNREG_FOR_NOTIFY struct unreg_for_notify_arg { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gattc_if; esp_bd_addr_t remote_bda; esp_gatt_srvc_id_t service_id; esp_gatt_id_t char_id; diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h index 0ba0f8869ab..4ad276f3eca 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h @@ -44,11 +44,11 @@ typedef union { } app_reg; //BTC_GATTS_ACT_APP_UNREGISTER, struct app_unreg_args { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gatts_if; } app_unreg; //BTC_GATTS_ACT_CREATE_SERVICE, struct create_srvc_args { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gatts_if; esp_gatt_srvc_id_t service_id; uint16_t num_handle; } create_srvc; @@ -99,7 +99,7 @@ typedef union { } send_rsp; //BTC_GATTS_ACT_OPEN, struct open_args { - esp_gatt_if_t gatt_if; + esp_gatt_if_t gatts_if; esp_bd_addr_t remote_bda; bool is_direct; } open; diff --git a/examples/12_blufi/components/blufi/blufi.c b/examples/12_blufi/components/blufi/blufi.c index 5a1c543b50c..7d61fca9284 100644 --- a/examples/12_blufi/components/blufi/blufi.c +++ b/examples/12_blufi/components/blufi/blufi.c @@ -69,7 +69,7 @@ static void blufi_data_recv(uint8_t *data, int len) } -static void blufi_callback(uint32_t event, void *param) +static void blufi_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) { /* actually, should post to blufi_task handle the procedure, * now, as a demo, we do simplely */ @@ -90,11 +90,6 @@ static void blufi_callback(uint32_t event, void *param) static esp_err_t blufi_startup_in_blufi_task(void *arg) { - /*set connectable,discoverable, pairable and paired only modes of local device*/ - tBTA_DM_DISC disc_mode = BTA_DM_BLE_GENERAL_DISCOVERABLE; - tBTA_DM_CONN conn_mode = BTA_DM_BLE_CONNECTABLE; - BTA_DmSetVisibility(disc_mode, conn_mode, (uint8_t)BTA_DM_NON_PAIRABLE, (uint8_t)BTA_DM_CONN_ALL); - esp_blufi_register_callback(blufi_callback); esp_blufi_profile_init(); @@ -111,8 +106,6 @@ esp_err_t blufi_enable(void *arg) { esp_err_t err; - BTM_SetTraceLevel(BT_TRACE_LEVEL_ERROR); - err = esp_enable_bluetooth(); if (err) { LOG_ERROR("%s failed\n", __func__); diff --git a/examples/14_gatt_server/main/gatts_demo.c b/examples/14_gatt_server/main/gatts_demo.c index 48576d362f1..64e84fd3782 100644 --- a/examples/14_gatt_server/main/gatts_demo.c +++ b/examples/14_gatt_server/main/gatts_demo.c @@ -2,7 +2,7 @@ // // 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 +// You may obtain A copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // @@ -30,6 +30,9 @@ #include "esp_bt_main.h" #include "esp_bt_main.h" +///Declare the static function +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *parama); + #define GATTS_SERVICE_UUID_TEST 0x00FF #define GATTS_CHAR_UUID_TEST 0xFF01 #define GATTS_DESCR_UUID_TEST 0x3333 @@ -74,8 +77,13 @@ static esp_ble_adv_params_t test_adv_params = { .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; -struct gatts_test_inst { - uint16_t gatt_if; +#define PROFILE_NUM 2 +#define PROFILE_A_APP_ID 0 +#define PROFILE_B_APP_ID 1 + +struct gatts_profile_inst { + esp_gatts_cb_t gatts_cb; + uint16_t gatts_if; uint16_t app_id; uint16_t conn_id; uint16_t service_handle; @@ -87,9 +95,20 @@ struct gatts_test_inst { uint16_t descr_handle; esp_bt_uuid_t descr_uuid; }; -static struct gatts_test_inst gl_test; -static void gap_event_handler(uint32_t event, void *param) +/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ +static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gatts_cb = gatts_profile_a_event_handler, + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, + [PROFILE_B_APP_ID] = { + .gatts_cb = NULL, /* This demo does not implement, similar as profile A */ + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { LOG_ERROR("GAP_EVT, event %d\n", event); @@ -102,42 +121,42 @@ static void gap_event_handler(uint32_t event, void *param) } } -static void gatts_event_handler(uint32_t event, void *param) -{ - esp_ble_gatts_cb_param_t *p = (esp_ble_gatts_cb_param_t *)param; - +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { switch (event) { case ESP_GATTS_REG_EVT: - LOG_INFO("REGISTER_APP_EVT, status %d, gatt_if %d, app_id %d\n", p->reg.status, p->reg.gatt_if, p->reg.app_id); - gl_test.gatt_if = p->reg.gatt_if; - gl_test.service_id.is_primary = true; - gl_test.service_id.id.inst_id = 0x00; - gl_test.service_id.id.uuid.len = ESP_UUID_LEN_16; - gl_test.service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST; + LOG_INFO("REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; + 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; + LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gap_set_device_name(TEST_DEVICE_NAME); + LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gap_config_adv_data(&test_adv_data); - esp_ble_gatts_create_service(gl_test.gatt_if, &gl_test.service_id, GATTS_NUM_HANDLE_TEST); + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST); + LOG_INFO("%s %d\n", __func__, __LINE__); break; case ESP_GATTS_READ_EVT: { - LOG_INFO("GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", p->read.conn_id, p->read.trans_id, p->read.handle); + LOG_INFO("GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); esp_gatt_rsp_t rsp; memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); - rsp.attr_value.handle = p->read.handle; + rsp.attr_value.handle = param->read.handle; rsp.attr_value.len = 4; rsp.attr_value.value[0] = 0xde; rsp.attr_value.value[1] = 0xed; rsp.attr_value.value[2] = 0xbe; rsp.attr_value.value[3] = 0xef; - esp_ble_gatts_send_response(p->read.conn_id, p->read.trans_id, + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); break; } case ESP_GATTS_WRITE_EVT: { - LOG_INFO("GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", p->write.conn_id, p->write.trans_id, p->write.handle); - LOG_INFO("GATT_WRITE_EVT, value len %d, value %08x\n", p->write.len, *(uint32_t *)p->write.value); - esp_ble_gatts_send_response(p->write.conn_id, p->write.trans_id, ESP_GATT_OK, NULL); + LOG_INFO("GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + LOG_INFO("GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); break; } case ESP_GATTS_EXEC_WRITE_EVT: @@ -146,48 +165,48 @@ static void gatts_event_handler(uint32_t event, void *param) case ESP_GATTS_UNREG_EVT: break; case ESP_GATTS_CREATE_EVT: - LOG_INFO("CREATE_SERVICE_EVT, status %d, gatt_if %d, service_handle %d\n", p->create.status, p->create.gatt_if, p->create.service_handle); - gl_test.service_handle = p->create.service_handle; - gl_test.char_uuid.len = ESP_UUID_LEN_16; - gl_test.char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST; + LOG_INFO("CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST; - esp_ble_gatts_start_service(gl_test.service_handle); + esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle); - esp_ble_gatts_add_char(gl_test.service_handle, &gl_test.char_uuid, + esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY); break; case ESP_GATTS_ADD_INCL_SRVC_EVT: break; case ESP_GATTS_ADD_CHAR_EVT: - LOG_INFO("ADD_CHAR_EVT, status %d, gatt_if %d, attr_handle %d, service_handle %d\n", - p->add_char.status, p->add_char.gatt_if, p->add_char.attr_handle, p->add_char.service_handle); + LOG_INFO("ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); - gl_test.char_handle = p->add_char.attr_handle; - gl_test.descr_uuid.len = ESP_UUID_LEN_16; - gl_test.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; - esp_ble_gatts_add_char_descr(gl_test.service_handle, &gl_test.descr_uuid, + gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); break; case ESP_GATTS_ADD_CHAR_DESCR_EVT: - LOG_INFO("ADD_DESCR_EVT, status %d, gatt_if %d, attr_handle %d, service_handle %d\n", - p->add_char.status, p->add_char.gatt_if, p->add_char.attr_handle, p->add_char.service_handle); + LOG_INFO("ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); break; case ESP_GATTS_DELETE_EVT: break; case ESP_GATTS_START_EVT: - LOG_INFO("SERVICE_START_EVT, status %d, gatt_if %d, service_handle %d\n", - p->start.status, p->start.gatt_if, p->start.service_handle); + LOG_INFO("SERVICE_START_EVT, status %d, service_handle %d\n", + param->start.status, param->start.service_handle); break; case ESP_GATTS_STOP_EVT: break; case ESP_GATTS_CONNECT_EVT: - LOG_INFO("SERVICE_START_EVT, conn_id %d, gatt_if %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", - p->connect.conn_id, p->connect.gatt_if, - p->connect.remote_bda[0], p->connect.remote_bda[1], p->connect.remote_bda[2], - p->connect.remote_bda[3], p->connect.remote_bda[4], p->connect.remote_bda[5], - p->connect.is_connected); - gl_test.conn_id = p->connect.conn_id; + LOG_INFO("SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", + 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); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; break; case ESP_GATTS_DISCONNECT_EVT: case ESP_GATTS_OPEN_EVT: @@ -200,6 +219,37 @@ static void gatts_event_handler(uint32_t event, void *param) } } +static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + LOG_INFO("EVT %d, gatts if %d\n", event, gatts_if); + + /* If event is register event, store the gatts_if for each profile */ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gl_profile_tab[param->reg.app_id].gatts_if = gatts_if; + } else { + LOG_INFO("Reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; + } + } + + /* If the gatts_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == gl_profile_tab[idx].gatts_if) { + if (gl_profile_tab[idx].gatts_cb) { + gl_profile_tab[idx].gatts_cb(event, gatts_if, param); + } + } + } + } while (0); +} + void app_main() { esp_err_t ret; @@ -219,7 +269,7 @@ void app_main() esp_ble_gatts_register_callback(gatts_event_handler); esp_ble_gap_register_callback(gap_event_handler); - esp_ble_gatts_app_register(GATTS_SERVICE_UUID_TEST); + esp_ble_gatts_app_register(PROFILE_A_APP_ID); return; } diff --git a/examples/15_gatt_client/main/gattc_demo.c b/examples/15_gatt_client/main/gattc_demo.c index 3dc6af6d756..f1e7c311c06 100644 --- a/examples/15_gatt_client/main/gattc_demo.c +++ b/examples/15_gatt_client/main/gattc_demo.c @@ -37,16 +37,17 @@ #include "esp_gatt_defs.h" #include "esp_bt_main.h" +///Declare static functions +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); + #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] -esp_gatt_if_t client_if; -esp_gatt_status_t status = ESP_GATT_ERROR; -bool connet = false; -uint16_t simpleClient_id = 0xEE; - -const char device_name[] = "Heart Rate"; +static bool connect = false; +static const char device_name[] = "Heart Rate"; static esp_ble_scan_params_t ble_scan_params = { .scan_type = BLE_SCAN_TYPE_ACTIVE, @@ -57,11 +58,74 @@ static esp_ble_scan_params_t ble_scan_params = { }; -static void esp_gap_cb(uint32_t event, void *param); +#define PROFILE_NUM 2 +#define PROFILE_A_APP_ID 0 +#define PROFILE_B_APP_ID 1 -static void esp_gattc_cb(uint32_t event, void *param); +struct gattc_profile_inst { + esp_gattc_cb_t gattc_cb; + uint16_t gattc_if; + uint16_t app_id; + uint16_t conn_id; +}; -static void esp_gap_cb(uint32_t event, void *param) +/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */ +static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gattc_cb = gattc_profile_a_event_handler, + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, + [PROFILE_B_APP_ID] = { + .gattc_cb = NULL, /* This demo does not implement, similar as profile A */ + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + uint16_t conn_id = 0; + esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; + + switch (event) { + case ESP_GATTC_REG_EVT: + LOG_INFO("REG_EVT\n"); + esp_ble_gap_set_scan_params(&ble_scan_params); + break; + case ESP_GATTC_OPEN_EVT: + conn_id = p_data->open.conn_id; + LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d\n", conn_id, gattc_if, p_data->open.status); + esp_ble_gattc_search_service(gattc_if, conn_id, NULL); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; + conn_id = p_data->open.conn_id; + LOG_INFO("SEARCH RES: conn_id = %x\n", conn_id); + if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { + LOG_INFO("UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { + LOG_INFO("UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { + LOG_INFO("UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], + srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], + srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], + srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], + srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], + srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); + } else { + LOG_ERROR("UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: + conn_id = p_data->search_cmpl.conn_id; + LOG_INFO("SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); + break; + default: + break; + } +} + +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { uint8_t *adv_name = NULL; uint8_t adv_name_len = 0; @@ -94,12 +158,12 @@ static void esp_gap_cb(uint32_t event, void *param) if (adv_name != NULL) { if (strcmp((char *)adv_name, device_name) == 0) { - LOG_INFO("the name eque to Heart Rate.\n"); - if (status == ESP_GATT_OK && connet == false) { - connet = true; - LOG_INFO("Connet to the remote device.\n"); + LOG_INFO("the name equal to Heart Rate\n"); + if (connect == false) { + connect = true; + LOG_INFO("Connect to the remote device.\n"); esp_ble_gap_stop_scanning(); - esp_ble_gattc_open(client_if, scan_result->scan_rst.bda, true); + esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, scan_result->scan_rst.bda, true); } } } @@ -116,55 +180,41 @@ static void esp_gap_cb(uint32_t event, void *param) } } - -static void esp_gattc_cb(uint32_t event, void *param) +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - uint16_t conn_id = 0; - esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; + LOG_INFO("EVT %d, gattc if %d\n", event, gattc_if); - LOG_INFO("esp_gattc_cb, event = %x\n", event); - switch (event) { - case ESP_GATTC_REG_EVT: - status = p_data->reg.status; - client_if = p_data->reg.gatt_if; - LOG_INFO("status = %x, client_if = %x\n", status, client_if); - break; - case ESP_GATTC_OPEN_EVT: - conn_id = p_data->open.conn_id; - LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d\n", conn_id, p_data->open.gatt_if, p_data->open.status); - esp_ble_gattc_search_service(conn_id, NULL); - break; - case ESP_GATTC_SEARCH_RES_EVT: { - esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; - conn_id = p_data->open.conn_id; - LOG_INFO("SEARCH RES: conn_id = %x\n", conn_id); - if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { - LOG_INFO("UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); - } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { - LOG_INFO("UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); - } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { - LOG_INFO("UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], - srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], - srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], - srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], - srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], - srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); + /* If event is register event, store the gattc_if for each profile */ + if (event == ESP_GATTC_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gl_profile_tab[param->reg.app_id].gattc_if = gattc_if; } else { - LOG_ERROR("UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + LOG_INFO("Reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; } - break; - } - case ESP_GATTC_SEARCH_CMPL_EVT: - conn_id = p_data->search_cmpl.conn_id; - LOG_INFO("SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); - break; - default: - break; } + + /* If the gattc_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) { + if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gattc_if == gl_profile_tab[idx].gattc_if) { + if (gl_profile_tab[idx].gattc_cb) { + gl_profile_tab[idx].gattc_cb(event, gattc_if, param); + } + } + } + } while (0); } void ble_client_appRegister(void) { + esp_err_t status; + LOG_INFO("register callback\n"); //register the scan callback function to the gap moudule @@ -178,8 +228,7 @@ void ble_client_appRegister(void) LOG_ERROR("gattc register error, error code = %x\n", status); return; } - esp_ble_gattc_app_register(simpleClient_id); - esp_ble_gap_set_scan_params(&ble_scan_params); + esp_ble_gattc_app_register(PROFILE_A_APP_ID); } void gattc_client_test(void) From 65fe6ab320c95da79b3311ea92006c094399a481 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Mon, 19 Dec 2016 17:12:43 +0800 Subject: [PATCH 003/167] component/bt : modify demo for new api 1. modify GATT Client demo 2. modify GATT Server demo 3. fix a register notify bug --- .../btc/profile/std/gatt/btc_gattc.c | 2 +- examples/14_gatt_server/main/gatts_demo.c | 126 +++++++++++-- examples/15_gatt_client/main/gattc_demo.c | 168 +++++++++++++++++- 3 files changed, 280 insertions(+), 16 deletions(-) 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 b7df92fcfa8..9bc7b0d2bd2 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -422,7 +422,7 @@ static void btc_gattc_reg_for_notify(btc_ble_gattc_args_t *arg) memset(¶m, 0, sizeof(esp_ble_gattc_cb_param_t)); param.reg_for_notify.status = status; memcpy(¶m.reg_for_notify.srvc_id, &arg->reg_for_notify.service_id, sizeof(esp_gatt_srvc_id_t)); - memcpy(¶m.reg_for_notify.char_id, &arg->reg_for_notify.service_id, sizeof(esp_gatt_id_t)); + memcpy(¶m.reg_for_notify.char_id, &arg->reg_for_notify.char_id, sizeof(esp_gatt_id_t)); BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_FOR_NOTIFY_EVT, arg->reg_for_notify.gattc_if, ¶m); } diff --git a/examples/14_gatt_server/main/gatts_demo.c b/examples/14_gatt_server/main/gatts_demo.c index 64e84fd3782..1785835d7c8 100644 --- a/examples/14_gatt_server/main/gatts_demo.c +++ b/examples/14_gatt_server/main/gatts_demo.c @@ -31,15 +31,20 @@ #include "esp_bt_main.h" ///Declare the static function -static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *parama); +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); +static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +#define GATTS_SERVICE_UUID_TEST_A 0x00FF +#define GATTS_CHAR_UUID_TEST_A 0xFF01 +#define GATTS_DESCR_UUID_TEST_A 0x3333 +#define GATTS_NUM_HANDLE_TEST_A 4 + +#define GATTS_SERVICE_UUID_TEST_B 0x00EE +#define GATTS_CHAR_UUID_TEST_B 0xEE01 +#define GATTS_DESCR_UUID_TEST_B 0x2222 +#define GATTS_NUM_HANDLE_TEST_B 4 -#define GATTS_SERVICE_UUID_TEST 0x00FF -#define GATTS_CHAR_UUID_TEST 0xFF01 -#define GATTS_DESCR_UUID_TEST 0x3333 -#define APP_ID_TEST 0x18 -#define GATTS_NUM_HANDLE_TEST 4 #define TEST_DEVICE_NAME "ESP_GATTS_DEMO" - #define TEST_MANUFACTURER_DATA_LEN 17 static uint8_t test_service_uuid128[32] = { /* LSB <--------------------------------------------------------------------------------> MSB */ @@ -103,7 +108,7 @@ static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = { .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ }, [PROFILE_B_APP_ID] = { - .gatts_cb = NULL, /* This demo does not implement, similar as profile A */ + .gatts_cb = gatts_profile_b_event_handler, /* This demo does not implement, similar as profile A */ .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ }, }; @@ -128,7 +133,7 @@ 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.is_primary = true; gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; 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; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A; LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gap_set_device_name(TEST_DEVICE_NAME); @@ -136,7 +141,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i esp_ble_gap_config_adv_data(&test_adv_data); LOG_INFO("%s %d\n", __func__, __LINE__); - esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST); + esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A); LOG_INFO("%s %d\n", __func__, __LINE__); break; case ESP_GATTS_READ_EVT: { @@ -168,7 +173,105 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i LOG_INFO("CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; - gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A; + + esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle); + + esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + break; + case ESP_GATTS_ADD_INCL_SRVC_EVT: + break; + case ESP_GATTS_ADD_CHAR_EVT: + LOG_INFO("ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + + gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); + break; + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + LOG_INFO("ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + break; + case ESP_GATTS_DELETE_EVT: + break; + case ESP_GATTS_START_EVT: + LOG_INFO("SERVICE_START_EVT, status %d, service_handle %d\n", + param->start.status, param->start.service_handle); + break; + case ESP_GATTS_STOP_EVT: + break; + case ESP_GATTS_CONNECT_EVT: + LOG_INFO("SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", + 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); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; + break; + case ESP_GATTS_DISCONNECT_EVT: + case ESP_GATTS_OPEN_EVT: + case ESP_GATTS_CANCEL_OPEN_EVT: + case ESP_GATTS_CLOSE_EVT: + case ESP_GATTS_LISTEN_EVT: + case ESP_GATTS_CONGEST_EVT: + default: + break; + } +} + +static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { + switch (event) { + case ESP_GATTS_REG_EVT: + LOG_INFO("REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; + 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_B; + + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gap_set_device_name(TEST_DEVICE_NAME); + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gap_config_adv_data(&test_adv_data); + + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_B); + LOG_INFO("%s %d\n", __func__, __LINE__); + break; + case ESP_GATTS_READ_EVT: { + LOG_INFO("GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); + esp_gatt_rsp_t rsp; + memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.len = 4; + rsp.attr_value.value[0] = 0xde; + rsp.attr_value.value[1] = 0xed; + rsp.attr_value.value[2] = 0xbe; + rsp.attr_value.value[3] = 0xef; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_OK, &rsp); + break; + } + case ESP_GATTS_WRITE_EVT: { + LOG_INFO("GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + LOG_INFO("GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); + break; + } + case ESP_GATTS_EXEC_WRITE_EVT: + case ESP_GATTS_MTU_EVT: + case ESP_GATTS_CONF_EVT: + case ESP_GATTS_UNREG_EVT: + break; + case ESP_GATTS_CREATE_EVT: + LOG_INFO("CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_B; esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle); @@ -270,6 +373,7 @@ void app_main() esp_ble_gatts_register_callback(gatts_event_handler); esp_ble_gap_register_callback(gap_event_handler); esp_ble_gatts_app_register(PROFILE_A_APP_ID); + esp_ble_gatts_app_register(PROFILE_B_APP_ID); return; } diff --git a/examples/15_gatt_client/main/gattc_demo.c b/examples/15_gatt_client/main/gattc_demo.c index f1e7c311c06..50faaa7c98b 100644 --- a/examples/15_gatt_client/main/gattc_demo.c +++ b/examples/15_gatt_client/main/gattc_demo.c @@ -41,13 +41,32 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static esp_gatt_srvc_id_t alert_service_id = { + .id = { + .uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = 0x1811,}, + }, + .inst_id = 0, + }, + .is_primary = true, +}; + +static esp_gatt_id_t notify_descr_id = { + .uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG,}, + }, + .inst_id = 0, +}; #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] static bool connect = false; -static const char device_name[] = "Heart Rate"; +static const char device_name[] = "Alert Notification"; static esp_ble_scan_params_t ble_scan_params = { .scan_type = BLE_SCAN_TYPE_ACTIVE, @@ -67,6 +86,7 @@ struct gattc_profile_inst { uint16_t gattc_if; uint16_t app_id; uint16_t conn_id; + esp_bd_addr_t remote_bda; }; /* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */ @@ -76,7 +96,7 @@ static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ }, [PROFILE_B_APP_ID] = { - .gattc_cb = NULL, /* This demo does not implement, similar as profile A */ + .gattc_cb = gattc_profile_b_event_handler, .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ }, }; @@ -93,12 +113,21 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i break; case ESP_GATTC_OPEN_EVT: conn_id = p_data->open.conn_id; - LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d\n", conn_id, gattc_if, p_data->open.status); + + memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->open.remote_bda, sizeof(esp_bd_addr_t)); + LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); + + LOG_INFO("REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", + gl_profile_tab[PROFILE_A_APP_ID].remote_bda[0], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[1], + gl_profile_tab[PROFILE_A_APP_ID].remote_bda[2], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[3], + gl_profile_tab[PROFILE_A_APP_ID].remote_bda[4], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[5] + ); + esp_ble_gattc_search_service(gattc_if, conn_id, NULL); break; case ESP_GATTC_SEARCH_RES_EVT: { esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; - conn_id = p_data->open.conn_id; + conn_id = p_data->search_res.conn_id; LOG_INFO("SEARCH RES: conn_id = %x\n", conn_id); if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { LOG_INFO("UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); @@ -119,6 +148,135 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i case ESP_GATTC_SEARCH_CMPL_EVT: conn_id = p_data->search_cmpl.conn_id; LOG_INFO("SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, NULL); + break; + case ESP_GATTC_GET_CHAR_EVT: + if (p_data->get_char.status != ESP_GATT_OK) { + break; + } + LOG_INFO("GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); + LOG_INFO("GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); + + if (p_data->get_char.char_id.uuid.uuid.uuid16 == 0x2a46) { + LOG_INFO("register notify\n"); + esp_ble_gattc_register_for_notify(gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, &alert_service_id, &p_data->get_char.char_id); + } + + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, &p_data->get_char.char_id); + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + uint16_t notify_en = 1; + LOG_INFO("REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); + LOG_INFO("REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); + + esp_ble_gattc_write_char_descr( + gattc_if, + conn_id, + &alert_service_id, + &p_data->reg_for_notify.char_id, + ¬ify_descr_id, + sizeof(notify_en), + (uint8_t *)¬ify_en, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + break; + } + case ESP_GATTC_NOTIFY_EVT: + LOG_INFO("NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); + break; + case ESP_GATTC_WRITE_DESCR_EVT: + LOG_INFO("WRITE: status %d\n", p_data->write.status); + break; + default: + break; + } +} + +static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + uint16_t conn_id = 0; + esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; + + switch (event) { + case ESP_GATTC_REG_EVT: + LOG_INFO("REG_EVT\n"); + //esp_ble_gap_set_scan_params(&ble_scan_params); + break; + case ESP_GATTC_OPEN_EVT: + conn_id = p_data->open.conn_id; + + memcpy(gl_profile_tab[PROFILE_B_APP_ID].remote_bda, p_data->open.remote_bda, sizeof(esp_bd_addr_t)); + LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); + + LOG_INFO("REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", + gl_profile_tab[PROFILE_B_APP_ID].remote_bda[0], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[1], + gl_profile_tab[PROFILE_B_APP_ID].remote_bda[2], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[3], + gl_profile_tab[PROFILE_B_APP_ID].remote_bda[4], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[5] + ); + + esp_ble_gattc_search_service(gattc_if, conn_id, NULL); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; + conn_id = p_data->search_res.conn_id; + LOG_INFO("SEARCH RES: conn_id = %x\n", conn_id); + if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { + LOG_INFO("UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { + LOG_INFO("UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); + } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { + LOG_INFO("UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], + srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], + srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], + srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], + srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], + srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); + } else { + LOG_ERROR("UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: + conn_id = p_data->search_cmpl.conn_id; + LOG_INFO("SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, NULL); + break; + case ESP_GATTC_GET_CHAR_EVT: + if (p_data->get_char.status != ESP_GATT_OK) { + break; + } + LOG_INFO("GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); + LOG_INFO("GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); + + if (p_data->get_char.char_id.uuid.uuid.uuid16 == 0x2a46) { + LOG_INFO("register notify\n"); + esp_ble_gattc_register_for_notify(gattc_if, gl_profile_tab[PROFILE_B_APP_ID].remote_bda, &alert_service_id, &p_data->get_char.char_id); + } + + esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, &p_data->get_char.char_id); + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + uint16_t notify_en = 1; + LOG_INFO("REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); + LOG_INFO("REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); + + esp_ble_gattc_write_char_descr( + gattc_if, + conn_id, + &alert_service_id, + &p_data->reg_for_notify.char_id, + ¬ify_descr_id, + sizeof(notify_en), + (uint8_t *)¬ify_en, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + break; + } + case ESP_GATTC_NOTIFY_EVT: + LOG_INFO("NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); + break; + case ESP_GATTC_WRITE_DESCR_EVT: + LOG_INFO("WRITE: status %d\n", p_data->write.status); break; default: break; @@ -164,6 +322,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par LOG_INFO("Connect to the remote device.\n"); esp_ble_gap_stop_scanning(); esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, scan_result->scan_rst.bda, true); + esp_ble_gattc_open(gl_profile_tab[PROFILE_B_APP_ID].gattc_if, scan_result->scan_rst.bda, true); } } } @@ -229,6 +388,7 @@ void ble_client_appRegister(void) return; } esp_ble_gattc_app_register(PROFILE_A_APP_ID); + esp_ble_gattc_app_register(PROFILE_B_APP_ID); } void gattc_client_test(void) From 2cffaf9cc841cbc5d2b495a326a2ba8bd3930d1b Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 22 Dec 2016 10:17:39 +0800 Subject: [PATCH 004/167] freertos: fix dual core issue This commit fixes: 1. xTaskGetCurrentTaskHandle may return wrong TCB when current task switch to a different core 2. Idle task may have problem when it terminate the task in both core --- components/freertos/tasks.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 16cce3b967c..baaccb43e58 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -3496,26 +3496,27 @@ static void prvCheckTasksWaitingTermination( void ) /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called too often in the idle task. */ + taskENTER_CRITICAL(&xTaskQueueMutex); while( uxTasksDeleted > ( UBaseType_t ) 0U ) { - taskENTER_CRITICAL(&xTaskQueueMutex); + //taskENTER_CRITICAL(&xTaskQueueMutex); { xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); } - taskEXIT_CRITICAL(&xTaskQueueMutex); + //taskEXIT_CRITICAL(&xTaskQueueMutex); if( xListIsEmpty == pdFALSE ) { TCB_t *pxTCB; - taskENTER_CRITICAL(&xTaskQueueMutex); + //taskENTER_CRITICAL(&xTaskQueueMutex); { pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); --uxCurrentNumberOfTasks; --uxTasksDeleted; } - taskEXIT_CRITICAL(&xTaskQueueMutex); + //taskEXIT_CRITICAL(&xTaskQueueMutex); #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) { @@ -3535,7 +3536,8 @@ static void prvCheckTasksWaitingTermination( void ) { mtCOVERAGE_TEST_MARKER(); } - } + } + taskEXIT_CRITICAL(&xTaskQueueMutex); } #endif /* vTaskDelete */ } @@ -3806,10 +3808,12 @@ TCB_t *pxTCB; { TaskHandle_t xReturn; + vPortCPUAcquireMutex(&xTaskQueueMutex); /* A critical section is not required as this is not called from an interrupt and the current TCB will always be the same for any individual execution thread. */ xReturn = pxCurrentTCB[ xPortGetCoreID() ]; + vPortCPUReleaseMutex(&xTaskQueueMutex); return xReturn; } From 57817f7c53e4f4de41d5829f1a272f1e41e1406e Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 24 Nov 2016 11:46:58 +0800 Subject: [PATCH 005/167] generate test result and commit to CI-test-result: 1. config git user name before commit 2. continue committing test result for failed jobs 3. update test result repository path 4. change escape key word in branch name, use '___' to escape key word '/' in report file name --- .gitlab-ci.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7cb63be8c39..20970597e0a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -149,11 +149,12 @@ test_build_system: test_report: stage: test_report + image: espressif/esp32-ci-env only: - master - triggers tags: - - test_report + - report variables: LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test" @@ -165,12 +166,33 @@ test_report: - $LOG_PATH expire_in: 12 mos script: + # calc log path + - VER_NUM=`git rev-list HEAD | wc -l | awk '{print $1}'` + - SHA_ID=`echo $CI_BUILD_REF | cut -c 1-7` + - REVISION="${VER_NUM}_${SHA_ID}" + # replace / to _ in branch name + - ESCAPED_BRANCH_NAME=`echo $CI_BUILD_REF_NAME | sed 's/\//___/g'` + # result path and artifacts path + - RESULT_PATH="$CI_PROJECT_NAME/$ESCAPED_BRANCH_NAME/$REVISION" + - ARTIFACTS_PATH="$GITLAB_HTTP_SERVER/idf/esp-idf/builds/$CI_BUILD_ID/artifacts/browse/$CI_BUILD_REF" # clone test bench - git clone $GITLAB_SSH_SERVER/yinling/auto_test_script.git - cd auto_test_script # generate report - - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH - + - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH -r $RESULT_PATH -a $ARTIFACTS_PATH || FAIL=True + # commit to CI-test-result project + - git clone $GITLAB_SSH_SERVER/qa/CI-test-result.git + - rm -rf CI-test-result/RawData/$RESULT_PATH + - cp -R $CI_PROJECT_NAME CI-test-result/RawData + - cd CI-test-result + # config git user + - git config --global user.email "ci-test-result@espressif.com" + - git config --global user.name "ci-test-result" + # commit test result + - git add . + - git commit . -m "update test result for $CI_PROJECT_NAME/$CI_BUILD_REF_NAME/$CI_BUILD_REF, pipeline ID $CI_PIPELINE_ID" || exit 0 + - git push origin master + - test "${FAIL}" = "True" && exit 1 push_master_to_github: before_script: From 3a2fbda35c7bb0d1cbc99a59a6db32258be549f1 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 22 Dec 2016 10:51:40 +0800 Subject: [PATCH 006/167] freertos: minor change according to review comments --- components/freertos/tasks.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index baaccb43e58..f2bdf8ccb0a 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -3499,24 +3499,20 @@ static void prvCheckTasksWaitingTermination( void ) taskENTER_CRITICAL(&xTaskQueueMutex); while( uxTasksDeleted > ( UBaseType_t ) 0U ) { - //taskENTER_CRITICAL(&xTaskQueueMutex); { xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); } - //taskEXIT_CRITICAL(&xTaskQueueMutex); if( xListIsEmpty == pdFALSE ) { TCB_t *pxTCB; - //taskENTER_CRITICAL(&xTaskQueueMutex); { pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); --uxCurrentNumberOfTasks; --uxTasksDeleted; } - //taskEXIT_CRITICAL(&xTaskQueueMutex); #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) { @@ -3809,9 +3805,6 @@ TCB_t *pxTCB; TaskHandle_t xReturn; vPortCPUAcquireMutex(&xTaskQueueMutex); - /* A critical section is not required as this is not called from - an interrupt and the current TCB will always be the same for any - individual execution thread. */ xReturn = pxCurrentTCB[ xPortGetCoreID() ]; vPortCPUReleaseMutex(&xTaskQueueMutex); From 2f9772860a1ad0747b509eb41c48040fd16adaee Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:18:15 +1100 Subject: [PATCH 007/167] Examples: Add READMEs for examples which did not have them Closes github #128 https://github.com/espressif/esp-idf/issues/128 --- examples/11_rmt_nec_tx_rx/README.md | 6 ++++++ examples/13_timer_group/README.md | 3 +++ examples/16_pcnt/README.md | 14 ++++++++++++++ examples/16_pcnt/main/pcnt_test.c | 24 ++++++++++++------------ examples/19_sigmadelta/README.md | 7 +++++++ 5 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 examples/11_rmt_nec_tx_rx/README.md create mode 100644 examples/13_timer_group/README.md create mode 100644 examples/16_pcnt/README.md create mode 100644 examples/19_sigmadelta/README.md diff --git a/examples/11_rmt_nec_tx_rx/README.md b/examples/11_rmt_nec_tx_rx/README.md new file mode 100644 index 00000000000..d578600d017 --- /dev/null +++ b/examples/11_rmt_nec_tx_rx/README.md @@ -0,0 +1,6 @@ +# Example: rmt_nec_tx_rx + +This example uses the remote control (RMT) peripheral to transmit and receive codes for the NEC infrared remote protocol. + +Configuration (pin numbers, etc.) can be modified in top of the main/infrared_nec.c file. + diff --git a/examples/13_timer_group/README.md b/examples/13_timer_group/README.md new file mode 100644 index 00000000000..0be2c847a8f --- /dev/null +++ b/examples/13_timer_group/README.md @@ -0,0 +1,3 @@ +# Example: timer_group + +This example uses the timer group driver to generate timer interrupts at two specified alarm intervals. diff --git a/examples/16_pcnt/README.md b/examples/16_pcnt/README.md new file mode 100644 index 00000000000..a7019ea9e74 --- /dev/null +++ b/examples/16_pcnt/README.md @@ -0,0 +1,14 @@ +# Example: pcnt + +This example uses the pulse counter module (PCNT) to count the rising edges of pulses generated by the LED Controller module (LEDC). + +By default GPIO18 is used as output pin, GPIO4 is used as pulse input pin and GPIO5 is used as control input pin. This configuration (pin numbers, etc.) can be modified in top of the main/pcnt_test.c file. + +* Open serial port to view the message printed on your screen +* To do this test, you should connect GPIO18 with GPIO4 +* GPIO5 is the control signal, you can leave it floating with internal pulled up, or connect it to ground. HIGH = Count increases, LOW = count decreases. +* An interrupt is configured to trigger when the count reaches threshold values. +* The counter will reset when it reaches the limit values. + + + diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index b8489ecb2f4..6089c8edd63 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -36,14 +36,14 @@ * When counter value reaches thresh1 or thresh0 value, it will trigger interrupt. * When counter value reaches l_lim value or h_lim value, counter value will be reset to zero and trigger interrupt. */ -#define PCNT_TEST_UNIT PCNT_UNIT_0 -#define PCNT_H_LIM_VAL (10) -#define PCNT_L_LIM_VAL (-10) -#define PCNT_THRESH1_VAL (5) -#define PCNT_THRESH0_VAL (-5) -#define PCNT_INPUT_SIG_IO (4) -#define PCNT_INPUT_CTRL_IO (5) -#define LEDC_OUPUT_IO (18) +#define PCNT_TEST_UNIT PCNT_UNIT_0 +#define PCNT_H_LIM_VAL 10 +#define PCNT_L_LIM_VAL -10 +#define PCNT_THRESH1_VAL 5 +#define PCNT_THRESH0_VAL -5 +#define PCNT_INPUT_SIG_IO 4 /* Pulse Input GPIO */ +#define PCNT_INPUT_CTRL_IO 5 /* Control GPIO HIGH=count up, LOW=count down */ +#define LEDC_OUTPUT_IO 18 /* Output GPIO */ xQueueHandle pcnt_evt_queue; /*A queue to handle pulse counter event*/ @@ -96,8 +96,8 @@ void IRAM_ATTR pcnt_intr_handler(void* arg) static void ledc_init(void) { ledc_channel_config_t ledc_channel; - /*use GPIO18 as output pin*/ - ledc_channel.gpio_num = LEDC_OUPUT_IO; + /*use LEDC_OUTPUT_IO as output pin*/ + ledc_channel.gpio_num = LEDC_OUTPUT_IO; /*LEDC high speed mode */ ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; /*use LEDC channel 1*/ @@ -125,9 +125,9 @@ static void ledc_init(void) static void pcnt_init(void) { pcnt_config_t pcnt_config = { - /*Set GPIO4 as pulse input gpio */ + /*Set PCNT_INPUT_SIG_IO as pulse input gpio */ .pulse_gpio_num = PCNT_INPUT_SIG_IO, - /*set gpio5 as control gpio */ + /*set PCNT_INPUT_CTRL_IO as control gpio */ .ctrl_gpio_num = PCNT_INPUT_CTRL_IO, /*Choose channel 0 */ .channel = PCNT_CHANNEL_0, diff --git a/examples/19_sigmadelta/README.md b/examples/19_sigmadelta/README.md new file mode 100644 index 00000000000..e8cac2c87b8 --- /dev/null +++ b/examples/19_sigmadelta/README.md @@ -0,0 +1,7 @@ +# Example: sigma_delta modulation + +This example uses the sigma_delta output modulation driver to generate modulated output on a GPIO. + +By default the GPIO output is 4, however you can edit this in the `sigmadelta_init()` function inside `main/sigmadelta_test.c`. + +If you connect an LED to the output GPIO, you will see it blinking slowly. From ab5915ff8b8cd9765b7814fe8f3acbe5912e03e5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:28:08 +1100 Subject: [PATCH 008/167] spi_flash: Standardise argument types & names used for flash offsets Closes github #88: https://github.com/espressif/esp-idf/issues/88 --- components/spi_flash/flash_mmap.c | 2 +- components/spi_flash/include/esp_spi_flash.h | 26 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 2165a784d19..15f75f36344 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -77,7 +77,7 @@ static void IRAM_ATTR spi_flash_mmap_init() } } -esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, +esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, const void** out_ptr, spi_flash_mmap_handle_t* out_handle) { esp_err_t ret; diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 91675088ae6..bb3ec39b45e 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -78,13 +78,13 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size); * @note If source address is in DROM, this function will return * ESP_ERR_INVALID_ARG. * - * @param dest destination address in Flash. Must be a multiple of 4 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 4 bytes. + * @param dest_addr destination address in Flash. Must be a multiple of 4 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 4 bytes. * * @return esp_err_t */ -esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); +esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size); /** @@ -97,24 +97,24 @@ esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); * @note If source address is in DROM, this function will return * ESP_ERR_INVALID_ARG. * - * @param dest destination address in Flash. Must be a multiple of 32 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 32 bytes. + * @param dest_addr destination address in Flash. Must be a multiple of 32 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 32 bytes. * * @return esp_err_t */ -esp_err_t spi_flash_write_encrypted(size_t dest, const void *src, size_t size); +esp_err_t spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size); /** * @brief Read data from Flash. * - * @param src source address of the data in Flash. - * @param dest pointer to the destination buffer - * @param size length of data + * @param src_addr source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data * * @return esp_err_t */ -esp_err_t spi_flash_read(size_t src, void *dest, size_t size); +esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call @@ -149,7 +149,7 @@ typedef uint32_t spi_flash_mmap_handle_t; * * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated */ -esp_err_t spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, +esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, const void** out_ptr, spi_flash_mmap_handle_t* out_handle); /** From 99f4c697ee16d69f1f02ce6e14e64a4e35fc2651 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 22 Dec 2016 13:37:07 +0800 Subject: [PATCH 009/167] freertos: enable dual core by default --- components/freertos/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index f03da6bc01a..b9db00e50ba 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -3,7 +3,7 @@ menu "FreeRTOS" # This is actually also handled in the ESP32 startup code, not only in FreeRTOS. config FREERTOS_UNICORE bool "Run FreeRTOS only on first core" - default y + default n help This version of FreeRTOS normally takes control of all cores of the CPU. Select this if you only want to start it on the first core. From 794f7dd2940b200bcaa052a78b1f0a4adbf38f62 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 22 Dec 2016 12:08:15 +0800 Subject: [PATCH 010/167] bugfix: uart event mismatch 1. Fix bug of uart frame error and parity error interrupt mismatch in driver code, which will cause the corresponding interrupt can not be cleared correctly, and will finally cause a interrupt watch dog. 2. Add gpio pull-up for rx pin and cts pin. --- components/driver/uart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index e85c54d8c4b..b9ebc4b5f0f 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -387,6 +387,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r if(rx_io_num >= 0) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rx_io_num], PIN_FUNC_GPIO); + gpio_set_pull_mode(rx_io_num, GPIO_PULLUP_ONLY); gpio_set_direction(rx_io_num, GPIO_MODE_INPUT); gpio_matrix_in(rx_io_num, rx_sig, 0); } @@ -397,6 +398,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r } if(cts_io_num >= 0) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[cts_io_num], PIN_FUNC_GPIO); + gpio_set_pull_mode(cts_io_num, GPIO_PULLUP_ONLY); gpio_set_direction(cts_io_num, GPIO_MODE_INPUT); gpio_matrix_in(cts_io_num, cts_sig, 0); } @@ -639,10 +641,10 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.brk_det = 1; uart_event.type = UART_BREAK; } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { - uart_reg->int_clr.parity_err = 1; + uart_reg->int_clr.frm_err = 1; uart_event.type = UART_FRAME_ERR; } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { - uart_reg->int_clr.frm_err = 1; + uart_reg->int_clr.parity_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); From e8b194d5e55ed5df252446ab5682f8921022db01 Mon Sep 17 00:00:00 2001 From: Malte Janduda Date: Thu, 22 Sep 2016 01:50:53 +0200 Subject: [PATCH 011/167] provide list of packages for homebrew --- docs/macos-setup.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/macos-setup.rst b/docs/macos-setup.rst index 53c6fe54c8d..67a1fa9906d 100644 --- a/docs/macos-setup.rst +++ b/docs/macos-setup.rst @@ -66,7 +66,9 @@ In any case, here are the steps to compile the toolchain yourself. sudo port install gsed gawk binutils gperf grep gettext ncurses - - with homebrew (*TODO: provide list of packages for homebrew*) + - with homebrew + + brew install gnu-sed gawk binutils gperf grep gettext ncurses Create a case-sensitive filesystem image:: From 15651b5923b61f4e113db1790f3d9a4e5fb73cdb Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Mon, 26 Dec 2016 15:47:20 +0800 Subject: [PATCH 012/167] lwip: add ip frag and reassembly option in menuconfig --- components/lwip/Kconfig | 13 +++++++++++++ components/lwip/include/lwip/port/lwipopts.h | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 2e7e31a8a9f..9e2b0316747 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -49,6 +49,19 @@ config LWIP_DHCP_MAX_NTP_SERVERS First argument of sntp_setserver/sntp_setservername functions is limited to this value. +config LWIP_IP_FRAG + bool "Enable fragment outgoing IP packets" + default 0 + help + Enabling this option allows fragmenting outgoing IP packets if their size + exceeds MTU. + +config LWIP_IP_REASSEMBLY + bool "Enable reassembly incoming fragmented IP packets" + default 0 + help + Enabling this option allows reassemblying incoming fragmented IP packets. + endmenu diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 8612eb11b0f..5000d63ba94 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -154,14 +154,14 @@ * this option does not affect outgoing packet sizes, which can be controlled * via IP_FRAG. */ -#define IP_REASSEMBLY 0 +#define IP_REASSEMBLY CONFIG_LWIP_IP_REASSEMBLY /** * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note * that this option does not affect incoming packet sizes, which can be * controlled via IP_REASSEMBLY. */ -#define IP_FRAG 0 +#define IP_FRAG CONFIG_LWIP_IP_FRAG /** * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) From d2e58193d235bcde15b1124af63358300412d31c Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 26 Dec 2016 19:04:41 +0800 Subject: [PATCH 013/167] add more protection for per-core data --- components/freertos/Kconfig | 6 ++- .../freertos/include/freertos/portmacro.h | 4 +- components/freertos/tasks.c | 54 +++++++++++++------ 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index b9db00e50ba..859ece2c09f 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -195,7 +195,11 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE If enabled, additional debug information will be printed for recursive portMUX usage. - +config FREERTOS_INT_DISABLING_DURATION_DEBUG + bool "Debug interrupt disabling duration" + default n + help + If enabled, the longest interrupt disabling duration will be recorded. endif # FREERTOS_DEBUG_INTERNALS diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index f20a4a1e269..9c15eebf9a6 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -213,7 +213,6 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux); #define portEXIT_CRITICAL_ISR(mux) vPortCPUReleaseMutex(mux) #endif - // Cleaner and preferred solution allows nested interrupts disabling and restoring via local registers or stack. // They can be called from interrupts too. //NOT SMP-COMPATIBLE! Use only if all you want is to disable the interrupts locally! @@ -225,6 +224,9 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I #define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state) +#define portDisableINT() portENTER_CRITICAL_NESTED() +#define portEnableINT(state) portEXIT_CRITICAL_NESTED((state)) + /* * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare * *mux to compare, and if it's the same, will set *mux to set. It will return the old value diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index f2bdf8ccb0a..00df0df8716 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -369,7 +369,7 @@ PRIVILEGED_DATA static portMUX_TYPE xTickCountMutex = portMUX_INITIALIZER_UNLOCK \ /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ the same priority get an equal share of the processor time. */ \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ xPortGetCoreID() ], &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); \ + listGET_OWNER_OF_NEXT_ENTRY( xTaskGetCurrentTaskHandle(), &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); \ } /* taskSELECT_HIGHEST_PRIORITY_TASK */ /*-----------------------------------------------------------*/ @@ -398,7 +398,7 @@ PRIVILEGED_DATA static portMUX_TYPE xTickCountMutex = portMUX_INITIALIZER_UNLOCK /* Find the highest priority queue that contains ready tasks. */ \ portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ xPortGetCoreID() ], &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + listGET_OWNER_OF_NEXT_ENTRY( xTaskGetCurrentTaskHandle(), &( pxReadyTasksLists[ uxTopPriority ] ) ); \ } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ /*-----------------------------------------------------------*/ @@ -456,7 +456,7 @@ count overflows. */ * see if the parameter is NULL and returns a pointer to the appropriate TCB. */ /* ToDo: See if this still works for multicore. */ -#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ] : ( TCB_t * ) ( pxHandle ) ) +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) xTaskGetCurrentTaskHandle() : ( TCB_t * ) ( pxHandle ) ) /* The item value of the event list item is normally used to hold the priority of the task to which it belongs (coded to allow it to be held in reverse @@ -631,9 +631,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode */ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority ) { + TCB_t *curTCB = xTaskGetCurrentTaskHandle(); BaseType_t i; + if (xCoreID != tskNO_AFFINITY) { - if ( pxCurrentTCB[ xCoreID ]->uxPriority < uxPriority ) { + if ( curTCB->uxPriority < uxPriority ) { vPortYieldOtherCore( xCoreID ); } } @@ -1039,6 +1041,7 @@ UBaseType_t x; static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) { + TCB_t *curTCB; BaseType_t i; /* Ensure interrupts don't access the task lists while the lists are being @@ -1111,6 +1114,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode portSETUP_TCB( pxNewTCB ); } + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; taskEXIT_CRITICAL(&xTaskQueueMutex); if( xSchedulerRunning != pdFALSE ) @@ -1121,17 +1125,17 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode the other processor will keep running the task it's working on, and only switch to the newer task on a timer interrupt. */ //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) + if( curTCB->uxPriority < pxNewTCB->uxPriority ) { /* Scheduler is running. If the created task is of a higher priority than an executing task then it should run now. No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. */ - if( tskCAN_RUN_HERE( xCoreID ) && pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) + if( tskCAN_RUN_HERE( xCoreID ) && curTCB->uxPriority < pxNewTCB->uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } - else if( xCoreID != xPortGetCoreID() ) { + else if( xCoreID != xPortGetCoreID() ) {//TODO taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority); } else @@ -1409,11 +1413,12 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode eTaskState eReturn; List_t *pxStateList; const TCB_t * const pxTCB = ( TCB_t * ) xTask; + TCB_t * curTCB = xTaskGetCurrentTaskHandle(); UNTESTED_FUNCTION(); configASSERT( pxTCB ); - if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) + if( pxTCB == curTCB ) { /* The task calling this function is querying its own state. */ eReturn = eRunning; @@ -1691,6 +1696,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode void vTaskSuspend( TaskHandle_t xTaskToSuspend ) { TCB_t *pxTCB; + TCB_t *curTCB; UNTESTED_FUNCTION(); taskENTER_CRITICAL(&xTaskQueueMutex); @@ -1723,10 +1729,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ); + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; } taskEXIT_CRITICAL(&xTaskQueueMutex); - if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) + if( pxTCB == curTCB ) { if( xSchedulerRunning != pdFALSE ) { @@ -2034,7 +2041,7 @@ void vTaskEndScheduler( void ) //Return global reent struct if FreeRTOS isn't running, struct _reent* __getreent() { //No lock needed because if this changes, we won't be running anymore. - TCB_t *currTask=pxCurrentTCB[ xPortGetCoreID() ]; + TCB_t *currTask=xTaskGetCurrentTaskHandle(); if (currTask==NULL) { //No task running. Return global struct. return _GLOBAL_REENT; @@ -2052,7 +2059,11 @@ void vTaskSuspendAll( void ) BaseType_t. Please read Richard Barry's reply in the following link to a post in the FreeRTOS support forum before reporting this as a bug! - http://goo.gl/wu4acr */ + unsigned state; + + state = portDisableINT(); ++uxSchedulerSuspended[ xPortGetCoreID() ]; + portEnableINT(state); } /*----------------------------------------------------------*/ @@ -2595,7 +2606,7 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are setting our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ]; + xTCB = ( TCB_t * ) xTaskGetCurrentTaskHandle(); } else { @@ -2626,7 +2637,7 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are calling our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ]; + xTCB = ( TCB_t * ) xTaskGetCurrentTaskHandle(); } else { @@ -3387,8 +3398,8 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { - pxTCB = prvGetTCBFromHandle( xTaskToSet ); taskENTER_CRITICAL(&xTaskQueueMutex); + pxTCB = prvGetTCBFromHandle( xTaskToSet ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; pxTCB->pvThreadLocalStoragePointersDelCallback[ xIndex ] = xDelCallback; taskEXIT_CRITICAL(&xTaskQueueMutex); @@ -3408,8 +3419,10 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { + taskENTER_CRITICAL(&xTaskQueueMutex); pxTCB = prvGetTCBFromHandle( xTaskToSet ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; + taskEXIT_CRITICAL(&xTaskQueueMutex); } } #endif /* configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS */ @@ -3803,10 +3816,11 @@ TCB_t *pxTCB; TaskHandle_t xTaskGetCurrentTaskHandle( void ) { TaskHandle_t xReturn; + unsigned state; - vPortCPUAcquireMutex(&xTaskQueueMutex); + state = portDisableINT(); xReturn = pxCurrentTCB[ xPortGetCoreID() ]; - vPortCPUReleaseMutex(&xTaskQueueMutex); + portEnableINT(state); return xReturn; } @@ -3832,7 +3846,9 @@ TCB_t *pxTCB; BaseType_t xTaskGetSchedulerState( void ) { BaseType_t xReturn; + unsigned state; + state = portDisableINT(); if( xSchedulerRunning == pdFALSE ) { xReturn = taskSCHEDULER_NOT_STARTED; @@ -3848,6 +3864,7 @@ TCB_t *pxTCB; xReturn = taskSCHEDULER_SUSPENDED; } } + portEnableINT(state); return xReturn; } @@ -4383,16 +4400,19 @@ TickType_t uxReturn; void *pvTaskIncrementMutexHeldCount( void ) { + TCB_t *curTCB; + /* If xSemaphoreCreateMutex() is called before any tasks have been created - then pxCurrentTCB will be NULL. */ + then xTaskGetCurrentTaskHandle() will be NULL. */ taskENTER_CRITICAL(&xTaskQueueMutex); if( pxCurrentTCB[ xPortGetCoreID() ] != NULL ) { ( pxCurrentTCB[ xPortGetCoreID() ]->uxMutexesHeld )++; } + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; taskEXIT_CRITICAL(&xTaskQueueMutex); - return pxCurrentTCB[ xPortGetCoreID() ]; + return curTCB; } #endif /* configUSE_MUTEXES */ From d049fd392953d8aaf2c3cb4270aa8b401e4f016a Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 27 Dec 2016 12:11:07 +0800 Subject: [PATCH 014/167] freertos: rework code based on review --- components/freertos/Kconfig | 6 ------ .../freertos/include/freertos/portmacro.h | 3 --- components/freertos/tasks.c | 20 ++++++++++--------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 859ece2c09f..d8b392d577a 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -195,12 +195,6 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE If enabled, additional debug information will be printed for recursive portMUX usage. -config FREERTOS_INT_DISABLING_DURATION_DEBUG - bool "Debug interrupt disabling duration" - default n - help - If enabled, the longest interrupt disabling duration will be recorded. - endif # FREERTOS_DEBUG_INTERNALS endmenu diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index 9c15eebf9a6..7cae4b05b64 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -224,9 +224,6 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I #define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state) -#define portDisableINT() portENTER_CRITICAL_NESTED() -#define portEnableINT(state) portEXIT_CRITICAL_NESTED((state)) - /* * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare * *mux to compare, and if it's the same, will set *mux to set. It will return the old value diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 00df0df8716..159d96c5b21 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -1041,7 +1041,7 @@ UBaseType_t x; static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) { - TCB_t *curTCB; + TCB_t *curTCB; BaseType_t i; /* Ensure interrupts don't access the task lists while the lists are being @@ -1119,6 +1119,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode if( xSchedulerRunning != pdFALSE ) { + taskENTER_CRITICAL(&xTaskQueueMutex); /* Scheduler is running. If the created task is of a higher priority than an executing task then it should run now. ToDo: This only works for the current core. If a task is scheduled on an other processor, @@ -1135,7 +1136,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode { taskYIELD_IF_USING_PREEMPTION(); } - else if( xCoreID != xPortGetCoreID() ) {//TODO + else if( xCoreID != xPortGetCoreID() ) { taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority); } else @@ -1147,6 +1148,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode { mtCOVERAGE_TEST_MARKER(); } + taskEXIT_CRITICAL(&xTaskQueueMutex); } else { @@ -2061,9 +2063,9 @@ void vTaskSuspendAll( void ) http://goo.gl/wu4acr */ unsigned state; - state = portDisableINT(); + state = portENTER_CRITICAL_NESTED(); ++uxSchedulerSuspended[ xPortGetCoreID() ]; - portEnableINT(state); + portEXIT_CRITICAL_NESTED(state); } /*----------------------------------------------------------*/ @@ -3818,9 +3820,9 @@ TCB_t *pxTCB; TaskHandle_t xReturn; unsigned state; - state = portDisableINT(); + state = portENTER_CRITICAL_NESTED(); xReturn = pxCurrentTCB[ xPortGetCoreID() ]; - portEnableINT(state); + portEXIT_CRITICAL_NESTED(state); return xReturn; } @@ -3848,7 +3850,7 @@ TCB_t *pxTCB; BaseType_t xReturn; unsigned state; - state = portDisableINT(); + state = portENTER_CRITICAL_NESTED(); if( xSchedulerRunning == pdFALSE ) { xReturn = taskSCHEDULER_NOT_STARTED; @@ -3864,7 +3866,7 @@ TCB_t *pxTCB; xReturn = taskSCHEDULER_SUSPENDED; } } - portEnableINT(state); + portEXIT_CRITICAL_NESTED(state); return xReturn; } @@ -4403,7 +4405,7 @@ TickType_t uxReturn; TCB_t *curTCB; /* If xSemaphoreCreateMutex() is called before any tasks have been created - then xTaskGetCurrentTaskHandle() will be NULL. */ + then pxCurrentTCB will be NULL. */ taskENTER_CRITICAL(&xTaskQueueMutex); if( pxCurrentTCB[ xPortGetCoreID() ] != NULL ) { From 6a39bc6996548aaedbbdb63178182c0076fa55fc Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Tue, 27 Dec 2016 12:32:40 +0100 Subject: [PATCH 015/167] Clarification on documenting examples --- docs/api/template.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/api/template.rst b/docs/api/template.rst index 6feb7ba271f..3cc8713ba4f 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -40,10 +40,15 @@ Application Example *INSTRUCTIONS* - 1. Provide one or more practical examples to demonstrate functionality of this API. - 2. Break down the code into parts and describe functionality of each part. - 3. Provide screenshots if applicable. - + 1. Prepare one or more practical examples to demonstrate functionality of this API. + 2. Each example should follow pattern of projects located in ``esp-idf/examples/`` folder. + 3. Place example in this folder complete with ``README.md`` file. + 4. Provide overview of demonstrated functionality in ``README.md``. + 5. With good overview reader should be able to understand what example does without opening the source code. + 6. Depending on complexity of example, break down description of code into parts and provide overview of functionality of each part. + 7. Include flow diagram and screenshots of application output if applicable. + 8. Finally add in this section synopsis of each example together with link to respective folder in ``esp-idf/examples/``. + API Reference ------------- From da977149f67487c023766974696cb1e8a5bc9117 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Dec 2016 16:33:24 -0800 Subject: [PATCH 016/167] panic handlers: Print the PC address where abort() was called, don't dump registers --- components/esp32/cpu_util.c | 12 +++++ components/esp32/include/soc/cpu.h | 9 ++++ components/esp32/panic.c | 75 +++++++++++++++++------------- components/newlib/syscalls.c | 9 ---- 4 files changed, 63 insertions(+), 42 deletions(-) diff --git a/components/esp32/cpu_util.c b/components/esp32/cpu_util.c index cff61ab796e..e3b4ef8f164 100644 --- a/components/esp32/cpu_util.c +++ b/components/esp32/cpu_util.c @@ -42,3 +42,15 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id) CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_PROCPU_C0_M); } } + +bool IRAM_ATTR esp_cpu_in_ocd_debug_mode() +{ +#if CONFIG_ESP32_DEBUG_OCDAWARE + int dcr; + int reg=0x10200C; //DSRSET register + asm("rer %0,%1":"=r"(dcr):"r"(reg)); + return (dcr&0x1); +#else + return false; // Always return false if "OCD aware" is disabled +#endif +} diff --git a/components/esp32/include/soc/cpu.h b/components/esp32/include/soc/cpu.h index 4457c81a22c..b89ae2875f7 100644 --- a/components/esp32/include/soc/cpu.h +++ b/components/esp32/include/soc/cpu.h @@ -94,4 +94,13 @@ void esp_cpu_stall(int cpu_id); */ void esp_cpu_unstall(int cpu_id); +/** + * @brief Returns true if a JTAG debugger is attached to CPU + * OCD (on chip debug) port. + * + * @note If "Make exception and panic handlers JTAG/OCD aware" + * is disabled, this function always returns false. + */ +bool esp_cpu_in_ocd_debug_mode(); + #endif diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 0efe56fe01b..3cdbfb3e397 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -36,7 +36,7 @@ /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level task switching / interrupt code runs into an unrecoverable error. The default task stack - overflow handler also is in here. + overflow handler and abort handler are also in here. */ /* @@ -95,15 +95,29 @@ inline static void panicPutHex(int a) { } inline static void panicPutDec(int a) { } #endif - void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { panicPutStr("***ERROR*** A stack overflow in task "); panicPutStr((char *)pcTaskName); panicPutStr(" has been detected.\r\n"); - configASSERT(0); + abort(); } +static bool abort_called; + +void abort() +{ +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT + ets_printf("abort() was called at PC 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); +#endif + abort_called = true; + while(1) { + __asm__ ("break 0,0"); + *((int*) 0) = 0; + } +} + + static const char *edesc[] = { "IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError", "Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue", @@ -118,7 +132,7 @@ static const char *edesc[] = { }; -void commonErrorHandler(XtExcFrame *frame); +static void commonErrorHandler(XtExcFrame *frame); //The fact that we've panic'ed probably means the other CPU is now running wild, possibly //messing up the serial output, so we stall it here. @@ -127,19 +141,6 @@ static void haltOtherCore() esp_cpu_stall( xPortGetCoreID() == 0 ? 1 : 0 ); } -//Returns true when a debugger is attached using JTAG. -static int inOCDMode() -{ -#if CONFIG_ESP32_DEBUG_OCDAWARE - int dcr; - int reg = 0x10200C; //DSRSET register - asm("rer %0,%1":"=r"(dcr):"r"(reg)); - return (dcr & 0x1); -#else - return 0; //Always return no debugger is attached. -#endif -} - void panicHandler(XtExcFrame *frame) { int *regs = (int *)frame; @@ -165,7 +166,7 @@ void panicHandler(XtExcFrame *frame) panicPutStr(reason); panicPutStr(")\r\n"); - if (inOCDMode()) { + if (esp_cpu_in_ocd_debug_mode()) { asm("break.n 1"); } commonErrorHandler(frame); @@ -197,7 +198,7 @@ void xt_unhandled_exception(XtExcFrame *frame) } panicPutStr(" occurred on core "); panicPutDec(xPortGetCoreID()); - if (inOCDMode()) { + if (esp_cpu_in_ocd_debug_mode()) { panicPutStr(" at pc="); panicPutHex(regs[1]); panicPutStr(". Setting bp and returning..\r\n"); @@ -255,6 +256,7 @@ static inline bool stackPointerIsSane(uint32_t sp) { return !(sp < 0x3ffae010 || sp > 0x3ffffff0 || ((sp & 0xf) != 0)); } + static void putEntry(uint32_t pc, uint32_t sp) { if (pc & 0x80000000) { @@ -265,7 +267,8 @@ static void putEntry(uint32_t pc, uint32_t sp) panicPutStr(":0x"); panicPutHex(sp); } -void doBacktrace(XtExcFrame *frame) + +static void doBacktrace(XtExcFrame *frame) { uint32_t i = 0, pc = frame->pc, sp = frame->a1; panicPutStr("\nBacktrace:"); @@ -291,7 +294,7 @@ void doBacktrace(XtExcFrame *frame) We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the serial port and either jump to the gdb stub, halt the CPU or reboot. */ -void commonErrorHandler(XtExcFrame *frame) +static void commonErrorHandler(XtExcFrame *frame) { int *regs = (int *)frame; int x, y; @@ -304,21 +307,28 @@ void commonErrorHandler(XtExcFrame *frame) //Feed the watchdogs, so they will give us time to print out debug info reconfigureAllWdts(); - panicPutStr("Register dump:\r\n"); + /* only dump registers for 'real' crashes, if crashing via abort() + the register window is no longer useful. + */ + if (!abort_called) { + panicPutStr("Register dump:\r\n"); - for (x = 0; x < 24; x += 4) { - for (y = 0; y < 4; y++) { - if (sdesc[x + y][0] != 0) { - panicPutStr(sdesc[x + y]); - panicPutStr(": 0x"); - panicPutHex(regs[x + y + 1]); - panicPutStr(" "); + for (x = 0; x < 24; x += 4) { + for (y = 0; y < 4; y++) { + if (sdesc[x + y][0] != 0) { + panicPutStr(sdesc[x + y]); + panicPutStr(": 0x"); + panicPutHex(regs[x + y + 1]); + panicPutStr(" "); + } } + panicPutStr("\r\n"); } - panicPutStr("\r\n"); } + /* With windowed ABI backtracing is easy, let's do it. */ doBacktrace(frame); + #if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); panicPutStr("Entering gdb stub now.\r\n"); @@ -339,8 +349,7 @@ void commonErrorHandler(XtExcFrame *frame) void esp_set_breakpoint_if_jtag(void *fn) { - if (!inOCDMode()) { - return; + if (esp_cpu_in_ocd_debug_mode()) { + setFirstBreakpoint((uint32_t)fn); } - setFirstBreakpoint((uint32_t)fn); } diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c index 3b2fbf62cac..74182d07f25 100644 --- a/components/newlib/syscalls.c +++ b/components/newlib/syscalls.c @@ -22,15 +22,6 @@ #include "esp_attr.h" #include "freertos/FreeRTOS.h" -void IRAM_ATTR abort() -{ - do - { - __asm__ ("break 0,0"); - *((int*) 0) = 0; - } while(true); -} - void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size) { return pvPortMalloc(size); From c1a6d5511663560bbed058e0ecd4d3ec6a3da37e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:34:11 +1100 Subject: [PATCH 017/167] WiFi interface: SSID and password fields should be uint8_t in all cases Closes github #40 https://github.com/espressif/esp-idf/issues/40 --- components/esp32/include/esp_wifi_types.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index 583d7a6a91d..ac7642829fb 100755 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -96,7 +96,7 @@ typedef enum { } wifi_second_chan_t; typedef struct { - char *ssid; /**< SSID of AP */ + uint8_t *ssid; /**< SSID of AP */ uint8_t *bssid; /**< MAC address of AP */ uint8_t channel; /**< channel, scan the specific channel */ bool show_hidden; /**< enable to scan AP whose SSID is hidden */ @@ -126,8 +126,8 @@ typedef enum { } wifi_bandwidth_t; typedef struct { - char ssid[32]; /**< SSID of ESP32 soft-AP */ - char password[64]; /**< Password of ESP32 soft-AP */ + uint8_t ssid[32]; /**< SSID of ESP32 soft-AP */ + uint8_t password[64]; /**< Password of ESP32 soft-AP */ uint8_t ssid_len; /**< Length of SSID. If softap_config.ssid_len==0, check the SSID until there is a termination character; otherwise, set the SSID length according to softap_config.ssid_len. */ uint8_t channel; /**< Channel of ESP32 soft-AP */ wifi_auth_mode_t authmode; /**< Auth mode of ESP32 soft-AP. Do not support AUTH_WEP in soft-AP mode */ @@ -137,8 +137,8 @@ typedef struct { } wifi_ap_config_t; typedef struct { - char ssid[32]; /**< SSID of target AP*/ - char password[64]; /**< password of target AP*/ + uint8_t ssid[32]; /**< SSID of target AP*/ + uint8_t password[64]; /**< password of target AP*/ bool bssid_set; /**< whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP.*/ uint8_t bssid[6]; /**< MAC address of target AP*/ } wifi_sta_config_t; @@ -215,7 +215,7 @@ typedef struct { typedef struct { wifi_pkt_rx_ctrl_t rx_ctrl; - char payload[0]; /**< ieee80211 packet buff, The length of payload is described by sig_len */ + uint8_t payload[0]; /**< ieee80211 packet buff, The length of payload is described by sig_len */ } wifi_promiscuous_pkt_t; /** From 1e44f72e98ba03e27a014c8518037345062e1667 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:37:03 +1100 Subject: [PATCH 018/167] esp_wifi_init: Update comment about init event_q Closes github #28 https://github.com/espressif/esp-idf/issues/28 --- components/esp32/include/esp_wifi.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 68d06aae529..ac49764f1f2 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -107,17 +107,15 @@ typedef struct { * WiFi NVS structure etc, this WiFi also start WiFi task * * @attention 1. This API must be called before all other WiFi API can be called - * @attention 2. Generally we should init event_q in *config, WiFi driver will post the event - * to this queue when event happens, such as, when station connects to WiFi, WiFi driver - * will post station connected event to this queue. If the queue is not initialized, WiFi - * will not post any events + * @attention 2. event_handler field in cfg should be set to a valid event handler function. + * In most cases, use the WIFI_INIT_CONFIG_DEFAULT macro which sets esp_event_send(). * * @param config provide WiFi init configuration * * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NO_MEM: out of memory - * - others: refer to error code esp_err.h + * - others: refer to error code esp_err.h */ esp_err_t esp_wifi_init(wifi_init_config_t *config); From ff1fa8a32340bff9ced31262857ddc66b17a6306 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:05:19 +1100 Subject: [PATCH 019/167] gpio driver: Fix gpio_set_level validation of gpio_num argument Closes #125 https://github.com/espressif/esp-idf/issues/125 --- components/driver/gpio.c | 2 +- components/driver/include/driver/gpio.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 1a38620dbb5..3201372bc1d 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -161,7 +161,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num) esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG); if (level) { if (gpio_num < 32) { GPIO.out_w1ts = (1 << gpio_num); diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 83d38068342..4f2a8007241 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -269,7 +269,7 @@ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); * * @return * - ESP_OK Success - * - GPIO_IS_VALID_GPIO GPIO number error + * - ESP_ERR_INVALID_ARG GPIO number error * */ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); From 41eca2c67ba276c3a85e5da094b6da5e2e569487 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:21:11 +1100 Subject: [PATCH 020/167] RMT: Don't require carrier_freq_hz to be non-zero if carrier_en unset Closes github #123 https://github.com/espressif/esp-idf/issues/123 --- components/driver/rmt.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/components/driver/rmt.c b/components/driver/rmt.c index e29f190024d..a16f0f8fe54 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -380,10 +380,16 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) uint8_t gpio_num = rmt_param->gpio_num; uint8_t mem_cnt = rmt_param->mem_block_num; int clk_div = rmt_param->clk_div; + uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; + bool carrier_en = rmt_param->tx_config.carrier_en; RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(GPIO_IS_VALID_GPIO(gpio_num), RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK((mem_cnt + channel <= 8 && mem_cnt > 0), RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK((clk_div > 0), RMT_CLK_DIV_ERROR_STR, ESP_ERR_INVALID_ARG); + if (mode == RMT_MODE_TX) { + RMT_CHECK((!carrier_en || carrier_freq_hz > 0), "RMT carrier frequency can't be zero", ESP_ERR_INVALID_ARG); + } + periph_module_enable(PERIPH_RMT_MODULE); RMT.conf_ch[channel].conf0.div_cnt = clk_div; @@ -397,7 +403,6 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) if(mode == RMT_MODE_TX) { uint32_t rmt_source_clk_hz = 0; - uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; uint16_t carrier_duty_percent = rmt_param->tx_config.carrier_duty_percent; uint8_t carrier_level = rmt_param->tx_config.carrier_level; uint8_t idle_level = rmt_param->tx_config.idle_level; @@ -416,16 +421,23 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) portEXIT_CRITICAL(&rmt_spinlock); /*Set carrier*/ - uint32_t duty_div, duty_h, duty_l; - duty_div = rmt_source_clk_hz / carrier_freq_hz; - duty_h = duty_div * carrier_duty_percent / 100; - duty_l = duty_div - duty_h; - RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; - RMT.carrier_duty_ch[channel].high = duty_h; - RMT.carrier_duty_ch[channel].low = duty_l; - RMT.conf_ch[channel].conf0.carrier_en = rmt_param->tx_config.carrier_en; + RMT.conf_ch[channel].conf0.carrier_en = carrier_en; + if (carrier_en) { + uint32_t duty_div, duty_h, duty_l; + duty_div = rmt_source_clk_hz / carrier_freq_hz; + duty_h = duty_div * carrier_duty_percent / 100; + duty_l = duty_div - duty_h; + RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; + RMT.carrier_duty_ch[channel].high = duty_h; + RMT.carrier_duty_ch[channel].low = duty_l; + } else { + RMT.conf_ch[channel].conf0.carrier_out_lv = 0; + RMT.carrier_duty_ch[channel].high = 0; + RMT.carrier_duty_ch[channel].low = 0; + } ESP_LOGD(RMT_TAG, "Rmt Tx Channel %u|Gpio %u|Sclk_Hz %u|Div %u|Carrier_Hz %u|Duty %u", - channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent); + channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent); + } else if(RMT_MODE_RX == mode) { uint8_t filter_cnt = rmt_param->rx_config.filter_ticks_thresh; From 45571b3c3810f174ee315d603f54f7cd34f96d31 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:30:24 +1100 Subject: [PATCH 021/167] LEDC driver: Use ledc_channel_t for all channel arguments Closes github #54: https://github.com/espressif/esp-idf/issues/54 --- components/driver/include/driver/ledc.h | 6 +++--- components/driver/ledc.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index fb97c6c011b..691379a3d82 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -253,7 +253,7 @@ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel); * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t gradule_direction, +esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t gradule_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale); /** @@ -354,7 +354,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); * - ESP_OK Success * */ -esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); +esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx); /***************************EXAMPLE********************************** * @@ -391,7 +391,7 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * * ----------------EXAMPLE OF SETTING DUTY --- ----------------- * @code{c} - * uint32_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) + * ledc_channel_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) * uint32_t duty = 2000; //duty range is 0 ~ ((2**bit_num)-1) * LEDC_set_duty(LEDC_HIGH_SPEED_MODE, ledc_channel, duty); //set speed mode, channel, and duty. * ledc_update_duty(LEDC_HIGH_SPEED_MODE, ledc_channel); //after set duty, we need to call ledc_update_duty to update the settings. diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 77ca9759696..c00cf26bb13 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -44,7 +44,7 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_ return ESP_OK; } -static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, uint32_t hpoint_val, uint32_t duty_val, +static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t hpoint_val, uint32_t duty_val, uint32_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) { portENTER_CRITICAL(&ledc_spinlock); @@ -58,7 +58,7 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, return ESP_OK; } -esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx) +esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx) { LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); @@ -239,7 +239,7 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } -esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, +esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); From 6395081503069ea569d94eb706742e684a4269cd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:43:42 +1100 Subject: [PATCH 022/167] uart driver: Set type of uart_driver_install queue param Closes github #91 https://github.com/espressif/esp-idf/issues/91 --- components/driver/include/driver/uart.h | 5 +++-- components/driver/uart.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index c193fb0ef8d..c788374bdc4 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -477,7 +477,8 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @param tx_buffer_size UART TX ring buffer size. * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param uart_queue UART event queue handle (out param). On success, a new queue handle is written here to provide + * access to UART events. If set to NULL, driver will not use an event queue. * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * @@ -485,7 +486,7 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t* uart_queue, int intr_alloc_flags); /** * @brief Uninstall UART driver. diff --git a/components/driver/uart.c b/components/driver/uart.c index e85c54d8c4b..45f4e23945f 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -950,7 +950,7 @@ esp_err_t uart_flush(uart_port_t uart_num) return ESP_OK; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((rx_buffer_size > UART_FIFO_LEN), "uart rx buffer length error(>128)", ESP_FAIL); @@ -978,7 +978,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); - *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; + *uart_queue = p_uart_obj[uart_num]->xQueueUart; ESP_LOGI(UART_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); } else { p_uart_obj[uart_num]->xQueueUart = NULL; From 948a2ba23af4bee60849d5d90dc0f6f28bef6cc0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:44:50 +1100 Subject: [PATCH 023/167] uart driver: Remove invalid UART_BITRATE_115200 enum from example Closes github #92 https://github.com/espressif/esp-idf/issues/92 --- components/driver/include/driver/uart.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index c788374bdc4..68d02a5e0a0 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -648,7 +648,7 @@ esp_err_t uart_enable_pattern_det_intr(uart_port_t uart_num, char pattern_chr, u * //a. Set UART parameter * int uart_num = 0; //uart port number * uart_config_t uart_config = { - * .baud_rate = UART_BITRATE_115200, //baudrate + * .baud_rate = 115200, //baudrate * .data_bits = UART_DATA_8_BITS, //data bit mode * .parity = UART_PARITY_DISABLE, //parity mode * .stop_bits = UART_STOP_BITS_1, //stop bit mode From 9496fda66285c949fc258b11049832d1953d3c86 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:54:42 +1100 Subject: [PATCH 024/167] RMT driver: Rename rmt_set_evt_intr_en to rmt_set_tx_thr_intr_en Closes github #115: https://github.com/espressif/esp-idf/issues/115 --- components/driver/include/driver/rmt.h | 6 ++++-- components/driver/rmt.c | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 24df1ac8ed0..36e33e732ed 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -524,7 +524,9 @@ esp_err_t rmt_set_err_intr_en(rmt_channel_t channel, bool en); esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en); /** - * @brief Set RMT TX event interrupt enable + * @brief Set RMT TX threshold event interrupt enable + * + * Causes an interrupt when a threshold number of items have been transmitted. * * @param channel RMT channel (0 - 7) * @@ -536,7 +538,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en); * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success */ -esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh); +esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh); /** * @brief Set RMT pins diff --git a/components/driver/rmt.c b/components/driver/rmt.c index a16f0f8fe54..d277ea00cda 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -341,7 +341,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en) return ESP_OK; } -esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh) +esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh) { RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(evt_thresh < 256, "RMT EVT THRESH ERR", ESP_ERR_INVALID_ARG); @@ -624,7 +624,7 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel) rmt_set_rx_intr_en(channel, 0); rmt_set_err_intr_en(channel, 0); rmt_set_tx_intr_en(channel, 0); - rmt_set_evt_intr_en(channel, 0, 0xffff); + rmt_set_tx_thr_intr_en(channel, 0, 0xffff); if(p_rmt_obj[channel]->tx_sem) { vSemaphoreDelete(p_rmt_obj[channel]->tx_sem); p_rmt_obj[channel]->tx_sem = NULL; @@ -697,7 +697,7 @@ esp_err_t rmt_write_items(rmt_channel_t channel, rmt_item32_t* rmt_item, int ite RMT.apb_conf.mem_tx_wrap_en = 1; len_rem -= item_block_len; RMT.conf_ch[channel].conf1.tx_conti_mode = 0; - rmt_set_evt_intr_en(channel, 1, item_sub_len); + rmt_set_tx_thr_intr_en(channel, 1, item_sub_len); p_rmt->tx_data = rmt_item + item_block_len; p_rmt->tx_len_rem = len_rem; p_rmt->tx_offset = 0; From 665dcc571280900ee206966fded16d39948ba3a2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 14:20:00 +1100 Subject: [PATCH 025/167] linux docs: Add note about precompiled gdb on Arch Closes github #150: https://github.com/espressif/esp-idf/issues/150 --- docs/linux-setup.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/linux-setup.rst b/docs/linux-setup.rst index cf5e78b63dd..20f460aa62d 100644 --- a/docs/linux-setup.rst +++ b/docs/linux-setup.rst @@ -17,7 +17,6 @@ To compile with ESP-IDF you need to get the following packages: sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial - Step 1: Download binary toolchain for the ESP32 ================================================== @@ -49,6 +48,16 @@ Alternatively, you may create an alias for the above command. This way you can g Then when you need the toolchain you can type ``get_esp32`` on the command line and the toolchain will be added to your ``PATH``. +Arch Linux Users +---------------- + +To run the precompiled gdb (xtensa-esp32-elf-gdb) in Arch Linux requires ncurses 5, but Arch uses ncurses 6. Backwards compatibility libraries are available in AUR_ for native and lib32 configurations: +- https://aur.archlinux.org/packages/ncurses5-compat-libs/ +- https://aur.archlinux.org/packages/lib32-ncurses5-compat-libs/ + +(Alternatively, use crosstool-NG to compile a gdb that links against ncurses 6.) + + Alternative Step 1: Compile the toolchain from source using crosstool-NG ======================================================================== @@ -156,3 +165,4 @@ Further reading If you'd like to use the Eclipse IDE instead of running ``make``, check out the Eclipse setup guide in this directory. +.. _AUR: https://wiki.archlinux.org/index.php/Arch_User_Repository From e6b09dc258a415c5cda5bdc282ffc02e59ed8821 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 14:47:14 +1100 Subject: [PATCH 026/167] FreeRTOS: Default to canary byte stack overflow checking Was mistakenly "none" due to name change not being propagated. Closes github issue #181: https://github.com/espressif/esp-idf/issues/181 --- components/freertos/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index f03da6bc01a..2489270c058 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -54,7 +54,7 @@ config FREERTOS_ASSERT_ON_UNTESTED_FUNCTION choice FREERTOS_CHECK_STACKOVERFLOW prompt "Check for stack overflow" - default FREERTOS_CHECK_STACKOVERFLOW_QUICK + default FREERTOS_CHECK_STACKOVERFLOW_CANARY help FreeRTOS can check for stack overflows in threads and trigger an user function called vApplicationStackOverflowHook when this happens. From 06e03ff52e0737ba6ecd6ec0e40a7d0ef8e48335 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:42:21 +1100 Subject: [PATCH 027/167] Replace backwards-compatible portTICK_RATE_MS with FreeRTOS v8+ portTICK_PERIOD_MS Closes github #51 https://github.com/espressif/esp-idf/issues/51 --- components/bt/bluedroid/btc/core/btc_task.c | 2 +- components/bt/bluedroid/hci/hci_hal_h4.c | 2 +- components/bt/bluedroid/hci/hci_layer.c | 2 +- components/bt/bluedroid/osi/osi_arch.c | 8 ++++---- components/bt/bluedroid/stack/btu/btu_task.c | 2 +- components/bt/bt.c | 2 +- components/esp32/test/test_intr_alloc.c | 16 ++++++++-------- components/ethernet/emac_main.c | 4 ++-- .../freertos/test/test_freertos_eventgroups.c | 4 ++-- components/lwip/port/freertos/sys_arch.c | 14 +++++++------- examples/01_hello_world/main/hello_world_main.c | 2 +- examples/02_blink/main/blink.c | 4 ++-- .../03_http_request/main/http_request_main.c | 10 +++++----- .../04_https_request/main/https_request_main.c | 2 +- examples/07_nvs_rw_value/main/nvs_rw_value.c | 2 +- examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 4 ++-- examples/11_rmt_nec_tx_rx/main/infrared_nec.c | 2 +- examples/12_blufi/components/blufi/blufi_task.c | 2 +- examples/16_pcnt/main/pcnt_test.c | 2 +- examples/17_ethernet/main/ethernet_main.c | 4 ++-- examples/19_sigmadelta/main/sigmadelta_test.c | 2 +- 21 files changed, 46 insertions(+), 46 deletions(-) diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index b4ce0d95caa..773d7889bb8 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -80,7 +80,7 @@ static bt_status_t btc_task_post(btc_msg_t *msg) return BT_STATUS_PARM_INVALID; } - if (xQueueSend(xBtcQueue, msg, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBtcQueue, msg, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("Btc Post failed\n"); return BT_STATUS_BUSY; } diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 922ee6ecc06..237226266ae 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -177,7 +177,7 @@ void hci_hal_h4_task_post(void) evt.sig = 0xff; evt.par = 0; - if (xQueueSend(xHciH4Queue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xHciH4Queue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xHciH4Queue failed\n"); } } diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index e0f15e0ea95..d71690d4a63 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -146,7 +146,7 @@ void hci_host_task_post(void) evt.sig = 0xff; evt.par = 0; - if (xQueueSend(xHciHostQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xHciHostQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xHciHostQueue failed\n"); } } diff --git a/components/bt/bluedroid/osi/osi_arch.c b/components/bt/bluedroid/osi/osi_arch.c index e896efd871d..d1d0185aec8 100644 --- a/components/bt/bluedroid/osi/osi_arch.c +++ b/components/bt/bluedroid/osi/osi_arch.c @@ -137,9 +137,9 @@ osi_sem_wait(osi_sem_t *sem, uint32_t timeout) StartTime = xTaskGetTickCount(); if (timeout != 0) { - if (xSemaphoreTake(*sem, timeout / portTICK_RATE_MS) == pdTRUE) { + if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) == pdTRUE) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -153,7 +153,7 @@ osi_sem_wait(osi_sem_t *sem, uint32_t timeout) while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE); EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -190,7 +190,7 @@ osi_now(void) void osi_delay_ms(uint32_t ms) { - vTaskDelay(ms / portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 5cca29dd838..4c640fad7c2 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -338,7 +338,7 @@ void btu_task_post(uint32_t sig) evt.sig = sig; evt.par = 0; - if (xQueueSend(xBtuQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBtuQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xBtuQueue failed\n"); } } diff --git a/components/bt/bt.c b/components/bt/bt.c index ef9a063d692..8d1fecb777a 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -85,7 +85,7 @@ static int32_t IRAM_ATTR semphr_give_from_isr_wrapper(void *semphr, void *hptw) static int32_t IRAM_ATTR semphr_take_wrapper(void *semphr, uint32_t block_time_ms) { - return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_RATE_MS); + return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS); } static void *IRAM_ATTR mutex_create_wrapper(void) diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp32/test/test_intr_alloc.c index 31991b4e41d..329fc9a9e4d 100644 --- a/components/esp32/test/test_intr_alloc.c +++ b/components/esp32/test/test_intr_alloc.c @@ -98,7 +98,7 @@ static void timer_test(int flags) { esp_intr_get_intno(inth[0]), esp_intr_get_intno(inth[1]), esp_intr_get_intno(inth[2]), esp_intr_get_intno(inth[3])); printf("Timer values on start: %d %d %d %d\n", count[0], count[1], count[2], count[3]); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]==0); TEST_ASSERT(count[1]!=0); @@ -110,7 +110,7 @@ static void timer_test(int flags) { esp_intr_disable(inth[1]); esp_intr_disable(inth[2]); for (x=0; x<4; x++) count[x]=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]!=0); TEST_ASSERT(count[1]==0); @@ -122,7 +122,7 @@ static void timer_test(int flags) { esp_intr_disable(inth[0]); esp_intr_disable(inth[3]); for (x=0; x<4; x++) count[x]=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]==0); TEST_ASSERT(count[1]!=0); @@ -152,18 +152,18 @@ void local_timer_test() printf("Int timer 1 intno %d\n", esp_intr_get_intno(ih)); xthal_set_ccompare(1, xthal_get_ccount()+8000000); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); printf("Disabling int\n"); esp_intr_disable(ih); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr==0); printf("Re-enabling\n"); esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); @@ -173,12 +173,12 @@ void local_timer_test() r=esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED, int_timer_handler, NULL, &ih); TEST_ASSERT(r==ESP_OK); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr==0); printf("Re-enabling\n"); esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); r=esp_intr_free(ih); diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index ab2ca8964c9..20d428cf7b3 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -585,7 +585,7 @@ void emac_link_check_func(void *pv_parameters) static bool emac_link_check_timer_init(void) { - emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_RATE_MS), + emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_PERIOD_MS), pdTRUE, (void *)rand(), emac_link_check_func); if (emac_timer == NULL) { return false; @@ -844,7 +844,7 @@ esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) evt.sig = sig; evt.par = par; - if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { return ESP_FAIL; } } diff --git a/components/freertos/test/test_freertos_eventgroups.c b/components/freertos/test/test_freertos_eventgroups.c index 35a5cc4ed2f..b17e127c284 100644 --- a/components/freertos/test/test_freertos_eventgroups.c +++ b/components/freertos/test/test_freertos_eventgroups.c @@ -34,7 +34,7 @@ static void task_event_group_call_response(void *param) printf("Task %d done\n", task_num); /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelete(NULL); } @@ -85,7 +85,7 @@ static void task_test_sync(void *param) printf("Done %d = %x\n", task_num, after_done); /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelete(NULL); } diff --git a/components/lwip/port/freertos/sys_arch.c b/components/lwip/port/freertos/sys_arch.c index 97ee32d1bd0..7f96c9b8591 100755 --- a/components/lwip/port/freertos/sys_arch.c +++ b/components/lwip/port/freertos/sys_arch.c @@ -164,9 +164,9 @@ sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) StartTime = xTaskGetTickCount(); if (timeout != 0) { - if (xSemaphoreTake(*sem, timeout / portTICK_RATE_MS) == pdTRUE) { + if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) == pdTRUE) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -180,7 +180,7 @@ sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE); EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -293,9 +293,9 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) sys_mutex_lock(&(*mbox)->lock); if (timeout != 0) { - if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), timeout / portTICK_RATE_MS)) { + if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), timeout / portTICK_PERIOD_MS)) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -323,7 +323,7 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) } EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -566,7 +566,7 @@ void sys_thread_sem_deinit(void) void sys_delay_ms(uint32_t ms) { - vTaskDelay(ms/portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } diff --git a/examples/01_hello_world/main/hello_world_main.c b/examples/01_hello_world/main/hello_world_main.c index 0e872522fa2..c8b9f5f0c95 100644 --- a/examples/01_hello_world/main/hello_world_main.c +++ b/examples/01_hello_world/main/hello_world_main.c @@ -17,7 +17,7 @@ void hello_task(void *pvParameter) printf("Hello world!\n"); for (int i = 10; i >= 0; i--) { printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("Restarting now.\n"); fflush(stdout); diff --git a/examples/02_blink/main/blink.c b/examples/02_blink/main/blink.c index 1e49e51b2f8..f97572ac219 100644 --- a/examples/02_blink/main/blink.c +++ b/examples/02_blink/main/blink.c @@ -33,10 +33,10 @@ void blink_task(void *pvParameter) while(1) { /* Blink off (output low) */ gpio_set_level(BLINK_GPIO, 0); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); /* Blink on (output high) */ gpio_set_level(BLINK_GPIO, 1); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } diff --git a/examples/03_http_request/main/http_request_main.c b/examples/03_http_request/main/http_request_main.c index 9fe1933373f..3831ae65b9d 100644 --- a/examples/03_http_request/main/http_request_main.c +++ b/examples/03_http_request/main/http_request_main.c @@ -115,7 +115,7 @@ static void http_get_task(void *pvParameters) if(err != 0 || res == NULL) { ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } @@ -129,7 +129,7 @@ static void http_get_task(void *pvParameters) if(s < 0) { ESP_LOGE(TAG, "... Failed to allocate socket."); freeaddrinfo(res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } ESP_LOGI(TAG, "... allocated socket\r\n"); @@ -138,7 +138,7 @@ static void http_get_task(void *pvParameters) ESP_LOGE(TAG, "... socket connect failed errno=%d", errno); close(s); freeaddrinfo(res); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); continue; } @@ -148,7 +148,7 @@ static void http_get_task(void *pvParameters) if (write(s, REQUEST, strlen(REQUEST)) < 0) { ESP_LOGE(TAG, "... socket send failed"); close(s); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); continue; } ESP_LOGI(TAG, "... socket send success"); @@ -166,7 +166,7 @@ static void http_get_task(void *pvParameters) close(s); for(int countdown = 10; countdown >= 0; countdown--) { ESP_LOGI(TAG, "%d... ", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ESP_LOGI(TAG, "Starting again!"); } diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index caf3f374a3a..933d97ac837 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -362,7 +362,7 @@ static void https_get_task(void *pvParameters) for(int countdown = 10; countdown >= 0; countdown--) { ESP_LOGI(TAG, "%d...", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ESP_LOGI(TAG, "Starting again!"); } diff --git a/examples/07_nvs_rw_value/main/nvs_rw_value.c b/examples/07_nvs_rw_value/main/nvs_rw_value.c index dac2d4077e5..1b3e06b8598 100644 --- a/examples/07_nvs_rw_value/main/nvs_rw_value.c +++ b/examples/07_nvs_rw_value/main/nvs_rw_value.c @@ -72,7 +72,7 @@ void app_main() // Restart module for (int i = 10; i >= 0; i--) { printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("Restarting now.\n"); fflush(stdout); diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c index 7c13c15ba7b..0d4b7db4ee4 100644 --- a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -164,7 +164,7 @@ void app_main() */ while (1) { if (gpio_get_level(GPIO_NUM_0) == 0) { - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); if(gpio_get_level(GPIO_NUM_0) == 0) { err = save_run_time(); if (err != ESP_OK) printf("Error (%d) saving run time blob to NVS!\n", err); @@ -173,6 +173,6 @@ void app_main() esp_restart(); } } - vTaskDelay(200 / portTICK_RATE_MS); + vTaskDelay(200 / portTICK_PERIOD_MS); } } diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c index d2f7b091fa3..ea42502749e 100644 --- a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c +++ b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c @@ -352,7 +352,7 @@ void rmt_nec_tx_task() rmt_wait_tx_done(channel); //before we free the data, make sure sending is already done. free(item); - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); } vTaskDelete(NULL); } diff --git a/examples/12_blufi/components/blufi/blufi_task.c b/examples/12_blufi/components/blufi/blufi_task.c index cda66c05112..75547c64328 100644 --- a/examples/12_blufi/components/blufi/blufi_task.c +++ b/examples/12_blufi/components/blufi/blufi_task.c @@ -69,7 +69,7 @@ static esp_err_t blufi_task_post(uint32_t sig, void *par, void *cb, void *arg) evt.cb = cb; evt.arg = arg; - if (xQueueSend(xBlufiTaskQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBlufiTaskQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("Blufi Post failed\n"); return ESP_FAIL; } diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index b8489ecb2f4..0185f9b6f93 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -196,7 +196,7 @@ void app_main() portBASE_TYPE res; while(1) { - res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_RATE_MS); + res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_PERIOD_MS); if(res == pdTRUE) { pcnt_get_counter_value(PCNT_TEST_UNIT, &count); printf("Event PCNT unit[%d]; cnt: %d\n", evt.unit, count); diff --git a/examples/17_ethernet/main/ethernet_main.c b/examples/17_ethernet/main/ethernet_main.c index 7e84a9badd1..fc4347f7d89 100644 --- a/examples/17_ethernet/main/ethernet_main.c +++ b/examples/17_ethernet/main/ethernet_main.c @@ -108,11 +108,11 @@ void eth_task(void *pvParameter) { tcpip_adapter_ip_info_t ip; memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); while (1) { - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); if (tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip) == 0) { ESP_LOGI(TAG, "\n~~~~~~~~~~~\n"); diff --git a/examples/19_sigmadelta/main/sigmadelta_test.c b/examples/19_sigmadelta/main/sigmadelta_test.c index 92bc6e0a835..60880311d52 100644 --- a/examples/19_sigmadelta/main/sigmadelta_test.c +++ b/examples/19_sigmadelta/main/sigmadelta_test.c @@ -46,7 +46,7 @@ void app_main() while(1) { sigmadelta_set_duty(SIGMADELTA_CHANNEL_0, duty); /*by changing delay time, you can change the blink frequency of LED. */ - vTaskDelay(10 / portTICK_RATE_MS); + vTaskDelay(10 / portTICK_PERIOD_MS); duty += inc; if(duty == 127 || duty == -127) inc = (-1) * inc; From ade7ee20920f733a682f5be6ed9e6705c5db5ceb Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Sat, 24 Dec 2016 20:45:57 +0800 Subject: [PATCH 028/167] gpio_driver: add per-pin interrupt handlers 1. add ISR handler apis so that users of different layers can hook their own isr handler on different GPIO. Audio project has different software layers, they need different gpio isr handler for layer instead of processing all GPIO interrupts in one handler. If this kind of calling a handler from isr is not proper, please kindly point out. 2. add gpio example code. 3. improve gpio.rst 4. add readme for gpio example Squashed commits: [278e50f] update: GPIO 1. coding style, add a space between conditional or loop keyword and an opening paren. 2. modify some return value and doc 3. use printf in example code Squashed commits: [efb23bb] minor change of comment --- components/driver/gpio.c | 134 +++++++++++++++++++++--- components/driver/include/driver/gpio.h | 128 ++++++++-------------- docs/api/gpio.rst | 28 +++-- examples/21_gpio/Makefile | 9 ++ examples/21_gpio/README.md | 20 ++++ examples/21_gpio/main/component.mk | 3 + examples/21_gpio/main/gpio_test.c | 114 ++++++++++++++++++++ 7 files changed, 330 insertions(+), 106 deletions(-) create mode 100644 examples/21_gpio/Makefile create mode 100644 examples/21_gpio/README.md create mode 100644 examples/21_gpio/main/component.mk create mode 100644 examples/21_gpio/main/gpio_test.c diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 3201372bc1d..4e837054086 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -72,45 +72,59 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_39 }; -esp_err_t gpio_pullup_en(gpio_num_t gpio_num) { +typedef struct { + gpio_isr_t fn; /*!< isr function */ + void* args; /*!< isr function args */ +} gpio_isr_func_t; + +static gpio_isr_func_t* gpio_isr_func = NULL; +static gpio_isr_handle_t gpio_isr_handle; +static portMUX_TYPE gpio_spinlock = portMUX_INITIALIZER_UNLOCKED; + +esp_err_t gpio_pullup_en(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pullup_en(gpio_num); - }else{ + } else { REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); } return ESP_OK; } -esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) { +esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pullup_dis(gpio_num); - }else{ + } else { REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); } return ESP_OK; } -esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) { +esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pulldown_en(gpio_num); - }else{ - REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); + } else { + REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); } return ESP_OK; } -esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) { +esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ - rtc_gpio_pulldown_dis(gpio_num); - }else{ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { + rtc_gpio_pulldown_dis(gpio_num); + } else { REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); } return ESP_OK; } + esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); @@ -323,6 +337,96 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) return ESP_OK; } +void IRAM_ATTR gpio_intr_service(void* arg) +{ + //GPIO intr process + uint32_t gpio_num = 0; + //read status to get interrupt status for GPIO0-31 + uint32_t gpio_intr_status; + gpio_intr_status = GPIO.status; + //read status1 to get interrupt status for GPIO32-39 + uint32_t gpio_intr_status_h; + gpio_intr_status_h = GPIO.status1.intr_st; + + if (gpio_isr_func == NULL) { + return; + } + do { + if (gpio_num < 32) { + if (gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 + if (gpio_isr_func[gpio_num].fn != NULL) { + gpio_isr_func[gpio_num].fn(gpio_isr_func[gpio_num].args); + } + GPIO.status_w1tc = BIT(gpio_num); + } + } else { + if (gpio_intr_status_h & BIT(gpio_num - 32)) { + if (gpio_isr_func[gpio_num].fn != NULL) { + gpio_isr_func[gpio_num].fn(gpio_isr_func[gpio_num].args); + } + GPIO.status1_w1tc.intr_st = BIT(gpio_num - 32); + } + } + } while (++gpio_num < GPIO_PIN_COUNT); +} + +esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void* args) +{ + GPIO_CHECK(gpio_isr_func != NULL, "GPIO isr service is not installed, call gpio_install_isr_service() first", ESP_ERR_INVALID_STATE); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&gpio_spinlock); + gpio_intr_disable(gpio_num); + if (gpio_isr_func) { + gpio_isr_func[gpio_num].fn = isr_handler; + gpio_isr_func[gpio_num].args = args; + } + gpio_intr_enable(gpio_num); + portEXIT_CRITICAL(&gpio_spinlock); + return ESP_OK; +} + +esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num) +{ + GPIO_CHECK(gpio_isr_func != NULL, "GPIO isr service is not installed, call gpio_install_isr_service() first", ESP_ERR_INVALID_STATE); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&gpio_spinlock); + gpio_intr_disable(gpio_num); + if (gpio_isr_func) { + gpio_isr_func[gpio_num].fn = NULL; + gpio_isr_func[gpio_num].args = NULL; + } + portEXIT_CRITICAL(&gpio_spinlock); + return ESP_OK; +} + +esp_err_t gpio_install_isr_service(int intr_alloc_flags) +{ + GPIO_CHECK(gpio_isr_func == NULL, "GPIO isr service already installed", ESP_FAIL); + esp_err_t ret; + portENTER_CRITICAL(&gpio_spinlock); + gpio_isr_func = (gpio_isr_func_t*) calloc(GPIO_NUM_MAX, sizeof(gpio_isr_func_t)); + if (gpio_isr_func == NULL) { + ret = ESP_ERR_NO_MEM; + } else { + ret = gpio_isr_register(gpio_intr_service, NULL, intr_alloc_flags, &gpio_isr_handle); + } + portEXIT_CRITICAL(&gpio_spinlock); + return ret; +} + +void gpio_uninstall_isr_service() +{ + if (gpio_isr_func == NULL) { + return; + } + portENTER_CRITICAL(&gpio_spinlock); + esp_intr_free(gpio_isr_handle); + free(gpio_isr_func); + gpio_isr_func = NULL; + portEXIT_CRITICAL(&gpio_spinlock); + return; +} + esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle) { GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); @@ -334,7 +438,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; - if ((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) { + if (( intr_type == GPIO_INTR_LOW_LEVEL ) || ( intr_type == GPIO_INTR_HIGH_LEVEL )) { GPIO.pin[gpio_num].int_type = intr_type; GPIO.pin[gpio_num].wakeup_enable = 0x1; } else { diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 4f2a8007241..1472ba83525 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -156,6 +156,7 @@ typedef enum { GPIO_NUM_37 = 37, /*!< GPIO37, input mode only */ GPIO_NUM_38 = 38, /*!< GPIO38, input mode only */ GPIO_NUM_39 = 39, /*!< GPIO39, input mode only */ + GPIO_NUM_MAX = 40, } gpio_num_t; typedef enum { @@ -205,9 +206,8 @@ typedef enum { } gpio_pull_mode_t; - +typedef void (*gpio_isr_t)(void*); typedef intr_handle_t gpio_isr_handle_t; -typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num); /** * @brief GPIO common configuration @@ -357,8 +357,6 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); */ esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle); - - /** * @brief Enable pull-up on GPIO. * @@ -403,93 +401,55 @@ esp_err_t gpio_pulldown_en(gpio_num_t gpio_num); */ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); +/** + * @brief Install a GPIO ISR service, so we can assign different ISR handler for different pins + * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Operation fail + * - ESP_ERR_NO_MEM No memory to install this service + */ +esp_err_t gpio_install_isr_service(int intr_alloc_flags); /** - * *************** ATTENTION ********************/ -/** - *@attention - * Each GPIO has its own separate configuration register, so we do not use - * a lock to serialize access to them. This works under the assumption that - * no situation will occur where two tasks try to configure the same GPIO - * pin simultaneously. It is up to the application developer to guarantee this. - */ + * @brief Un-install GPIO ISR service, free the resources. + */ +void gpio_uninstall_isr_service(); /** - *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * - * @code{c} - * gpio_config_t io_conf; - * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - * io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode - * io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 0; //disable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * @endcode - **/ + * @brief Add ISR handler for the corresponding GPIO. + * + * Interrupt handlers no longer need to be declared with IRAM_ATTR, unless you pass the ESP_INTR_FLAG_IRAM flag + * when allocating the ISR in gpio_install_isr_service(). + * This ISR handler will be called from an ISR. So there probably is some stack size limit, and this limit + * is smaller compared to a "raw" interrupt handler due to another level of indirection. + * + * @param gpio_num GPIO number + * @param isr_handler ISR handler function for the corresponding GPIO number. + * @param args parameter for ISR handler. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Wrong state, the ISR service has not been initialized. + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void* args); /** - *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * - * @code{c} - * io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt - * io_conf.mode = GPIO_MODE_INPUT; //set as input - * io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 1; //enable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * @endcode - */ -/** - *----------EXAMPLE TO SET ISR HANDLER ---------------------- - * @code{c} - * gpio_isr_register(gpio_intr_test, 0, NULL); //hook the isr handler for GPIO interrupt - * @endcode - */ -/** - *-------------EXAMPLE OF HANDLER FUNCTION-------------------* - * @code{c} - * #include "esp_attr.h" - * void IRAM_ATTR gpio_intr_test(void* arg) - * { - * //GPIO intr process - * ets_printf("in gpio_intr\n"); - * uint32_t gpio_num = 0; - * uint32_t gpio_intr_status = READ_PERI_REG(GPIO_STATUS_REG); //read status to get interrupt status for GPIO0-31 - * uint32_t gpio_intr_status_h = READ_PERI_REG(GPIO_STATUS1_REG);//read status1 to get interrupt status for GPIO32-39 - * SET_PERI_REG_MASK(GPIO_STATUS_W1TC_REG, gpio_intr_status); //Clear intr for gpio0-gpio31 - * SET_PERI_REG_MASK(GPIO_STATUS1_W1TC_REG, gpio_intr_status_h); //Clear intr for gpio32-39 - * do { - * if(gpio_num < 32) { - * if(gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 - * ets_printf("Intr GPIO%d ,val: %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } else { - * if(gpio_intr_status_h & BIT(gpio_num - 32)) { - * ets_printf("Intr GPIO%d, val : %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } - * } while(++gpio_num < GPIO_PIN_COUNT); - * } - * @endcode - */ + * @brief Remove ISR handler for the corresponding GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Wrong state, the ISR service has not been initialized. + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num); -/** - *----EXAMPLE OF I2C CONFIG AND PICK SIGNAL FOR IO MATRIX---* - * @code{c} - * gpio_config_t io_conf; - * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - * io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; //set as output mode - * io_conf.pin_bit_mask = GPIO_SEL_21 | GPIO_SEL_22; //bit mask of the pins that you want to set,e.g.GPIO21/22 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 1; //enable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * gpio_matrix_out(21, EXT_I2C_SCL_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_out(22, EXT_I2C_SDA_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_in( 22, EXT_I2C_SDA_I_IDX, 0); //set input signal for io_matrix - * @endcode - * - */ #ifdef __cplusplus } diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 0cd4eca3652..f331b9d2651 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -4,20 +4,17 @@ GPIO Overview -------- -`Instructions`_ +The ESP32 chip features 40 physical GPIO pads. Some GPIO pads cannot be used or do not have the corresponding pin on the chip package(refer to technical reference manual ). Each pad can be used as a general purpose I/O or can be connected to an internal peripheral signal. +Note that GPIO6-11 are usually used for SPI flash. GPIO34-39 can only be set as input mode. Application Example ------------------- -`Instructions`_ +GPIO output and input interrupt example: `examples/21_gpio `_. API Reference ------------- -`Instructions`_ - -.. _Instructions: template.html - Header Files ^^^^^^^^^^^^ @@ -110,7 +107,8 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygentypedef:: gpio_event_callback +.. doxygentypedef:: gpio_isr_t +.. doxygentypedef:: gpio_isr_handle_t Enumerations ^^^^^^^^^^^^ @@ -122,6 +120,13 @@ Enumerations .. doxygenenum:: gpio_pulldown_t .. doxygenenum:: gpio_pull_mode_t +Structures +^^^^^^^^^^ + +.. doxygenstruct:: gpio_config_t + :members: + + Functions ^^^^^^^^^ @@ -136,3 +141,12 @@ Functions .. doxygenfunction:: gpio_wakeup_enable .. doxygenfunction:: gpio_wakeup_disable .. doxygenfunction:: gpio_isr_register +.. doxygenfunction:: gpio_pullup_en +.. doxygenfunction:: gpio_pullup_dis +.. doxygenfunction:: gpio_pulldown_en +.. doxygenfunction:: gpio_pulldown_dis +.. doxygenfunction:: gpio_install_isr_service +.. doxygenfunction:: gpio_uninstall_isr_service +.. doxygenfunction:: gpio_isr_handler_add +.. doxygenfunction:: gpio_isr_handler_remove + diff --git a/examples/21_gpio/Makefile b/examples/21_gpio/Makefile new file mode 100644 index 00000000000..0589a9f21d7 --- /dev/null +++ b/examples/21_gpio/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := gpio + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/21_gpio/README.md b/examples/21_gpio/README.md new file mode 100644 index 00000000000..7436d77532e --- /dev/null +++ b/examples/21_gpio/README.md @@ -0,0 +1,20 @@ +# Example: GPIO + +###This test code shows how to configure gpio and how to use gpio interrupt. + + +####GPIO functions: + + * GPIO18: output + * GPIO19: output + * GPIO4: input, pulled up, interrupt from rising edge and falling edge + * GPIO5: input, pulled up, interrupt from rising edge. + +####Test: + * Connect GPIO18 with GPIO4 + * Connect GPIO19 with GPIO5 + * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5 + + + + diff --git a/examples/21_gpio/main/component.mk b/examples/21_gpio/main/component.mk new file mode 100644 index 00000000000..44bd2b5273f --- /dev/null +++ b/examples/21_gpio/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/21_gpio/main/gpio_test.c b/examples/21_gpio/main/gpio_test.c new file mode 100644 index 00000000000..a5e9571bcac --- /dev/null +++ b/examples/21_gpio/main/gpio_test.c @@ -0,0 +1,114 @@ +/* GPIO Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" + +/** + * Brief: + * This test code shows how to configure gpio and how to use gpio interrupt. + * + * GPIO status: + * GPIO18: output + * GPIO19: output + * GPIO4: input, pulled up, interrupt from rising edge and falling edge + * GPIO5: input, pulled up, interrupt from rising edge. + * + * Test: + * Connect GPIO18 with GPIO4 + * Connect GPIO19 with GPIO5 + * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5 + * + */ + +#define GPIO_OUTPUT_IO_0 18 +#define GPIO_OUTPUT_IO_1 19 +#define GPIO_OUTPUT_PIN_SEL ((1< Date: Thu, 29 Dec 2016 17:29:14 +0800 Subject: [PATCH 029/167] Add i2s driver --- components/driver/i2s.c | 807 +++++++++++++++++++++++++ components/driver/include/driver/i2s.h | 380 ++++++++++++ examples/22_i2s/Makefile | 8 + examples/22_i2s/main/app_main.c | 72 +++ examples/22_i2s/main/component.mk | 10 + 5 files changed, 1277 insertions(+) create mode 100644 components/driver/i2s.c create mode 100644 components/driver/include/driver/i2s.h create mode 100644 examples/22_i2s/Makefile create mode 100644 examples/22_i2s/main/app_main.c create mode 100644 examples/22_i2s/main/component.mk diff --git a/components/driver/i2s.c b/components/driver/i2s.c new file mode 100644 index 00000000000..d8e6198370d --- /dev/null +++ b/components/driver/i2s.c @@ -0,0 +1,807 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" + +#include "soc/dport_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/sens_reg.h" +#include "rom/lldesc.h" + +#include "driver/gpio.h" +#include "driver/i2s.h" + +#include "esp_intr.h" +#include "esp_err.h" +#include "esp_log.h" + +static const char* I2S_TAG = "I2S"; +#define I2S_CHECK(a, str, ret) if (!(a)) { \ + ESP_LOGE(I2S_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } +#define I2S_BASE_CLK (2*APB_CLK_FREQ) +#define I2S_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) +#define gpio_matrix_out_check(a, b, c, d) if(a != -1) gpio_matrix_out(a, b, c, d) //if pin = -1, do not need to configure +#define gpio_matrix_in_check(a, b, c) if(a != -1) gpio_matrix_in(a, b, c) + + +/** + * @brief DMA buffer object + * + */ +typedef struct { + char **buf; + int buf_size; + int rw_pos; + void *curr_ptr; + SemaphoreHandle_t mux; + xQueueHandle queue; + lldesc_t **desc; +} i2s_dma_t; + +/** + * @brief I2S object instance + * + */ +typedef struct { + i2s_port_t i2s_num; /*!< I2S port number*/ + int queue_size; /*!< I2S event queue size*/ + QueueHandle_t i2s_queue; /*!< I2S queue handler*/ + int dma_buf_count; /*!< DMA buffer count, number of buffer*/ + int dma_buf_len; /*!< DMA buffer length, length of each buffer*/ + i2s_dma_t *rx; /*!< DMA Tx buffer*/ + i2s_dma_t *tx; /*!< DMA Rx buffer*/ + i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ + int channel_num; /*!< Number of channels*/ + int bytes_per_sample; /*!< Bytes per sample*/ + i2s_mode_t mode; /*!< I2S Working mode*/ +} i2s_obj_t; + +static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0}; +static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1}; +static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->conf.rx_fifo_reset = 1; + I2S[i2s_num]->conf.rx_fifo_reset = 0; + I2S[i2s_num]->conf.tx_fifo_reset = 1; + I2S[i2s_num]->conf.tx_fifo_reset = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_clear_intr_status(i2s_port_t i2s_num, uint32_t clr_mask) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S[i2s_num]->int_clr.val = clr_mask; + return ESP_OK; +} + +esp_err_t i2s_enable_rx_intr(i2s_port_t i2s_num) +{ + + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.in_suc_eof = 1; + I2S[i2s_num]->int_ena.in_dscr_err = 1; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_disable_rx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.in_suc_eof = 0; + I2S[i2s_num]->int_ena.in_dscr_err = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_disable_tx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.out_eof = 0; + I2S[i2s_num]->int_ena.out_dscr_err = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.out_eof = 1; + I2S[i2s_num]->int_ena.out_dscr_err = 1; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +static esp_err_t i2s_isr_register(i2s_port_t i2s_num, uint8_t intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle) +{ + return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle); +} + +static esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint8_t bits, bool fuzzy) +{ + int factor = (256%bits)? 384 : 256; // According to hardware codec requirement(supported 256fs or 384fs) + int clkmInteger, clkmDecimals, bck = 0; + float denom = (float)1 / 64; + int channel = 2; + + float clkmdiv = (float)I2S_BASE_CLK / (rate * factor); + if (clkmdiv > 256) { + ESP_LOGE(I2S_TAG, "clkmdiv is too large\r\n"); + return ESP_FAIL; + } + clkmInteger = clkmdiv; + clkmDecimals = (clkmdiv - clkmInteger) / denom; + float mclk = clkmInteger + denom * clkmDecimals; + bck = factor/(bits * channel); + + I2S[i2s_num]->clkm_conf.clka_en = 0; + I2S[i2s_num]->clkm_conf.clkm_div_a = 63; + I2S[i2s_num]->clkm_conf.clkm_div_b = clkmDecimals; + I2S[i2s_num]->clkm_conf.clkm_div_num = clkmInteger; + I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck; + I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck; + I2S[i2s_num]->sample_rate_conf.tx_bits_mod = bits; + I2S[i2s_num]->sample_rate_conf.rx_bits_mod = bits; + float real_rate = (float)(I2S_BASE_CLK / (bck * bits * clkmInteger)/2); + ESP_LOGI(I2S_TAG, "Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, real_rate, bits, clkmInteger, bck, (float)I2S_BASE_CLK / mclk, real_rate *16*2, 64, clkmDecimals); + return ESP_OK; +} + +static void IRAM_ATTR i2s_intr_handler_default(void *arg) +{ + i2s_obj_t *p_i2s = (i2s_obj_t*) arg; + uint8_t i2s_num = p_i2s->i2s_num; + i2s_dev_t* i2s_reg = I2S[i2s_num]; + i2s_event_t i2s_event; + int dummy; + + portBASE_TYPE high_priority_task_awoken = 0; + + lldesc_t *finish_desc; + + if (i2s_reg->int_st.out_dscr_err || i2s_reg->int_st.in_dscr_err) { + ESP_LOGE(I2S_TAG, "out_dscr_err: %d or in_dscr_err:%d", i2s_reg->int_st.out_dscr_err == 1, i2s_reg->int_st.in_dscr_err == 1); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_DMA_ERROR; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + + if (i2s_reg->int_st.out_eof && p_i2s->tx) { + finish_desc = (lldesc_t*) i2s_reg->out_eof_des_addr; + // All buffers are empty. This means we have an underflow on our hands. + if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { + xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->tx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_TX_DONE; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + + } + + if (i2s_reg->int_st.in_suc_eof && p_i2s->rx) { + // All buffers are full. This means we have an overflow. + finish_desc = (lldesc_t*) i2s_reg->in_eof_des_addr; + if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { + xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->rx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_RX_DONE; + if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + if (high_priority_task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + + i2s_reg->int_clr.val = I2S[i2s_num]->int_st.val; +} + +static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) +{ + int bux_idx; + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Not initialized yet"); + return ESP_FAIL; + } + if (dma == NULL) { + return ESP_FAIL; + } + for (bux_idx = 0; bux_idx < p_i2s_obj[i2s_num]->dma_buf_count; bux_idx++) { + if (dma->desc && dma->desc[bux_idx]) + free(dma->desc[bux_idx]); + if (dma->buf && dma->buf[bux_idx]) + free(dma->buf[bux_idx]); + } + if (dma->buf) + free(dma->buf); + if (dma->desc) + free(dma->desc); + vQueueDelete(dma->queue); + vSemaphoreDelete(dma->mux); + return ESP_OK; +} + +static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) +{ + int bux_idx; + int sample_size = p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + i2s_dma_t *dma = (i2s_dma_t*) malloc(sizeof(i2s_dma_t)); + if (dma == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc i2s_dma_t"); + return NULL; + } + memset(dma, 0, sizeof(i2s_dma_t)); + + dma->buf = (char **)malloc(sizeof(char*) * dma_buf_count); + if (dma->buf == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer pointer"); + + return NULL; + } + memset(dma->buf, 0, sizeof(char*) * dma_buf_count); + + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->buf[bux_idx] = (char*) malloc(dma_buf_len * sample_size); + if (dma->buf[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + ESP_LOGD(I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); + memset(dma->buf[bux_idx], 0, dma_buf_len * sample_size); + } + + dma->desc = (lldesc_t**) malloc(sizeof(lldesc_t*) * dma_buf_count); + if (dma->desc == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx] = (lldesc_t*) malloc(sizeof(lldesc_t)); + if (dma->desc[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description entry"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + } + + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx]->owner = 1; + dma->desc[bux_idx]->eof = 1; + dma->desc[bux_idx]->sosf = 0; + dma->desc[bux_idx]->length = dma_buf_len * sample_size; + dma->desc[bux_idx]->size = dma_buf_len * sample_size; + dma->desc[bux_idx]->buf = (uint8_t *) dma->buf[bux_idx]; + dma->desc[bux_idx]->offset = 0; + dma->desc[bux_idx]->empty = (uint32_t)((bux_idx < (dma_buf_count - 1)) ? (dma->desc[bux_idx + 1]) : dma->desc[0]); + } + dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char*)); + dma->mux = xSemaphoreCreateMutex(); + dma->rw_pos = 0; + dma->buf_size = dma_buf_len * sample_size; + dma->curr_ptr = NULL; + ESP_LOGI(I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_buf_len * sample_size, dma_buf_count); + return dma; +} + + +esp_err_t i2s_start(i2s_port_t i2s_num) +{ + //start DMA link + I2S_ENTER_CRITICAL(); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S[i2s_num]->int_clr.val = 0xFFFFFFFF; + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + ESP_LOGD(I2S_TAG, "I2S_MODE_TX"); + i2s_enable_tx_intr(i2s_num); + I2S[i2s_num]->out_link.start = 1; + I2S[i2s_num]->conf.tx_start = 1; + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + ESP_LOGD(I2S_TAG, "I2S_MODE_RX"); + i2s_enable_rx_intr(i2s_num); + I2S[i2s_num]->in_link.start = 1; + I2S[i2s_num]->conf.rx_start = 1; + } + esp_intr_enable(p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_stop(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + I2S[i2s_num]->out_link.stop = 1; + I2S[i2s_num]->conf.tx_start = 0; + i2s_disable_tx_intr(i2s_num); + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + I2S[i2s_num]->in_link.stop = 1; + I2S[i2s_num]->conf.rx_start = 0; + i2s_disable_rx_intr(i2s_num); + } + I2S_EXIT_CRITICAL(); + return 0; +} + +static esp_err_t configure_dac_pin(void) +{ + SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_DIG_FORCE_M); + SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_CLK_INV_M); + + SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC_XPD_FORCE_M); + SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC_M); + + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RUE_M); + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RDE_M); + + SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC_XPD_FORCE_M); + SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC_M); + + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RUE_M); + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RDE_M); + return ESP_OK; +} + +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (pin == NULL) { + return configure_dac_pin(); + } + if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->bck_io_num)) { + ESP_LOGE(I2S_TAG, "bck_io_num error"); + return ESP_FAIL; + } + if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->ws_io_num)) { + ESP_LOGE(I2S_TAG, "ws_io_num error"); + return ESP_FAIL; + } + if (pin->data_out_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_out_num)) { + ESP_LOGE(I2S_TAG, "data_out_num error"); + return ESP_FAIL; + } + if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_in_num)) { + ESP_LOGE(I2S_TAG, "data_in_num error"); + return ESP_FAIL; + } + + int bck_sig = -1, ws_sig = -1, data_out_sig = -1, data_in_sig = -1; + //TX & RX + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + bck_sig = I2S0I_BCK_OUT_IDX; + ws_sig = I2S0I_WS_OUT_IDX; + data_in_sig = I2S0I_DATA_IN15_IDX; + if (i2s_num == I2S_NUM_1) { + bck_sig = I2S1I_BCK_OUT_IDX; + ws_sig = I2S1I_WS_OUT_IDX; + data_in_sig = I2S1I_DATA_IN15_IDX; + } + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + bck_sig = I2S0O_BCK_OUT_IDX; + ws_sig = I2S0O_WS_OUT_IDX; + data_out_sig = I2S0O_DATA_OUT23_IDX; + if (i2s_num == I2S_NUM_1) { + bck_sig = I2S1O_BCK_OUT_IDX; + ws_sig = I2S1O_WS_OUT_IDX; + data_out_sig = I2S1O_DATA_OUT23_IDX; + } + } + + gpio_matrix_out_check(pin->data_out_num, data_out_sig, 0, 0); + gpio_matrix_in_check(pin->data_in_num, data_in_sig, 0); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { + gpio_matrix_out_check(pin->ws_io_num, ws_sig, 0, 0); + gpio_matrix_out_check(pin->bck_io_num, bck_sig, 0, 0); + } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { + gpio_matrix_in_check(pin->ws_io_num, ws_sig, 0); + gpio_matrix_in_check(pin->bck_io_num, bck_sig, 0); + } + ESP_LOGE(I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, data_in_sig, ws_sig, bck_sig); + return ESP_OK; +} + +esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((p_i2s_obj[i2s_num]->bytes_per_sample > 0), "bits_per_sample not set", ESP_FAIL); + return i2s_set_clk(i2s_num, rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0); +} +static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((i2s_config), "param null", ESP_FAIL); + if (i2s_num == I2S_NUM_1) { + periph_module_enable(PERIPH_I2S1_MODULE); + } else { + periph_module_enable(PERIPH_I2S0_MODULE); + } + // configure I2S data port interface. + i2s_reset_fifo(i2s_num); + + //reset i2s + I2S[i2s_num]->conf.tx_reset = 1; + I2S[i2s_num]->conf.tx_reset = 0; + I2S[i2s_num]->conf.rx_reset = 1; + I2S[i2s_num]->conf.rx_reset = 0; + + + //reset dma + I2S[i2s_num]->lc_conf.in_rst = 1; + I2S[i2s_num]->lc_conf.in_rst = 0; + I2S[i2s_num]->lc_conf.out_rst = 1; + I2S[i2s_num]->lc_conf.out_rst = 0; + + + //Enable and configure DMA + I2S[i2s_num]->lc_conf.check_owner = 0; + I2S[i2s_num]->lc_conf.out_loop_test = 0; + I2S[i2s_num]->lc_conf.out_auto_wrback = 0; + I2S[i2s_num]->lc_conf.out_data_burst_en = 0; + I2S[i2s_num]->lc_conf.outdscr_burst_en = 0; + I2S[i2s_num]->lc_conf.out_no_restart_clr = 0; + I2S[i2s_num]->lc_conf.indscr_burst_en = 0; + I2S[i2s_num]->lc_conf.out_eof_mode = 1; + + + I2S[i2s_num]->conf2.lcd_en = 0; + I2S[i2s_num]->conf2.camera_en = 0; + I2S[i2s_num]->pdm_conf.pcm2pdm_conv_en = 0; + I2S[i2s_num]->pdm_conf.pdm2pcm_conv_en = 0; + + I2S[i2s_num]->fifo_conf.dscr_en = 0; + p_i2s_obj[i2s_num]->channel_num = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; + + I2S[i2s_num]->conf_chan.tx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left + I2S[i2s_num]->fifo_conf.tx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->conf.tx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + + I2S[i2s_num]->conf_chan.rx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left + I2S[i2s_num]->fifo_conf.rx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->conf.rx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->fifo_conf.dscr_en = 1;//connect dma to fifo + + I2S[i2s_num]->conf.tx_start = 0; + I2S[i2s_num]->conf.rx_start = 0; + + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_msb_right = 0; + I2S[i2s_num]->conf.tx_right_first = 0; + + I2S[i2s_num]->conf.tx_slave_mod = 0; // Master + I2S[i2s_num]->fifo_conf.tx_fifo_mod_force_en = 1;//? + + if (i2s_config->mode & I2S_MODE_SLAVE) { + I2S[i2s_num]->conf.tx_slave_mod = 1;//TX Slave + } + } + + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_msb_right = 0; + I2S[i2s_num]->conf.rx_right_first = 0; + I2S[i2s_num]->conf.rx_slave_mod = 0; // Master + I2S[i2s_num]->fifo_conf.rx_fifo_mod_force_en = 1;//? + I2S[i2s_num]->rx_eof_num = (i2s_config->dma_buf_len); + if (i2s_config->mode & I2S_MODE_SLAVE) { + I2S[i2s_num]->conf.rx_slave_mod = 1;//RX Slave + } + } + + if (i2s_config->mode & I2S_MODE_DAC_BUILT_IN) { + I2S[i2s_num]->conf2.lcd_en = 1; + I2S[i2s_num]->conf.tx_right_first = 1; + I2S[i2s_num]->fifo_conf.tx_fifo_mod = 3; + } + + if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S) { + I2S[i2s_num]->conf.tx_short_sync = 0; + I2S[i2s_num]->conf.rx_short_sync = 0; + I2S[i2s_num]->conf.tx_msb_shift = 1; + I2S[i2s_num]->conf.rx_msb_shift = 1; + if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S_LSB) { + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_msb_shift = 0; + } + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_msb_shift = 0; + } + } + } + + if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM) { + I2S[i2s_num]->conf.tx_msb_shift = 0; + I2S[i2s_num]->conf.rx_msb_shift = 0; + I2S[i2s_num]->conf.tx_short_sync = 0; + I2S[i2s_num]->conf.rx_short_sync = 0; + if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM_SHORT) { + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_short_sync = 1; + } + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_short_sync = 1; + } + } + } + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) { + I2S[i2s_num]->conf.sig_loopback = 1; + } + i2s_set_clk(i2s_num, i2s_config->sample_rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0); + return ESP_OK; +} + +esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) +{ + int total_buffer_in_bytes = p_i2s_obj[i2s_num]->dma_buf_count * p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + const uint32_t zero_sample[2] = { 0 }; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + while (total_buffer_in_bytes > 0) { + i2s_push_sample(i2s_num, (const char*) zero_sample, 10); + total_buffer_in_bytes -= p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + } + return ESP_OK; +} + +esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((i2s_config != NULL), "I2S configuration must not NULL", ESP_FAIL); + I2S_CHECK((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), "I2S buffer count less than 128 and more than 2", ESP_FAIL); + I2S_CHECK((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 2048), "I2S buffer length at most 2048 and more than 8", ESP_FAIL); + if (p_i2s_obj[i2s_num] == NULL) { + p_i2s_obj[i2s_num] = (i2s_obj_t*) malloc(sizeof(i2s_obj_t)); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Malloc I2S driver error"); + return ESP_FAIL; + } + + p_i2s_obj[i2s_num]->i2s_num = i2s_num; + p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; + p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; + p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; + p_i2s_obj[i2s_num]->mode = i2s_config->mode; + p_i2s_obj[i2s_num]->bytes_per_sample = i2s_config->bits_per_sample/8; + + //initial dma + if (ESP_FAIL == i2s_isr_register(i2s_num, i2s_config->intr_alloc_flags, i2s_intr_handler_default, p_i2s_obj[i2s_num], &p_i2s_obj[i2s_num]->i2s_isr_handle)) { + free(p_i2s_obj[i2s_num]); + ESP_LOGE(I2S_TAG, "Register I2S Interrupt error"); + return ESP_FAIL; + } + i2s_stop(i2s_num); + i2s_param_config(i2s_num, i2s_config); + + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len); + if (p_i2s_obj[i2s_num]->tx == NULL) { + ESP_LOGE(I2S_TAG, "Failed to create tx dma buffer"); + i2s_driver_uninstall(i2s_num); + return ESP_FAIL; + } + I2S[i2s_num]->out_link.addr = (uint32_t) p_i2s_obj[i2s_num]->tx->desc[0]; + } + + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len); + if (p_i2s_obj[i2s_num]->rx == NULL){ + ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer"); + i2s_driver_uninstall(i2s_num); + return ESP_FAIL; + } + I2S[i2s_num]->in_link.addr = (uint32_t) p_i2s_obj[i2s_num]->rx->desc[0]; + } + + + if (i2s_queue) { + p_i2s_obj[i2s_num]->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); + *((QueueHandle_t*) i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; + ESP_LOGI(I2S_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_i2s_obj[i2s_num]->i2s_queue)); + } else { + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + + i2s_start(i2s_num); + } else { + ESP_LOGE(I2S_TAG, "I2S driver already installed"); + return ESP_FAIL; + } + + return ESP_OK; +} + +esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGI(I2S_TAG, "ALREADY NULL"); + return ESP_OK; + } + i2s_stop(i2s_num); + esp_intr_free(p_i2s_obj[i2s_num]->i2s_isr_handle); + + if (p_i2s_obj[i2s_num]->tx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->tx); + p_i2s_obj[i2s_num]->tx = NULL; + } + if (p_i2s_obj[i2s_num]->rx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->rx); + p_i2s_obj[i2s_num]->rx = NULL; + } + + if (p_i2s_obj[i2s_num]->i2s_queue) { + vQueueDelete(p_i2s_obj[i2s_num]->i2s_queue); + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + + free(p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + + if (i2s_num == I2S_NUM_0) { + periph_module_disable(PERIPH_I2S0_MODULE); + } else if (i2s_num == I2S_NUM_1) { + periph_module_disable(PERIPH_I2S1_MODULE); + } + return ESP_OK; +} + +int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait) +{ + char *data_ptr; + int bytes_can_write, bytes_writen = 0; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->tx == NULL) { + return 0; + } + xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + while (size > 0) { + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + ESP_LOGD(I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + bytes_can_write = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + if (bytes_can_write > size) { + bytes_can_write = size; + } + memcpy(data_ptr, src, bytes_can_write); + size -= bytes_can_write; + src += bytes_can_write; + p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + bytes_writen += bytes_can_write; + } + xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + return bytes_writen; +} + +int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait) +{ + char *data_ptr; + int bytes_can_read, byte_read = 0; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->rx == NULL) { + return 0; + } + xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + while (size > 0) { + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->rx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + bytes_can_read = p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; + if (bytes_can_read > size) { + bytes_can_read = size; + } + memcpy(dest, data_ptr, bytes_can_read); + size -= bytes_can_read; + dest += bytes_can_read; + p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; + byte_read += bytes_can_read; + } + xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); + return byte_read; +} +int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait) +{ + int i, bytes_to_push = 0; + char *data_ptr; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + return 0; + } + ESP_LOGD(I2S_TAG, "rw_pos: %d, buf_size: %d, curr_ptr: %d", p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *data_ptr++ = *sample++; + bytes_to_push ++; + } + if (p_i2s_obj[i2s_num]->channel_num == 2) { + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *data_ptr++ = *sample++; + bytes_to_push ++; + } + } + + p_i2s_obj[i2s_num]->tx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + return bytes_to_push; +} + +int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait) +{ + int i, bytes_to_pop = 0; + char *data_ptr; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + return 0; + } + p_i2s_obj[i2s_num]->rx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *sample++ = *data_ptr++; + bytes_to_pop++; + } + if (p_i2s_obj[i2s_num]->channel_num == 2) { + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *sample++ = *data_ptr++; + bytes_to_pop++; + } + } + + p_i2s_obj[i2s_num]->rx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + return bytes_to_pop; +} diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h new file mode 100644 index 00000000000..d44bb676a38 --- /dev/null +++ b/components/driver/include/driver/i2s.h @@ -0,0 +1,380 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_I2S_H_ +#define _DRIVER_I2S_H_ +#include "esp_err.h" +#include +#include "soc/gpio_reg.h" +#include "soc/soc.h" +#include "soc/i2s_struct.h" +#include "soc/i2s_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/io_mux_reg.h" +#include "rom/gpio.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include "driver/periph_ctrl.h" +#include "freertos/semphr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define I2S_PIN_NO_CHANGE (-1) + + +/** + * @brief I2S bit width per sample. + * + */ +typedef enum { + I2S_BITS_PER_SAMPLE_8BIT = 8, /*!< I2S bits per sample: 8-bits*/ + I2S_BITS_PER_SAMPLE_16BIT = 16, /*!< I2S bits per sample: 16-bits*/ + I2S_BITS_PER_SAMPLE_24BIT = 24, /*!< I2S bits per sample: 24-bits*/ + I2S_BITS_PER_SAMPLE_32BIT = 32, /*!< I2S bits per sample: 32-bits*/ +} i2s_bits_per_sample_t; + +/** + * @brief I2S communication standard format + * + */ +typedef enum { + I2S_COMM_FORMAT_I2S = 0x01, /*!< I2S communication format I2S*/ + I2S_COMM_FORMAT_I2S_MSB = 0x02, /*!< I2S format MSB*/ + I2S_COMM_FORMAT_I2S_LSB = 0x04, /*!< I2S format LSB*/ + I2S_COMM_FORMAT_PCM = 0x08, /*!< I2S communication format PCM*/ + I2S_COMM_FORMAT_PCM_SHORT = 0x10, /*!< PCM Short*/ + I2S_COMM_FORMAT_PCM_LONG = 0x20, /*!< PCM Long*/ +} i2s_comm_format_t; + + +/** + * @brief I2S channel format type + */ +typedef enum { + I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00, + I2S_CHANNEL_FMT_ALL_RIGHT, + I2S_CHANNEL_FMT_ALL_LEFT, + I2S_CHANNEL_FMT_ONLY_RIGHT, + I2S_CHANNEL_FMT_ONLY_LEFT, +} i2s_channel_fmt_t; + +/** + * @brief PDM sample rate ratio, measured in Hz. + * + */ +typedef enum { + PDM_SAMPLE_RATE_RATIO_64, + PDM_SAMPLE_RATE_RATIO_128, +} pdm_sample_rate_ratio_t; + +/** + * @brief PDM PCM convter enable/disable. + * + */ +typedef enum { + PDM_PCM_CONV_ENABLE, + PDM_PCM_CONV_DISABLE, +} pdm_pcm_conv_t; + + +/** + * @brief I2S Peripheral, 0 & 1. + * + */ +typedef enum { + I2S_NUM_0 = 0x0, /*!< I2S 0*/ + I2S_NUM_1 = 0x1, /*!< I2S 1*/ + I2S_NUM_MAX, +} i2s_port_t; + +/** + * @brief I2S Mode, defaut is I2S_MODE_MASTER | I2S_MODE_TX + * + */ +typedef enum { + I2S_MODE_MASTER = 1, + I2S_MODE_SLAVE = 2, + I2S_MODE_TX = 4, + I2S_MODE_RX = 8, + I2S_MODE_DAC_BUILT_IN = 16 +} i2s_mode_t; + +/** + * @brief I2S configuration parameters for i2s_param_config function + * + */ +typedef struct { + i2s_mode_t mode; /*!< I2S work mode*/ + int sample_rate; /*!< I2S sample rate*/ + i2s_bits_per_sample_t bits_per_sample; /*!< I2S bits per sample*/ + i2s_channel_fmt_t channel_format; /*!< I2S channel format */ + i2s_comm_format_t communication_format; /*!< I2S communication format */ + int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */ + int dma_buf_count; /*!< I2S DMA Buffer Count */ + int dma_buf_len; /*!< I2S DMA Buffer Length */ +} i2s_config_t; + +/** + * @brief I2S event types + * + */ +typedef enum { + I2S_EVENT_DMA_ERROR, + I2S_EVENT_TX_DONE, /*!< I2S DMA finish sent 1 buffer*/ + I2S_EVENT_RX_DONE, /*!< I2S DMA finish received 1 buffer*/ + I2S_EVENT_MAX, /*!< I2S event max index*/ +} i2s_event_type_t; + +/** + * @brief Event structure used in I2S event queue + * + */ +typedef struct { + i2s_event_type_t type; /*!< I2S event type */ + size_t size; /*!< I2S data size for I2S_DATA event*/ +} i2s_event_t; + +/** + * @brief I2S pin number for i2s_set_pin + * + */ +typedef struct { + int bck_io_num; /*!< BCK in out pin*/ + int ws_io_num; /*!< WS in out pin*/ + int data_out_num; /*!< DATA out pin*/ + int data_in_num; /*!< DATA in pin*/ +} i2s_pin_config_t; + +typedef intr_handle_t i2s_isr_handle_t; +/** + * @brief Set I2S pin number + * + * @note + * Internal signal can be output to multiple GPIO pads + * Only one GPIO pad can connect with input signal + * + * @param i2s_num I2S_NUM_0 or I2S_NUM_1 + * + * @param pin I2S Pin struct, or NULL for 2-channels, 8-bits DAC pin configuration (GPIO25 & GPIO26) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin); + +/** + * @brief i2s install and start driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param i2s_config I2S configurations - see i2s_config_t struct + * + * @param queue_size I2S event queue size/depth. + * + * @param i2s_queue I2S event queue handle, if set NULL, driver will not use an event queue. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue); + +/** + * @brief Uninstall I2S driver. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num); + +/** + * @brief i2s read data buffer to i2s dma buffer + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param src source address to write + * + * @param size size of data (size in bytes) + * + * @param ticks_to_wait Write timeout + * + * @return number of written bytes + */ +int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait); + +/** + * @brief i2s write data buffer to i2s dma buffer + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param dest destination address to read + * + * @param size size of data (size in bytes) + * + * @param ticks_to_wait Read timeout + * + * @return number of read bytes + */ +int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait); + +/** + * @brief i2s push 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8) + * + * @param ticks_to_wait Push timeout + * + * @return number of push bytes + */ +int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait); + +/** + * @brief Pop 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8) + * + * @param ticks_to_wait Pop timeout + * + * @return number of pop bytes + */ +int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait); + + +/** + * @brief Set clock rate used for I2S RX and TX + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param rate I2S clock (ex: 8000, 44100...) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate); + +/** + * @brief Start driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * +* @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_start(i2s_port_t i2s_num); + +/** + * @brief Stop driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_stop(i2s_port_t i2s_num); + +/** + * @brief Set the TX DMA buffer contents to all zeroes + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num); + +/***************************EXAMPLE********************************** + * + * + * ----------------EXAMPLE OF I2S SETTING --------------------- + * @code{c} + * + * #include "freertos/queue.h" + * #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h + * int i2s_num = 0; //i2s port number + * i2s_config_t i2s_config = { + * .mode = I2S_MODE_MASTER | I2S_MODE_TX, + * .sample_rate = 44100, + * .bits_per_sample = 16, //16, 32 + * .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //format LEFT_RIGHT + * .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + * .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + * .dma_buf_count = 8, + * .dma_buf_len = 64 + * }; + * + * i2s_pin_config_t pin_config = { + * .bck_io_num = 26, + * .ws_io_num = 25, + * .data_out_num = 22, + * .data_in_num = I2S_PIN_NO_CHANGE + * }; + * + * i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + * + * i2s_set_pin(i2s_num, &pin_config); + * + * i2s_set_sample_rates(i2s_num, 22050); //set sample rates + * + * + * i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + *@endcode + * + * ----------------EXAMPLE USING I2S WITH DAC --------------------- + * @code{c} + * + * #include "freertos/queue.h" + * #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h + * int i2s_num = 0; //i2s port number + * i2s_config_t i2s_config = { + * .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, + * .sample_rate = 44100, + * .bits_per_sample = 8, // Only 8-bit DAC support + * .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // + * .communication_format = I2S_COMM_FORMAT_I2S_MSB, + * .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + * .dma_buf_count = 8, + * .dma_buf_len = 64 + * }; + * + * + * i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + * + * i2s_set_pin(i2s_num, NULL); //for internal DAC + * + * i2s_set_sample_rates(i2s_num, 22050); //set sample rates + * + * i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + *@endcode + *-----------------------------------------------------------------------------* + ***************************END OF EXAMPLE**********************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_I2S_H_ */ diff --git a/examples/22_i2s/Makefile b/examples/22_i2s/Makefile new file mode 100644 index 00000000000..b15a137b884 --- /dev/null +++ b/examples/22_i2s/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +VERBOSE = 1 +PROJECT_NAME := esp32-i2s-driver-example +include $(IDF_PATH)/make/project.mk + diff --git a/examples/22_i2s/main/app_main.c b/examples/22_i2s/main/app_main.c new file mode 100644 index 00000000000..9c8f80fd568 --- /dev/null +++ b/examples/22_i2s/main/app_main.c @@ -0,0 +1,72 @@ +/* I2S Example + + This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "driver/i2s.h" +#include + + +#define SAMPLE_RATE (36000) +#define I2S_NUM (0) +#define WAVE_FREQ_HZ (100) +#define PI 3.14159265 + +#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ) + +void app_main() +{ + unsigned int i, sample_val; + float sin_float, triangle_float, triangle_step = 65536.0 / SAMPLE_PER_CYCLE; + //for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes each sample) + //using 6 buffers, we need 60-samples per buffer + //2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = SAMPLE_RATE, + .bits_per_sample = 16, //16-bit per channel + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + .dma_buf_count = 6, + .dma_buf_len = 60, // + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1 + }; + i2s_pin_config_t pin_config = { + .bck_io_num = 26, + .ws_io_num = 25, + .data_out_num = 22, + .data_in_num = -1 //Not used + }; + + nvs_flash_init(); + i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM, &pin_config); + + triangle_float = -32767; + + for(i = 0; i < SAMPLE_PER_CYCLE; i++) { + sin_float = sin(i * PI / 180.0); + if(sin_float >= 0) + triangle_float += triangle_step; + else + triangle_float -= triangle_step; + sin_float *= 32767; + + sample_val = 0; + sample_val += (short)triangle_float; + sample_val = sample_val << 16; + sample_val += (short) sin_float; + + i2s_push_sample(I2S_NUM, (char *)&sample_val, portMAX_DELAY); + } +} diff --git a/examples/22_i2s/main/component.mk b/examples/22_i2s/main/component.mk new file mode 100644 index 00000000000..dbc5a7a9e7f --- /dev/null +++ b/examples/22_i2s/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# + +COMPONENT_ADD_INCLUDEDIRS := . From d0fccbce15360ff520bc1133f311592895c1e024 Mon Sep 17 00:00:00 2001 From: Chen Wu Date: Mon, 19 Dec 2016 15:40:21 +0800 Subject: [PATCH 030/167] examples: Add OTA demo --- components/app_update/esp_ota_ops.c | 2 +- examples/18_ota/Makefile | 9 + examples/18_ota/OTA_workflow.png | Bin 0 -> 58211 bytes examples/18_ota/README.md | 101 ++++++++ examples/18_ota/main/Kconfig.projbuild | 40 ++++ examples/18_ota/main/component.mk | 4 + examples/18_ota/main/ota.c | 318 +++++++++++++++++++++++++ examples/18_ota/sdkconfig | 5 + 8 files changed, 478 insertions(+), 1 deletion(-) create mode 100644 examples/18_ota/Makefile create mode 100644 examples/18_ota/OTA_workflow.png create mode 100644 examples/18_ota/README.md create mode 100644 examples/18_ota/main/Kconfig.projbuild create mode 100644 examples/18_ota/main/component.mk create mode 100644 examples/18_ota/main/ota.c create mode 100644 examples/18_ota/sdkconfig diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index e95937256f8..439117b5963 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -378,4 +378,4 @@ const esp_partition_t *esp_ota_get_boot_partition(void) ESP_LOGE(TAG, "not found current bin"); return NULL; } -} \ No newline at end of file +} diff --git a/examples/18_ota/Makefile b/examples/18_ota/Makefile new file mode 100644 index 00000000000..7ffb10234f1 --- /dev/null +++ b/examples/18_ota/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ota + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/18_ota/OTA_workflow.png b/examples/18_ota/OTA_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..3e91dafb3801df54ddd86cceb069d6ecf9bae75f GIT binary patch literal 58211 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

^@Oi^){Hpy^ltaO74E6F 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 literal 0 HcmV?d00001 diff --git a/examples/18_ota/README.md b/examples/18_ota/README.md new file mode 100644 index 00000000000..49a47d3799d --- /dev/null +++ b/examples/18_ota/README.md @@ -0,0 +1,101 @@ + +# Simple OTA Demo + +This example demonstrates a working OTA (over the air) firmware update workflow. + +This example is a *simplified demonstration*, for production firmware updates you should use a secure protocol such as HTTPS. + +--- + +# Aim + +An app running on ESP32 can upgrade itself by downloading a new app "image" binary file, and storing it in flash. + +In this example, the ESP32 has 3 images in flash: factory, OTA_0, OTA_1. Each of these is a self-contained partition. The number of OTA image partition is determined by the partition table layout. + +Flashing the example over serial with "make flash" updates the factory app image. On first boot, the bootloader loads this factory app image which then performs an OTA update (triggered in the example code). The update downloads a new image from an http server and saves it into the OTA_0 partition. At this point the example code updates the ota_data partition to indicate the new app partition, and resets. The bootloader reads ota_data, determines the new OTA image has been selected, and runs it. + + +# Worflow + +The OTA_workflow.png diagram demonstrates the overall workflow: + +![OTA Workflow diagram](OTA_workflow.png) + +## Step 1: Connect to AP + +Connect your host PC to the same AP that you will use for the ESP32. + +## Step 2: Run HTTP Server + +Python has a built-in HTTP server that can be used for example purposes. + +For our upgrade example OTA file, we're going to use the `01_hello_world` example. + +Open a new terminal to run the HTTP server, then run these commands to build the example and start the server: + +``` +cd $IDF_PATH/examples/01_hello_world +make +cd build +python -m SimpleHTTPServer 8070 +``` + +While the server is running, the contents of the build directory can be browsed at http://localhost:8070/ + +NB: On some systems, the command may be `python2 -m SimpleHTTPServer`. + +NB: You've probably noticed there is nothing special about the "hello world" example when used for OTA updates. This is because any .bin app file which is built by esp-idf can be used as an app image for OTA. The only difference is whether it is written to a factory partition or an OTA partition. + +If you have any firewall software running that will block incoming access to port 8070, configure it to allow access while running the example. + +## Step 3: Build OTA Example + +Change back to the OTA example directory, and type `make menuconfig` to configure the OTA example. Under the "Example Configuration" submenu, fill in the following details: + +* WiFi SSID & Password +* IP address of your host PC as "HTTP Server" +* HTTP Port number (if using the Python HTTP server above, the default is correct) + +If serving the "hello world" example, you can leave the default filename as-is. + +Save your changes, and type `make` to build the example. + +## Step 4: Flash OTA Example + +When flashing, use the `erase_flash` target first to erase the entire flash (this deletes any leftover data in the ota_data partition). Then flash the factory image over serial: + +``` +make erase_flash flash +``` + +(The `make erase_flash flash` means "erase everything, then flash". `make flash` only erases the parts of flash which are being rewritten.) + +## Step 5: Run the OTA Example + +When the example starts up, it will print "ota: Starting OTA example..." then: + +1. Connect to the AP with configured SSID and password. +2. Connect to the HTTP server and download the new image. +3. Write the image to flash, and configure the next boot from this image. +4. Reboot + +# Troubleshooting + +* Check your PC can ping the ESP32 at its IP, and that the IP, AP and other configuration settings are correct in menuconfig. +* Check if any firewall software is preventing incoming connections on the PC. +* Check you can see the configured file (default hello-world.bin) if you browse the file listing at http://127.0.0.1/ +* If you have another PC or a phone, try viewing the file listing from the separate host. + +## Error "ota_begin error err=0x104" + +If you see this error then check that the configured (and actual) flash size is large enough for the partitions in the partition table. The default "two OTA slots" partition table only works with 4MB flash size. To use OTA with smaller flash sizes, create a custom partition table CSV (look in components/partition_table) and configure it in menuconfig. + +If changing partition layout, it is usually wise to run "make erase_flash" between steps. + +## Production Implementation + +If scaling this example for production use, please consider: + +* Using an encrypted communications channel such as HTTPS. +* Dealing with timeouts or WiFi disconnections while flashing. diff --git a/examples/18_ota/main/Kconfig.projbuild b/examples/18_ota/main/Kconfig.projbuild new file mode 100644 index 00000000000..8727d1b76c9 --- /dev/null +++ b/examples/18_ota/main/Kconfig.projbuild @@ -0,0 +1,40 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + + Can be left blank if the network has no security set. + +config SERVER_IP + string "HTTP Server IP" + default "192.168.0.3" + help + HTTP Server IP to download the image file from. + + See example README.md for details. + +config SERVER_PORT + string "HTTP Server Port" + default "8070" + help + HTTP Server port to connect to. + Should be chosen not to conflict with any other port used + on the system. + +config EXAMPLE_FILENAME + string "HTTP GET Filename" + default "/hello-world.bin" + help + Filename of the app image file to download for + the OTA update. + +endmenu diff --git a/examples/18_ota/main/component.mk b/examples/18_ota/main/component.mk new file mode 100644 index 00000000000..a98f634eae0 --- /dev/null +++ b/examples/18_ota/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/18_ota/main/ota.c b/examples/18_ota/main/ota.c new file mode 100644 index 00000000000..8c8caf2d376 --- /dev/null +++ b/examples/18_ota/main/ota.c @@ -0,0 +1,318 @@ +/* OTA example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_partition.h" + +#include "nvs_flash.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" + +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD +#define EXAMPLE_SERVER_IP CONFIG_SERVER_IP +#define EXAMPLE_SERVER_PORT CONFIG_SERVER_PORT +#define EXAMPLE_FILENAME CONFIG_EXAMPLE_FILENAME +#define BUFFSIZE 1024 +#define TEXT_BUFFSIZE 1024 + +static const char *TAG = "ota"; +/*an ota data write buffer ready to write to the flash*/ +char ota_write_data[BUFFSIZE + 1] = { 0 }; +/*an packet receive buffer*/ +char text[BUFFSIZE + 1] = { 0 }; +/* an image total length*/ +int binary_file_length = 0; +/*socket id*/ +int socket_id = -1; +char http_request[64] = {0}; +/* operate handle : uninitialized value is zero ,every ota begin would exponential growth*/ +esp_ota_handle_t out_handle = 0; +esp_partition_t operate_partition; + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +/*read buffer by byte still delim ,return read bytes counts*/ +int read_until(char *buffer, char delim, int len) +{ +// /*TODO: delim check,buffer check,further: do an buffer length limited*/ + int i = 0; + while (buffer[i] != delim && i < len) { + ++i; + } + return i + 1; +} + +/* resolve a packet from http socket + * return true if packet including \r\n\r\n that means http packet header finished,start to receive packet body + * otherwise return false + * */ +bool resolve_pkg(char text[], int total_len, esp_ota_handle_t out_handle) +{ + /* i means current position */ + int i = 0, i_read_len = 0; + while (text[i] != 0 && i < total_len) { + i_read_len = read_until(&text[i], '\n', total_len); + // if we resolve \r\n line,we think packet header is finished + if (i_read_len == 2) { + int i_write_len = total_len - (i + 2); + memset(ota_write_data, 0, BUFFSIZE); + /*copy first http packet body to write buffer*/ + memcpy(ota_write_data, &(text[i + 2]), i_write_len); + /*check write packet header first byte:0xE9 second byte:0x09 */ + if (ota_write_data[0] == 0xE9 && i_write_len >= 2 && ota_write_data[1] == 0x09) { + ESP_LOGI(TAG, "OTA Write Header format Check OK. first byte is %02x ,second byte is %02x", ota_write_data[0], ota_write_data[1]); + } else { + ESP_LOGE(TAG, "OTA Write Header format Check Failed! first byte is %02x ,second byte is %02x", ota_write_data[0], ota_write_data[1]); + return false; + } + + esp_err_t err = esp_ota_write( out_handle, (const void *)ota_write_data, i_write_len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err); + return false; + } else { + ESP_LOGI(TAG, "esp_ota_write header OK"); + binary_file_length += i_write_len; + } + return true; + } + i += i_read_len; + } + return false; +} + +bool connect_to_http_server() +{ + ESP_LOGI(TAG, "Server IP: %s Server Port:%s", EXAMPLE_SERVER_IP, EXAMPLE_SERVER_PORT); + sprintf(http_request, "GET %s HTTP/1.1\r\nHost: %s:%s \r\n\r\n", EXAMPLE_FILENAME, EXAMPLE_SERVER_IP, EXAMPLE_SERVER_PORT); + + int http_connect_flag = -1; + struct sockaddr_in sock_info; + + socket_id = socket(AF_INET, SOCK_STREAM, 0); + if (socket_id == -1) { + ESP_LOGE(TAG, "Create socket failed!"); + return false; + } + + // set connect info + memset(&sock_info, 0, sizeof(struct sockaddr_in)); + sock_info.sin_family = AF_INET; + sock_info.sin_addr.s_addr = inet_addr(EXAMPLE_SERVER_IP); + sock_info.sin_port = htons(atoi(EXAMPLE_SERVER_PORT)); + + // connect to http server + http_connect_flag = connect(socket_id, (struct sockaddr *)&sock_info, sizeof(sock_info)); + if (http_connect_flag == -1) { + ESP_LOGE(TAG, "Connect to server failed! errno=%d", errno); + close(socket_id); + return false; + } else { + ESP_LOGI(TAG, "Connected to server"); + return true; + } + return false; +} + +bool ota_init() +{ + esp_err_t err; + const esp_partition_t *esp_current_partition = esp_ota_get_boot_partition(); + if (esp_current_partition->type != ESP_PARTITION_TYPE_APP) { + ESP_LOGE(TAG, "Error: esp_current_partition->type != ESP_PARTITION_TYPE_APP"); + return false; + } + + esp_partition_t find_partition; + memset(&operate_partition, 0, sizeof(esp_partition_t)); + /*choose which OTA image should we write to*/ + switch (esp_current_partition->subtype) { + case ESP_PARTITION_SUBTYPE_APP_FACTORY: + find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0; + break; + case ESP_PARTITION_SUBTYPE_APP_OTA_0: + find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_1; + break; + case ESP_PARTITION_SUBTYPE_APP_OTA_1: + find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0; + break; + default: + break; + } + find_partition.type = ESP_PARTITION_TYPE_APP; + + const esp_partition_t *partition = esp_partition_find_first(find_partition.type, find_partition.subtype, NULL); + assert(partition != NULL); + memset(&operate_partition, 0, sizeof(esp_partition_t)); + err = esp_ota_begin( partition, OTA_SIZE_UNKNOWN, &out_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_begin failed err=0x%x!", err); + return false; + } else { + memcpy(&operate_partition, partition, sizeof(esp_partition_t)); + ESP_LOGI(TAG, "esp_ota_begin init OK"); + return true; + } + return false; +} + +void __attribute__((noreturn)) task_fatal_error() +{ + ESP_LOGE(TAG, "Exiting task due to fatal error..."); + close(socket_id); + (void)vTaskDelete(NULL); +} + +void main_task(void *pvParameter) +{ + esp_err_t err; + ESP_LOGI(TAG, "Starting OTA example..."); + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server...."); + + /*connect to http server*/ + if (connect_to_http_server()) { + ESP_LOGI(TAG, "Connected to http server"); + } else { + ESP_LOGE(TAG, "Connect to http server failed!"); + task_fatal_error(); + } + + int res = -1; + /*send GET request to http server*/ + res = send(socket_id, http_request, strlen(http_request), 0); + if (res == -1) { + ESP_LOGE(TAG, "Send GET request to server failed"); + task_fatal_error(); + } else { + ESP_LOGI(TAG, "Send GET request to server succeeded"); + } + + if ( ota_init() ) { + ESP_LOGI(TAG, "OTA Init succeeded"); + } else { + ESP_LOGE(TAG, "OTA Init failed"); + task_fatal_error(); + } + + bool pkg_body_start = false, flag = true; + /*deal with all receive packet*/ + while (flag) { + memset(text, 0, TEXT_BUFFSIZE); + memset(ota_write_data, 0, BUFFSIZE); + int buff_len = recv(socket_id, text, TEXT_BUFFSIZE, 0); + if (buff_len < 0) { /*receive error*/ + ESP_LOGE(TAG, "Error: receive data error! errno=%d", errno); + task_fatal_error(); + } else if (buff_len > 0 && !pkg_body_start) { /*deal with packet header*/ + memcpy(ota_write_data, text, buff_len); + pkg_body_start = resolve_pkg(text, buff_len, out_handle); + } else if (buff_len > 0 && pkg_body_start) { /*deal with packet body*/ + memcpy(ota_write_data, text, buff_len); + err = esp_ota_write( out_handle, (const void *)ota_write_data, buff_len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err); + task_fatal_error(); + } + binary_file_length += buff_len; + ESP_LOGI(TAG, "Have written image length %d", binary_file_length); + } else if (buff_len == 0) { /*packet over*/ + flag = false; + ESP_LOGI(TAG, "Connection closed, all packets received"); + close(socket_id); + } else { + ESP_LOGE(TAG, "Unexpected recv result"); + } + } + + ESP_LOGI(TAG, "Total Write binary data length : %d", binary_file_length); + + if (esp_ota_end(out_handle) != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_end failed!"); + task_fatal_error(); + } + err = esp_ota_set_boot_partition(&operate_partition); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%x", err); + task_fatal_error(); + } + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); + return ; +} + +void app_main() +{ + nvs_flash_init(); + initialise_wifi(); + xTaskCreate(&main_task, "main_task", 8192, NULL, 5, NULL); +} diff --git a/examples/18_ota/sdkconfig b/examples/18_ota/sdkconfig new file mode 100644 index 00000000000..070b529a05e --- /dev/null +++ b/examples/18_ota/sdkconfig @@ -0,0 +1,5 @@ +# Some sdkconfig parameters overriden from the defaults for this example, +# This file is in git but will be overwritten with the autogenerated file +# the first time "menuconfig" is run (changes ignored by git). +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_TWO_OTA=y From 2350288a33a3fd6396603eee0642d494fbf42c0d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 21 Dec 2016 12:46:24 +1100 Subject: [PATCH 031/167] examples: Move sdkconfig.defaults support into build system Is used fairly widely, and a little bit buggy in the form where it was in each Makefile (didn't always get copied in place). --- docs/build_system.rst | 9 +++++++++ examples/05_ble_adv/Makefile | 8 -------- examples/12_blufi/Makefile | 8 -------- examples/14_gatt_server/Makefile | 8 -------- examples/15_gatt_client/Makefile | 8 -------- examples/18_ota/sdkconfig | 5 ----- examples/18_ota/sdkconfig.defaults | 4 ++++ make/project_config.mk | 18 +++++++++++++++--- 8 files changed, 28 insertions(+), 40 deletions(-) delete mode 100644 examples/18_ota/sdkconfig create mode 100644 examples/18_ota/sdkconfig.defaults diff --git a/docs/build_system.rst b/docs/build_system.rst index 6687fa69ed2..2e388bd4956 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -477,3 +477,12 @@ is set then the component can instruct the linker to link other binaries instead .. _esp-idf-template: https://github.com/espressif/esp-idf-template .. _GNU Make Manual: https://www.gnu.org/software/make/manual/make.html .. _[_f1]: Actually, some components in esp-idf are "pure configuration" components that don't have a component.mk file, only a Makefile.projbuild and/or Kconfig.projbuild file. However, these components are unusual and most components have a component.mk file. + + +Custom sdkconfig defaults +------------------------- + +For example projects or other projects where you don't want to specify a full sdkconfig configuration, but you do want to override some key values from the esp-idf defaults, it is possible to create a file ``sdkconfig.defaults`` in the project directory. This file will be used when running ``make defconfig``, or creating a new config from scratch. + +To override the name of this file, set the ``SDKCONFIG_DEFAULTS`` environment variable. + diff --git a/examples/05_ble_adv/Makefile b/examples/05_ble_adv/Makefile index 3a913b817bc..2319786d4a9 100644 --- a/examples/05_ble_adv/Makefile +++ b/examples/05_ble_adv/Makefile @@ -6,11 +6,3 @@ PROJECT_NAME := ble_adv include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/12_blufi/Makefile b/examples/12_blufi/Makefile index 7e7548444f4..9c80f26c1a5 100644 --- a/examples/12_blufi/Makefile +++ b/examples/12_blufi/Makefile @@ -8,11 +8,3 @@ PROJECT_NAME := blufi_demo COMPONENT_ADD_INCLUDEDIRS := components/include include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/14_gatt_server/Makefile b/examples/14_gatt_server/Makefile index d7732bd2807..2f76e60b607 100644 --- a/examples/14_gatt_server/Makefile +++ b/examples/14_gatt_server/Makefile @@ -8,11 +8,3 @@ PROJECT_NAME := gatt_server_demos COMPONENT_ADD_INCLUDEDIRS := components/include include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/15_gatt_client/Makefile b/examples/15_gatt_client/Makefile index 93f793308f7..700ffd7add7 100644 --- a/examples/15_gatt_client/Makefile +++ b/examples/15_gatt_client/Makefile @@ -8,11 +8,3 @@ PROJECT_NAME := gatt_client_demo COMPONENT_ADD_INCLUDEDIRS := components/include include $(IDF_PATH)/make/project.mk - -# Copy some defaults into the sdkconfig by default -# so BT stack is enabled -sdkconfig: sdkconfig.defaults - $(Q) cp $< $@ - -menuconfig: sdkconfig -defconfig: sdkconfig diff --git a/examples/18_ota/sdkconfig b/examples/18_ota/sdkconfig deleted file mode 100644 index 070b529a05e..00000000000 --- a/examples/18_ota/sdkconfig +++ /dev/null @@ -1,5 +0,0 @@ -# Some sdkconfig parameters overriden from the defaults for this example, -# This file is in git but will be overwritten with the autogenerated file -# the first time "menuconfig" is run (changes ignored by git). -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_PARTITION_TABLE_TWO_OTA=y diff --git a/examples/18_ota/sdkconfig.defaults b/examples/18_ota/sdkconfig.defaults new file mode 100644 index 00000000000..2289a82300f --- /dev/null +++ b/examples/18_ota/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Default sdkconfig parameters to use the OTA +# partition table layout, with a 4MB flash size +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_TWO_OTA=y diff --git a/make/project_config.mk b/make/project_config.mk index 187d1ac2825..b8e40f43576 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -11,6 +11,10 @@ KCONFIG_TOOL_DIR=$(IDF_PATH)/tools/kconfig # unless it's overriden (happens for bootloader) SDKCONFIG ?= $(PROJECT_PATH)/sdkconfig +# SDKCONFIG_DEFAULTS is an optional file containing default +# overrides (usually used for esp-idf examples) +SDKCONFIG_DEFAULTS ?= $(PROJECT_PATH)/sdkconfig.defaults + # reset MAKEFLAGS as the menuconfig makefile uses implicit compile rules $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf: MAKEFLAGS=$(ORIGINAL_MAKEFLAGS) CC=$(HOSTCC) LD=$(HOSTLD) \ @@ -21,21 +25,29 @@ KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfi COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" KCONFIG_CONFIG=$(SDKCONFIG) \ COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" -menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig +menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig | defconfig $(summary) MENUCONFIG $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(SDKCONFIG))","") ifeq ("$(call prereq_if_explicit,defconfig)","") -# if not configuration is present and defconfig is not a target, run makeconfig -$(SDKCONFIG): menuconfig +# if not configuration is present and defconfig is not a target, run defconfig then menuconfig +$(SDKCONFIG): defconfig menuconfig else +# otherwise, just defconfig $(SDKCONFIG): defconfig endif endif +$(wildcard $(PROJECT_PATH)/sdkconfig.defaults): | menuconfig defconfig + cp $< $@ + +# defconfig creates a default config, based on SDKCONFIG_DEFAULTS if present defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) $(summary) DEFCONFIG +ifneq ("$(wildcard $(SDKCONFIG_DEFAULTS))","") + cp $(SDKCONFIG_DEFAULTS) $(SDKCONFIG) +endif mkdir -p $(BUILD_DIR_BASE)/include/config $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig From d245f016ea013b6c12e1ca4755a671eb57a1cc61 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Dec 2016 18:02:47 +1100 Subject: [PATCH 032/167] esptool: Add new options to reset before/after, detect flash size --- components/bootloader/Makefile.projbuild | 5 ++- components/esptool_py/Kconfig.projbuild | 57 ++++++++++++++++++++++++ components/esptool_py/Makefile.projbuild | 9 +++- components/esptool_py/esptool | 2 +- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 35be94e4a39..9e7b17b65fc 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -50,11 +50,14 @@ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH # One time flashing requires user to run esptool.py command themselves, # and warning is printed about inability to reflash. +# +# The flashing command is deliberately printed without an auto-reset +# step, so the device doesn't immediately reset to flash itself. bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "Bootloader built. One-time flash command is:" - @echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" + @echo "$(subst hard_reset,no_reset,$(ESPTOOLPY_WRITE_FLASH)) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index 8bab51225eb..dd2077cbb3f 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -121,4 +121,61 @@ config ESPTOOLPY_FLASHSIZE default "8MB" if ESPTOOLPY_FLASHSIZE_8MB default "16MB" if ESPTOOLPY_FLASHSIZE_16MB +config ESPTOOLPY_FLASHSIZE_DETECT + bool "Detect flash size when flashing bootloader" + default y + help + If this option is set, 'make flash' targets will automatically detect + the flash size and update the bootloader image when flashing. + +choice ESPTOOLPY_BEFORE + prompt "Before flashing" + default ESPTOOLPY_BEFORE_RESET + help + Configure whether esptool.py should reset the ESP32 before flashing. + + Automatic resetting depends on the RTS & DTR signals being + wired from the serial port to the ESP32. Most USB development + boards do this internally. + + The "Reset with ESP32R0 Windows workaround" option works + around an automatic reset bug in hardware, when using Windows + with some development boards. This fix only works if you're + using a silicon revision 0 ESP32. + +config ESPTOOLPY_BEFORE_RESET + bool "Reset to bootloader" +config ESPTOOLPY_BEFORE_NORESET + bool "No reset" +config ESPTOOLPY_BEFORE_ESP32R0 + bool "Reset with ESP32R0 Windows workaround" +endchoice + +config ESPTOOLPY_BEFORE + string + default "default_reset" if ESPTOOLPY_BEFORE_RESET + default "no_reset" if ESPTOOLPY_BEFORE_NORESET + default "esp32r0" if ESPTOOLPY_BEFORE_ESP32R0 + +choice ESPTOOLPY_AFTER + prompt "After flashing" + default ESPTOOLPY_AFTER_RESET + help + Configure whether esptool.py should reset the ESP32 after flashing. + + Automatic resetting depends on the RTS & DTR signals being + wired from the serial port to the ESP32. Most USB development + boards do this internally. + +config ESPTOOLPY_AFTER_RESET + bool "Reset after flashing" +config ESPTOOLPY_AFTER_NORESET + bool "Stay in bootloader" +endchoice + +config ESPTOOLPY_AFTER + string + default "hard_reset" if ESPTOOLPY_AFTER_RESET + default "no_reset" if ESPTOOLPY_AFTER_NORESET + endmenu diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index b5992a4d963..88b5894731a 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -13,7 +13,7 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) # ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 -ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) --before $(CONFIG_ESPTOOLPY_BEFORE) --after $(CONFIG_ESPTOOLPY_AFTER) # Supporting esptool command line tools ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py @@ -21,10 +21,15 @@ ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py export ESPSECUREPY # is used in bootloader_support component ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) +ifdef CONFIG_ESPTOOLPY_FLASHSIZE_DETECT +ESPTOOL_WRITE_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size detect +else +ESPTOOL_WRITE_FLASH_OPTIONS := $(ESPTOOL_FLASH_OPTIONS) +endif ESPTOOL_ELF2IMAGE_OPTIONS := -ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) +ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_WRITE_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index adc914b91ac..fe69994270e 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit adc914b91ac6d2cfd1ace56307b4374eb9439e14 +Subproject commit fe69994270e2a450aad3e94a409b58460b1a214f From 76e61ded3055c8933c9616688a1241bfdaaab1cc Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 13:15:01 +1100 Subject: [PATCH 033/167] bootloader: Call esp_partition_table_basic_verify() as part of standard boot Was previously only verified during flash encryption. --- .../bootloader/src/main/bootloader_start.c | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 7dd753b3f88..e5fda9cf530 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -40,6 +40,7 @@ #include "esp_image_format.h" #include "esp_secure_boot.h" #include "esp_flash_encrypt.h" +#include "esp_flash_partitions.h" #include "bootloader_flash.h" #include "bootloader_config.h" @@ -116,16 +117,14 @@ bool load_partition_table(bootloader_state_t* bs) { const esp_partition_info_t *partitions; const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ - const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); char *partition_usage; - - ESP_LOGI(TAG, "Partition Table:"); - ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + esp_err_t err; + int num_partitions; #ifdef CONFIG_SECURE_BOOT_ENABLED if(esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying partition table signature..."); - esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; @@ -141,17 +140,21 @@ bool load_partition_table(bootloader_state_t* bs) } ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); - for(int i = 0; i < MAX_PARTITIONS; i++) { + err = esp_partition_table_basic_verify(partitions, true, &num_partitions); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table"); + return false; + } + + ESP_LOGI(TAG, "Partition Table:"); + ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + + for(int i = 0; i < num_partitions; i++) { const esp_partition_info_t *partition = &partitions[i]; ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); partition_usage = "unknown"; - if (partition->magic != ESP_PARTITION_MAGIC) { - /* invalid partition definition indicates end-of-table */ - break; - } - /* valid partition table */ switch(partition->type) { case PART_TYPE_APP: /* app partition */ From 3783e28f0e57bf47ba12c67df9e0f09143b17abd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 13:16:22 +1100 Subject: [PATCH 034/167] bootloader: Check all partitions fit inside configured flash size --- .../bootloader_support/src/flash_partitions.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/components/bootloader_support/src/flash_partitions.c b/components/bootloader_support/src/flash_partitions.c index ed427df1a6b..b60968f9696 100644 --- a/components/bootloader_support/src/flash_partitions.c +++ b/components/bootloader_support/src/flash_partitions.c @@ -13,32 +13,43 @@ // limitations under the License. #include "esp_flash_partitions.h" #include "esp_log.h" +#include "rom/spi_flash.h" static const char *TAG = "flash_parts"; esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions) { int num_parts; + uint32_t chip_size = g_rom_flashchip.chip_size; *num_partitions = 0; for(num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) { const esp_partition_info_t *part = &partition_table[num_parts]; - if(part->magic == 0xFFFF - && part->type == PART_TYPE_END - && part->subtype == PART_SUBTYPE_END) { + if (part->magic == 0xFFFF + && part->type == PART_TYPE_END + && part->subtype == PART_SUBTYPE_END) { /* TODO: check md5 */ ESP_LOGD(TAG, "partition table verified, %d entries", num_parts); *num_partitions = num_parts; return ESP_OK; } - if(part->magic != ESP_PARTITION_MAGIC) { + if (part->magic != ESP_PARTITION_MAGIC) { if (log_errors) { ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic); } return ESP_ERR_INVALID_STATE; } + + const esp_partition_pos_t *pos = &part->pos; + if (pos->offset > chip_size || pos->offset + pos->size > chip_size) { + if (log_errors) { + ESP_LOGE(TAG, "partition %d invalid - offset 0x%x size 0x%x exceeds flash chip size 0x%x", + num_parts, pos->offset, pos->size, chip_size); + } + return ESP_ERR_INVALID_SIZE; + } } if (log_errors) { From 79d6d9f7019b5d33ff7cb4ebedcbf49457be25c2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 15:20:49 +1100 Subject: [PATCH 035/167] CI build_examples: Correctly detect example build failures "pipefail" regression when fail-on-warnings was added... --- make/build_examples.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index f1b37b7b4bf..dc10d95ab29 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -30,10 +30,11 @@ for example in ${IDF_PATH}/examples/*; do # build non-verbose first BUILDLOG=$(mktemp -t examplebuild.XXXX.log) ( + set -o pipefail # so result of make all isn't lost when piping to tee set -e make clean defconfig - make all 2>&1 | tee $BUILDLOG - ) || (RESULT=$?; make V=1) # only build verbose if there's an error + make $* all 2>&1 | tee $BUILDLOG + ) || { RESULT=$?; make V=1; } # only build verbose if there's an error popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) From eb8324e2c280837aecde5d454ba52d663d3c77a8 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 16:12:48 +1100 Subject: [PATCH 036/167] examples: Fix build errors that weren't being caught by CI --- examples/12_blufi/components/blufi/blufi.c | 1 + examples/12_blufi/main/demo_main.c | 14 +++++++------- .../{14_ethernet => 17_ethernet}/main/tlk110_phy.h | 0 3 files changed, 8 insertions(+), 7 deletions(-) rename examples/{14_ethernet => 17_ethernet}/main/tlk110_phy.h (100%) diff --git a/examples/12_blufi/components/blufi/blufi.c b/examples/12_blufi/components/blufi/blufi.c index 5a1c543b50c..ce473667a47 100644 --- a/examples/12_blufi/components/blufi/blufi.c +++ b/examples/12_blufi/components/blufi/blufi.c @@ -30,6 +30,7 @@ #include "bt_trace.h" #include "bt_types.h" +#include "bta_api.h" #include "blufi.h" diff --git a/examples/12_blufi/main/demo_main.c b/examples/12_blufi/main/demo_main.c index f1c38077410..90992313c22 100644 --- a/examples/12_blufi/main/demo_main.c +++ b/examples/12_blufi/main/demo_main.c @@ -46,15 +46,15 @@ const int CONNECTED_BIT = BIT0; static wifi_config_t sta_config; static char tmp_ssid[33]; -static char tmp_passwd[33]; +static char tmp_passwd[65]; static bool confirm = false; void wifi_set_blue_config(char *ssid, char *passwd) { - memset(tmp_ssid, 0, 33); - memset(tmp_passwd, 0, 33); - strcpy(tmp_ssid, ssid); - strcpy(tmp_passwd, passwd); + memset(tmp_ssid, 0, sizeof(tmp_ssid)); + memset(tmp_passwd, 0, sizeof(tmp_passwd)); + strlcpy(tmp_ssid, ssid, sizeof(tmp_ssid)); + strlcpy(tmp_passwd, passwd, sizeof(tmp_passwd)); confirm = true; LOG_DEBUG("confirm true\n"); } @@ -105,8 +105,8 @@ void wifiTestTask(void *pvParameters) if (confirm) { confirm = false; - strcpy(sta_config.sta.ssid, tmp_ssid); - strcpy(sta_config.sta.password, tmp_passwd); + memcpy(sta_config.sta.ssid, tmp_ssid, sizeof(sta_config.sta.ssid)); + memcpy(sta_config.sta.password, tmp_passwd, sizeof(sta_config.sta.password)); sta_config.sta.bssid_set = 0; ret = esp_wifi_disconnect(); diff --git a/examples/14_ethernet/main/tlk110_phy.h b/examples/17_ethernet/main/tlk110_phy.h similarity index 100% rename from examples/14_ethernet/main/tlk110_phy.h rename to examples/17_ethernet/main/tlk110_phy.h From 6b87419d420427ef4cec012a784b046731b27714 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 16:13:07 +1100 Subject: [PATCH 037/167] CI build_examples: Don't stop on first failed example, print failure summary --- make/build_examples.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index dc10d95ab29..ab592087952 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -11,11 +11,10 @@ EXAMPLE_NUM=1 RESULT=0 +FAILED_EXAMPLES="" RESULT_WARNINGS=22 # magic number result code for "warnings found" -set -e - for example in ${IDF_PATH}/examples/*; do [ -f ${example}/Makefile ] || continue echo "Building ${example} as ${EXAMPLE_NUM}..." @@ -34,13 +33,13 @@ for example in ${IDF_PATH}/examples/*; do set -e make clean defconfig make $* all 2>&1 | tee $BUILDLOG - ) || { RESULT=$?; make V=1; } # only build verbose if there's an error + ) || { RESULT=$?; FAILED_EXAMPLES+=" ${example}"; make V=1; } # only build verbose if there's an error popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) - if [ $RESULT -eq 0 ] && grep -q ": warning:" $BUILDLOG; then - echo "Build will fail, due to warnings in this example" - RESULT=$RESULT_WARNINGS + if grep -q ": warning:" $BUILDLOG; then + [ $RESULT -eq 0 ] && RESULT=$RESULT_WARNINGS + FAILED_EXAMPLES+=" ${example} (warnings)" fi rm -f $BUILDLOG @@ -50,5 +49,7 @@ if [ $RESULT -eq $RESULT_WARNINGS ]; then echo "Build would have passed, except for warnings." fi +[ $RESULT -eq 0 ] || echo "Failed examples: $FAILED_EXAMPLES" + exit $RESULT From 2b41c1b8b26e144523812b84daf3e7706f9798e7 Mon Sep 17 00:00:00 2001 From: liuhan Date: Sat, 24 Dec 2016 12:58:37 +0800 Subject: [PATCH 038/167] components/coap: Add libcoap library as submodule --- .gitmodules | 3 +++ components/coap/libcoap | 1 + 2 files changed, 4 insertions(+) create mode 160000 components/coap/libcoap diff --git a/.gitmodules b/.gitmodules index c26f92e80c0..9cba0ec5efc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "components/micro-ecc/micro-ecc"] path = components/micro-ecc/micro-ecc url = https://github.com/kmackay/micro-ecc.git +[submodule "components/coap/libcoap"] + path = components/coap/libcoap + url = https://github.com/obgm/libcoap.git diff --git a/components/coap/libcoap b/components/coap/libcoap new file mode 160000 index 00000000000..6468887a126 --- /dev/null +++ b/components/coap/libcoap @@ -0,0 +1 @@ +Subproject commit 6468887a12666f88b8704d797fc176cd4f40ee4c From d0b10ba2dd30ee5fbd3343778b8e77859f621aee Mon Sep 17 00:00:00 2001 From: liuhan Date: Sat, 24 Dec 2016 14:43:53 +0800 Subject: [PATCH 039/167] components/coap: Add libcoap port for ESP32 platform --- components/coap/Makefile.projbuild | 1 + components/coap/component.mk | 9 + components/coap/port/coap_io_socket.c | 468 ++++++++++++++++++ components/coap/port/include/coap/coap.h | 50 ++ components/coap/port/include/coap_config.h | 39 ++ .../coap/port/include/coap_config_posix.h | 41 ++ components/lwip/include/lwip/port/arpa/inet.h | 20 + .../lwip/include/lwip/port/netinet/in.h | 22 + components/nghttp/Makefile.projbuild | 3 - 9 files changed, 650 insertions(+), 3 deletions(-) create mode 100644 components/coap/Makefile.projbuild create mode 100644 components/coap/component.mk create mode 100644 components/coap/port/coap_io_socket.c create mode 100644 components/coap/port/include/coap/coap.h create mode 100644 components/coap/port/include/coap_config.h create mode 100644 components/coap/port/include/coap_config_posix.h create mode 100644 components/lwip/include/lwip/port/arpa/inet.h create mode 100644 components/lwip/include/lwip/port/netinet/in.h diff --git a/components/coap/Makefile.projbuild b/components/coap/Makefile.projbuild new file mode 100644 index 00000000000..e976ee32f6e --- /dev/null +++ b/components/coap/Makefile.projbuild @@ -0,0 +1 @@ +CFLAGS += -DWITH_POSIX diff --git a/components/coap/component.mk b/components/coap/component.mk new file mode 100644 index 00000000000..b5f6ec43c25 --- /dev/null +++ b/components/coap/component.mk @@ -0,0 +1,9 @@ +# +# Component Makefile +# + +COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap + +COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_time.o libcoap/src/debug.o libcoap/src/encode.o libcoap/src/hashkey.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o port/coap_io_socket.o + +COMPONENT_SRCDIRS := libcoap/src libcoap port diff --git a/components/coap/port/coap_io_socket.c b/components/coap/port/coap_io_socket.c new file mode 100644 index 00000000000..eec8cc1319a --- /dev/null +++ b/components/coap/port/coap_io_socket.c @@ -0,0 +1,468 @@ +/* + * Network function implementation with socket for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * network operations are in use. + * + * coap_io.h -- Default network I/O functions for libcoap + * + * Copyright (C) 2012,2014 Olaf Bergmann + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see + * README for terms of use. + */ + +#include "coap_config.h" + +#ifdef HAVE_STDIO_H +# include +#endif + +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_SYS_UIO_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#include + +#ifdef WITH_CONTIKI +# include "uip.h" +#endif + +#include "pdu.h" +#include "debug.h" +#include "mem.h" +#include "coap_io.h" + +#ifdef WITH_POSIX +/* define generic PKTINFO for IPv4 */ +#if defined(IP_PKTINFO) +# define GEN_IP_PKTINFO IP_PKTINFO +#elif defined(IP_RECVDSTADDR) +# define GEN_IP_PKTINFO IP_RECVDSTADDR +#else +# error "Need IP_PKTINFO or IP_RECVDSTADDR to request ancillary data from OS." +#endif /* IP_PKTINFO */ + +/* define generic KTINFO for IPv6 */ +#ifdef IPV6_RECVPKTINFO +# define GEN_IPV6_PKTINFO IPV6_RECVPKTINFO +#elif defined(IPV6_PKTINFO) +# define GEN_IPV6_PKTINFO IPV6_PKTINFO +#else +# error "Need IPV6_PKTINFO or IPV6_RECVPKTINFO to request ancillary data from OS." +#endif /* IPV6_RECVPKTINFO */ + +struct coap_packet_t { + coap_if_handle_t hnd; /**< the interface handle */ + coap_address_t src; /**< the packet's source address */ + coap_address_t dst; /**< the packet's destination address */ + const coap_endpoint_t *interface; + + int ifindex; + void *session; /**< opaque session data */ + + size_t length; /**< length of payload */ + unsigned char payload[]; /**< payload */ +}; +#endif + +#ifdef CUSTOM_COAP_NETWORK_ENDPOINT + +#ifdef WITH_CONTIKI +static int ep_initialized = 0; + +static inline struct coap_endpoint_t * +coap_malloc_contiki_endpoint() { + static struct coap_endpoint_t ep; + + if (ep_initialized) { + return NULL; + } else { + ep_initialized = 1; + return &ep; + } +} + +static inline void +coap_free_contiki_endpoint(struct coap_endpoint_t *ep) { + ep_initialized = 0; +} + +coap_endpoint_t * +coap_new_endpoint(const coap_address_t *addr, int flags) { + struct coap_endpoint_t *ep = coap_malloc_contiki_endpoint(); + + if (ep) { + memset(ep, 0, sizeof(struct coap_endpoint_t)); + ep->handle.conn = udp_new(NULL, 0, NULL); + + if (!ep->handle.conn) { + coap_free_endpoint(ep); + return NULL; + } + + coap_address_init(&ep->addr); + uip_ipaddr_copy(&ep->addr.addr, &addr->addr); + ep->addr.port = addr->port; + udp_bind((struct uip_udp_conn *)ep->handle.conn, addr->port); + } + return ep; +} + +void +coap_free_endpoint(coap_endpoint_t *ep) { + if (ep) { + if (ep->handle.conn) { + uip_udp_remove((struct uip_udp_conn *)ep->handle.conn); + } + coap_free_contiki_endpoint(ep); + } +} + +#else /* WITH_CONTIKI */ +static inline struct coap_endpoint_t * +coap_malloc_posix_endpoint(void) { + return (struct coap_endpoint_t *)coap_malloc(sizeof(struct coap_endpoint_t)); +} + +static inline void +coap_free_posix_endpoint(struct coap_endpoint_t *ep) { + coap_free(ep); +} + +coap_endpoint_t * +coap_new_endpoint(const coap_address_t *addr, int flags) { + int sockfd = socket(addr->addr.sa.sa_family, SOCK_DGRAM, 0); + int on = 1; + struct coap_endpoint_t *ep; + + if (sockfd < 0) { + coap_log(LOG_WARNING, "coap_new_endpoint: socket"); + return NULL; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + coap_log(LOG_WARNING, "coap_new_endpoint: setsockopt SO_REUSEADDR"); + + if (bind(sockfd, &addr->addr.sa, addr->size) < 0) { + coap_log(LOG_WARNING, "coap_new_endpoint: bind"); + close (sockfd); + return NULL; + } + + ep = coap_malloc_posix_endpoint(); + if (!ep) { + coap_log(LOG_WARNING, "coap_new_endpoint: malloc"); + close(sockfd); + return NULL; + } + + memset(ep, 0, sizeof(struct coap_endpoint_t)); + ep->handle.fd = sockfd; + ep->flags = flags; + + ep->addr.size = addr->size; + if (getsockname(sockfd, &ep->addr.addr.sa, &ep->addr.size) < 0) { + coap_log(LOG_WARNING, "coap_new_endpoint: cannot determine local address"); + close (sockfd); + return NULL; + } + +#ifndef NDEBUG + if (LOG_DEBUG <= coap_get_log_level()) { +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 40 +#endif + unsigned char addr_str[INET6_ADDRSTRLEN+8]; + + if (coap_print_addr(&ep->addr, addr_str, INET6_ADDRSTRLEN+8)) { + debug("created %sendpoint %s\n", + ep->flags & COAP_ENDPOINT_DTLS ? "DTLS " : "", + addr_str); + } + } +#endif /* NDEBUG */ + + return (coap_endpoint_t *)ep; +} + +void +coap_free_endpoint(coap_endpoint_t *ep) { + if(ep) { + if (ep->handle.fd >= 0) + close(ep->handle.fd); + coap_free_posix_endpoint((struct coap_endpoint_t *)ep); + } +} + +#endif /* WITH_CONTIKI */ +#endif /* CUSTOM_COAP_NETWORK_ENDPOINT */ + +#ifdef CUSTOM_COAP_NETWORK_SEND + +#if defined(WITH_POSIX) != defined(HAVE_NETINET_IN_H) +/* define struct in6_pktinfo and struct in_pktinfo if not available + FIXME: check with configure +*/ +struct in6_pktinfo { + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ +}; + +struct in_pktinfo { + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +#if defined(WITH_POSIX) && !defined(SOL_IP) +/* Solaris expects level IPPROTO_IP for ancillary data. */ +#define SOL_IP IPPROTO_IP +#endif + +#ifdef __GNUC__ +#define UNUSED_PARAM __attribute__ ((unused)) +#else /* not a GCC */ +#define UNUSED_PARAM +#endif /* GCC */ + +ssize_t +coap_network_send(struct coap_context_t *context UNUSED_PARAM, + const coap_endpoint_t *local_interface, + const coap_address_t *dst, + unsigned char *data, + size_t datalen) { + + struct coap_endpoint_t *ep = + (struct coap_endpoint_t *)local_interface; + +#ifndef WITH_CONTIKI + return sendto(ep->handle.fd, data, datalen, 0, (struct sockaddr*)&dst->addr.sa, sizeof(struct sockaddr)); +#else /* WITH_CONTIKI */ + /* FIXME: untested */ + /* FIXME: is there a way to check if send was successful? */ + uip_udp_packet_sendto((struct uip_udp_conn *)ep->handle.conn, data, datalen, + &dst->addr, dst->port); + return datalen; +#endif /* WITH_CONTIKI */ +} + +#endif /* CUSTOM_COAP_NETWORK_SEND */ + +#ifdef CUSTOM_COAP_NETWORK_READ + +#define SIN6(A) ((struct sockaddr_in6 *)(A)) + +#ifdef WITH_POSIX +static coap_packet_t * +coap_malloc_packet(void) { + coap_packet_t *packet; + const size_t need = sizeof(coap_packet_t) + COAP_MAX_PDU_SIZE; + + packet = (coap_packet_t *)coap_malloc(need); + if (packet) { + memset(packet, 0, need); + } + return packet; +} + +void +coap_free_packet(coap_packet_t *packet) { + coap_free(packet); +} +#endif /* WITH_POSIX */ +#ifdef WITH_CONTIKI +static inline coap_packet_t * +coap_malloc_packet(void) { + return (coap_packet_t *)coap_malloc_type(COAP_PACKET, 0); +} + +void +coap_free_packet(coap_packet_t *packet) { + coap_free_type(COAP_PACKET, packet); +} +#endif /* WITH_CONTIKI */ + +static inline size_t +coap_get_max_packetlength(const coap_packet_t *packet UNUSED_PARAM) { + return COAP_MAX_PDU_SIZE; +} + +void +coap_packet_populate_endpoint(coap_packet_t *packet, coap_endpoint_t *target) +{ + target->handle = packet->interface->handle; + memcpy(&target->addr, &packet->dst, sizeof(target->addr)); + target->ifindex = packet->ifindex; + target->flags = 0; /* FIXME */ +} +void +coap_packet_copy_source(coap_packet_t *packet, coap_address_t *target) +{ + memcpy(target, &packet->src, sizeof(coap_address_t)); +} +void +coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t *length) +{ + *address = packet->payload; + *length = packet->length; +} + +/** + * Checks if a message with destination address @p dst matches the + * local interface with address @p local. This function returns @c 1 + * if @p dst is a valid match, and @c 0 otherwise. + */ +static inline int +is_local_if(const coap_address_t *local, const coap_address_t *dst) { + return coap_address_isany(local) || coap_address_equals(dst, local) || + coap_is_mcast(dst); +} + +ssize_t +coap_network_read(coap_endpoint_t *ep, coap_packet_t **packet) { + ssize_t len = -1; + +#ifdef WITH_POSIX + #define SOC_APPDATA_LEN 1460 + char *soc_appdata = NULL; + struct sockaddr_in soc_srcipaddr; + socklen_t soc_srcsize = sizeof(struct sockaddr_in); +#endif /* WITH_POSIX */ + + assert(ep); + assert(packet); + + *packet = coap_malloc_packet(); + + if (!*packet) { + warn("coap_network_read: insufficient memory, drop packet\n"); + return -1; + } + + coap_address_init(&(*packet)->dst); /* the local interface address */ + coap_address_init(&(*packet)->src); /* the remote peer */ + +#ifdef WITH_POSIX + soc_appdata = coap_malloc(SOC_APPDATA_LEN); + if (soc_appdata){ + len = recvfrom(ep->handle.fd, soc_appdata, SOC_APPDATA_LEN, 0, (struct sockaddr *)&soc_srcipaddr, (socklen_t *)&soc_srcsize); + + if (len < 0){ + coap_log(LOG_WARNING, "coap_network_read: %s\n", strerror(errno)); + goto error; + } else { + /* use getsockname() to get the local port */ + (*packet)->dst.size = sizeof((*packet)->dst.addr); + if (getsockname(ep->handle.fd, &(*packet)->dst.addr.sa, &(*packet)->dst.size) < 0) { + coap_log(LOG_DEBUG, "cannot determine local port\n"); + goto error; + } + + /* local interface for IPv4 */ + (*packet)->src.size = sizeof((*packet)->src.addr); + memcpy(&(*packet)->src.addr.sa, &soc_srcipaddr, (*packet)->src.size); + + if (len > coap_get_max_packetlength(*packet)) { + /* FIXME: we might want to send back a response */ + warn("discarded oversized packet\n"); + goto error; + } + + if (!is_local_if(&ep->addr, &(*packet)->dst)) { + coap_log(LOG_DEBUG, "packet received on wrong interface, dropped\n"); + printf("error 3\n"); + goto error; + } + + (*packet)->length = len; + + memcpy(&(*packet)->payload, soc_appdata, len); + } + + coap_free(soc_appdata); + soc_appdata = NULL; + } else { + goto error; + } +#endif /* WITH_POSIX */ +#ifdef WITH_CONTIKI + /* FIXME: untested, make this work */ +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN]) + + if(uip_newdata()) { + uip_ipaddr_copy(&(*packet)->src.addr, &UIP_IP_BUF->srcipaddr); + (*packet)->src.port = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&(*packet)->dst.addr, &UIP_IP_BUF->destipaddr); + (*packet)->dst.port = UIP_UDP_BUF->destport; + + if (!is_local_if(&ep->addr, &(*packet)->dst)) { + coap_log(LOG_DEBUG, "packet received on wrong interface, dropped\n"); + goto error; + } + + len = uip_datalen(); + + if (len > coap_get_max_packetlength(*packet)) { + /* FIXME: we might want to send back a response */ + warn("discarded oversized packet\n"); + return -1; + } + + ((char *)uip_appdata)[len] = 0; +#ifndef NDEBUG + if (LOG_DEBUG <= coap_get_log_level()) { +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 40 +#endif + unsigned char addr_str[INET6_ADDRSTRLEN+8]; + + if (coap_print_addr(&(*packet)->src, addr_str, INET6_ADDRSTRLEN+8)) { + debug("received %zd bytes from %s\n", len, addr_str); + } + } +#endif /* NDEBUG */ + + (*packet)->length = len; + memcpy(&(*packet)->payload, uip_appdata, len); + } + +#undef UIP_IP_BUF +#undef UIP_UDP_BUF +#endif /* WITH_CONTIKI */ +#ifdef WITH_LWIP +#error "coap_network_read() not implemented on this platform" +#endif + + (*packet)->interface = ep; + + return len; + error: +#ifdef WITH_POSIX + if (soc_appdata) + coap_free(soc_appdata); + soc_appdata = NULL; +#endif + coap_free_packet(*packet); + *packet = NULL; + return -1; +} + +#undef SIN6 + +#endif /* CUSTOM_COAP_NETWORK_READ */ diff --git a/components/coap/port/include/coap/coap.h b/components/coap/port/include/coap/coap.h new file mode 100644 index 00000000000..cbdc9dfc81b --- /dev/null +++ b/components/coap/port/include/coap/coap.h @@ -0,0 +1,50 @@ +/* Modify head file implementation for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * define operations are in use. + * + * coap.h -- main header file for CoAP stack of libcoap + * + * Copyright (C) 2010-2012,2015-2016 Olaf Bergmann + * 2015 Carsten Schoenert + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#ifndef _COAP_H_ +#define _COAP_H_ + +#include "libcoap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "address.h" +#include "async.h" +#include "bits.h" +#include "block.h" +#include "coap_io.h" +#include "coap_time.h" +#include "debug.h" +#include "encode.h" +#include "mem.h" +#include "net.h" +#include "option.h" +#include "pdu.h" +#include "prng.h" +#include "resource.h" +#include "str.h" +#include "subscribe.h" +#include "uri.h" +#include "uthash.h" +#include "utlist.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _COAP_H_ */ diff --git a/components/coap/port/include/coap_config.h b/components/coap/port/include/coap_config.h new file mode 100644 index 00000000000..db314f2de90 --- /dev/null +++ b/components/coap/port/include/coap_config.h @@ -0,0 +1,39 @@ +/* + * libcoap configure implementation for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * configure operations are in use. + * + * coap.h -- main header file for CoAP stack of libcoap + * + * Copyright (C) 2010-2012,2015-2016 Olaf Bergmann + * 2015 Carsten Schoenert + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#ifdef WITH_POSIX +#include "coap_config_posix.h" +#endif + +#define HAVE_STDIO_H +#define HAVE_ASSERT_H + +#define PACKAGE_STRING PACKAGE_NAME PACKAGE_VERSION + +/* it's just provided by libc. i hope we don't get too many of those, as + * actually we'd need autotools again to find out what environment we're + * building in */ +#define HAVE_STRNLEN 1 + +#define HAVE_LIMITS_H + +#define COAP_RESOURCES_NOHASH + +#endif /* _CONFIG_H_ */ diff --git a/components/coap/port/include/coap_config_posix.h b/components/coap/port/include/coap_config_posix.h new file mode 100644 index 00000000000..a77e97f074e --- /dev/null +++ b/components/coap/port/include/coap_config_posix.h @@ -0,0 +1,41 @@ +/* + * libcoap configure implementation for ESP32 platform. + * + * Uses libcoap software implementation for failover when concurrent + * configure operations are in use. + * + * coap.h -- main header file for CoAP stack of libcoap + * + * Copyright (C) 2010-2012,2015-2016 Olaf Bergmann + * 2015 Carsten Schoenert + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#ifndef COAP_CONFIG_POSIX_H_ +#define COAP_CONFIG_POSIX_H_ + +#ifdef WITH_POSIX + +#include + +#define HAVE_SYS_SOCKET_H +#define HAVE_MALLOC +#define HAVE_ARPA_INET_H + +#define IP_PKTINFO IP_MULTICAST_IF +#define IPV6_PKTINFO IPV6_V6ONLY + +#define PACKAGE_NAME "libcoap-posix" +#define PACKAGE_VERSION "?" + +#define CUSTOM_COAP_NETWORK_ENDPOINT +#define CUSTOM_COAP_NETWORK_SEND +#define CUSTOM_COAP_NETWORK_READ + +#endif + +#endif /* COAP_CONFIG_POSIX_H_ */ diff --git a/components/lwip/include/lwip/port/arpa/inet.h b/components/lwip/include/lwip/port/arpa/inet.h new file mode 100644 index 00000000000..94c6c17ed5a --- /dev/null +++ b/components/lwip/include/lwip/port/arpa/inet.h @@ -0,0 +1,20 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INET_H_ +#define INET_H_ + +#include "lwip/inet.h" + +#endif /* INET_H_ */ diff --git a/components/lwip/include/lwip/port/netinet/in.h b/components/lwip/include/lwip/port/netinet/in.h new file mode 100644 index 00000000000..7eaec63342f --- /dev/null +++ b/components/lwip/include/lwip/port/netinet/in.h @@ -0,0 +1,22 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef IN_H_ +#define IN_H_ + +#include "lwip/inet.h" + +#define IN6_IS_ADDR_MULTICAST(a) IN_MULTICAST(a) + +#endif /* IN_H_ */ diff --git a/components/nghttp/Makefile.projbuild b/components/nghttp/Makefile.projbuild index a93010ade98..5c6ce7fc9b9 100644 --- a/components/nghttp/Makefile.projbuild +++ b/components/nghttp/Makefile.projbuild @@ -1,4 +1 @@ -# Anyone compiling mbedTLS code needs the name of the -# alternative config file - CFLAGS += -DHAVE_CONFIG_H From 9c7cc86793e6c710554942b61556b7382100fb9a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 16 Dec 2016 23:41:04 +0800 Subject: [PATCH 040/167] 1. modify i2c_set_pin function 2. update example comments and other minor changes 3. rename API: i2c_cmd_link_create/i2c_cmd_link_delete (+4 squashed commits) Squashed commits: [2e0ac3e] 1. coding style: add one space after condition key words. 2. modify i2c.h, use gpio_num_t instead of int, improve comments of return values 3. add i2c index in index.rst 4. add readme for i2c example [4991d92] update i2c.doc [88b672e] driver: i2c 1. add mux and spin lock to run in a thread-safe way. 2. modify example code [4eb15fe] driver: i2c code 1. add i2c master code 2. add i2c slave code 3. add i2c example code 4. add DRAM_ATTR for I2C array --- components/driver/i2c.c | 1037 +++++++++++++++++++++ components/driver/include/driver/i2c.h | 514 ++++++++++ components/esp32/include/soc/i2c_reg.h | 2 + components/esp32/include/soc/i2c_struct.h | 6 +- docs/api/i2c.rst | 82 ++ docs/index.rst | 1 + examples/18_i2c/Makefile | 9 + examples/18_i2c/README.md | 29 + examples/18_i2c/main/component.mk | 3 + examples/18_i2c/main/i2c_test.c | 303 ++++++ 10 files changed, 1983 insertions(+), 3 deletions(-) create mode 100644 components/driver/i2c.c create mode 100644 components/driver/include/driver/i2c.h create mode 100644 docs/api/i2c.rst create mode 100644 examples/18_i2c/Makefile create mode 100644 examples/18_i2c/README.md create mode 100644 examples/18_i2c/main/component.mk create mode 100644 examples/18_i2c/main/i2c_test.c diff --git a/components/driver/i2c.c b/components/driver/i2c.c new file mode 100644 index 00000000000..311f01516c9 --- /dev/null +++ b/components/driver/i2c.c @@ -0,0 +1,1037 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "malloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/dport_reg.h" +#include "soc/i2c_struct.h" +#include "soc/i2c_reg.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +static const char* I2C_TAG = "i2c"; +#define I2C_CHECK(a, str, ret) if(!(a)) { \ + ESP_LOGE(I2C_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } + +static portMUX_TYPE i2c_spinlock[I2C_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +/* DRAM_ATTR is required to avoid I2C array placed in flash, due to accessed from ISR */ +static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; + + +#define I2C_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) +#define I2C_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +#define I2C_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define I2C_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +#define I2C_DRIVER_ERR_STR "i2c driver install error" +#define I2C_DRIVER_MALLOC_ERR_STR "i2c driver malloc error" +#define I2C_NUM_ERROR_STR "i2c number error" +#define I2C_ADDR_ERROR_STR "i2c null address error" +#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed" +#define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too short for slave mode" +#define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error" +#define I2C_SEM_ERR_STR "i2c semaphore error" +#define I2C_BUF_ERR_STR "i2c ringbuffer error" +#define I2C_MASTER_MODE_ERR_STR "Only allowed in master mode" +#define I2C_MODE_SLAVE_ERR_STR "Only allowed in slave mode" +#define I2C_CMD_MALLOC_ERR_STR "i2c command link malloc error" +#define I2C_TRANS_MODE_ERR_STR "i2c trans mode error" +#define I2C_MODE_ERR_STR "i2c mode error" +#define I2C_SDA_IO_ERR_STR "sda gpio number error" +#define I2C_SCL_IO_ERR_STR "scl gpio number error" +#define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error" +#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin do not support internal pull-up" +#define I2C_FIFO_FULL_THRESH_VAL (28) +#define I2C_FIFO_EMPTY_THRESH_VAL (5) + +typedef struct { + uint8_t byte_num; /*!< cmd byte number */ + uint8_t ack_en; /*!< ack check enable */ + uint8_t ack_exp; /*!< expected ack level to get */ + uint8_t ack_val; /*!< ack value to send */ + uint8_t* data; /*!< data address */ + uint8_t byte_cmd; /*!< to save cmd for one byte command mode */ + i2c_opmode_t op_code; /*!< haredware cmd type */ +}i2c_cmd_t; + +typedef struct i2c_cmd_link{ + i2c_cmd_t cmd; /*!< command in current cmd link */ + struct i2c_cmd_link *next; /*!< next cmd link */ +} i2c_cmd_link_t; + +typedef struct { + i2c_cmd_link_t* head; /*!< head of the command link */ + i2c_cmd_link_t* cur; /*!< last node of the command link */ + i2c_cmd_link_t* free; /*!< the first node to free of the command link */ +} i2c_cmd_desc_t; + +typedef enum { + I2C_STATUS_READ, /*!< read status for current master command */ + I2C_STATUS_WRITE, /*!< write status for current master command */ + I2C_STATUS_IDLE, /*!< idle status for current master command */ + I2C_STATUS_ACK_ERROR, /*!< ack error status for current master command */ + I2C_STATUS_DONE, /*!< I2C command done */ +} i2c_status_t; + +typedef struct { + int i2c_num; /*!< I2C port number */ + int mode; /*!< I2C mode, master or slave */ + intr_handle_t intr_handle; /*!< I2C interrupt handle*/ + + int cmd_idx; /*!< record current command index, for master mode */ + int status; /*!< record current command status, for master mode */ + int rx_cnt; /*!< record current read index, for master mode */ + uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */ + i2c_cmd_desc_t cmd_link; /*!< I2C command link */ + xSemaphoreHandle cmd_sem; /*!< semaphore to sync command status */ + xSemaphoreHandle cmd_mux; /*!< semaphore to lock command process */ + size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */ + size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */ + + xSemaphoreHandle slv_rx_mux; /*!< slave rx buffer mux */ + xSemaphoreHandle slv_tx_mux; /*!< slave tx buffer mux */ + size_t rx_buf_length; /*!< rx buffer length */ + RingbufHandle_t rx_ring_buf; /*!< rx ringbuffer handler of slave mode */ + size_t tx_buf_length; /*!< tx buffer length */ + RingbufHandle_t tx_ring_buf; /*!< tx ringbuffer handler of slave mode */ +} i2c_obj_t; + +static i2c_obj_t *p_i2c_obj[I2C_NUM_MAX] = {0}; +static void i2c_isr_handler_default(void* arg); +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num); + +/* + For i2c master mode, we don't need to use a buffer for the data, the APIs will execute the master commands +and return after all of the commands have been sent out or when error occurs. So when we send master commands, +we should free or modify the source data only after the i2c_master_cmd_begin function returns. + For i2c slave mode, we need a data buffer to stash the sending and receiving data, because the hardware fifo +has only 32 bytes. +*/ + +esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_buf_len, size_t slv_tx_buf_len, + int intr_alloc_flags) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(mode == I2C_MODE_MASTER || ( slv_rx_buf_len > 100 || slv_tx_buf_len > 100 ), I2C_SLAVE_BUFFER_LEN_ERR_STR, + ESP_ERR_INVALID_ARG); + uint32_t intr_mask = 0; + if (p_i2c_obj[i2c_num] == NULL) { + p_i2c_obj[i2c_num] = (i2c_obj_t*) calloc(1, sizeof(i2c_obj_t)); + if (p_i2c_obj[i2c_num] == NULL) { + ESP_LOGE(I2C_TAG, I2C_DRIVER_MALLOC_ERR_STR); + return ESP_FAIL; + } + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + p_i2c->i2c_num = i2c_num; + p_i2c->mode = mode; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->status = I2C_STATUS_IDLE; + + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + + if (mode == I2C_MODE_SLAVE) { + //we only use ringbuffer for slave mode. + if (slv_rx_buf_len > 0) { + p_i2c->rx_ring_buf = xRingbufferCreate(slv_rx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->rx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->rx_buf_length = slv_rx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (slv_tx_buf_len > 0) { + p_i2c->tx_ring_buf = xRingbufferCreate(slv_tx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->tx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->tx_buf_length = slv_tx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + p_i2c->slv_rx_mux = xSemaphoreCreateMutex(); + p_i2c->slv_tx_mux = xSemaphoreCreateMutex(); + if (p_i2c->slv_rx_mux == NULL || p_i2c->slv_rx_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + intr_mask |= ( I2C_RXFIFO_FULL_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M ); + } else { + //semaphore to sync sending process, because we only have 32 bytes for hardware fifo. + p_i2c->cmd_sem = xSemaphoreCreateBinary(); + p_i2c->cmd_mux = xSemaphoreCreateMutex(); + if (p_i2c->cmd_sem == NULL || p_i2c->cmd_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + //command link + p_i2c->cmd_link.cur = NULL; + p_i2c->cmd_link.head = NULL; + p_i2c->cmd_link.free = NULL; + + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + } else { + ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR); + return ESP_FAIL; + } + //hook isr handler + i2c_isr_register(i2c_num, i2c_isr_handler_default, p_i2c_obj[i2c_num], intr_alloc_flags, &p_i2c_obj[i2c_num]->intr_handle); + intr_mask |= ( I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M ); + SET_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + return ESP_OK; + + err: + //Some error has happened. Free/destroy all allocated things and return ESP_FAIL. + if (p_i2c_obj[i2c_num]) { + if (p_i2c_obj[i2c_num]->rx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->rx_ring_buf); + p_i2c_obj[i2c_num]->rx_ring_buf = NULL; + p_i2c_obj[i2c_num]->rx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->tx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->tx_ring_buf); + p_i2c_obj[i2c_num]->tx_ring_buf = NULL; + p_i2c_obj[i2c_num]->tx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->cmd_sem) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_sem); + } + if (p_i2c_obj[i2c_num]->cmd_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux); + } + if (p_i2c_obj[i2c_num]->slv_rx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_rx_mux); + } + if (p_i2c_obj[i2c_num]->slv_tx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_tx_mux); + } + } + free(p_i2c_obj[i2c_num]); + return ESP_FAIL; +} + +esp_err_t i2c_driver_delete(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + if (p_i2c->cmd_mux) { + xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY); + vSemaphoreDelete(p_i2c->cmd_mux); + } + if (p_i2c->cmd_sem) { + vSemaphoreDelete(p_i2c->cmd_sem); + } + if (p_i2c->slv_rx_mux) { + vSemaphoreDelete(p_i2c->slv_rx_mux); + } + if (p_i2c->slv_tx_mux) { + vSemaphoreDelete(p_i2c->slv_tx_mux); + } + + if (p_i2c->rx_ring_buf) { + vRingbufferDelete(p_i2c->rx_ring_buf); + p_i2c->rx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (p_i2c->tx_ring_buf) { + vRingbufferDelete(p_i2c->tx_ring_buf); + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + uint32_t intr_mask = I2C_MASTER_TRAN_COMP_INT_ENA_M | + I2C_TIME_OUT_INT_ENA_M | + I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_TX_SEND_EMPTY_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_RX_REC_FULL_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M; + CLEAR_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + esp_intr_free(p_i2c->intr_handle); + p_i2c->intr_handle = NULL; + free(p_i2c_obj[i2c_num]); + p_i2c_obj[i2c_num] = NULL; + return ESP_OK; +} + +esp_err_t i2c_reset_tx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_reset_rx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +static void i2c_isr_handler_default(void* arg) +{ + i2c_obj_t* p_i2c = (i2c_obj_t*) arg; + int i2c_num = p_i2c->i2c_num; + uint32_t status = I2C[i2c_num]->int_status.val; + int idx = 0; + portBASE_TYPE HPTaskAwoken = pdFALSE; + while (status != 0) { + status = I2C[i2c_num]->int_status.val; + if (status & I2C_TX_SEND_EMPTY_INT_ST_M) { + I2C[i2c_num]->int_clr.tx_send_empty = 1; + } else if (status & I2C_RX_REC_FULL_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_rec_full = 1; + } else if (status & I2C_ACK_ERR_INT_ST_M) { + I2C[i2c_num]->int_clr.ack_err = 1; + if (p_i2c->mode == I2C_MODE_MASTER) { + p_i2c_obj[i2c_num]->status = I2C_STATUS_ACK_ERROR; + I2C[i2c_num]->int_clr.ack_err = 1; + //get error ack value from slave device, stop the commands + i2c_master_cmd_begin_static(i2c_num); + } + } else if (status & I2C_TRANS_START_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_start = 1; + } else if (status & I2C_TIME_OUT_INT_ST_M) { + I2C[i2c_num]->int_clr.time_out = 1; + } else if (status & I2C_TRANS_COMPLETE_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_complete = 1; + if (p_i2c->mode == I2C_MODE_SLAVE) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + if (p_i2c->status != I2C_STATUS_ACK_ERROR) { + i2c_master_cmd_begin_static(i2c_num); + } + } + } else if (status & I2C_MASTER_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.master_tran_comp = 1; + } else if (status & I2C_ARBITRATION_LOST_INT_ST_M) { + I2C[i2c_num]->int_clr.arbitration_lost = 1; + } else if (status & I2C_SLAVE_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.slave_tran_comp = 1; + } else if (status & I2C_END_DETECT_INT_ST_M) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + i2c_master_cmd_begin_static(i2c_num); + } else if (status & I2C_RXFIFO_OVF_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_fifo_ovf = 1; + } else if (status & I2C_TXFIFO_EMPTY_INT_ST_M) { + int tx_fifo_rem = I2C_FIFO_LEN - I2C[i2c_num]->status_reg.tx_fifo_cnt; + size_t size = 0; + uint8_t *data = (uint8_t*) xRingbufferReceiveUpToFromISR(p_i2c->tx_ring_buf, &size, tx_fifo_rem); + if (data) { + for (idx = 0; idx < size; idx++) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), data[idx]); + } + vRingbufferReturnItemFromISR(p_i2c->tx_ring_buf, data, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } else { + I2C[i2c_num]->int_ena.tx_fifo_empty = 0; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } + } else if (status & I2C_RXFIFO_FULL_INT_ST_M) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + I2C[i2c_num]->int_clr.val = status; + } + } +} + +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(tx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(rx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = rx_trans_mode; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = tx_trans_mode; //set tx data msb first + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + if (tx_trans_mode) { + *tx_trans_mode = I2C[i2c_num]->ctr.tx_lsb_first; + } + if (rx_trans_mode) { + *rx_trans_mode = I2C[i2c_num]->ctr.rx_lsb_first; + } + return ESP_OK; +} + +esp_err_t i2c_param_config(i2c_port_t i2c_num, i2c_config_t* i2c_conf) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf->mode < I2C_MODE_MAX, I2C_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num, + i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode); + if (ret != ESP_OK) { + return ret; + } + if (i2c_num == I2C_NUM_0) { + periph_module_enable(PERIPH_I2C0_MODULE); + } else if (i2c_num == I2C_NUM_1) { + periph_module_enable(PERIPH_I2C1_MODULE); + } + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set tx data msb first + I2C[i2c_num]->ctr.ms_mode = i2c_conf->mode; //mode for master or slave + I2C[i2c_num]->ctr.sda_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.scl_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.sample_scl_level = 0; //sample at high level of clock + + if (i2c_conf->mode == I2C_MODE_SLAVE) { //slave mode + I2C[i2c_num]->slave_addr.addr = i2c_conf->slave.slave_addr; + I2C[i2c_num]->slave_addr.en_10bit = i2c_conf->slave.addr_10bit_en; + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + I2C[i2c_num]->fifo_conf.fifo_addr_cfg_en = 0; + I2C[i2c_num]->fifo_conf.rx_fifo_full_thrhd = I2C_FIFO_FULL_THRESH_VAL; + I2C[i2c_num]->fifo_conf.tx_fifo_empty_thrhd = I2C_FIFO_EMPTY_THRESH_VAL; + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C[i2c_num]->ctr.trans_start = 0; + } else { + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + } + //set frequency + int half_cycle = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) / 2; + I2C[i2c_num]->scl_low_period.period = half_cycle - 1; + I2C[i2c_num]->scl_high_period.period = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) - half_cycle - 1; + //set timing for start signal + I2C[i2c_num]->scl_start_hold.time = half_cycle; + I2C[i2c_num]->scl_rstart_setup.time = half_cycle; + //set timing for stop signal + I2C[i2c_num]->scl_stop_hold.time = half_cycle; + I2C[i2c_num]->scl_stop_setup.time = half_cycle; + //set timing for data + I2C[i2c_num]->sda_hold.time = half_cycle / 2; + I2C[i2c_num]->sda_sample.time = half_cycle / 2; + //set timeout of receving data + I2C[i2c_num]->timeout.tout = 200000; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->scl_high_period.period = high_period; + I2C[i2c_num]->scl_low_period.period = low_period; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (high_period) { + *high_period = I2C[i2c_num]->scl_high_period.period; + } + if (low_period) { + *low_period = I2C[i2c_num]->scl_low_period.period; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_start_hold.time = hold_time; + I2C[i2c_num]->scl_rstart_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_start_hold.time; + } + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_rstart_setup.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_stop_hold.time = hold_time; + I2C[i2c_num]->scl_stop_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_stop_setup.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_stop_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->sda_hold.time = hold_time; + I2C[i2c_num]->sda_sample.time = sample_time; + return ESP_OK; +} + +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (sample_time) { + *sample_time = I2C[i2c_num]->sda_sample.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->sda_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_isr_register(i2c_port_t i2c_num, void (*fn)(void*), void * arg, int intr_alloc_flags, intr_handle_t *handle) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(fn != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + esp_err_t ret; + switch (i2c_num) { + case I2C_NUM_1: + ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + case I2C_NUM_0: + default: + ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + } + return ret; +} + +esp_err_t i2c_isr_free(intr_handle_t handle) +{ + return esp_intr_free(handle); +} + +esp_err_t i2c_set_pin(i2c_port_t i2c_num, gpio_num_t sda_io_num, gpio_num_t scl_io_num, gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(((GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num))), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + (GPIO_IS_VALID_GPIO(scl_io_num) && mode == I2C_MODE_SLAVE), + I2C_SCL_IO_ERR_STR, + ESP_ERR_INVALID_ARG); + I2C_CHECK((sda_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num)) || + sda_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((scl_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + scl_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + + int sda_in_sig, sda_out_sig, scl_in_sig, scl_out_sig; + switch (i2c_num) { + case I2C_NUM_1: + sda_out_sig = I2CEXT1_SDA_OUT_IDX; + sda_in_sig = I2CEXT1_SDA_IN_IDX; + scl_out_sig = I2CEXT1_SCL_OUT_IDX; + scl_in_sig = I2CEXT1_SCL_IN_IDX; + break; + case I2C_NUM_0: + default: + sda_out_sig = I2CEXT0_SDA_OUT_IDX; + sda_in_sig = I2CEXT0_SDA_IN_IDX; + scl_out_sig = I2CEXT0_SCL_OUT_IDX; + scl_in_sig = I2CEXT0_SCL_IN_IDX; + break; + } + if (sda_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[sda_io_num], PIN_FUNC_GPIO); + gpio_set_direction(sda_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + if (sda_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(sda_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(sda_io_num, GPIO_FLOATING); + } + gpio_matrix_out(sda_io_num, sda_out_sig, 0, 0); + gpio_matrix_in(sda_io_num, sda_in_sig, 0); + } + + if (scl_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[scl_io_num], PIN_FUNC_GPIO); + if (mode == I2C_MODE_MASTER) { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_matrix_out(scl_io_num, scl_out_sig, 0, 0); + } else { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT); + } + if (scl_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(scl_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(scl_io_num, GPIO_FLOATING); + } + gpio_matrix_in(scl_io_num, scl_in_sig, 0); + } + return ESP_OK; +} + +i2c_cmd_handle_t i2c_cmd_link_create() +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) calloc(1, sizeof(i2c_cmd_desc_t)); + return (i2c_cmd_handle_t) cmd_desc; +} + +void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle) +{ + if (cmd_handle == NULL) { + return; + } + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + while (cmd->free) { + i2c_cmd_link_t* ptmp = cmd->free; + cmd->free = cmd->free->next; + free(ptmp); + } + cmd->cur = NULL; + cmd->free = NULL; + cmd->head = NULL; + free(cmd_handle); + return; +} + +static esp_err_t i2c_cmd_link_append(i2c_cmd_handle_t cmd_handle, i2c_cmd_t* cmd) +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) cmd_handle; + if (cmd_desc->head == NULL) { + cmd_desc->head = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->head == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->head; + cmd_desc->free = cmd_desc->head; + } else { + cmd_desc->cur->next = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->cur->next == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->cur->next; + } + memcpy((uint8_t*) &cmd_desc->cur->cmd, (uint8_t*) cmd, sizeof(i2c_cmd_t)); + cmd_desc->cur->next = NULL; + return ESP_OK; + + err: + return ESP_FAIL; +} + +esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_RESTART; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_STOP; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, bool ack_en) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + uint8_t len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = NULL; + cmd.byte_cmd = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + int len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_READ; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_READ; + cmd.data = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE HPTaskAwoken = pdFALSE; + //This should never happen + if (p_i2c->mode == I2C_MODE_SLAVE) { + return; + } + if (p_i2c->status == I2C_STATUS_DONE) { + return; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 0; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } else if (p_i2c->status == I2C_STATUS_READ) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + while (p_i2c->rx_cnt-- > 0) { + *cmd->data++ = READ_PERI_REG(I2C_DATA_APB_REG(i2c_num)); + } + if (cmd->byte_num > 0) { + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + } + if (p_i2c->cmd_link.head == NULL) { + p_i2c->cmd_link.cur = NULL; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } + while (p_i2c->cmd_link.head) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + I2C[i2c_num]->command[p_i2c->cmd_idx].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_en = cmd->ack_en; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_exp = cmd->ack_exp; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx].op_code = cmd->op_code; + if (cmd->op_code == I2C_CMD_WRITE) { + //TODO: to reduce interrupt number + if (cmd->data) { + while (p_i2c->tx_fifo_remain > 0 && cmd->byte_num > 0) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), *cmd->data++); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + } else { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), cmd->byte_cmd); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num -= cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + if (cmd->byte_num > 0) { + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + p_i2c->status = I2C_STATUS_WRITE; + break; + } else if(cmd->op_code == I2C_CMD_READ) { + //TODO: to reduce interrupt number + p_i2c->rx_cnt = cmd->byte_num > p_i2c->rx_fifo_remain ? p_i2c->rx_fifo_remain : cmd->byte_num; + cmd->byte_num -= p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->status = I2C_STATUS_READ; + break; + } else { + } + p_i2c->cmd_idx++; + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + if (p_i2c->cmd_link.head == NULL || p_i2c->cmd_idx >= 15) { + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + p_i2c->status = I2C_STATUS_IDLE; + break; + } + } + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_clr.master_tran_comp = 1; + I2C[i2c_num]->int_ena.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 1; + I2C[i2c_num]->ctr.trans_start = 0; + I2C[i2c_num]->ctr.trans_start = 1; + return; +} + +esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_NOT_INSTALL_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_MASTER, I2C_MASTER_MODE_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret; + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait); + if (res == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + xSemaphoreTake(p_i2c->cmd_sem, 0); + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + p_i2c->cmd_link.free = cmd->free; + p_i2c->cmd_link.cur = cmd->cur; + p_i2c->cmd_link.head = cmd->head; + p_i2c->status = I2C_STATUS_IDLE; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + + //start send commands, at most 32 bytes one time, isr handler will process the remaining commands. + i2c_master_cmd_begin_static(i2c_num); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xSemaphoreTake(p_i2c->cmd_sem, ticks_to_wait); + if (res == pdFALSE) { + ret = ESP_ERR_TIMEOUT; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + ret = ESP_FAIL; + } else { + ret = ESP_OK; + } + p_i2c->status = I2C_STATUS_DONE; + xSemaphoreGive(p_i2c->cmd_mux); + return ret; +} + +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + + portBASE_TYPE res; + int cnt = 0; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + + res = xSemaphoreTake(p_i2c->slv_tx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xRingbufferSend(p_i2c->tx_ring_buf, data, size, ticks_to_wait); + if (res == pdFALSE) { + cnt = 0; + } else { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + cnt = size; + } + xSemaphoreGive(p_i2c->slv_tx_mux); + return cnt; +} + +static int i2c_slave_read(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + size_t size; + uint8_t* pdata = (uint8_t*) xRingbufferReceiveUpTo(p_i2c->rx_ring_buf, &size, ticks_to_wait, max_size); + if (pdata && size > 0) { + memcpy(data, pdata, size); + vRingbufferReturnItem(p_i2c->rx_ring_buf, pdata); + } + return size; +} + +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE res; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + res = xSemaphoreTake(p_i2c->slv_rx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + int cnt = i2c_slave_read(i2c_num, data, max_size, ticks_to_wait); + if (cnt > 0) { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + if (cnt < max_size && ticks_to_wait > 0) { + cnt += i2c_slave_read(i2c_num, data + cnt, max_size - cnt, ticks_to_wait); + } + } else { + cnt = 0; + } + xSemaphoreGive(p_i2c->slv_rx_mux); + return cnt; +} + + + + diff --git a/components/driver/include/driver/i2c.h b/components/driver/include/driver/i2c.h new file mode 100644 index 00000000000..69b80b1e670 --- /dev/null +++ b/components/driver/include/driver/i2c.h @@ -0,0 +1,514 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_I2C_H_ +#define _DRIVER_I2C_H_ + + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" +#include "driver/gpio.h" + +#define I2C_APB_CLK_FREQ APB_CLK_FREQ /*!< I2C source clock is APB clock, 80MHz */ +#define I2C_FIFO_LEN (32) /*!< I2C hardware fifo length */ +typedef enum{ + I2C_MODE_SLAVE = 0, /*!< I2C slave mode */ + I2C_MODE_MASTER, /*!< I2C master mode */ + I2C_MODE_MAX, +}i2c_mode_t; + +typedef enum { + I2C_MASTER_WRITE = 0, /*!< I2C write data */ + I2C_MASTER_READ, /*!< I2C read data */ +} i2c_rw_t; + +typedef enum { + I2C_DATA_MODE_MSB_FIRST = 0, /*!< I2C data msb first */ + I2C_DATA_MODE_LSB_FIRST = 1, /*!< I2C data lsb first */ + I2C_DATA_MODE_MAX +} i2c_trans_mode_t; + +typedef enum{ + I2C_CMD_RESTART = 0, /*!=0) The number of data bytes that pushed to the I2C slave buffer. + */ +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait); + +/** + * @brief I2C slave read data from internal buffer. When I2C slave receive data, isr will copy received data + * from hardware rx fifo to internal ringbuffer. Then users can read from internal ringbuffer. + * @note + * Only call this function in I2C slave mode + * + * @param i2c_num I2C port number + * @param data data pointer to write into internal buffer + * @param max_size Maximum data size to read + * @param ticks_to_wait Maximum waiting ticks + * + * @return + * - ESP_FAIL(-1) Parameter error + * - Others(>=0) The number of data bytes that read from I2C slave buffer. + */ +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait); + +/** + * @brief set I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period clock cycle number during SCL is high level, high_period is a 14 bit value + * @param low_period clock cycle number during SCL is low level, low_period is a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period); + +/** + * @brief get I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period pointer to get clock cycle number during SCL is high level, will get a 14 bit value + * @param low_period pointer to get clock cycle number during SCL is low level, will get a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period); + +/** + * @brief set I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock number between the falling-edge of SDA and rising-edge of SCL for start mark, it's a 10-bit value. + * @param hold_time clock num between the falling-edge of SDA and falling-edge of SCL for start mark, it's a 10-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock num between the rising-edge of SCL and the rising-edge of SDA, it's a 10-bit value. + * @param hold_time clock number after the STOP bit's rising-edge, it's a 14-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time. + * @param hold_time pointer to get hold time. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time clock number I2C used to sample data on SDA after the rising-edge of SCL, it's a 10-bit value + * @param hold_time clock number I2C used to hold the data after the falling-edge of SCL, it's a 10-bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time); + +/** + * @brief get I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time pointer to get sample time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time); + +/** + * @brief set I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode I2C sending data mode + * @param rx_trans_mode I2C receving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode); + +/** + * @brief get I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode pointer to get I2C sending data mode + * @param rx_trans_mode pointer to get I2C receiving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode); + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_I2C_H_*/ diff --git a/components/esp32/include/soc/i2c_reg.h b/components/esp32/include/soc/i2c_reg.h index d5c9858a92b..9693ca5b591 100644 --- a/components/esp32/include/soc/i2c_reg.h +++ b/components/esp32/include/soc/i2c_reg.h @@ -261,6 +261,8 @@ #define I2C_RXFIFO_FULL_THRHD_V 0x1F #define I2C_RXFIFO_FULL_THRHD_S 0 +#define I2C_DATA_APB_REG(i) (0x60013000 + (i) * 0x14000 + 0x001c) + #define I2C_DATA_REG(i) (REG_I2C_BASE(i) + 0x001c) /* I2C_FIFO_RDATA : RO ;bitpos:[7:0] ;default: 8'b0 ; */ /*description: The register represent the byte data read from rxfifo when use apb fifo access*/ diff --git a/components/esp32/include/soc/i2c_struct.h b/components/esp32/include/soc/i2c_struct.h index a29a9c52860..ea50d6bee5d 100644 --- a/components/esp32/include/soc/i2c_struct.h +++ b/components/esp32/include/soc/i2c_struct.h @@ -16,7 +16,7 @@ typedef volatile struct { union { struct { - uint32_t scl_low_period:14; /*This register is used to configure the low level width of SCL clock.*/ + uint32_t period:14; /*This register is used to configure the low level width of SCL clock.*/ uint32_t reserved14: 18; }; uint32_t val; @@ -58,7 +58,7 @@ typedef volatile struct { } status_reg; union { struct { - uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data.*/ + uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ uint32_t reserved20:12; }; uint32_t val; @@ -282,7 +282,7 @@ typedef volatile struct { uint32_t reserved_f4; uint32_t date; /**/ uint32_t reserved_fc; - uint32_t fifo_start_addr; /*This the start address for ram when use apb nonfifo access.*/ + uint32_t ram_data[32]; /*This the start address for ram when use apb nonfifo access.*/ } i2c_dev_t; extern i2c_dev_t I2C0; extern i2c_dev_t I2C1; diff --git a/docs/api/i2c.rst b/docs/api/i2c.rst new file mode 100644 index 00000000000..1186e0583bf --- /dev/null +++ b/docs/api/i2c.rst @@ -0,0 +1,82 @@ +I2C +=========== + +Overview +-------- + +ESP32 has two I2C controllers which can be set as master mode or slave mode. + +Application Example +------------------- + +I2C master and slave example: `examples/18_i2c `_. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/i2c.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: I2C_APB_CLK_FREQ +.. doxygendefine:: I2C_FIFO_LEN + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: i2c_cmd_handle_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: i2c_mode_t +.. doxygenenum:: i2c_rw_t +.. doxygenenum:: i2c_trans_mode_t +.. doxygenenum:: i2c_opmode_t +.. doxygenenum:: i2c_port_t +.. doxygenenum:: i2c_addr_mode_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: i2c_config_t + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: i2c_driver_install +.. doxygenfunction:: i2c_driver_delete +.. doxygenfunction:: i2c_param_config +.. doxygenfunction:: i2c_reset_tx_fifo +.. doxygenfunction:: i2c_reset_rx_fifo +.. doxygenfunction:: i2c_isr_register +.. doxygenfunction:: i2c_isr_free +.. doxygenfunction:: i2c_set_pin +.. doxygenfunction:: i2c_master_start +.. doxygenfunction:: i2c_master_write_byte +.. doxygenfunction:: i2c_master_write +.. doxygenfunction:: i2c_master_read_byte +.. doxygenfunction:: i2c_master_read +.. doxygenfunction:: i2c_master_stop +.. doxygenfunction:: i2c_master_cmd_begin +.. doxygenfunction:: i2c_slave_write_buffer +.. doxygenfunction:: i2c_slave_read +.. doxygenfunction:: i2c_set_period +.. doxygenfunction:: i2s_get_period +.. doxygenfunction:: i2c_set_start_timing +.. doxygenfunction:: i2c_get_start_timing +.. doxygenfunction:: i2c_set_stop_timing +.. doxygenfunction:: i2c_get_stop_timing +.. doxygenfunction:: i2c_set_data_timing +.. doxygenfunction:: i2c_get_data_timing +.. doxygenfunction:: i2c_set_data_mode +.. doxygenfunction:: i2c_get_data_mode +.. doxygenfunction:: i2c_cmd_link_create +.. doxygenfunction:: i2c_cmd_link_delete + diff --git a/docs/index.rst b/docs/index.rst index ba25b496917..9c2b0643f4a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -107,6 +107,7 @@ Contents: LED Control Remote Control Timer + I2C Pulse Counter Sigma-delta Modulation SPI Flash and Partition APIs diff --git a/examples/18_i2c/Makefile b/examples/18_i2c/Makefile new file mode 100644 index 00000000000..d575e29b9d4 --- /dev/null +++ b/examples/18_i2c/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := i2c + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/18_i2c/README.md b/examples/18_i2c/README.md new file mode 100644 index 00000000000..7a88894bf16 --- /dev/null +++ b/examples/18_i2c/README.md @@ -0,0 +1,29 @@ +# I2C Example + + +* This example will show you how to use I2C module by running two tasks on i2c bus: + + * read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + +* Pin assignment: + + * slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + +* Connection: + + * connect GPIO18 with GPIO25 + * connect GPIO19 with GPIO26 + * connect sda/scl of sensor with GPIO18/GPIO19 + * no need to add external pull-up resistors, driver will enable internal pull-up resistors. + +* Test items: + + * read the sensor data, if connected. + * i2c master(ESP32) will write data to i2c slave(ESP32). + * i2c master(ESP32) will read data from i2c slave(ESP32). diff --git a/examples/18_i2c/main/component.mk b/examples/18_i2c/main/component.mk new file mode 100644 index 00000000000..44bd2b5273f --- /dev/null +++ b/examples/18_i2c/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/18_i2c/main/i2c_test.c b/examples/18_i2c/main/i2c_test.c new file mode 100644 index 00000000000..2ef6d75b2f6 --- /dev/null +++ b/examples/18_i2c/main/i2c_test.c @@ -0,0 +1,303 @@ +/* i2c - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "driver/i2c.h" + +/** + * TEST CODE BRIEF + * + * This example will show you how to use I2C module by running two tasks on i2c bus: + * + * - read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + * + * Pin assignment: + * + * - slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * - master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + * + * Connection: + * + * - connect GPIO18 with GPIO25 + * - connect GPIO19 with GPIO26 + * - connect sda/scl of sensor with GPIO18/GPIO19 + * - no need to add external pull-up resistors, driver will enable internal pull-up resistors. + * + * Test items: + * + * - read the sensor data, if connected. + * - i2c master(ESP32) will write data to i2c slave(ESP32). + * - i2c master(ESP32) will read data from i2c slave(ESP32). + */ + +#define DATA_LENGTH 512 /*! 1) { + i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); + } + i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief Test code to write esp-i2c-slave + * Master device write data to slave(both esp32), + * the data will be stored in slave buffer. + * We can read them out from slave buffer. + * + * ___________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | + * --------|---------------------------|----------------------|------| + * + */ +esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief test code to write esp-i2c-slave + * + * 1. set mode + * _________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop | + * --------|---------------------------|---------------------|------| + * 2. wait more than 24 ms + * 3. read data + * ______________________________________________________________________________________ + * | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop | + * --------|---------------------------|--------------------|--------------------|------| + */ +esp_err_t i2c_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN); + i2c_master_stop(cmd); + int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ret; + } + vTaskDelay(30 / portTICK_RATE_MS); + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN); + i2c_master_read_byte(cmd, data_h, ACK_VAL); + i2c_master_read_byte(cmd, data_l, NACK_VAL); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ESP_FAIL; + } + return ESP_OK; +} + +/** + * @brief i2c master initialization + */ +void i2c_master_init() +{ + int i2c_master_port = I2C_MASTER_NUM; + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = I2C_MASTER_SDA_IO; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = I2C_MASTER_SCL_IO; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + i2c_param_config(i2c_master_port, &conf); + i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); +} + +/** + * @brief i2c slave initialization + */ +void i2c_slave_init() +{ + int i2c_slave_port = I2C_SLAVE_NUM; + i2c_config_t conf_slave; + conf_slave.sda_io_num = I2C_SLAVE_SDA_IO; + conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.scl_io_num = I2C_SLAVE_SCL_IO; + conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.mode = I2C_MODE_SLAVE; + conf_slave.slave.addr_10bit_en = 0; + conf_slave.slave.slave_addr = ESP_SLAVE_ADDR; + i2c_param_config(i2c_slave_port, &conf_slave); + i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0); +} + +/** + * @brief test function to show buffer + */ +void disp_buf(uint8_t* buf, int len) +{ + int i; + for (i = 0; i < len; i++) { + printf("%02x ", buf[i]); + if (( i + 1 ) % 16 == 0) { + printf("\n"); + } + } + printf("\n"); +} + +void i2c_test_task(void* arg) +{ + int i = 0; + int ret; + uint32_t task_idx = (uint32_t) arg; + uint8_t* data = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_wr = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_rd = (uint8_t*) malloc(DATA_LENGTH); + uint8_t sensor_data_h, sensor_data_l; + + while (1) { + ret = i2c_master_sensor_test( I2C_MASTER_NUM, &sensor_data_h, &sensor_data_l); + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ SENSOR( BH1750 )\n", task_idx); + printf("*******************\n"); + if (ret == ESP_OK) { + printf("data_h: %02x\n", sensor_data_h); + printf("data_l: %02x\n", sensor_data_l); + printf("sensor val: %f\n", ( sensor_data_h << 8 | sensor_data_l ) / 1.2); + } else { + printf("No ack, sensor not connected...skip...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + + //--------------------------------------------------- + for (i = 0; i < DATA_LENGTH; i++) { + data[i] = i; + } + size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + if (d_size == 0) { + printf("i2c slave tx buffer full\n"); + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, DATA_LENGTH); + } else { + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ FROM SLAVE\n", task_idx); + printf("*******************\n"); + printf("====TASK[%d] Slave buffer data ====\n", task_idx); + disp_buf(data, d_size); + if (ret == ESP_OK) { + printf("====TASK[%d] Master read ====\n", task_idx); + disp_buf(data_rd, d_size); + } else { + printf("Master read slave error, IO not connected...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + //--------------------------------------------------- + int size; + for (i = 0; i < DATA_LENGTH; i++) { + data_wr[i] = i + 10; + } + //we need to fill the slave buffer so that master can read later + ret = i2c_master_write_slave( I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + size = i2c_slave_read_buffer( I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER WRITE TO SLAVE\n", task_idx); + printf("*******************\n"); + printf("----TASK[%d] Master write ----\n", task_idx); + disp_buf(data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + printf("----TASK[%d] Slave read: [%d] bytes ----\n", task_idx, size); + disp_buf(data, size); + } else { + printf("TASK[%d] Master write slave error, IO not connected....\n", task_idx); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + } +} + +void app_main() +{ + print_mux = xSemaphoreCreateMutex(); + i2c_slave_init(); + i2c_master_init(); + + xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL); + xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void* ) 1, 10, NULL); +} + From 03551ec2da506dd6471de566803505e48c64e2db Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 12:19:02 +1100 Subject: [PATCH 041/167] build system: Add 'make monitor' target from arduino-esp32 Originally added to arduino-esp32 by @me-no-dev. It's so useful, we want it in esp-idf as well! :) --- README.md | 10 ++++++ components/esptool_py/Kconfig.projbuild | 40 ++++++++++++++++++++++++ components/esptool_py/Makefile.projbuild | 8 +++++ make/project.mk | 1 + 4 files changed, 59 insertions(+) diff --git a/README.md b/README.md index f3007fdb88c..32c65bb8201 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,16 @@ This will flash the entire project (app, bootloader and partition table) to a ne You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it. +# Viewing Serial Output + +The `make monitor` target will use the already-installed [miniterm](http://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.miniterm) (a part of pyserial) to display serial output from the ESP32 on the terminal console. + +Exit miniterm by typing Ctrl-]. + +To flash and monitor output in one pass, you can run: + +`make flash monitor` + # Compiling & Flashing Just the App After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table: diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index dd2077cbb3f..edff88c55d8 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -178,4 +178,44 @@ config ESPTOOLPY_AFTER default "hard_reset" if ESPTOOLPY_AFTER_RESET default "no_reset" if ESPTOOLPY_AFTER_NORESET +choice MONITOR_BAUD + prompt "'make monitor' baud rate" + default MONITOR_BAUD_115200B + help + Baud rate to use when running 'make monitor' to view serial output + from a running chip. + + Can override by setting the MONITORBAUD environment variable. + +config MONITOR_BAUD_9600B + bool "9600 bps" +config MONITOR_BAUD_57600B + bool "57600 bps" +config MONITOR_BAUD_115200B + bool "115200 bps" +config MONITOR_BAUD_230400B + bool "230400 bps" +config MONITOR_BAUD_921600B + bool "921600 bps" +config MONITOR_BAUD_2MB + bool "2 Mbps" +config MONITOR_BAUD_OTHER + bool "Custom baud rate" + +endchoice + +config MONITOR_BAUD_OTHER_VAL + int "Custom baud rate value" if MONITOR_BAUD_OTHER + default 115200 + +config MONITOR_BAUD + int + default 9600 if MONITOR_BAUD_9600B + default 57600 if MONITOR_BAUD_57600B + default 115200 if MONITOR_BAUD_115200B + default 230400 if MONITOR_BAUD_230400B + default 921600 if MONITOR_BAUD_921600B + default 2000000 if MONITOR_BAUD_2MB + default MONITOR_BAUD_OTHER_VAL if MONITOR_BAUD_OTHER + endmenu diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 88b5894731a..e249d14432f 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -67,4 +67,12 @@ erase_flash: @echo "Erasing entire flash..." $(ESPTOOLPY_SERIAL) erase_flash +MONITORBAUD ?= $(CONFIG_MONITOR_BAUD) + +# note: if you want to run miniterm from command line, can simply run +# miniterm.py on the console. The '$(PYTHON) -m serial.tools.miniterm' +# is to allow for the $(PYTHON) variable overriding the python path. +monitor: $(call prereq_if_explicit,%flash) + $(PYTHON) -m serial.tools.miniterm --rts 0 --dtr 0 --raw $(ESPPORT) $(MONITORBAUD) + .PHONY: erase_flash diff --git a/make/project.mk b/make/project.mk index a5e487b85c2..e2a93ce1190 100644 --- a/make/project.mk +++ b/make/project.mk @@ -30,6 +30,7 @@ help: @echo "make clean - Remove all build output" @echo "make size - Display the memory footprint of the app" @echo "make erase_flash - Erase entire flash contents" + @echo "make monitor - Display serial output on terminal console" @echo "" @echo "make app - Build just the app" @echo "make app-flash - Flash just the app" From 4f7314a760377e9947af2b57d655b5d797e6cf27 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 12:19:39 +1100 Subject: [PATCH 042/167] README: Add a note about parallel builds --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 32c65bb8201..5290aa79811 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,16 @@ After the initial flash, you may just want to build and flash just your app, not (There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.) +# Parallel Builds + +esp-idf supports compiling multiple files in parallel, so all of the above commands can be run as `make -jN` where `N` is the number of parallel make processes to run (generally N should be equal to or one more than the number of CPU cores in your system.) + +Multiple make functions can be combined into one. For example: to build the app & bootloader using 5 jobs in parallel, then flash everything, and then display serial output from the ESP32 run: + +``` +make -j5 flash monitor +``` + # The Partition Table Once you've compiled your project, the "build" directory will contain a binary file with a name like "my_app.bin". This is an ESP32 image binary that can be loaded by the bootloader. From 5e96070c27ecd0901c4501666d1f5b274adadf94 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 10:38:21 +1100 Subject: [PATCH 043/167] linker script: Remove KEEP from RAM-resident sections Reduce RAM usage when not all data/bss sections in source files were used. --- components/esp32/ld/esp32.common.ld | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index aafafbb4951..09b7634445c 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -99,7 +99,7 @@ SECTIONS *(.sbss2.*) *(.gnu.linkonce.sb2.*) *(.dynbss) - KEEP(*(.bss)) + *(.bss) *(.bss.*) *(.share.mem) *(.gnu.linkonce.b.*) @@ -111,17 +111,17 @@ SECTIONS .dram0.data : { _data_start = ABSOLUTE(.); - KEEP(*(.data)) - KEEP(*(.data.*)) - KEEP(*(.gnu.linkonce.d.*)) - KEEP(*(.data1)) - KEEP(*(.sdata)) - KEEP(*(.sdata.*)) - KEEP(*(.gnu.linkonce.s.*)) - KEEP(*(.sdata2)) - KEEP(*(.sdata2.*)) - KEEP(*(.gnu.linkonce.s2.*)) - KEEP(*(.jcr)) + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) *(.dram1 .dram1.*) *libesp32.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.); From c47bc43afdea856c130a99a8ae77ba0ff99f739f Mon Sep 17 00:00:00 2001 From: Yinling Date: Mon, 2 Jan 2017 13:12:30 +0800 Subject: [PATCH 044/167] CI: fix bug that restore initial condition fail: in STA+AP mode, when STA is reconnecting to external AP, its AP interface is not connectable. do disconnect on STA interface first --- .../integration_test/InitialConditionAll.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/idf_test/integration_test/InitialConditionAll.yml b/components/idf_test/integration_test/InitialConditionAll.yml index 3821894552a..5d4ce7ab3f1 100644 --- a/components/idf_test/integration_test/InitialConditionAll.yml +++ b/components/idf_test/integration_test/InitialConditionAll.yml @@ -260,6 +260,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] initial condition detail: testing ap on sta + ap mode (autogen by APM1) @@ -271,6 +273,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] restore post cmd set: @@ -305,6 +309,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] - - WIFI CONN @@ -319,6 +325,8 @@ initial condition: - ['R SSC1 C +MAC:AP,OK'] - - SSC SSC1 dhcp -S -o 2 - [R SSC1 C +DHCP] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC1 ap -S -s -p -t - ['R SSC1 C +SAP:OK'] - - WIFI CONN @@ -2854,6 +2862,8 @@ initial condition: - ['R SSC1 C +MODE:OK'] - - SSC SSC2 op -S -o 3 - ['R SSC2 C +MODE:OK'] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC2 sta -D - ['R SSC2 C +QAP:'] - - SSC SSC2 soc -T @@ -2873,6 +2883,8 @@ initial condition: - ['R SSC1 C +MODE:OK'] - - SSC SSC2 op -S -o 3 - ['R SSC2 C +MODE:OK'] + - - SSC SSC1 sta -D + - ['R SSC1 C +QAP'] - - SSC SSC2 sta -D - ['R SSC2 C +QAP:'] - - SSC SSC2 soc -T From b9a6d276a2cda8a7aa0ec32fdff2ca71fd8b64fc Mon Sep 17 00:00:00 2001 From: Yinling Date: Mon, 2 Jan 2017 13:15:10 +0800 Subject: [PATCH 045/167] CI: update known issues --- .../idf_test/integration_test/KnownIssues | 109 +++++++++++++----- 1 file changed, 81 insertions(+), 28 deletions(-) diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index e0991f39b00..c6cb7b77b95 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -5,24 +5,6 @@ TCPIP_ICMP_0101 ^TCPIP_ICMP_0101 -# IGMP cases are not supported for now -TCPIP_IGMP_0101 -TCPIP_IGMP_0102 -TCPIP_IGMP_0103 -TCPIP_IGMP_0104 -TCPIP_IGMP_0201 -TCPIP_IGMP_0202 -TCPIP_IGMP_0203 -TCPIP_IGMP_0204 -^TCPIP_IGMP_0101 -^TCPIP_IGMP_0102 -^TCPIP_IGMP_0103 -^TCPIP_IGMP_0104 -^TCPIP_IGMP_0201 -^TCPIP_IGMP_0202 -^TCPIP_IGMP_0203 -^TCPIP_IGMP_0204 - # don't support PHY mode command WIFI_SCAN_0201 WIFI_SCAN_0302 @@ -48,29 +30,62 @@ WIFI_CONN_0801 ^WIFI_CONN_0801 # disconnect reason -WIFI_CONN_0904 -^WIFI_CONN_0904 WIFI_CONN_0901 ^WIFI_CONN_0901 +WIFI_CONN_0904 +^WIFI_CONN_0904 +^WIFI_CONN_0902 +WIFI_CONN_0902 # Wifi connect issue -WIFI_CONN_0104 -^WIFI_CONN_0104 +^WIFI_CONN_0101 +WIFI_CONN_0101 +WIFI_CONN_0103 +^WIFI_CONN_0401 +^WIFI_CONN_0103 +WIFI_CONN_0102 +WIFI_CONN_0601 ^WIFI_CONN_0601 +WIFI_ADDR_0102 +^WIFI_ADDR_0102 +WIFI_CONN_0502 +WIFI_CONN_0501 # Wifi scan issue -WIFI_SCAN_0303 +^WIFI_SCAN_0101 +^WIFI_SCAN_0102 +WIFI_SCAN_0103 ^WIFI_SCAN_0103 +WIFI_SCAN_0104 +^WIFI_SCAN_0104 ^WIFI_SCAN_0105 +WIFI_SCAN_0303 +WIFI_SCAN_0304 +^WIFI_SCAN_0104 -# set mac address may lead to exception -WIFI_ADDR_0101 -^WIFI_ADDR_0101 +# IGMP cases are supported but as UDP is not stable, exclude them first +TCPIP_IGMP_0101 +TCPIP_IGMP_0102 +TCPIP_IGMP_0103 +TCPIP_IGMP_0104 +TCPIP_IGMP_0201 +TCPIP_IGMP_0202 +TCPIP_IGMP_0203 +TCPIP_IGMP_0204 +^TCPIP_IGMP_0101 +^TCPIP_IGMP_0102 +^TCPIP_IGMP_0103 +^TCPIP_IGMP_0104 +^TCPIP_IGMP_0201 +^TCPIP_IGMP_0202 +^TCPIP_IGMP_0203 +^TCPIP_IGMP_0204 # DHCP issues ^TCPIP_DHCP_0301 TCPIP_DHCP_0301 TCPIP_DHCP_0101 +^TCPIP_DHCP_0101 TCPIP_DHCP_0207 ^TCPIP_DHCP_0207 TCPIP_DHCP_0208 @@ -79,6 +94,14 @@ TCPIP_DHCP_0205 ^TCPIP_DHCP_0205 TCPIP_DHCP_0209 ^TCPIP_DHCP_0209 +^TCPIP_DHCP_0204 +TCPIP_DHCP_0204 +^TCPIP_DHCP_0210 +TCPIP_DHCP_0210 +^TCPIP_DHCP_0211 +TCPIP_DHCP_0211 +^TCPIP_DHCP_0206 +^TCPIP_DHCP_0302 # TCP issue TCPIP_TCP_0402 @@ -90,17 +113,47 @@ TCPIP_TCP_0103 ^TCPIP_TCP_0103 TCPIP_TCP_0112 ^TCPIP_TCP_0112 - +TCPIP_TCP_0106 +TCPIP_TCP_0107 +TCPIP_TCP_0105 +^TCPIP_TCP_0407 +^TCPIP_TCP_0404 +^TCPIP_TCP_0102 +^TCPIP_TCP_0105 +^TCPIP_TCP_0107 +^TCPIP_TCP_0206 +TCPIP_TCP_0102 +^TCPIP_TCP_0403 +^TCPIP_TCP_0402 +^TCPIP_TCP_0101 +^TCPIP_TCP_0203 +^TCPIP_TCP_0106 +^TCPIP_TCP_0201 +^TCPIP_TCP_0412 +TCPIP_TCP_0101 # UDP issue TCPIP_UDP_0103 ^TCPIP_UDP_0103 +^TCPIP_UDP_0108 TCPIP_UDP_0110 ^TCPIP_UDP_0110 +^TCPIP_UDP_0112 TCPIP_UDP_0305 ^TCPIP_UDP_0305 ^TCPIP_UDP_0304 TCPIP_UDP_0104 +^TCPIP_UDP_0104 +^TCPIP_UDP_0301 +TCPIP_UDP_0301 +TCPIP_UDP_0302 +TCPIP_UDP_0303 +^TCPIP_UDP_0303 +^TCPIP_UDP_0307 +^TCPIP_UDP_0302 +^TCPIP_UDP_0306 - +#DNS +^TCPIP_DNS_0103 +^TCPIP_DNS_0102 From 16de41941e7fde28a7b83a2b71893879645c10dc Mon Sep 17 00:00:00 2001 From: Yinling Date: Mon, 2 Jan 2017 16:25:26 +0800 Subject: [PATCH 046/167] CI: fix bug that test report job failed --- .gitlab-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 75a1fb59ffd..8cb5c8bcac5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -180,7 +180,8 @@ test_report: - git clone $GITLAB_SSH_SERVER/yinling/auto_test_script.git - cd auto_test_script # generate report - - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH -r $RESULT_PATH -a $ARTIFACTS_PATH || FAIL=True + - TEST_RESULT=Pass + - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH -r $RESULT_PATH -a $ARTIFACTS_PATH || TEST_RESULT=Fail # commit to CI-test-result project - git clone $GITLAB_SSH_SERVER/qa/CI-test-result.git - rm -rf CI-test-result/RawData/$RESULT_PATH @@ -193,7 +194,7 @@ test_report: - git add . - git commit . -m "update test result for $CI_PROJECT_NAME/$CI_BUILD_REF_NAME/$CI_BUILD_REF, pipeline ID $CI_PIPELINE_ID" || exit 0 - git push origin master - - test "${FAIL}" = "True" && exit 1 + - test "${TEST_RESULT}" = "Pass" || exit 1 push_master_to_github: before_script: From 2e3ca1c2f7f35775010cd7637bd85fac0ef6dbef Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Jan 2017 15:36:40 +1100 Subject: [PATCH 047/167] bootloader: Boost bootloader CPU to 80MHz Partially needed to use RNG, also useful to improve boot performance. --- .../bootloader/src/main/bootloader_start.c | 46 +++++++++---------- components/bootloader/src/main/component.mk | 8 ++++ components/esp32/system_api.c | 4 ++ 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index e5fda9cf530..cd845b0b92e 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -44,6 +44,7 @@ #include "bootloader_flash.h" #include "bootloader_config.h" +#include "rtc.h" extern int _bss_start; extern int _bss_end; @@ -232,6 +233,13 @@ static bool ota_select_valid(const esp_ota_select_entry_t *s) void bootloader_main() { + /* Set CPU to 80MHz. + Start by ensuring it is set to XTAL, as PLL must be off first + (may still be on due to soft reset.) + */ + rtc_set_cpu_freq(CPU_XTAL); + rtc_set_cpu_freq(CPU_80M); + uart_console_configure(); ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); #if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED) @@ -666,26 +674,6 @@ void print_flash_info(const esp_image_header_t* phdr) #endif } -#if CONFIG_CONSOLE_UART_CUSTOM -static uint32_t get_apb_freq(void) -{ - // Get the value of APB clock from RTC memory. - // The value is initialized in ROM code, and updated by librtc.a - // when APB clock is changed. - // This value is stored in RTC_CNTL_STORE5_REG as follows: - // RTC_CNTL_STORE5_REG = (freq >> 12) | ((freq >> 12) << 16) - uint32_t apb_freq_reg = REG_READ(RTC_CNTL_STORE5_REG); - uint32_t apb_freq_l = apb_freq_reg & 0xffff; - uint32_t apb_freq_h = apb_freq_reg >> 16; - if (apb_freq_l == apb_freq_h && apb_freq_l != 0) { - return apb_freq_l << 12; - } else { - // fallback value - return APB_CLK_FREQ_ROM; - } -} -#endif - static void uart_console_configure(void) { #if CONFIG_CONSOLE_UART_NONE @@ -695,20 +683,21 @@ static void uart_console_configure(void) uartAttach(); ets_install_uart_printf(); + // ROM bootloader may have put a lot of text into UART0 FIFO. + // Wait for it to be printed. + uart_tx_wait_idle(0); + #if CONFIG_CONSOLE_UART_CUSTOM // Some constants to make the following code less upper-case const int uart_num = CONFIG_CONSOLE_UART_NUM; const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; const int uart_tx_gpio = CONFIG_CONSOLE_UART_TX_GPIO; const int uart_rx_gpio = CONFIG_CONSOLE_UART_RX_GPIO; - // ROM bootloader may have put a lot of text into UART0 FIFO. - // Wait for it to be printed. - uart_tx_wait_idle(0); // Switch to the new UART (this just changes UART number used for // ets_printf in ROM code). uart_tx_switch(uart_num); // Set new baud rate - uart_div_modify(uart_num, (((uint64_t) get_apb_freq()) << 4) / uart_baud); + uart_div_modify(uart_num, (APB_CLK_FREQ << 4) / uart_baud); // If console is attached to UART1 or if non-default pins are used, // need to reconfigure pins using GPIO matrix if (uart_num != 0 || uart_tx_gpio != 1 || uart_rx_gpio != 3) { @@ -727,3 +716,12 @@ static void uart_console_configure(void) #endif // CONFIG_CONSOLE_UART_CUSTOM #endif // CONFIG_CONSOLE_UART_NONE } + +/* empty rtc_printf implementation, to work with librtc + linking. Can be removed once -lrtc is removed from bootloader's + main component.mk. +*/ +int rtc_printf(void) +{ + return 0; +} diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index e98545fc473..9bcc5352a06 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -10,3 +10,11 @@ LINKER_SCRIPTS := esp32.bootloader.ld $(IDF_PATH)/components/esp32/ld/esp32.rom. COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain $(addprefix -T ,$(LINKER_SCRIPTS)) COMPONENT_ADD_LINKER_DEPS := $(LINKER_SCRIPTS) + +ifdef IS_BOOTLOADER_BUILD +# following lines are a workaround to link librtc into the +# bootloader, until clock setting code is in a source-based esp-idf +# component. See also rtc_printf() in bootloader_start.c +COMPONENT_ADD_LDFLAGS += -L $(IDF_PATH)/components/esp32/lib/ -lrtc +COMPONENT_EXTRA_INCLUDES += $(IDF_PATH)/components/esp32/ +endif diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index e8013441b14..63a28de5334 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -29,6 +29,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/xtensa_api.h" +#include "rtc.h" static const char* TAG = "system_api"; @@ -119,6 +120,9 @@ void IRAM_ATTR esp_restart(void) DPORT_TIMERS_RST | DPORT_SPI_RST_1 | DPORT_UART_RST); REG_WRITE(DPORT_PERIP_RST_EN_REG, 0); + // Set CPU back to XTAL source, no PLL, same as hard reset + rtc_set_cpu_freq(CPU_XTAL); + // Reset CPUs if (core_id == 0) { // Running on PRO CPU: APP CPU is stalled. Can reset both CPUs. From 83442526e0c21b32e2501f2c2c0e238670260037 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Jan 2017 16:16:41 +1100 Subject: [PATCH 048/167] bootloader: Allow custom baud rate on UART 0 --- components/bootloader/src/main/bootloader_start.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index cd845b0b92e..23434a11d68 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -680,6 +680,8 @@ static void uart_console_configure(void) ets_install_putc1(NULL); ets_install_putc2(NULL); #else // CONFIG_CONSOLE_UART_NONE + const int uart_num = CONFIG_CONSOLE_UART_NUM; + uartAttach(); ets_install_uart_printf(); @@ -689,15 +691,11 @@ static void uart_console_configure(void) #if CONFIG_CONSOLE_UART_CUSTOM // Some constants to make the following code less upper-case - const int uart_num = CONFIG_CONSOLE_UART_NUM; - const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; const int uart_tx_gpio = CONFIG_CONSOLE_UART_TX_GPIO; const int uart_rx_gpio = CONFIG_CONSOLE_UART_RX_GPIO; // Switch to the new UART (this just changes UART number used for // ets_printf in ROM code). uart_tx_switch(uart_num); - // Set new baud rate - uart_div_modify(uart_num, (APB_CLK_FREQ << 4) / uart_baud); // If console is attached to UART1 or if non-default pins are used, // need to reconfigure pins using GPIO matrix if (uart_num != 0 || uart_tx_gpio != 1 || uart_rx_gpio != 3) { @@ -714,6 +712,11 @@ static void uart_console_configure(void) gpio_matrix_in(uart_rx_gpio, rx_idx, 0); } #endif // CONFIG_CONSOLE_UART_CUSTOM + + // Set configured UART console baud rate + const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; + uart_div_modify(uart_num, (APB_CLK_FREQ << 4) / uart_baud); + #endif // CONFIG_CONSOLE_UART_NONE } From 3922ce47b26ea5eaefbd07b47c7c92434c92901a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Jan 2017 15:36:04 +1100 Subject: [PATCH 049/167] bootloader: Enable early boot RNG entropy source This reverts commit ceb85669702f01f3e30ddfcdd841dfe64e1a597c. --- .../bootloader/src/main/bootloader_start.c | 9 +- .../include_priv/bootloader_random.h | 24 ++ .../src/bootloader_random.c | 110 ++++++- .../bootloader_support/src/flash_encrypt.c | 17 +- .../bootloader_support/src/secure_boot.c | 19 +- components/esp32/include/soc/soc.h | 1 + components/esp32/include/soc/syscon_reg.h | 294 ++++++++++++++++++ components/esp32/include/soc/syscon_struct.h | 120 +++++++ docs/security/flash-encryption.rst | 3 - docs/security/secure-boot.rst | 2 - 10 files changed, 569 insertions(+), 30 deletions(-) create mode 100644 components/esp32/include/soc/syscon_reg.h create mode 100644 components/esp32/include/soc/syscon_struct.h diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 23434a11d68..dfcfb0a081f 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -25,6 +25,7 @@ #include "rom/rtc.h" #include "rom/uart.h" #include "rom/gpio.h" +#include "rom/secure_boot.h" #include "soc/soc.h" #include "soc/cpu.h" @@ -42,7 +43,7 @@ #include "esp_flash_encrypt.h" #include "esp_flash_partitions.h" #include "bootloader_flash.h" - +#include "bootloader_random.h" #include "bootloader_config.h" #include "rtc.h" @@ -259,6 +260,9 @@ void bootloader_main() REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); + ESP_LOGI(TAG, "Enabling RNG early entropy source..."); + bootloader_random_enable(); + if(esp_image_load_header(0x1000, true, &fhdr) != ESP_OK) { ESP_LOGE(TAG, "failed to load bootloader header!"); return; @@ -370,6 +374,9 @@ void bootloader_main() } #endif + ESP_LOGI(TAG, "Disabling RNG early entropy source..."); + bootloader_random_disable(); + // copy loaded segments to RAM, set up caches for mapped segments, and start application ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); unpack_load_app(&load_part_pos); diff --git a/components/bootloader_support/include_priv/bootloader_random.h b/components/bootloader_support/include_priv/bootloader_random.h index 86d42e31d90..bb3b2a81dde 100644 --- a/components/bootloader_support/include_priv/bootloader_random.h +++ b/components/bootloader_support/include_priv/bootloader_random.h @@ -16,6 +16,30 @@ #include +/** + * @brief Enable early entropy source for RNG + * + * Uses the SAR ADC to feed entropy into the HWRNG. The ADC is put + * into a test mode that reads the 1.1V internal reference source and + * feeds the LSB of data into the HWRNG. + * + * Can also be used from app code early during operation, if entropy + * is required before WiFi stack is initialised. Call this function + * from app code only if WiFi/BT are not yet enabled and I2S and SAR + * ADC are not in use. + * + * Call bootloader_random_disable() when done. + */ +void bootloader_random_enable(void); + +/** + * @brief Disable early entropy source for RNG + * + * Disables SAR ADC source and resets the I2S hardware. + * + */ +void bootloader_random_disable(void); + /** * @brief Fill buffer with 'length' random bytes * diff --git a/components/bootloader_support/src/bootloader_random.c b/components/bootloader_support/src/bootloader_random.c index fd780c4b7b5..c8b6c24b1fe 100644 --- a/components/bootloader_support/src/bootloader_random.c +++ b/components/bootloader_support/src/bootloader_random.c @@ -12,37 +12,45 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "bootloader_random.h" +#include "soc/cpu.h" #include "soc/wdev_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" +#include "soc/syscon_reg.h" +#include "soc/dport_reg.h" +#include "soc/i2s_reg.h" +#include "esp_log.h" #ifndef BOOTLOADER_BUILD #include "esp_system.h" #endif +const char *TAG = "boot_rng"; + void bootloader_fill_random(void *buffer, size_t length) { uint8_t *buffer_bytes = (uint8_t *)buffer; uint32_t random; - - /* TODO: enable HW RNG clock - - Until this clock is enabled, this is not secure - */ +#ifdef BOOTLOADER_BUILD + uint32_t start, now; +#endif for (int i = 0; i < length; i++) { if (i == 0 || i % 4 == 0) { /* redundant check is for a compiler warning */ #ifdef BOOTLOADER_BUILD - /* HW RNG generates 32 bits entropy per 16 APB cycles, - in bootloader CPU clock == APB clock. + /* in bootloader with ADC feeding HWRNG, we accumulate 1 + bit of entropy per 40 APB cycles (==80 CPU cycles.) - We are being conservative here and waiting at least - that long, as loop shift overhead, etc will add more - cycles. + To avoid reading the entire RNG hardware state out + as-is, we repeatedly read the RNG register and XOR all + values. */ - asm volatile("nop; nop; nop; nop;"); - asm volatile("nop; nop; nop; nop;"); - asm volatile("nop; nop; nop; nop;"); - asm volatile("nop; nop; nop; nop;"); random = REG_READ(WDEV_RND_REG); + RSR(CCOUNT, start); + do { + random ^= REG_READ(WDEV_RND_REG); + RSR(CCOUNT, now); + } while(now - start < 80*32*2); /* extra factor of 2 is precautionary */ #else random = esp_random(); #endif @@ -51,3 +59,77 @@ void bootloader_fill_random(void *buffer, size_t length) buffer_bytes[i] = random >> ((i % 4) * 8); } } + +void bootloader_random_enable(void) +{ + /* Enable SAR ADC in test mode to feed ADC readings of the 1.1V + reference via I2S into the RNG entropy input. + + Note: I2S requires the PLL to be running, so the call to rtc_set_cpu_freq(CPU_80M) + in early bootloader startup must have been made. + */ + SET_PERI_REG_BITS(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_DTEST_RTC, 2, RTC_CNTL_DTEST_RTC_S); + SET_PERI_REG_MASK(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_ENT_RTC); + SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST); + + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP); + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP); + // Test pattern configuration byte 0xAD: + //--[7:4] channel_sel: 10-->en_test + //--[3:2] bit_width : 3-->12bit + //--[1:0] atten : 1-->3dB attenuation + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB1_REG, 0xADADADAD); + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB2_REG, 0xADADADAD); + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB3_REG, 0xADADADAD); + WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB4_REG, 0xADADADAD); + + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DIG_FORCE); + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DIG_FORCE); + SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX); + SET_PERI_REG_BITS(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR_CLK_DIV, 4, SYSCON_SARADC_SAR_CLK_DIV_S); + + SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_RSTB_WAIT, 8, SYSCON_SARADC_RSTB_WAIT_S); /* was 1 */ + SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_START_WAIT, 10, SYSCON_SARADC_START_WAIT_S); + SET_PERI_REG_BITS(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_WORK_MODE, 0, SYSCON_SARADC_WORK_MODE_S); + SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR_SEL); + CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_DATA_SAR_SEL); + + SET_PERI_REG_BITS(I2S_SAMPLE_RATE_CONF_REG(0), I2S_RX_BCK_DIV_NUM, 20, I2S_RX_BCK_DIV_NUM_S); + + SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG,SYSCON_SARADC_DATA_TO_I2S); + + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_CAMERA_EN); + SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_LCD_EN); + SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE); + SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE_TEST_EN); + SET_PERI_REG_MASK(I2S_CONF_REG(0), I2S_RX_START); +} + +void bootloader_random_disable(void) +{ + /* Disable i2s clock */ + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + + + /* Reset some i2s configuration (possibly redundant as we reset entire + I2S peripheral further down). */ + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_CAMERA_EN); + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_LCD_EN); + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE_TEST_EN); + CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE); + CLEAR_PERI_REG_MASK(I2S_CONF_REG(0), I2S_RX_START); + + /* Restore SYSCON mode registers */ + CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DIG_FORCE); + CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DIG_FORCE); + CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX | SYSCON_SARADC_SAR_SEL); + + /* Restore SAR ADC mode */ + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST); + + /* Reset i2s peripheral */ + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); +} diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c index f4fd7e783df..8b9ca60cd1a 100644 --- a/components/bootloader_support/src/flash_encrypt.c +++ b/components/bootloader_support/src/flash_encrypt.c @@ -78,12 +78,19 @@ static esp_err_t initialise_flash_encryption(void) && REG_READ(EFUSE_BLK1_RDATA5_REG) == 0 && REG_READ(EFUSE_BLK1_RDATA6_REG) == 0 && REG_READ(EFUSE_BLK1_RDATA7_REG) == 0) { + ESP_LOGI(TAG, "Generating new flash encryption key..."); + uint32_t buf[8]; + bootloader_fill_random(buf, sizeof(buf)); + for (int i = 0; i < 8; i++) { + ESP_LOGV(TAG, "EFUSE_BLK1_WDATA%d_REG = 0x%08x", i, buf[i]); + REG_WRITE(EFUSE_BLK1_WDATA0_REG + 4*i, buf[i]); + } + bzero(buf, sizeof(buf)); + esp_efuse_burn_new_values(); - /* On-device key generation is temporarily disabled, until - * RNG operation during bootloader is qualified. - * See docs/security/flash-encryption.rst for details. */ - ESP_LOGE(TAG, "On-device key generation is not yet available."); - return ESP_ERR_NOT_SUPPORTED; + ESP_LOGI(TAG, "Read & write protecting new key..."); + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK1 | EFUSE_RD_DIS_BLK1); + esp_efuse_burn_new_values(); } else { if(!(efuse_key_read_protected && efuse_key_write_protected)) { diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index c5ae1f19ea6..0230e85ad53 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -130,12 +130,21 @@ esp_err_t esp_secure_boot_permanently_enable(void) { && REG_READ(EFUSE_BLK2_RDATA5_REG) == 0 && REG_READ(EFUSE_BLK2_RDATA6_REG) == 0 && REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) { + ESP_LOGI(TAG, "Generating new secure boot key..."); + uint32_t buf[8]; + bootloader_fill_random(buf, sizeof(buf)); + for (int i = 0; i < 8; i++) { + ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); + REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); + } + bzero(buf, sizeof(buf)); + burn_efuses(); + ESP_LOGI(TAG, "Read & write protecting new key..."); + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2); + burn_efuses(); + efuse_key_read_protected = true; + efuse_key_write_protected = true; - /* On-device key generation is temporarily disabled, until - * RNG operation during bootloader is qualified. - * See docs/security/secure-boot.rst for details. */ - ESP_LOGE(TAG, "On-device key generation is not yet available."); - return ESP_ERR_NOT_SUPPORTED; } else { ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2"); } diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index b93bae72980..94d3e1f3e5f 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -158,6 +158,7 @@ #define DR_REG_RTCMEM0_BASE 0x3ff61000 #define DR_REG_RTCMEM1_BASE 0x3ff62000 #define DR_REG_RTCMEM2_BASE 0x3ff63000 +#define DR_REG_SYSCON_BASE 0x3ff66000 #define DR_REG_HINF_BASE 0x3ff4B000 #define DR_REG_UHCI1_BASE 0x3ff4C000 #define DR_REG_I2S_BASE 0x3ff4F000 diff --git a/components/esp32/include/soc/syscon_reg.h b/components/esp32/include/soc/syscon_reg.h new file mode 100644 index 00000000000..5012b27e573 --- /dev/null +++ b/components/esp32/include/soc/syscon_reg.h @@ -0,0 +1,294 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SOC_SYSCON_REG_H_ +#define _SOC_SYSCON_REG_H_ + +#include "soc.h" +#define SYSCON_SYSCLK_CONF_REG (DR_REG_SYSCON_BASE + 0x0) +/* SYSCON_QUICK_CLK_CHNG : R/W ;bitpos:[13] ;default: 1'b1 ; */ +/*description: */ +#define SYSCON_QUICK_CLK_CHNG (BIT(13)) +#define SYSCON_QUICK_CLK_CHNG_M (BIT(13)) +#define SYSCON_QUICK_CLK_CHNG_V 0x1 +#define SYSCON_QUICK_CLK_CHNG_S 13 +/* SYSCON_RST_TICK_CNT : R/W ;bitpos:[12] ;default: 1'b0 ; */ +/*description: */ +#define SYSCON_RST_TICK_CNT (BIT(12)) +#define SYSCON_RST_TICK_CNT_M (BIT(12)) +#define SYSCON_RST_TICK_CNT_V 0x1 +#define SYSCON_RST_TICK_CNT_S 12 +/* SYSCON_CLK_EN : R/W ;bitpos:[11] ;default: 1'b0 ; */ +/*description: */ +#define SYSCON_CLK_EN (BIT(11)) +#define SYSCON_CLK_EN_M (BIT(11)) +#define SYSCON_CLK_EN_V 0x1 +#define SYSCON_CLK_EN_S 11 +/* SYSCON_CLK_320M_EN : R/W ;bitpos:[10] ;default: 1'b0 ; */ +/*description: */ +#define SYSCON_CLK_320M_EN (BIT(10)) +#define SYSCON_CLK_320M_EN_M (BIT(10)) +#define SYSCON_CLK_320M_EN_V 0x1 +#define SYSCON_CLK_320M_EN_S 10 +/* SYSCON_PRE_DIV_CNT : R/W ;bitpos:[9:0] ;default: 10'h0 ; */ +/*description: */ +#define SYSCON_PRE_DIV_CNT 0x000003FF +#define SYSCON_PRE_DIV_CNT_M ((SYSCON_PRE_DIV_CNT_V)<<(SYSCON_PRE_DIV_CNT_S)) +#define SYSCON_PRE_DIV_CNT_V 0x3FF +#define SYSCON_PRE_DIV_CNT_S 0 + +#define SYSCON_XTAL_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0x4) +/* SYSCON_XTAL_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd39 ; */ +/*description: */ +#define SYSCON_XTAL_TICK_NUM 0x000000FF +#define SYSCON_XTAL_TICK_NUM_M ((SYSCON_XTAL_TICK_NUM_V)<<(SYSCON_XTAL_TICK_NUM_S)) +#define SYSCON_XTAL_TICK_NUM_V 0xFF +#define SYSCON_XTAL_TICK_NUM_S 0 + +#define SYSCON_PLL_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0x8) +/* SYSCON_PLL_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd79 ; */ +/*description: */ +#define SYSCON_PLL_TICK_NUM 0x000000FF +#define SYSCON_PLL_TICK_NUM_M ((SYSCON_PLL_TICK_NUM_V)<<(SYSCON_PLL_TICK_NUM_S)) +#define SYSCON_PLL_TICK_NUM_V 0xFF +#define SYSCON_PLL_TICK_NUM_S 0 + +#define SYSCON_CK8M_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0xC) +/* SYSCON_CK8M_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd11 ; */ +/*description: */ +#define SYSCON_CK8M_TICK_NUM 0x000000FF +#define SYSCON_CK8M_TICK_NUM_M ((SYSCON_CK8M_TICK_NUM_V)<<(SYSCON_CK8M_TICK_NUM_S)) +#define SYSCON_CK8M_TICK_NUM_V 0xFF +#define SYSCON_CK8M_TICK_NUM_S 0 + +#define SYSCON_SARADC_CTRL_REG (DR_REG_SYSCON_BASE + 0x10) +/* SYSCON_SARADC_DATA_TO_I2S : R/W ;bitpos:[26] ;default: 1'b0 ; */ +/*description: 1: I2S input data is from SAR ADC (for DMA) 0: I2S input data + is from GPIO matrix*/ +#define SYSCON_SARADC_DATA_TO_I2S (BIT(26)) +#define SYSCON_SARADC_DATA_TO_I2S_M (BIT(26)) +#define SYSCON_SARADC_DATA_TO_I2S_V 0x1 +#define SYSCON_SARADC_DATA_TO_I2S_S 26 +/* SYSCON_SARADC_DATA_SAR_SEL : R/W ;bitpos:[25] ;default: 1'b0 ; */ +/*description: 1: sar_sel will be coded by the MSB of the 16-bit output data + in this case the resolution should not be larger than 11 bits.*/ +#define SYSCON_SARADC_DATA_SAR_SEL (BIT(25)) +#define SYSCON_SARADC_DATA_SAR_SEL_M (BIT(25)) +#define SYSCON_SARADC_DATA_SAR_SEL_V 0x1 +#define SYSCON_SARADC_DATA_SAR_SEL_S 25 +/* SYSCON_SARADC_SAR2_PATT_P_CLEAR : R/W ;bitpos:[24] ;default: 1'd0 ; */ +/*description: clear the pointer of pattern table for DIG ADC2 CTRL*/ +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR (BIT(24)) +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR_M (BIT(24)) +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR_V 0x1 +#define SYSCON_SARADC_SAR2_PATT_P_CLEAR_S 24 +/* SYSCON_SARADC_SAR1_PATT_P_CLEAR : R/W ;bitpos:[23] ;default: 1'd0 ; */ +/*description: clear the pointer of pattern table for DIG ADC1 CTRL*/ +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR (BIT(23)) +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR_M (BIT(23)) +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR_V 0x1 +#define SYSCON_SARADC_SAR1_PATT_P_CLEAR_S 23 +/* SYSCON_SARADC_SAR2_PATT_LEN : R/W ;bitpos:[22:19] ;default: 4'd15 ; */ +/*description: 0 ~ 15 means length 1 ~ 16*/ +#define SYSCON_SARADC_SAR2_PATT_LEN 0x0000000F +#define SYSCON_SARADC_SAR2_PATT_LEN_M ((SYSCON_SARADC_SAR2_PATT_LEN_V)<<(SYSCON_SARADC_SAR2_PATT_LEN_S)) +#define SYSCON_SARADC_SAR2_PATT_LEN_V 0xF +#define SYSCON_SARADC_SAR2_PATT_LEN_S 19 +/* SYSCON_SARADC_SAR1_PATT_LEN : R/W ;bitpos:[18:15] ;default: 4'd15 ; */ +/*description: 0 ~ 15 means length 1 ~ 16*/ +#define SYSCON_SARADC_SAR1_PATT_LEN 0x0000000F +#define SYSCON_SARADC_SAR1_PATT_LEN_M ((SYSCON_SARADC_SAR1_PATT_LEN_V)<<(SYSCON_SARADC_SAR1_PATT_LEN_S)) +#define SYSCON_SARADC_SAR1_PATT_LEN_V 0xF +#define SYSCON_SARADC_SAR1_PATT_LEN_S 15 +/* SYSCON_SARADC_SAR_CLK_DIV : R/W ;bitpos:[14:7] ;default: 8'd4 ; */ +/*description: SAR clock divider*/ +#define SYSCON_SARADC_SAR_CLK_DIV 0x000000FF +#define SYSCON_SARADC_SAR_CLK_DIV_M ((SYSCON_SARADC_SAR_CLK_DIV_V)<<(SYSCON_SARADC_SAR_CLK_DIV_S)) +#define SYSCON_SARADC_SAR_CLK_DIV_V 0xFF +#define SYSCON_SARADC_SAR_CLK_DIV_S 7 +/* SYSCON_SARADC_SAR_CLK_GATED : R/W ;bitpos:[6] ;default: 1'b1 ; */ +/*description: */ +#define SYSCON_SARADC_SAR_CLK_GATED (BIT(6)) +#define SYSCON_SARADC_SAR_CLK_GATED_M (BIT(6)) +#define SYSCON_SARADC_SAR_CLK_GATED_V 0x1 +#define SYSCON_SARADC_SAR_CLK_GATED_S 6 +/* SYSCON_SARADC_SAR_SEL : R/W ;bitpos:[5] ;default: 1'd0 ; */ +/*description: 0: SAR1 1: SAR2 only work for single SAR mode*/ +#define SYSCON_SARADC_SAR_SEL (BIT(5)) +#define SYSCON_SARADC_SAR_SEL_M (BIT(5)) +#define SYSCON_SARADC_SAR_SEL_V 0x1 +#define SYSCON_SARADC_SAR_SEL_S 5 +/* SYSCON_SARADC_WORK_MODE : R/W ;bitpos:[4:3] ;default: 2'd0 ; */ +/*description: 0: single mode 1: double mode 2: alternate mode*/ +#define SYSCON_SARADC_WORK_MODE 0x00000003 +#define SYSCON_SARADC_WORK_MODE_M ((SYSCON_SARADC_WORK_MODE_V)<<(SYSCON_SARADC_WORK_MODE_S)) +#define SYSCON_SARADC_WORK_MODE_V 0x3 +#define SYSCON_SARADC_WORK_MODE_S 3 +/* SYSCON_SARADC_SAR2_MUX : R/W ;bitpos:[2] ;default: 1'd0 ; */ +/*description: 1: SAR ADC2 is controlled by DIG ADC2 CTRL 0: SAR ADC2 is controlled + by PWDET CTRL*/ +#define SYSCON_SARADC_SAR2_MUX (BIT(2)) +#define SYSCON_SARADC_SAR2_MUX_M (BIT(2)) +#define SYSCON_SARADC_SAR2_MUX_V 0x1 +#define SYSCON_SARADC_SAR2_MUX_S 2 +/* SYSCON_SARADC_START : R/W ;bitpos:[1] ;default: 1'd0 ; */ +/*description: */ +#define SYSCON_SARADC_START (BIT(1)) +#define SYSCON_SARADC_START_M (BIT(1)) +#define SYSCON_SARADC_START_V 0x1 +#define SYSCON_SARADC_START_S 1 +/* SYSCON_SARADC_START_FORCE : R/W ;bitpos:[0] ;default: 1'd0 ; */ +/*description: */ +#define SYSCON_SARADC_START_FORCE (BIT(0)) +#define SYSCON_SARADC_START_FORCE_M (BIT(0)) +#define SYSCON_SARADC_START_FORCE_V 0x1 +#define SYSCON_SARADC_START_FORCE_S 0 + +#define SYSCON_SARADC_CTRL2_REG (DR_REG_SYSCON_BASE + 0x14) +/* SYSCON_SARADC_SAR2_INV : R/W ;bitpos:[10] ;default: 1'd0 ; */ +/*description: 1: data to DIG ADC2 CTRL is inverted otherwise not*/ +#define SYSCON_SARADC_SAR2_INV (BIT(10)) +#define SYSCON_SARADC_SAR2_INV_M (BIT(10)) +#define SYSCON_SARADC_SAR2_INV_V 0x1 +#define SYSCON_SARADC_SAR2_INV_S 10 +/* SYSCON_SARADC_SAR1_INV : R/W ;bitpos:[9] ;default: 1'd0 ; */ +/*description: 1: data to DIG ADC1 CTRL is inverted otherwise not*/ +#define SYSCON_SARADC_SAR1_INV (BIT(9)) +#define SYSCON_SARADC_SAR1_INV_M (BIT(9)) +#define SYSCON_SARADC_SAR1_INV_V 0x1 +#define SYSCON_SARADC_SAR1_INV_S 9 +/* SYSCON_SARADC_MAX_MEAS_NUM : R/W ;bitpos:[8:1] ;default: 8'd255 ; */ +/*description: max conversion number*/ +#define SYSCON_SARADC_MAX_MEAS_NUM 0x000000FF +#define SYSCON_SARADC_MAX_MEAS_NUM_M ((SYSCON_SARADC_MAX_MEAS_NUM_V)<<(SYSCON_SARADC_MAX_MEAS_NUM_S)) +#define SYSCON_SARADC_MAX_MEAS_NUM_V 0xFF +#define SYSCON_SARADC_MAX_MEAS_NUM_S 1 +/* SYSCON_SARADC_MEAS_NUM_LIMIT : R/W ;bitpos:[0] ;default: 1'd0 ; */ +/*description: */ +#define SYSCON_SARADC_MEAS_NUM_LIMIT (BIT(0)) +#define SYSCON_SARADC_MEAS_NUM_LIMIT_M (BIT(0)) +#define SYSCON_SARADC_MEAS_NUM_LIMIT_V 0x1 +#define SYSCON_SARADC_MEAS_NUM_LIMIT_S 0 + +#define SYSCON_SARADC_FSM_REG (DR_REG_SYSCON_BASE + 0x18) +/* SYSCON_SARADC_SAMPLE_CYCLE : R/W ;bitpos:[31:24] ;default: 8'd2 ; */ +/*description: sample cycles*/ +#define SYSCON_SARADC_SAMPLE_CYCLE 0x000000FF +#define SYSCON_SARADC_SAMPLE_CYCLE_M ((SYSCON_SARADC_SAMPLE_CYCLE_V)<<(SYSCON_SARADC_SAMPLE_CYCLE_S)) +#define SYSCON_SARADC_SAMPLE_CYCLE_V 0xFF +#define SYSCON_SARADC_SAMPLE_CYCLE_S 24 +/* SYSCON_SARADC_START_WAIT : R/W ;bitpos:[23:16] ;default: 8'd8 ; */ +/*description: */ +#define SYSCON_SARADC_START_WAIT 0x000000FF +#define SYSCON_SARADC_START_WAIT_M ((SYSCON_SARADC_START_WAIT_V)<<(SYSCON_SARADC_START_WAIT_S)) +#define SYSCON_SARADC_START_WAIT_V 0xFF +#define SYSCON_SARADC_START_WAIT_S 16 +/* SYSCON_SARADC_STANDBY_WAIT : R/W ;bitpos:[15:8] ;default: 8'd255 ; */ +/*description: */ +#define SYSCON_SARADC_STANDBY_WAIT 0x000000FF +#define SYSCON_SARADC_STANDBY_WAIT_M ((SYSCON_SARADC_STANDBY_WAIT_V)<<(SYSCON_SARADC_STANDBY_WAIT_S)) +#define SYSCON_SARADC_STANDBY_WAIT_V 0xFF +#define SYSCON_SARADC_STANDBY_WAIT_S 8 +/* SYSCON_SARADC_RSTB_WAIT : R/W ;bitpos:[7:0] ;default: 8'd8 ; */ +/*description: */ +#define SYSCON_SARADC_RSTB_WAIT 0x000000FF +#define SYSCON_SARADC_RSTB_WAIT_M ((SYSCON_SARADC_RSTB_WAIT_V)<<(SYSCON_SARADC_RSTB_WAIT_S)) +#define SYSCON_SARADC_RSTB_WAIT_V 0xFF +#define SYSCON_SARADC_RSTB_WAIT_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB1_REG (DR_REG_SYSCON_BASE + 0x1C) +/* SYSCON_SARADC_SAR1_PATT_TAB1 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: item 0 ~ 3 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB1 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB1_M ((SYSCON_SARADC_SAR1_PATT_TAB1_V)<<(SYSCON_SARADC_SAR1_PATT_TAB1_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB1_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB1_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB2_REG (DR_REG_SYSCON_BASE + 0x20) +/* SYSCON_SARADC_SAR1_PATT_TAB2 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 4 ~ 7 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB2 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB2_M ((SYSCON_SARADC_SAR1_PATT_TAB2_V)<<(SYSCON_SARADC_SAR1_PATT_TAB2_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB2_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB2_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB3_REG (DR_REG_SYSCON_BASE + 0x24) +/* SYSCON_SARADC_SAR1_PATT_TAB3 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 8 ~ 11 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB3 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB3_M ((SYSCON_SARADC_SAR1_PATT_TAB3_V)<<(SYSCON_SARADC_SAR1_PATT_TAB3_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB3_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB3_S 0 + +#define SYSCON_SARADC_SAR1_PATT_TAB4_REG (DR_REG_SYSCON_BASE + 0x28) +/* SYSCON_SARADC_SAR1_PATT_TAB4 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 12 ~ 15 for pattern table 1 (each item one byte)*/ +#define SYSCON_SARADC_SAR1_PATT_TAB4 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB4_M ((SYSCON_SARADC_SAR1_PATT_TAB4_V)<<(SYSCON_SARADC_SAR1_PATT_TAB4_S)) +#define SYSCON_SARADC_SAR1_PATT_TAB4_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR1_PATT_TAB4_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB1_REG (DR_REG_SYSCON_BASE + 0x2C) +/* SYSCON_SARADC_SAR2_PATT_TAB1 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: item 0 ~ 3 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB1 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB1_M ((SYSCON_SARADC_SAR2_PATT_TAB1_V)<<(SYSCON_SARADC_SAR2_PATT_TAB1_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB1_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB1_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB2_REG (DR_REG_SYSCON_BASE + 0x30) +/* SYSCON_SARADC_SAR2_PATT_TAB2 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 4 ~ 7 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB2 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB2_M ((SYSCON_SARADC_SAR2_PATT_TAB2_V)<<(SYSCON_SARADC_SAR2_PATT_TAB2_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB2_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB2_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB3_REG (DR_REG_SYSCON_BASE + 0x34) +/* SYSCON_SARADC_SAR2_PATT_TAB3 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 8 ~ 11 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB3 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB3_M ((SYSCON_SARADC_SAR2_PATT_TAB3_V)<<(SYSCON_SARADC_SAR2_PATT_TAB3_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB3_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB3_S 0 + +#define SYSCON_SARADC_SAR2_PATT_TAB4_REG (DR_REG_SYSCON_BASE + 0x38) +/* SYSCON_SARADC_SAR2_PATT_TAB4 : R/W ;bitpos:[31:0] ;default: 32'hf0f0f0f ; */ +/*description: Item 12 ~ 15 for pattern table 2 (each item one byte)*/ +#define SYSCON_SARADC_SAR2_PATT_TAB4 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB4_M ((SYSCON_SARADC_SAR2_PATT_TAB4_V)<<(SYSCON_SARADC_SAR2_PATT_TAB4_S)) +#define SYSCON_SARADC_SAR2_PATT_TAB4_V 0xFFFFFFFF +#define SYSCON_SARADC_SAR2_PATT_TAB4_S 0 + +#define SYSCON_APLL_TICK_CONF_REG (DR_REG_SYSCON_BASE + 0x3C) +/* SYSCON_APLL_TICK_NUM : R/W ;bitpos:[7:0] ;default: 8'd99 ; */ +/*description: */ +#define SYSCON_APLL_TICK_NUM 0x000000FF +#define SYSCON_APLL_TICK_NUM_M ((SYSCON_APLL_TICK_NUM_V)<<(SYSCON_APLL_TICK_NUM_S)) +#define SYSCON_APLL_TICK_NUM_V 0xFF +#define SYSCON_APLL_TICK_NUM_S 0 + +#define SYSCON_DATE_REG (DR_REG_SYSCON_BASE + 0x7C) +/* SYSCON_DATE : R/W ;bitpos:[31:0] ;default: 32'h16042000 ; */ +/*description: */ +#define SYSCON_DATE 0xFFFFFFFF +#define SYSCON_DATE_M ((SYSCON_DATE_V)<<(SYSCON_DATE_S)) +#define SYSCON_DATE_V 0xFFFFFFFF +#define SYSCON_DATE_S 0 + + + + +#endif /*_SOC_SYSCON_REG_H_ */ + + diff --git a/components/esp32/include/soc/syscon_struct.h b/components/esp32/include/soc/syscon_struct.h new file mode 100644 index 00000000000..700aeecf4c4 --- /dev/null +++ b/components/esp32/include/soc/syscon_struct.h @@ -0,0 +1,120 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SOC_SYSCON_STRUCT_H_ +#define _SOC_SYSCON_STRUCT_H_ +typedef struct { + union { + struct { + volatile uint32_t pre_div: 10; + volatile uint32_t clk_320m_en: 1; + volatile uint32_t clk_en: 1; + volatile uint32_t rst_tick: 1; + volatile uint32_t quick_clk_chng: 1; + volatile uint32_t reserved14: 18; + }; + volatile uint32_t val; + }clk_conf; + union { + struct { + volatile uint32_t xtal_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }xtal_tick_conf; + union { + struct { + volatile uint32_t pll_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }pll_tick_conf; + union { + struct { + volatile uint32_t ck8m_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }ck8m_tick_conf; + union { + struct { + volatile uint32_t start_force: 1; + volatile uint32_t start: 1; + volatile uint32_t sar2_mux: 1; /*1: SAR ADC2 is controlled by DIG ADC2 CTRL 0: SAR ADC2 is controlled by PWDET CTRL*/ + volatile uint32_t work_mode: 2; /*0: single mode 1: double mode 2: alternate mode*/ + volatile uint32_t sar_sel: 1; /*0: SAR1 1: SAR2 only work for single SAR mode*/ + volatile uint32_t sar_clk_gated: 1; + volatile uint32_t sar_clk_div: 8; /*SAR clock divider*/ + volatile uint32_t sar1_patt_len: 4; /*0 ~ 15 means length 1 ~ 16*/ + volatile uint32_t sar2_patt_len: 4; /*0 ~ 15 means length 1 ~ 16*/ + volatile uint32_t sar1_patt_p_clear: 1; /*clear the pointer of pattern table for DIG ADC1 CTRL*/ + volatile uint32_t sar2_patt_p_clear: 1; /*clear the pointer of pattern table for DIG ADC2 CTRL*/ + volatile uint32_t data_sar_sel: 1; /*1: sar_sel will be coded by the MSB of the 16-bit output data in this case the resolution should not be larger than 11 bits.*/ + volatile uint32_t data_to_i2s: 1; /*1: I2S input data is from SAR ADC (for DMA) 0: I2S input data is from GPIO matrix*/ + volatile uint32_t reserved27: 5; + }; + volatile uint32_t val; + }saradc_ctrl; + union { + struct { + volatile uint32_t meas_num_limit: 1; + volatile uint32_t max_meas_num: 8; /*max conversion number*/ + volatile uint32_t sar1_inv: 1; /*1: data to DIG ADC1 CTRL is inverted otherwise not*/ + volatile uint32_t sar2_inv: 1; /*1: data to DIG ADC2 CTRL is inverted otherwise not*/ + volatile uint32_t reserved11: 21; + }; + volatile uint32_t val; + }saradc_ctrl2; + union { + struct { + volatile uint32_t rstb_wait: 8; + volatile uint32_t standby_wait: 8; + volatile uint32_t start_wait: 8; + volatile uint32_t sample_cycle: 8; /*sample cycles*/ + }; + volatile uint32_t val; + }saradc_fsm; + volatile uint32_t saradc_sar1_patt_tab1; /*item 0 ~ 3 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar1_patt_tab2; /*Item 4 ~ 7 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar1_patt_tab3; /*Item 8 ~ 11 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar1_patt_tab4; /*Item 12 ~ 15 for pattern table 1 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab1; /*item 0 ~ 3 for pattern table 2 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab2; /*Item 4 ~ 7 for pattern table 2 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab3; /*Item 8 ~ 11 for pattern table 2 (each item one byte)*/ + volatile uint32_t saradc_sar2_patt_tab4; /*Item 12 ~ 15 for pattern table 2 (each item one byte)*/ + union { + struct { + volatile uint32_t apll_tick: 8; + volatile uint32_t reserved8: 24; + }; + volatile uint32_t val; + }apll_tick_conf; + volatile uint32_t reserved_40; + volatile uint32_t reserved_44; + volatile uint32_t reserved_48; + volatile uint32_t reserved_4c; + volatile uint32_t reserved_50; + volatile uint32_t reserved_54; + volatile uint32_t reserved_58; + volatile uint32_t reserved_5c; + volatile uint32_t reserved_60; + volatile uint32_t reserved_64; + volatile uint32_t reserved_68; + volatile uint32_t reserved_6c; + volatile uint32_t reserved_70; + volatile uint32_t reserved_74; + volatile uint32_t reserved_78; + volatile uint32_t date; /**/ +} syscon_dev_t; + +#endif /* _SOC_SYSCON_STRUCT_H_ */ diff --git a/docs/security/flash-encryption.rst b/docs/security/flash-encryption.rst index b73d8a9ad35..298a0a75bf6 100644 --- a/docs/security/flash-encryption.rst +++ b/docs/security/flash-encryption.rst @@ -7,9 +7,6 @@ Flash Encryption is separate from the `Secure Boot` feature, and you can use fla **IMPORTANT: Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including `Limitations of Flash Encryption` and understand the implications of enabling flash encryption.** -**IMPORTANT: Flash Encryption feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the flash encryption key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to flash encryption keys being generated on first boot, however for now it is necessary to follow the additional steps shown under `Precalculated Flash Encryption Key`.** - - Background ---------- diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 7aaf9abf815..3c247d4a46e 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -5,8 +5,6 @@ Secure Boot is a feature for ensuring only your code can run on the chip. Data l Secure Boot is separate from the `Flash Encryption` feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. -**IMPORTANT: Secure Boot feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the secure boot key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to "One-Time Flashable" mode (where keys are generated on the device), but for now only the `Re-Flashable Software Bootloader` mode is available.** - Background ---------- From 63e9806d855718d848c1dde953048e4eaba20e8b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Jan 2017 16:30:07 +1100 Subject: [PATCH 050/167] esp_random: XOR the RNG register value several times before returning it Probably unnecessary, but avoids returning internal RNG state as-is. --- components/esp32/hw_random.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/esp32/hw_random.c b/components/esp32/hw_random.c index 11c7af936b6..3e5cb81a0a0 100644 --- a/components/esp32/hw_random.c +++ b/components/esp32/hw_random.c @@ -29,13 +29,19 @@ uint32_t IRAM_ATTR esp_random(void) * this function needs to wait for at least 16 APB clock cycles after reading * previous word. This implementation may actually wait a bit longer * due to extra time spent in arithmetic and branch statements. + * + * As a (probably unncessary) precaution to avoid returning the + * RNG state as-is, the result is XORed with additional + * WDEV_RND_REG reads while waiting. */ static uint32_t last_ccount = 0; uint32_t ccount; + uint32_t result = 0; do { ccount = XTHAL_GET_CCOUNT(); + result ^= REG_READ(WDEV_RND_REG); } while (ccount - last_ccount < XT_CLOCK_FREQ / APB_CLK_FREQ * 16); last_ccount = ccount; - return REG_READ(WDEV_RND_REG); + return result ^ REG_READ(WDEV_RND_REG); } From 6c41080fd91fb8d20dd63aabe1afeb3268fab027 Mon Sep 17 00:00:00 2001 From: Yinling Date: Mon, 2 Jan 2017 19:28:01 +0800 Subject: [PATCH 051/167] CI: update known issues --- .../idf_test/integration_test/KnownIssues | 139 ++++++++++++------ 1 file changed, 95 insertions(+), 44 deletions(-) diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index c6cb7b77b95..884bd2f590d 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -38,30 +38,45 @@ WIFI_CONN_0904 WIFI_CONN_0902 # Wifi connect issue -^WIFI_CONN_0101 WIFI_CONN_0101 -WIFI_CONN_0103 -^WIFI_CONN_0401 -^WIFI_CONN_0103 +^WIFI_CONN_0101 WIFI_CONN_0102 +^WIFI_CONN_0102 +WIFI_CONN_0103 +^WIFI_CONN_0103 +WIFI_CONN_0104 +^WIFI_CONN_0104 +WIFI_CONN_0201 +^WIFI_CONN_0201 +WIFI_CONN_0401 +^WIFI_CONN_0401 WIFI_CONN_0601 ^WIFI_CONN_0601 WIFI_ADDR_0102 ^WIFI_ADDR_0102 WIFI_CONN_0502 +^WIFI_CONN_0502 WIFI_CONN_0501 +^WIFI_CONN_0501 # Wifi scan issue +WIFI_SCAN_0101 ^WIFI_SCAN_0101 +WIFI_SCAN_0102 ^WIFI_SCAN_0102 WIFI_SCAN_0103 ^WIFI_SCAN_0103 WIFI_SCAN_0104 ^WIFI_SCAN_0104 +WIFI_SCAN_0105 ^WIFI_SCAN_0105 WIFI_SCAN_0303 +^WIFI_SCAN_0303 WIFI_SCAN_0304 -^WIFI_SCAN_0104 +^WIFI_SCAN_0304 +WIFI_MODE_0101 +WIFI_MODE_0102 +WIFI_MODE_0103 # IGMP cases are supported but as UDP is not stable, exclude them first TCPIP_IGMP_0101 @@ -82,78 +97,114 @@ TCPIP_IGMP_0204 ^TCPIP_IGMP_0204 # DHCP issues -^TCPIP_DHCP_0301 -TCPIP_DHCP_0301 TCPIP_DHCP_0101 ^TCPIP_DHCP_0101 +TCPIP_DHCP_0202 +^TCPIP_DHCP_0202 +TCPIP_DHCP_0204 +^TCPIP_DHCP_0204 +TCPIP_DHCP_0205 +^TCPIP_DHCP_0205 +TCPIP_DHCP_0206 +^TCPIP_DHCP_0206 TCPIP_DHCP_0207 ^TCPIP_DHCP_0207 TCPIP_DHCP_0208 ^TCPIP_DHCP_0208 -TCPIP_DHCP_0205 -^TCPIP_DHCP_0205 TCPIP_DHCP_0209 ^TCPIP_DHCP_0209 -^TCPIP_DHCP_0204 -TCPIP_DHCP_0204 -^TCPIP_DHCP_0210 TCPIP_DHCP_0210 -^TCPIP_DHCP_0211 +^TCPIP_DHCP_0210 TCPIP_DHCP_0211 -^TCPIP_DHCP_0206 +^TCPIP_DHCP_0211 +TCPIP_DHCP_0301 +^TCPIP_DHCP_0301 +TCPIP_DHCP_0302 ^TCPIP_DHCP_0302 # TCP issue -TCPIP_TCP_0402 -^TCPIP_TCP_0406 -^TCPIP_TCP_0401 -TCPIP_TCP_0210 -^TCPIP_TCP_0210 +TCPIP_TCP_0101 +^TCPIP_TCP_0101 +TCPIP_TCP_0102 +^TCPIP_TCP_0102 TCPIP_TCP_0103 ^TCPIP_TCP_0103 +TCPIP_TCP_0104 +^TCPIP_TCP_0104 +TCPIP_TCP_0105 +^TCPIP_TCP_0105 +TCPIP_TCP_0106 +^TCPIP_TCP_0106 +TCPIP_TCP_0107 +^TCPIP_TCP_0107 TCPIP_TCP_0112 ^TCPIP_TCP_0112 -TCPIP_TCP_0106 -TCPIP_TCP_0107 -TCPIP_TCP_0105 -^TCPIP_TCP_0407 -^TCPIP_TCP_0404 -^TCPIP_TCP_0102 -^TCPIP_TCP_0105 -^TCPIP_TCP_0107 -^TCPIP_TCP_0206 -TCPIP_TCP_0102 -^TCPIP_TCP_0403 -^TCPIP_TCP_0402 -^TCPIP_TCP_0101 -^TCPIP_TCP_0203 -^TCPIP_TCP_0106 +TCPIP_TCP_0201 ^TCPIP_TCP_0201 +TCPIP_TCP_0202 +^TCPIP_TCP_0202 +TCPIP_TCP_0203 +^TCPIP_TCP_0203 +TCPIP_TCP_0204 +^TCPIP_TCP_0204 +TCPIP_TCP_0206 +^TCPIP_TCP_0206 +TCPIP_TCP_0208 +^TCPIP_TCP_0208 +TCPIP_TCP_0210 +^TCPIP_TCP_0210 +TCPIP_TCP_0401 +^TCPIP_TCP_0401 +TCPIP_TCP_0402 +^TCPIP_TCP_0402 +TCPIP_TCP_0403 +^TCPIP_TCP_0403 +TCPIP_TCP_0404 +^TCPIP_TCP_0404 +TCPIP_TCP_0406 +^TCPIP_TCP_0406 +TCPIP_TCP_0407 +^TCPIP_TCP_0407 +TCPIP_TCP_0408 +^TCPIP_TCP_0408 +TCPIP_TCP_0412 ^TCPIP_TCP_0412 -TCPIP_TCP_0101 +TCPIP_TCP_0411 +^TCPIP_TCP_0411 # UDP issue +TCPIP_UDP_0102 +^TCPIP_UDP_0102 TCPIP_UDP_0103 ^TCPIP_UDP_0103 +TCPIP_UDP_0104 +^TCPIP_UDP_0104 +TCPIP_UDP_0108 ^TCPIP_UDP_0108 TCPIP_UDP_0110 ^TCPIP_UDP_0110 +TCPIP_UDP_0112 ^TCPIP_UDP_0112 -TCPIP_UDP_0305 -^TCPIP_UDP_0305 -^TCPIP_UDP_0304 -TCPIP_UDP_0104 -^TCPIP_UDP_0104 -^TCPIP_UDP_0301 TCPIP_UDP_0301 +^TCPIP_UDP_0301 TCPIP_UDP_0302 +^TCPIP_UDP_0302 TCPIP_UDP_0303 ^TCPIP_UDP_0303 -^TCPIP_UDP_0307 -^TCPIP_UDP_0302 +TCPIP_UDP_0304 +^TCPIP_UDP_0304 +TCPIP_UDP_0305 +^TCPIP_UDP_0305 +TCPIP_UDP_0306 ^TCPIP_UDP_0306 +TCPIP_UDP_0307 +^TCPIP_UDP_0307 #DNS -^TCPIP_DNS_0103 +TCPIP_DNS_0101 +^TCPIP_DNS_0101 +TCPIP_DNS_0102 ^TCPIP_DNS_0102 +TCPIP_DNS_0103 +^TCPIP_DNS_0103 From b1cac83f610ad7a9918c61c0b87bb05e97a7ffbb Mon Sep 17 00:00:00 2001 From: Chen Wu Date: Wed, 4 Jan 2017 17:39:08 +0800 Subject: [PATCH 052/167] examples: Rename 18_ota to 26_ota example numbers should accord with our internal definition. --- examples/{18_ota => 26_ota}/Makefile | 0 examples/{18_ota => 26_ota}/OTA_workflow.png | Bin examples/{18_ota => 26_ota}/README.md | 0 examples/{18_ota => 26_ota}/main/Kconfig.projbuild | 0 examples/{18_ota => 26_ota}/main/component.mk | 0 examples/{18_ota => 26_ota}/main/ota.c | 0 examples/{18_ota => 26_ota}/sdkconfig.defaults | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename examples/{18_ota => 26_ota}/Makefile (100%) rename examples/{18_ota => 26_ota}/OTA_workflow.png (100%) rename examples/{18_ota => 26_ota}/README.md (100%) rename examples/{18_ota => 26_ota}/main/Kconfig.projbuild (100%) rename examples/{18_ota => 26_ota}/main/component.mk (100%) rename examples/{18_ota => 26_ota}/main/ota.c (100%) rename examples/{18_ota => 26_ota}/sdkconfig.defaults (100%) diff --git a/examples/18_ota/Makefile b/examples/26_ota/Makefile similarity index 100% rename from examples/18_ota/Makefile rename to examples/26_ota/Makefile diff --git a/examples/18_ota/OTA_workflow.png b/examples/26_ota/OTA_workflow.png similarity index 100% rename from examples/18_ota/OTA_workflow.png rename to examples/26_ota/OTA_workflow.png diff --git a/examples/18_ota/README.md b/examples/26_ota/README.md similarity index 100% rename from examples/18_ota/README.md rename to examples/26_ota/README.md diff --git a/examples/18_ota/main/Kconfig.projbuild b/examples/26_ota/main/Kconfig.projbuild similarity index 100% rename from examples/18_ota/main/Kconfig.projbuild rename to examples/26_ota/main/Kconfig.projbuild diff --git a/examples/18_ota/main/component.mk b/examples/26_ota/main/component.mk similarity index 100% rename from examples/18_ota/main/component.mk rename to examples/26_ota/main/component.mk diff --git a/examples/18_ota/main/ota.c b/examples/26_ota/main/ota.c similarity index 100% rename from examples/18_ota/main/ota.c rename to examples/26_ota/main/ota.c diff --git a/examples/18_ota/sdkconfig.defaults b/examples/26_ota/sdkconfig.defaults similarity index 100% rename from examples/18_ota/sdkconfig.defaults rename to examples/26_ota/sdkconfig.defaults From 0fb2ab9f5cefd84da023008e120703dc676c8443 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 2 Jan 2017 20:03:10 +0800 Subject: [PATCH 053/167] lwip/freertos/esp32: add throughput optimization related code 1. Update wifi lib which contains ampdu and other optimizations 2. Add throughput code debug code 3. Other misc modification about throughput optimization --- components/esp32/include/esp_wifi_types.h | 2 +- components/esp32/lib | 2 +- components/freertos/Kconfig | 2 + components/freertos/port.c | 6 +++ components/lwip/api/api_lib.c | 4 ++ components/lwip/api/api_msg.c | 4 ++ components/lwip/api/sockets.c | 1 + components/lwip/api/tcpip.c | 15 +++---- components/lwip/include/lwip/lwip/opt.h | 2 +- components/lwip/include/lwip/port/lwipopts.h | 41 ++++++++++++++------ components/lwip/netif/ethernet.c | 4 ++ components/lwip/port/netif/ethernetif.c | 4 -- components/lwip/port/netif/wlanif.c | 3 -- 13 files changed, 59 insertions(+), 31 deletions(-) diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index ac7642829fb..be3183ba5d1 100755 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -121,7 +121,7 @@ typedef enum { #define WIFI_PROTOCOL_11N 4 typedef enum { - WIFI_BW_HT20 = 0, /* Bandwidth is HT20 */ + WIFI_BW_HT20 = 1, /* Bandwidth is HT20 */ WIFI_BW_HT40, /* Bandwidth is HT40 */ } wifi_bandwidth_t; diff --git a/components/esp32/lib b/components/esp32/lib index 02232f974b0..074303d74fc 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 02232f974b0ff1568ddd6d7015a41fb4f4870994 +Subproject commit 074303d74fc9c68823adee0a38fc1e8de42943b6 diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index fc4cf56061d..fe7e6afd88a 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -187,6 +187,7 @@ config FREERTOS_PORTMUX_DEBUG If enabled, debug information (including integrity checks) will be printed to UART for the port-specific MUX implementation. +if !FREERTOS_UNICORE config FREERTOS_PORTMUX_DEBUG_RECURSIVE bool "Debug portMUX Recursion" depends on FREERTOS_PORTMUX_DEBUG @@ -194,6 +195,7 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE help If enabled, additional debug information will be printed for recursive portMUX usage. +endif #FREERTOS_UNICORE endif # FREERTOS_DEBUG_INTERNALS diff --git a/components/freertos/port.c b/components/freertos/port.c index 25480ed4756..e5898038bcb 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -282,6 +282,7 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux, const char *fnName, int line) { #else void vPortCPUAcquireMutex(portMUX_TYPE *mux) { #endif +#if !CONFIG_FREERTOS_UNICORE uint32_t res; uint32_t recCnt; unsigned int irqStatus; @@ -324,6 +325,7 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux) { } #endif portEXIT_CRITICAL_NESTED(irqStatus); +#endif } /* @@ -335,6 +337,7 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux, const char *fnName, int li #else portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { #endif +#if !CONFIG_FREERTOS_UNICORE uint32_t res=0; uint32_t recCnt; unsigned int irqStatus; @@ -379,6 +382,9 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { } portEXIT_CRITICAL_NESTED(irqStatus); return ret; +#else //!CONFIG_FREERTOS_UNICORE + return 0; +#endif } #if CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG diff --git a/components/lwip/api/api_lib.c b/components/lwip/api/api_lib.c index ecebf4f8138..86ee5576aba 100755 --- a/components/lwip/api/api_lib.c +++ b/components/lwip/api/api_lib.c @@ -508,6 +508,10 @@ netconn_recv_data(struct netconn *conn, void **new_buf) } #endif /* (LWIP_UDP || LWIP_RAW) */ +#ifdef ESP_PERF + if (len > DBG_PERF_FILTER_LEN) { DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_SOC_IN); } +#endif + #if LWIP_SO_RCVBUF SYS_ARCH_DEC(conn->recv_avail, len); #endif /* LWIP_SO_RCVBUF */ diff --git a/components/lwip/api/api_msg.c b/components/lwip/api/api_msg.c index 2d98734b67d..87e5aeb8e5c 100755 --- a/components/lwip/api/api_msg.c +++ b/components/lwip/api/api_msg.c @@ -202,6 +202,10 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, #endif /* LWIP_NETBUF_RECVINFO */ } +#ifdef ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_OUT); +#endif + len = p->tot_len; if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { ESP_STATS_INC(esp.rx_udpmbox_post_fail); diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 4acf518cc90..c163b3ac0b5 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -1438,6 +1438,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, err = netbuf_ref(&buf, data, short_size); #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ if (err == ERR_OK) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_TX, DBG_PERF_POINT_SOC_OUT); /* send the data */ err = netconn_send(sock->conn, &buf); } diff --git a/components/lwip/api/tcpip.c b/components/lwip/api/tcpip.c index 72de714e2a9..dfb235718b3 100755 --- a/components/lwip/api/tcpip.c +++ b/components/lwip/api/tcpip.c @@ -219,6 +219,12 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn) msg->msg.inp.p = p; msg->msg.inp.netif = inp; msg->msg.inp.input_fn = input_fn; +#ifdef ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_WIFI_OUT); + } +#endif + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { ESP_STATS_INC(esp.tcpip_inpkt_post_fail); memp_free(MEMP_TCPIP_MSG_INPKT, msg); @@ -492,20 +498,11 @@ tcpip_init(tcpip_init_done_fn initfunc, void *arg) #endif /* LWIP_TCPIP_CORE_LOCKING */ -#if ESP_LWIP -#if ESP_DUAL_CORE - sys_thread_t xLwipTaskHandle = 0; - xTaskCreatePinnedToCore(tcpip_thread, TCPIP_THREAD_NAME, TCPIP_THREAD_STACKSIZE, NULL, TCPIP_THREAD_PRIO, NULL, 1); -#else sys_thread_t xLwipTaskHandle = sys_thread_new(TCPIP_THREAD_NAME , tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); -#endif printf("tcpip_task_hdlxxx : %x, prio:%d,stack:%d\n", (u32_t)xLwipTaskHandle,TCPIP_THREAD_PRIO,TCPIP_THREAD_STACKSIZE); -#else - sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); -#endif } diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index c42f3cd7350..4d8d6bf70c1 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -1665,7 +1665,7 @@ * LWIP_STATS==1: Enable statistics collection in lwip_stats. */ #ifndef LWIP_STATS -#define LWIP_STATS 1 +#define LWIP_STATS 0 #endif #if LWIP_STATS diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 5000d63ba94..933d55a5103 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -572,21 +572,37 @@ #define ESP_LIGHT_SLEEP 1 #define ESP_L2_TO_L3_COPY CONFIG_L2_TO_L3_COPY #define ESP_CNT_DEBUG 0 -#define ESP_DUAL_CORE 0 -#define TCP_WND_DEFAULT (4*TCP_MSS) -#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) +#define TCP_WND_DEFAULT (4*TCP_MSS) +#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) + +#if ESP_PERF +#define DBG_PERF_PATH_SET(dir, point) +#define DBG_PERF_FILTER_LEN 1000 + +enum { + DBG_PERF_DIR_RX = 0, + DBG_PERF_DIR_TX, +}; + +enum { + DBG_PERF_POINT_INT = 0, + DBG_PERF_POINT_WIFI_IN = 1, + DBG_PERF_POINT_WIFI_OUT = 2, + DBG_PERF_POINT_LWIP_IN = 3, + DBG_PERF_POINT_LWIP_OUT = 4, + DBG_PERF_POINT_SOC_IN = 5, + DBG_PERF_POINT_SOC_OUT = 6, +}; + +#else +#define DBG_PERF_PATH_SET(dir, point) +#define DBG_PERF_FILTER_LEN 1000 +#endif #if ESP_PER_SOC_TCP_WND -#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) -#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) -#else -#if ESP_PERF -extern unsigned char misc_prof_get_tcpw(void); -extern unsigned char misc_prof_get_tcp_snd_buf(void); -#define TCP_WND(pcb) (misc_prof_get_tcpw()*TCP_MSS) -#define TCP_SND_BUF(pcb) (misc_prof_get_tcp_snd_buf()*TCP_MSS) -#endif +#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) +#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) #endif /** @@ -595,6 +611,7 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void); #define DHCP_DEBUG LWIP_DBG_OFF #define LWIP_DEBUG LWIP_DBG_OFF #define TCP_DEBUG LWIP_DBG_OFF +#define ESP_STATS 0 #define CHECKSUM_CHECK_UDP 0 #define CHECKSUM_CHECK_IP 0 diff --git a/components/lwip/netif/ethernet.c b/components/lwip/netif/ethernet.c index 6d843913da0..5d618f6a313 100755 --- a/components/lwip/netif/ethernet.c +++ b/components/lwip/netif/ethernet.c @@ -72,6 +72,10 @@ ethernet_input(struct pbuf *p, struct netif *netif) s16_t ip_hdr_offset = SIZEOF_ETH_HDR; #endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ +#ifdef ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_IN); +#endif + if (p->len <= SIZEOF_ETH_HDR) { /* a packet with only an ethernet header (or less) is not valid for us */ ETHARP_STATS_INC(etharp.proterr); diff --git a/components/lwip/port/netif/ethernetif.c b/components/lwip/port/netif/ethernetif.c index 79f21f6b3f7..6b1245e2e6d 100755 --- a/components/lwip/port/netif/ethernetif.c +++ b/components/lwip/port/netif/ethernetif.c @@ -56,9 +56,6 @@ #define IFNAME1 'n' static char hostname[16]; -#if ESP_PERF -uint32_t g_rx_alloc_pbuf_fail_cnt = 0; -#endif /** * In this function, the hardware should be initialized. @@ -163,7 +160,6 @@ ethernetif_input(struct netif *netif, void *buffer, uint16_t len) #if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { - //g_rx_alloc_pbuf_fail_cnt++; return; } memcpy(p->payload, buffer, len); diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index fea163f8cc3..e114105f307 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -57,9 +57,6 @@ #define IFNAME1 'n' static char hostname[16]; -#if ESP_PERF -uint32_t g_rx_alloc_pbuf_fail_cnt = 0; -#endif /** * In this function, the hardware should be initialized. From d98b99f4f05cf367046af045931cf1fd909e92e4 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 5 Jan 2017 12:22:49 +0800 Subject: [PATCH 054/167] lwip: rework according review comments --- components/lwip/api/api_lib.c | 6 ++++-- components/lwip/api/api_msg.c | 6 ++++-- components/lwip/api/sockets.c | 1 + components/lwip/api/tcpip.c | 2 +- components/lwip/netif/ethernet.c | 6 ++++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/components/lwip/api/api_lib.c b/components/lwip/api/api_lib.c index 86ee5576aba..087115f0b9d 100755 --- a/components/lwip/api/api_lib.c +++ b/components/lwip/api/api_lib.c @@ -508,8 +508,10 @@ netconn_recv_data(struct netconn *conn, void **new_buf) } #endif /* (LWIP_UDP || LWIP_RAW) */ -#ifdef ESP_PERF - if (len > DBG_PERF_FILTER_LEN) { DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_SOC_IN); } +#if ESP_PERF + if (len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_SOC_IN); + } #endif #if LWIP_SO_RCVBUF diff --git a/components/lwip/api/api_msg.c b/components/lwip/api/api_msg.c index 87e5aeb8e5c..878cd289b06 100755 --- a/components/lwip/api/api_msg.c +++ b/components/lwip/api/api_msg.c @@ -202,8 +202,10 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, #endif /* LWIP_NETBUF_RECVINFO */ } -#ifdef ESP_PERF - if (p->len > DBG_PERF_FILTER_LEN) DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_OUT); +#if ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_OUT); + } #endif len = p->tot_len; diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index c163b3ac0b5..7335246eb93 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -1437,6 +1437,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, #else /* LWIP_NETIF_TX_SINGLE_PBUF */ err = netbuf_ref(&buf, data, short_size); #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { DBG_PERF_PATH_SET(DBG_PERF_DIR_TX, DBG_PERF_POINT_SOC_OUT); /* send the data */ diff --git a/components/lwip/api/tcpip.c b/components/lwip/api/tcpip.c index dfb235718b3..f3422af706b 100755 --- a/components/lwip/api/tcpip.c +++ b/components/lwip/api/tcpip.c @@ -219,7 +219,7 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn) msg->msg.inp.p = p; msg->msg.inp.netif = inp; msg->msg.inp.input_fn = input_fn; -#ifdef ESP_PERF +#if ESP_PERF if (p->len > DBG_PERF_FILTER_LEN) { DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_WIFI_OUT); } diff --git a/components/lwip/netif/ethernet.c b/components/lwip/netif/ethernet.c index 5d618f6a313..90f62583bc7 100755 --- a/components/lwip/netif/ethernet.c +++ b/components/lwip/netif/ethernet.c @@ -72,8 +72,10 @@ ethernet_input(struct pbuf *p, struct netif *netif) s16_t ip_hdr_offset = SIZEOF_ETH_HDR; #endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ -#ifdef ESP_PERF - if (p->len > DBG_PERF_FILTER_LEN) DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_IN); +#if ESP_PERF + if (p->len > DBG_PERF_FILTER_LEN) { + DBG_PERF_PATH_SET(DBG_PERF_DIR_RX, DBG_PERF_POINT_LWIP_IN); + } #endif if (p->len <= SIZEOF_ETH_HDR) { From 790a3d9ab365844dc4940a3c8ff5f6c500d63238 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 5 Jan 2017 14:56:16 +0800 Subject: [PATCH 055/167] component/bt : update bluetooth api doxygen ref --- docs/api/esp_bt_defs.rst | 1 - docs/api/esp_gap_ble.rst | 1 + docs/api/esp_gatt_defs.rst | 1 + docs/api/esp_gattc.rst | 1 + docs/api/esp_gatts.rst | 1 + 5 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/api/esp_bt_defs.rst b/docs/api/esp_bt_defs.rst index 9a2131f74ca..801b13df789 100644 --- a/docs/api/esp_bt_defs.rst +++ b/docs/api/esp_bt_defs.rst @@ -40,7 +40,6 @@ Type Definitions ^^^^^^^^^^^^^^^^ .. doxygentypedef:: esp_bd_addr_t -.. doxygentypedef:: esp_profile_cb_t Enumerations ^^^^^^^^^^^^ diff --git a/docs/api/esp_gap_ble.rst b/docs/api/esp_gap_ble.rst index f1837d4025a..f16fa0da142 100644 --- a/docs/api/esp_gap_ble.rst +++ b/docs/api/esp_gap_ble.rst @@ -38,6 +38,7 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ +.. doxygentypedef:: esp_gap_ble_cb_t Enumerations ^^^^^^^^^^^^ diff --git a/docs/api/esp_gatt_defs.rst b/docs/api/esp_gatt_defs.rst index b5a416d436f..637acaab990 100644 --- a/docs/api/esp_gatt_defs.rst +++ b/docs/api/esp_gatt_defs.rst @@ -85,6 +85,7 @@ Macros .. doxygendefine:: ESP_GATT_UUID_SCAN_REFRESH .. doxygendefine:: ESP_GATT_ILLEGAL_UUID .. doxygendefine:: ESP_GATT_MAX_ATTR_LEN +.. doxygendefine:: ESP_GATT_IF_NONE Type Definitions ^^^^^^^^^^^^^^^^ diff --git a/docs/api/esp_gattc.rst b/docs/api/esp_gattc.rst index efd2623ad9c..7ff1e9de7e3 100644 --- a/docs/api/esp_gattc.rst +++ b/docs/api/esp_gattc.rst @@ -37,6 +37,7 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ +.. doxygentypedef:: esp_gattc_cb_t Enumerations ^^^^^^^^^^^^ diff --git a/docs/api/esp_gatts.rst b/docs/api/esp_gatts.rst index fdb0e055a95..fbaa1c236db 100644 --- a/docs/api/esp_gatts.rst +++ b/docs/api/esp_gatts.rst @@ -37,6 +37,7 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ +.. doxygentypedef:: esp_gatts_cb_t Enumerations ^^^^^^^^^^^^ From 899f61f4a2496bb261e388a28b7d3d6e232b2832 Mon Sep 17 00:00:00 2001 From: Liu Han Date: Sat, 10 Dec 2016 13:48:38 +0800 Subject: [PATCH 056/167] examples: Add CoAP server demo Test CoAP protocol server --- examples/24_coap_server/Makefile | 9 + .../24_coap_server/main/Kconfig.projbuild | 22 ++ examples/24_coap_server/main/coap_server.c | 190 ++++++++++++++++++ examples/24_coap_server/main/coap_server.h | 38 ++++ examples/24_coap_server/main/component.mk | 5 + 5 files changed, 264 insertions(+) create mode 100644 examples/24_coap_server/Makefile create mode 100644 examples/24_coap_server/main/Kconfig.projbuild create mode 100644 examples/24_coap_server/main/coap_server.c create mode 100644 examples/24_coap_server/main/coap_server.h create mode 100644 examples/24_coap_server/main/component.mk diff --git a/examples/24_coap_server/Makefile b/examples/24_coap_server/Makefile new file mode 100644 index 00000000000..f82e5fc2782 --- /dev/null +++ b/examples/24_coap_server/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := coap_server + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/24_coap_server/main/Kconfig.projbuild b/examples/24_coap_server/main/Kconfig.projbuild new file mode 100644 index 00000000000..4926bfb2002 --- /dev/null +++ b/examples/24_coap_server/main/Kconfig.projbuild @@ -0,0 +1,22 @@ +menu "Example Configuration" + +config LOCAL_PORT_NUMBER + int "Local port number" + range 0 65535 + default 5683 + help + Local port number for the example to use. + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/24_coap_server/main/coap_server.c b/examples/24_coap_server/main/coap_server.c new file mode 100644 index 00000000000..4e066689bb7 --- /dev/null +++ b/examples/24_coap_server/main/coap_server.c @@ -0,0 +1,190 @@ +/* CoAP server Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include "coap_server.h" + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" +#include + +#include "coap_config.h" +#include "resource.h" +#include "coap.h" + + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "CoAP_demo"; + +static coap_async_state_t *async = NULL; + +static void +send_async_response(coap_context_t *ctx, const coap_endpoint_t *local_if) +{ + coap_pdu_t *response; + unsigned char buf[3]; + const char* response_data = "Hello World!"; + size_t size = sizeof(coap_hdr_t) + 20; + response = coap_pdu_init(async->flags & COAP_MESSAGE_CON, COAP_RESPONSE_CODE(205), 0, size); + response->hdr->id = coap_new_message_id(ctx); + if (async->tokenlen) + coap_add_token(response, async->tokenlen, async->token); + coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf); + coap_add_data (response, strlen(response_data), (unsigned char *)response_data); + + if (coap_send(ctx, local_if, &async->peer, response) == COAP_INVALID_TID) { + + } + coap_delete_pdu(response); + coap_async_state_t *tmp; + coap_remove_async(ctx, async->id, &tmp); + coap_free_async(async); + async = NULL; +} + +/* + * The resource handler + */ +static void +async_handler(coap_context_t *ctx, struct coap_resource_t *resource, + const coap_endpoint_t *local_interface, coap_address_t *peer, + coap_pdu_t *request, str *token, coap_pdu_t *response) +{ + async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM, (void*)"no data"); +} + +static void coap_demo_thread(void *p) +{ + coap_context_t* ctx = NULL; + coap_address_t serv_addr; + coap_resource_t* resource = NULL; + fd_set readfds; + struct timeval tv; + int flags = 0; + /* Prepare the CoAP server socket */ + coap_address_init(&serv_addr); + serv_addr.addr.sin.sin_family = AF_INET; + serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + ctx = coap_new_context(&serv_addr); + if (ctx) { + flags = fcntl(ctx->sockfd, F_GETFL, 0); + fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); + + tv.tv_usec = COAP_DEFAULT_TIME_USEC; + tv.tv_sec = COAP_DEFAULT_TIME_SEC; + /* Initialize the resource */ + resource = coap_resource_init((unsigned char *)"Espressif", 9, 0); + if (resource){ + coap_register_handler(resource, COAP_REQUEST_GET, async_handler); + coap_add_resource(ctx, resource); + /*For incoming connections*/ + for (;;) { + FD_ZERO(&readfds); + FD_CLR( ctx->sockfd, &readfds); + FD_SET( ctx->sockfd, &readfds); + + int result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); + if (result > 0){ + if (FD_ISSET( ctx->sockfd, &readfds )) + coap_read(ctx); + } else if (result < 0){ + break; + } else { + printf("select timeout\n"); + } + + if (async) + send_async_response(ctx, ctx->endpoint); + } + } + + coap_free_context(ctx); + } + + vTaskDelete(NULL); +} + +static void coap_server_init(void) +{ + int ret = pdPASS; + xTaskHandle coap_handle = NULL; + + ret = xTaskCreate(coap_demo_thread, + COAP_DEMO_THREAD_NAME, + COAP_DEMO_THREAD_STACK_WORDS, + NULL, + COAP_DEMO_THREAD_PRORIOTY, + &coap_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", COAP_DEMO_THREAD_NAME); + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + coap_server_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/24_coap_server/main/coap_server.h b/examples/24_coap_server/main/coap_server.h new file mode 100644 index 00000000000..e9e8728fc37 --- /dev/null +++ b/examples/24_coap_server/main/coap_server.h @@ -0,0 +1,38 @@ +/* CoAP server Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef _COAP_SERVER_H_ +#define _COAP_SERVER_H_ + +#include + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define COAP_DEMO_THREAD_NAME "CoAP_demo" +#define COAP_DEMO_THREAD_STACK_WORDS 10240 +#define COAP_DEMO_THREAD_PRORIOTY 8 + +/* The examples use local port number of 5683 that you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define OPENSSL_DEMO_TARGET_TCP_PORT 5683 +*/ +#define COAP_DEFAULT_PORT CONFIG_LOCAL_PORT_NUMBER +#define COAP_DEFAULT_TIME_SEC 5 +#define COAP_DEFAULT_TIME_USEC 0 + +#endif + diff --git a/examples/24_coap_server/main/component.mk b/examples/24_coap_server/main/component.mk new file mode 100644 index 00000000000..0b9d7585e76 --- /dev/null +++ b/examples/24_coap_server/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + From 56514d5ab20c5d506f382a7a9c7966bbcd1ba65a Mon Sep 17 00:00:00 2001 From: Liu Han Date: Sat, 10 Dec 2016 14:34:50 +0800 Subject: [PATCH 057/167] examples: Add CoAP client demo Test CoAP protocol client --- examples/23_coap_client/Makefile | 9 + .../23_coap_client/main/Kconfig.projbuild | 34 ++++ examples/23_coap_client/main/coap_client.c | 176 ++++++++++++++++++ examples/23_coap_client/main/coap_client.h | 43 +++++ examples/23_coap_client/main/component.mk | 5 + 5 files changed, 267 insertions(+) create mode 100644 examples/23_coap_client/Makefile create mode 100644 examples/23_coap_client/main/Kconfig.projbuild create mode 100644 examples/23_coap_client/main/coap_client.c create mode 100644 examples/23_coap_client/main/coap_client.h create mode 100644 examples/23_coap_client/main/component.mk diff --git a/examples/23_coap_client/Makefile b/examples/23_coap_client/Makefile new file mode 100644 index 00000000000..c9a25d117f3 --- /dev/null +++ b/examples/23_coap_client/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := coap_client + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/23_coap_client/main/Kconfig.projbuild b/examples/23_coap_client/main/Kconfig.projbuild new file mode 100644 index 00000000000..ba3e0d458ba --- /dev/null +++ b/examples/23_coap_client/main/Kconfig.projbuild @@ -0,0 +1,34 @@ +menu "Example Configuration" + +config TARGET_DOMAIN + string "Target Domain" + default "californium.eclipse.org" + help + Target domain for the example to connect to. + +config TARGET_DOMAIN_URI + string "Target Uri" + default "coap://californium.eclipse.org" + help + Target uri for the example to use. + +config TARGET_PORT_NUMBER + int "Target port number" + range 0 65535 + default 5683 + help + Target port number for the example to connect to. + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/23_coap_client/main/coap_client.c b/examples/23_coap_client/main/coap_client.c new file mode 100644 index 00000000000..cef24f4c6e2 --- /dev/null +++ b/examples/23_coap_client/main/coap_client.c @@ -0,0 +1,176 @@ +/* CoAP client Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include "coap_client.h" + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" +#include + +#include "coap_config.h" +#include "resource.h" +#include "coap.h" + + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "CoAP_demo"; + +static void message_handler(struct coap_context_t *ctx, const coap_endpoint_t *local_interface, const coap_address_t *remote, + coap_pdu_t *sent, coap_pdu_t *received, + const coap_tid_t id) +{ + unsigned char* data = NULL; + size_t data_len; + if (COAP_RESPONSE_CLASS(received->hdr->code) == 2) { + if (coap_get_data(received, &data_len, &data)) { + printf("Received: %s\n", data); + } + } +} + +static void coap_demo_thread(void *p) +{ + coap_context_t* ctx = NULL; + coap_address_t dst_addr, src_addr; + static coap_uri_t uri; + fd_set readfds; + struct timeval tv; + int flags, result; + coap_pdu_t* request = NULL; + const char* server_uri = COAP_DEFAULT_DEMO_URI; + uint8_t get_method = 1; + + coap_address_init(&src_addr); + src_addr.addr.sin.sin_family = AF_INET; + src_addr.addr.sin.sin_port = htons(0); + src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + + ctx = coap_new_context(&src_addr); + if (ctx) { + coap_address_init(&dst_addr); + dst_addr.addr.sin.sin_family = AF_INET; + dst_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + dst_addr.addr.sin.sin_addr.s_addr = inet_addr(COAP_DEFAULT_DEMO_ADDR); + + coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri); + request = coap_new_pdu(); + if (request){ + request->hdr->type = COAP_MESSAGE_CON; + request->hdr->id = coap_new_message_id(ctx); + request->hdr->code = get_method; + coap_add_option(request, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s); + + coap_register_response_handler(ctx, message_handler); + coap_send_confirmed(ctx, ctx->endpoint, &dst_addr, request); + + flags = fcntl(ctx->sockfd, F_GETFL, 0); + fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); + + tv.tv_usec = COAP_DEFAULT_TIME_USEC; + tv.tv_sec = COAP_DEFAULT_TIME_SEC; + + for(;;) { + FD_ZERO(&readfds); + FD_CLR( ctx->sockfd, &readfds ); + FD_SET( ctx->sockfd, &readfds ); + result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); + if (result > 0) { + if (FD_ISSET( ctx->sockfd, &readfds )) + coap_read(ctx); + } else if (result < 0) { + break; + } else { + printf("select timeout\n"); + } + } + } + coap_free_context(ctx); + } + + vTaskDelete(NULL); +} + +static void coap_server_init(void) +{ + int ret = pdPASS; + xTaskHandle coap_handle = NULL; + + ret = xTaskCreate(coap_demo_thread, + COAP_DEMO_THREAD_NAME, + COAP_DEMO_THREAD_STACK_WORDS, + NULL, + COAP_DEMO_THREAD_PRORIOTY, + &coap_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", COAP_DEMO_THREAD_NAME); + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + coap_server_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/23_coap_client/main/coap_client.h b/examples/23_coap_client/main/coap_client.h new file mode 100644 index 00000000000..50f4ccd16fe --- /dev/null +++ b/examples/23_coap_client/main/coap_client.h @@ -0,0 +1,43 @@ +/* CoAP client Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef _COAP_CLIENT_H_ +#define _COAP_CLIENT_H_ + +#include + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define COAP_DEMO_THREAD_NAME "CoAP_demo" +#define COAP_DEMO_THREAD_STACK_WORDS 10240 +#define COAP_DEMO_THREAD_PRORIOTY 8 + +#define COAP_DEFAULT_TIME_SEC 5 +#define COAP_DEFAULT_TIME_USEC 0 + +/* The examples use domain of "californium.eclipse.org",uri "coap://californium.eclipse.org" and port number of 5683 that + you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define COAP_DEFAULT_DEMO_ADDR "californium.eclipse.org" + , ie #define COAP_DEFAULT_DEMO_URI "coap://californium.eclipse.org" and ie #define COAP_DEFAULT_PORT 5683 +*/ +#define COAP_DEFAULT_PORT CONFIG_TARGET_PORT_NUMBER +#define COAP_DEFAULT_DEMO_ADDR CONFIG_TARGET_DOMAIN +#define COAP_DEFAULT_DEMO_URI CONFIG_TARGET_DOMAIN_URI + +#endif + diff --git a/examples/23_coap_client/main/component.mk b/examples/23_coap_client/main/component.mk new file mode 100644 index 00000000000..0b9d7585e76 --- /dev/null +++ b/examples/23_coap_client/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + From 8c7dfef3173f03e7fd498ad3584cc76854e99010 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 21 Dec 2016 12:04:26 +0800 Subject: [PATCH 058/167] examples/10_openssl_server: fixup SSL server with method of specific version 1. add method of any version supporting at OpenSSL and add API in header file 2. change OpenSSL server context method to be method of any version Fixes http://esp32.com/viewtopic.php?f=14&t=696. --- components/openssl/include/openssl/ssl.h | 18 +++++++++++++++++ components/openssl/platform/ssl_pm.c | 3 +++ examples/10_openssl_server/README.md | 2 +- .../10_openssl_server/main/Kconfig.projbuild | 2 +- .../10_openssl_server/main/openssl_server.c | 20 +++++++++++++------ .../10_openssl_server/main/openssl_server.h | 6 ++++-- 6 files changed, 41 insertions(+), 10 deletions(-) mode change 100644 => 100755 components/openssl/include/openssl/ssl.h mode change 100644 => 100755 components/openssl/platform/ssl_pm.c mode change 100644 => 100755 examples/10_openssl_server/README.md mode change 100644 => 100755 examples/10_openssl_server/main/Kconfig.projbuild mode change 100644 => 100755 examples/10_openssl_server/main/openssl_server.c mode change 100644 => 100755 examples/10_openssl_server/main/openssl_server.h diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h old mode 100644 new mode 100755 index 7f8eb88302f..39d4bf737c2 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -214,6 +214,14 @@ const SSL_METHOD* TLSv1_1_client_method(void); */ const SSL_METHOD* TLSv1_2_client_method(void); +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLS any version SSL context client method + */ +const SSL_METHOD* TLS_client_method(void); /** * @brief create the target SSL context server method @@ -260,6 +268,16 @@ const SSL_METHOD* TLSv1_server_method(void); */ const SSL_METHOD* SSLv3_server_method(void); +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLS any version SSL context server method + */ +const SSL_METHOD* TLS_server_method(void); + + /** * @brief set the SSL context ALPN select callback function * diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c old mode 100644 new mode 100755 index 522721ad7c8..15015107f0b --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -125,6 +125,9 @@ int ssl_pm_new(SSL *ssl) mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + } else { + mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); } mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md old mode 100644 new mode 100755 index 333cb3d6a62..984a83c308e --- a/examples/10_openssl_server/README.md +++ b/examples/10_openssl_server/README.md @@ -5,7 +5,7 @@ The Example contains of OpenSSL server demo. First you should configure the project by "make menuconfig": Example Configuration -> 1. WIFI SSID: WIFI network to which your PC is also connected to. - 1. WIFI Password: WIFI password + 2. WIFI Password: WIFI password IF you want to test the OpenSSL server demo: 1. compile the code and load the firmware diff --git a/examples/10_openssl_server/main/Kconfig.projbuild b/examples/10_openssl_server/main/Kconfig.projbuild old mode 100644 new mode 100755 index 7a9cb97a0e0..176d8fb3343 --- a/examples/10_openssl_server/main/Kconfig.projbuild +++ b/examples/10_openssl_server/main/Kconfig.projbuild @@ -12,4 +12,4 @@ config WIFI_PASSWORD help WiFi password (WPA or WPA2) for the example to use. -endmenu \ No newline at end of file +endmenu diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c old mode 100644 new mode 100755 index 756c1407f55..1eea2110cee --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -43,7 +43,8 @@ const static char *TAG = "Openssl_demo"; "OpenSSL demo\r\n" \ "OpenSSL server demo!\r\n" \ "\r\n" \ - "\r\n" + "\r\n" \ + "\r\n" static void openssl_demo_thread(void *p) { @@ -70,7 +71,7 @@ static void openssl_demo_thread(void *p) const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; ESP_LOGI(TAG, "SSL server context create ......"); - ctx = SSL_CTX_new(SSLv3_server_method()); + ctx = SSL_CTX_new(TLS_server_method()); if (!ctx) { ESP_LOGI(TAG, "failed"); goto failed1; @@ -155,14 +156,21 @@ reconnect: if (ret <= 0) { break; } - if (strstr(recv_buf, "GET / HTTP/1.1")) { - SSL_write(ssl, send_data, send_bytes); + ESP_LOGI(TAG, "SSL read: %s", recv_buf); + if (strstr(recv_buf, "GET ") && + strstr(recv_buf, " HTTP/1.1")) { + ESP_LOGI(TAG, "SSL get matched message") + ESP_LOGI(TAG, "SSL write message") + ret = SSL_write(ssl, send_data, send_bytes); + if (ret > 0) { + ESP_LOGI(TAG, "OK") + } else { + ESP_LOGI(TAG, "error") + } break; } } while (1); - ESP_LOGI(TAG, "result %d", ret); - SSL_shutdown(ssl); failed5: close(new_socket); diff --git a/examples/10_openssl_server/main/openssl_server.h b/examples/10_openssl_server/main/openssl_server.h old mode 100644 new mode 100755 index 5f49de35f29..51708535f52 --- a/examples/10_openssl_server/main/openssl_server.h +++ b/examples/10_openssl_server/main/openssl_server.h @@ -7,8 +7,10 @@ CONDITIONS OF ANY KIND, either express or implied. */ -#ifndef _OPENSSL_DEMO_H_ -#define _OPENSSL_DEMO_H_ +#ifndef _OPENSSL_SERVER_H_ +#define _OPENSSL_SERVER_H_ + +#include "sdkconfig.h" /* The examples use simple WiFi configuration that you can set via 'make menuconfig'. From daf58e385291b9abfdd7244ae2870ee994ced6fc Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Fri, 23 Dec 2016 15:57:44 +0800 Subject: [PATCH 059/167] component/bt : fix advertising bug 1. adv data flag 2. default adv data not BR/EDR in demo --- components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c | 1 + examples/14_gatt_server/main/gatts_demo.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index 39b392e62f7..4040ee42e08 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -109,6 +109,7 @@ static void btc_to_bta_adv_data(esp_ble_adv_data_t *p_adv_data, tBTA_BLE_ADV_DAT if (p_adv_data->flag != 0) { mask = BTM_BLE_AD_BIT_FLAGS; + bta_adv_data->flag = p_adv_data->flag; } if (p_adv_data->include_name) { diff --git a/examples/14_gatt_server/main/gatts_demo.c b/examples/14_gatt_server/main/gatts_demo.c index 1785835d7c8..58deadc5c9f 100644 --- a/examples/14_gatt_server/main/gatts_demo.c +++ b/examples/14_gatt_server/main/gatts_demo.c @@ -68,7 +68,7 @@ static esp_ble_adv_data_t test_adv_data = { .p_service_data = NULL, .service_uuid_len = 32, .p_service_uuid = test_service_uuid128, - .flag = 0x2, + .flag = 0x6, }; static esp_ble_adv_params_t test_adv_params = { From d6fcec73b2d623cab3dd6582485e11c2f445d1f4 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 5 Jan 2017 15:24:09 +0800 Subject: [PATCH 060/167] component/bt : add macro for adv_data_flag 1. add macro for adv data flag 2. add docs for doxygen --- .../bt/bluedroid/api/include/esp_gap_ble_api.h | 15 ++++++++++++++- docs/api/esp_gap_ble.rst | 6 ++++++ examples/14_gatt_server/main/gatts_demo.c | 2 +- 3 files changed, 21 insertions(+), 2 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 f500f840314..f92143e6af8 100644 --- a/components/bt/bluedroid/api/include/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/esp_gap_ble_api.h @@ -25,6 +25,19 @@ extern "C" { #endif +/**@{ + * BLE_ADV_DATA_FLAG data flag bit definition used for advertising data flag + */ +#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) +#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) +/** + * @} + */ + /// GAP BLE callback event type typedef enum { ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT = 0, /*!< When advertising data set complete, the event comes */ @@ -126,7 +139,7 @@ typedef struct { uint8_t *p_service_data; /*!< Service data point */ uint16_t service_uuid_len; /*!< Service uuid length */ uint8_t *p_service_uuid; /*!< Service uuid array point */ - uint8_t flag; /*!< Advertising flag of discovery mode */ + uint8_t flag; /*!< Advertising flag of discovery mode, see BLE_ADV_DATA_FLAG detail */ } esp_ble_adv_data_t; /// Own BD address source of the device diff --git a/docs/api/esp_gap_ble.rst b/docs/api/esp_gap_ble.rst index f16fa0da142..856ed273025 100644 --- a/docs/api/esp_gap_ble.rst +++ b/docs/api/esp_gap_ble.rst @@ -33,6 +33,12 @@ Header Files Macros ^^^^^^ +.. doxygendefine:: ESP_BLE_ADV_FLAG_LIMIT_DISC +.. doxygendefine:: ESP_BLE_ADV_FLAG_GEN_DISC +.. doxygendefine:: ESP_BLE_ADV_FLAG_BREDR_NOT_SPT +.. doxygendefine:: ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT +.. doxygendefine:: ESP_BLE_ADV_FLAG_DMT_HOST_SPT +.. doxygendefine:: ESP_BLE_ADV_FLAG_NON_LIMIT_DISC .. doxygendefine:: ESP_BLE_ADV_DATA_LEN_MAX Type Definitions diff --git a/examples/14_gatt_server/main/gatts_demo.c b/examples/14_gatt_server/main/gatts_demo.c index 58deadc5c9f..cf9a5789b4e 100644 --- a/examples/14_gatt_server/main/gatts_demo.c +++ b/examples/14_gatt_server/main/gatts_demo.c @@ -68,7 +68,7 @@ static esp_ble_adv_data_t test_adv_data = { .p_service_data = NULL, .service_uuid_len = 32, .p_service_uuid = test_service_uuid128, - .flag = 0x6, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), }; static esp_ble_adv_params_t test_adv_params = { From 6b5e7349012bb2efaf45c9ea9671a8c05c5c648c Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Sun, 25 Dec 2016 23:11:24 +0800 Subject: [PATCH 061/167] driver: ledc - update fading functions, add example and doc 1. add fading functions. 2. clear up ledc.c and ledc.h 3. update api doc. 4. add ledc example 5. edit ledc.rst and add readme for example code. 6. add s_ prefix for static global values. 7. add mutex for fade functions 8. minor changes according to the comments. --- components/driver/include/driver/ledc.h | 267 ++++++++---------- components/driver/ledc.c | 355 ++++++++++++++++++++---- docs/api/ledc.rst | 32 ++- examples/29_ledc/Makefile | 9 + examples/29_ledc/README.md | 16 ++ examples/29_ledc/main/component.mk | 3 + examples/29_ledc/main/ledc_fade.c | 131 +++++++++ 7 files changed, 596 insertions(+), 217 deletions(-) create mode 100644 examples/29_ledc/Makefile create mode 100644 examples/29_ledc/README.md create mode 100644 examples/29_ledc/main/component.mk create mode 100644 examples/29_ledc/main/ledc_fade.c diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 691379a3d82..af7c6b80785 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -16,9 +16,6 @@ #define _DRIVER_LEDC_H_ #include "esp_err.h" #include "soc/soc.h" -#include "soc/ledc_reg.h" -#include "soc/ledc_reg.h" -#include "soc/ledc_struct.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" @@ -68,6 +65,7 @@ typedef enum { LEDC_CHANNEL_5, /*!< LEDC channel 5 */ LEDC_CHANNEL_6, /*!< LEDC channel 6 */ LEDC_CHANNEL_7, /*!< LEDC channel 7 */ + LEDC_CHANNEL_MAX, } ledc_channel_t; typedef enum { @@ -79,6 +77,11 @@ typedef enum { LEDC_TIMER_15_BIT = 15, /*!< LEDC PWM depth 15Bit */ } ledc_timer_bit_t; +typedef enum { + LEDC_FADE_NO_WAIT = 0, /*!< LEDC fade function will return immediately */ + LEDC_FADE_WAIT_DONE, /*!< LEDC fade function will block until fading to the target duty*/ + LEDC_FADE_MAX, +} ledc_fade_mode_t; /** * @brief Configuration parameters of LEDC channel for ledc_channel_config function */ @@ -104,43 +107,39 @@ typedef struct { typedef intr_handle_t ledc_isr_handle_t; /** - * @brief LEDC channel configuration + * @brief LEDC channel configuration + * Configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC depth * - * User this Function, configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC depth + * @param ledc_conf Pointer of LEDC channel configure struct * - * @param ledc_conf Pointer of LEDC channel configure struct * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error - * */ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf); /** - * @brief LEDC timer configuration - * - * User this Function, configure LEDC timer with the given source timer/frequency(Hz)/bit_num + * @brief LEDC timer configuration + * Configure LEDC timer with the given source timer/frequency(Hz)/bit_num * * @param timer_conf Pointer of LEDC timer configure struct * - * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error * - ESP_FAIL Can not find a proper pre-divider number base on the given frequency and the current bit_num. - * */ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); /** - * @brief LEDC update channel parameters + * @brief LEDC update channel parameters + * Call this function to activate the LEDC updated parameters. + * After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings. * - * Call this function to activate the LEDC updated parameters. - * After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings. - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param channel LEDC channel(0-7), select from ledc_channel_t + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * now we only support high-speed mode. + * We will access low-speed mode in next version + * @param channel LEDC channel(0-7), select from ledc_channel_t * * @return * - ESP_OK Success @@ -150,14 +149,11 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); /** - * @brief LEDC stop - * - * Disable LEDC output, and set idle level + * @brief LEDC stop. + * Disable LEDC output, and set idle level * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param idle_level Set output idle level after LEDC stops. * * @return @@ -167,14 +163,10 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level); /** - * @brief LEDC set channel frequency(Hz) - * - * Set LEDC frequency(Hz) + * @brief LEDC set channel frequency(Hz) * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_num LEDC timer index(0-3), select from ledc_timer_t - * * @param freq_hz Set the LEDC frequency * * @return @@ -188,25 +180,20 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t * @brief LEDC get channel frequency(Hz) * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_num LEDC timer index(0-3), select from ledc_timer_t * * @return * - 0 error * - Others Current LEDC frequency - * */ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); /** - * @brief LEDC set duty - * - * Set LEDC duty, After the function calls the ledc_update_duty function, the function can take effect. + * @brief LEDC set duty + * Only after calling ledc_update_duty will the duty update. * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param duty Set the LEDC duty, the duty range is [0, (2**bit_num) - 1] * * @return @@ -216,37 +203,27 @@ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty); /** - * @brief LEDC get duty + * @brief LEDC get duty * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t * - * * @return * - (-1) parameter error * - Others Current LEDC duty - * */ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel); /** - * @brief LEDC set gradient - * - * Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect. + * @brief LEDC set gradient + * Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect. * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param duty Set the start of the gradient duty, the duty range is [0, (2**bit_num) - 1] - * * @param gradule_direction Set the direction of the gradient - * * @param step_num Set the number of the gradient - * * @param duty_cyle_num Set how many LEDC tick each time the gradient lasts - * * @param duty_scale Set gradient change amplitude * * @return @@ -257,16 +234,16 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale); /** - * @brief register LEDC interrupt handler, the handler is an ISR. - * The handler will be attached to the same CPU core that this function is running on. + * @brief Register LEDC interrupt handler, the handler is an ISR. + * The handler will be attached to the same CPU core that this function is running on. * - * @param fn Interrupt handler function. - * @param arg User-supplied argument passed to the handler function. - * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) - * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. - * @param arg Parameter for handler function - * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will - * be returned here. + * @param fn Interrupt handler function. + * @param arg User-supplied argument passed to the handler function. + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param arg Parameter for handler function + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success @@ -275,48 +252,38 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, ledc_isr_handle_t *handle); /** - * @brief configure LEDC settings + * @brief Configure LEDC settings * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param timer_sel Timer index(0-3), there are 4 timers in LEDC module - * - * @param div_num Timer clock divide number, the timer clock is divided from the selected clock source - * - * @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) - * - * @param clk_src Select LEDC source clock. + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param timer_sel Timer index(0-3), there are 4 timers in LEDC module + * @param div_num Timer clock divide number, the timer clock is divided from the selected clock source + * @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) + * @param clk_src Select LEDC source clock. * * @return * - (-1) Parameter error * - Other Current LEDC duty - * */ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src); /** - * @brief reset LEDC timer + * @brief Reset LEDC timer * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * - * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success - * */ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief pause LEDC timer counter + * @brief Pause LEDC timer counter * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * - * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success @@ -325,104 +292,96 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief pause LEDC timer resume - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t + * @brief Resume LEDC timer * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success - * */ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief bind LEDC channel with the selected timer - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param channel LEDC channel index(0-7), select from ledc_channel_t - * - * @param timer_idx LEDC timer index(0-3), select from ledc_timer_t + * @brief Bind LEDC channel with the selected timer * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param timer_idx LEDC timer index(0-3), select from ledc_timer_t * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success + */ +esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); + +/** + * @brief Set LEDC fade function. Should call ledc_fade_func_install() before calling this function. + * Call ledc_fade_start() after this to start fading. + * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * For now we only support high-speed mode. We will access low-speed mode soon. + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1))) + * @param scale Controls the increase or decrease step scale. + * @param cycle_num increase or decrease the duty every cycle_num cycles + * + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + */ +esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num); + +/** + * @brief Set LEDC fade function, with a limited time. Should call ledc_fade_func_install() before calling this function. + * Call ledc_fade_start() after this to start fading. + * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * For now we only support high-speed mode. We will access low-speed mode soon. + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1))) + * @param max_fade_time_ms The maximum time of the fading ( ms ). + * + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + */ +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms); + +/** + * @brief Install ledc fade function. This function will occupy interrupt of LEDC module. + * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * + * @return + * - ESP_ERR_NO_MEM No enough memory + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function already installed. + */ +esp_err_t ledc_fade_func_install(int intr_alloc_flags); + +/** + * @brief Uninstall LEDC fade function. * */ -esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx); +void ledc_fade_func_uninstall(); -/***************************EXAMPLE********************************** +/** + * @brief Start LEDC fading. * + * @param channel LEDC channel number + * @param wait_done Whether to block until fading done. * - * ----------------EXAMPLE OF LEDC SETTING --------------------- - * @code{c} - * //1. enable LEDC - * //enable LEDC module, or you can not set any register of it. - * periph_module_enable(PERIPH_LEDC_MODULE); - * @endcode - * - * @code{c} - * //2. set LEDC timer - * ledc_timer_config_t timer_conf = { - * .bit_num = LEDC_TIMER_12_BIT, //set timer counter bit number - * .freq_hz = 1000, //set frequency of pwm, here, 1000Hz - * .speed_mode = LEDC_HIGH_SPEED_MODE, //timer mode, - * .timer_num = LEDC_TIMER_0, //timer number - * }; - * ledc_timer_config(&timer_conf); //setup timer. - * @endcode - * - * @code{c} - * //3. set LEDC channel - * ledc_channel_config_t ledc_conf = { - * .channel = LEDC_CHANNEL_0; //set LEDC channel 0 - * .duty = 1000; //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) - * .gpio_num = 16; //GPIO number - * .intr_type = LEDC_INTR_FADE_END; //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. - * .speed_mode = LEDC_HIGH_SPEED_MODE; //set LEDC mode, from ledc_mode_t - * .timer_sel = LEDC_TIMER_0; //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same - * } - * ledc_channel_config(&ledc_conf); //setup the configuration - * - * ----------------EXAMPLE OF SETTING DUTY --- ----------------- - * @code{c} - * ledc_channel_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) - * uint32_t duty = 2000; //duty range is 0 ~ ((2**bit_num)-1) - * LEDC_set_duty(LEDC_HIGH_SPEED_MODE, ledc_channel, duty); //set speed mode, channel, and duty. - * ledc_update_duty(LEDC_HIGH_SPEED_MODE, ledc_channel); //after set duty, we need to call ledc_update_duty to update the settings. - * @endcode - * - * ----------------EXAMPLE OF LEDC INTERRUPT ------------------ - * @code{c} - * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. - * ledc_isr_register(ledc_isr_handler, NULL, 0); //hook the isr handler for LEDC interrupt - * @endcode - * - * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- - * @code{c} - * #include "esp_attr.h" - * void IRAM_ATTR ledc_isr_handler(void* arg) //we should add 'IRAM_ATTR' attribution when we declare the isr function - * { - * uint32_t intr_st = LEDC.int_st.val; //read LEDC interrupt status. - * - * //you will find which channels have triggered fade_end interrupt here, - * //then, you can post some event to RTOS queue to process the event. - * //later we will add a queue in the driver code. - * - * LEDC.int_clr.val = intr_st; //clear LEDC interrupt status. - * } - * @endcode - * - *--------------------------END OF EXAMPLE -------------------------- + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + * - ESP_ERR_INVALID_ARG Parameter error. */ - - - +esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done); #ifdef __cplusplus } diff --git a/components/driver/ledc.c b/components/driver/ledc.c index c00cf26bb13..6944f1b1155 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -19,6 +19,8 @@ #include "freertos/xtensa_api.h" #include "soc/gpio_sig_map.h" #include "driver/ledc.h" +#include "soc/ledc_reg.h" +#include "soc/ledc_struct.h" #include "esp_log.h" static const char* LEDC_TAG = "ledc"; @@ -29,26 +31,56 @@ static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; return (ret_val); \ } +typedef struct { + uint16_t speed_mode; + uint16_t direction; + int target_duty; + int cycle_num; + int scale; + ledc_fade_mode_t mode; + xSemaphoreHandle ledc_fade_sem; + xSemaphoreHandle ledc_fade_mux; +} ledc_fade_t; +static ledc_fade_t* s_ledc_fade_rec = NULL; +static ledc_isr_handle_t s_ledc_fade_isr_handle = NULL; + +#define LEDC_VAL_NO_CHANGE (-1) +#define LEDC_STEP_NUM_MAX (1023) +#define LEDC_DUTY_DECIMAL_BIT_NUM (4) + +#define LEDC_MODE_ERR_STR "LEDC mode error" +#define LEDC_TIMER_ERR_STR "LEDC timer error" +#define LEDC_CHANNEL_ERR_STR "LEDC channel error" +#define LEDC_GPIO_OUT_ERR_STR "LEDC GPIO output number error" +#define LEDC_FADE_DIR_ERR_STR "LEDC fade direction error" +#define LEDC_FADE_SERVICE_ERR_STR "LEDC fade service not installed" +#define LEDC_FADE_TARGET_ERR_STR "LEDC fade target duty error" +#define LEDC_FADE_INSTALLED_ERR_STR "LEDC fade service already installed" +#define LEDC_FADE_MODE_ERR_STR "LEDC fade mode error" + + esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num; LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src; LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num = bit_num; - if(speed_mode != LEDC_HIGH_SPEED_MODE) { + if (speed_mode != LEDC_HIGH_SPEED_MODE) { LEDC.timer_group[speed_mode].timer[timer_sel].conf.low_speed_update = 1; } portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } -static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t hpoint_val, uint32_t duty_val, +static IRAM_ATTR esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, int hpoint_val, uint32_t duty_val, uint32_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) { portENTER_CRITICAL(&ledc_spinlock); - LEDC.channel_group[speed_mode].channel[channel_num].hpoint.hpoint = hpoint_val; + if (hpoint_val >= 0) { + LEDC.channel_group[speed_mode].channel[channel_num].hpoint.hpoint = hpoint_val; + } LEDC.channel_group[speed_mode].channel[channel_num].duty.duty = duty_val; LEDC.channel_group[speed_mode].channel[channel_num].conf1.val = ((duty_direction & LEDC_DUTY_INC_HSCH0_V) << LEDC_DUTY_INC_HSCH0_S) | ((duty_num & LEDC_DUTY_NUM_HSCH0_V) << LEDC_DUTY_NUM_HSCH0_S) | @@ -60,8 +92,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_idx <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx; portEXIT_CRITICAL(&ledc_spinlock); @@ -70,8 +102,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1; LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0; @@ -81,8 +113,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1; portEXIT_CRITICAL(&ledc_spinlock); @@ -91,8 +123,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0; portEXIT_CRITICAL(&ledc_spinlock); @@ -101,15 +133,15 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); uint32_t value; uint32_t intr_type = type; portENTER_CRITICAL(&ledc_spinlock); value = LEDC.int_ena.val; - if(intr_type == LEDC_INTR_FADE_END) { + if (intr_type == LEDC_INTR_FADE_END) { LEDC.int_ena.val = value | BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel); } else { - LEDC.int_ena.val = (value & (~(BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel)))); + LEDC.int_ena.val = ( value & ( ~( BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel) ) ) ); } portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; @@ -120,7 +152,7 @@ esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, esp_err_t ret; LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); - ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + ret = esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); portEXIT_CRITICAL(&ledc_spinlock); return ret; } @@ -131,32 +163,35 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) int bit_num = timer_conf->bit_num; int timer_num = timer_conf->timer_num; int speed_mode = timer_conf->speed_mode; - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + periph_module_enable(PERIPH_LEDC_MODULE); + if (freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u", freq_hz, bit_num); return ESP_ERR_INVALID_ARG; } - if(timer_num > LEDC_TIMER_3) { + if (timer_num > LEDC_TIMER_3) { ESP_LOGE(LEDC_TAG, "invalid timer #%u", timer_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; - uint32_t precision = (0x1 << bit_num); // 2**depth + uint32_t precision = ( 0x1 << bit_num ); // 2**depth // Try calculating divisor based on LEDC_APB_CLK ledc_clk_src_t timer_clk_src = LEDC_APB_CLK; // div_param is a Q10.8 fixed point value - uint64_t div_param = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision; + uint64_t div_param = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision; if (div_param < 256) { // divisor is too low - ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try reducing freq_hz or bit_num. div_param=%d", (uint32_t) div_param); + ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try reducing freq_hz or bit_num. div_param=%d", + (uint32_t ) div_param); ret = ESP_FAIL; } if (div_param > LEDC_DIV_NUM_HSTIMER0_V) { // APB_CLK results in divisor which too high. Try using REF_TICK as clock source. timer_clk_src = LEDC_REF_TICK; - div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; - if(div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { - ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try increasing freq_hz or bit_num. div_param=%d", (uint32_t) div_param); + div_param = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision; + if (div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { + ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try increasing freq_hz or bit_num. div_param=%d", + (uint32_t ) div_param); ret = ESP_FAIL; } } @@ -169,12 +204,12 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) { - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), LEDC_GPIO_OUT_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO); gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); - if(speed_mode == LEDC_HIGH_SPEED_MODE) { + if (speed_mode == LEDC_HIGH_SPEED_MODE) { gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0); } else { ESP_LOGE(LEDC_TAG, "low speed mode is not implemented"); @@ -191,10 +226,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) uint32_t timer_select = ledc_conf->timer_sel; uint32_t intr_type = ledc_conf->intr_type; uint32_t duty = ledc_conf->duty; - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), LEDC_GPIO_OUT_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_select <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); periph_module_enable(PERIPH_LEDC_MODULE); esp_err_t ret = ESP_OK; /*set channel parameters*/ @@ -207,7 +242,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select); /*set interrupt type*/ ledc_enable_intr_type(speed_mode, ledc_channel, intr_type); - ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", + ESP_LOGD(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", ledc_channel, gpio_num, duty, timer_select ); /*set LEDC signal in gpio matrix*/ @@ -219,8 +254,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; @@ -230,8 +265,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1; LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0; @@ -239,16 +274,20 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } + esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error", ESP_ERR_INVALID_ARG); - if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, LEDC_FADE_DIR_ERR_STR, ESP_ERR_INVALID_ARG); + if (step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale); return ESP_ERR_INVALID_ARG; } + if (s_ledc_fade_rec) { + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + } ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -263,8 +302,11 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + if (s_ledc_fade_rec) { + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + } ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -279,26 +321,26 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (-1)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, (-1)); uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4); return duty; } esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); esp_err_t ret = ESP_OK; uint32_t div_num = 0; uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; - uint32_t precision = (0x1 << bit_num); - if(timer_source_clk == LEDC_APB_CLK) { - div_num = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision; + uint32_t precision = ( 0x1 << bit_num ); + if (timer_source_clk == LEDC_APB_CLK) { + div_num = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision; } else { - div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; + div_num = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision; } - if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { + if (div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num); ret = ESP_FAIL; } @@ -309,18 +351,217 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (0)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ( 0 )); portENTER_CRITICAL(&ledc_spinlock); uint32_t freq = 0; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num; uint32_t div_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num; - uint32_t precision = (0x1 << bit_num); - if(timer_source_clk == LEDC_APB_CLK) { - freq = ((uint64_t) LEDC_APB_CLK_HZ << 8) / precision / div_num; + uint32_t precision = ( 0x1 << bit_num ); + if (timer_source_clk == LEDC_APB_CLK) { + freq = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / precision / div_num; } else { - freq = ((uint64_t) LEDC_REF_CLK_HZ << 8) / precision / div_num; + freq = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / precision / div_num; } portEXIT_CRITICAL(&ledc_spinlock); return freq; } + +void IRAM_ATTR ledc_fade_isr() +{ + int i; + portBASE_TYPE HPTaskAwoken = pdFALSE; + uint32_t intr_status = LEDC.int_st.val; //read LEDC interrupt status. + LEDC.int_clr.val = intr_status; //clear LEDC interrupt status. + for (i = 0; i < 8; i++) { + if (intr_status & BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ST_S + i)) { + int speed_mode = s_ledc_fade_rec[i].speed_mode; + int duty_cur = LEDC.channel_group[speed_mode].channel[i].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + if (duty_cur == s_ledc_fade_rec[i].target_duty) { + if(s_ledc_fade_rec[i].mode == LEDC_FADE_WAIT_DONE) { + xSemaphoreGiveFromISR(s_ledc_fade_rec[i].ledc_fade_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } + } + continue; + } + int duty_tar = s_ledc_fade_rec[i].target_duty; + int scale = s_ledc_fade_rec[i].scale; + if (scale == 0) { + continue; + } + int cycle = s_ledc_fade_rec[i].cycle_num; + int delta = s_ledc_fade_rec[i].direction == LEDC_DUTY_DIR_DECREASE ? duty_cur - duty_tar : duty_tar - duty_cur; + int step = delta / scale > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : delta / scale; + + if (delta > scale) { + ledc_duty_config( + speed_mode, + i, + LEDC_VAL_NO_CHANGE, + duty_cur << LEDC_DUTY_DECIMAL_BIT_NUM, + s_ledc_fade_rec[i].direction, + step, + cycle, + scale); + } else { + ledc_duty_config( + speed_mode, + i, + LEDC_VAL_NO_CHANGE, + duty_tar << LEDC_DUTY_DECIMAL_BIT_NUM, + s_ledc_fade_rec[i].direction, + 1, + 1, + 0); + } + LEDC.channel_group[speed_mode].channel[i].conf1.duty_start = 1; + } + } + LEDC.int_clr.val = intr_status; //clear LEDC interrupt status. +} + +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms) +{ + int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel; + int max_duty = ( 1 << ( LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num ) ) - 1; + LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG); + uint32_t freq = ledc_get_freq(speed_mode, timer_sel); + int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + int duty_delta = target_duty > duty_cur ? target_duty - duty_cur : duty_cur - target_duty; + + if (duty_delta == 0) { + return ESP_OK; + } + int total_cycles = max_fade_time_ms * freq / 1000; + int scale, cycle_num; + if (total_cycles > duty_delta) { + scale = 1; + cycle_num = total_cycles / duty_delta; + } else { + cycle_num = 1; + scale = ( duty_delta + total_cycles - 1 ) / total_cycles; + } + return ledc_set_fade_with_step(speed_mode, channel, target_duty, scale, cycle_num); +} + +esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num) +{ + LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel < LEDC_CHANNEL_MAX, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel; + int max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num)) - 1; + LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG); + //disable the interrupt, so the operation will not mess up + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + + portENTER_CRITICAL(&ledc_spinlock); + int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + int duty_delta = target_duty > duty_cur ? target_duty - duty_cur : duty_cur - target_duty; + if (duty_delta == 0) { + return ESP_OK; + } + s_ledc_fade_rec[channel].speed_mode = speed_mode; + s_ledc_fade_rec[channel].target_duty = target_duty; + s_ledc_fade_rec[channel].cycle_num = cycle_num; + s_ledc_fade_rec[channel].scale = scale; + int step_num; + if (duty_cur > target_duty) { + s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_DECREASE; + step_num = ( duty_cur - target_duty ) / scale; + step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num; + } else { + s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_INCREASE; + step_num = ( target_duty - duty_cur ) / scale; + step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num; + } + portEXIT_CRITICAL(&ledc_spinlock); + + ledc_set_fade( + speed_mode, + channel, + duty_cur, + s_ledc_fade_rec[channel].direction, + step_num, + s_ledc_fade_rec[channel].cycle_num, + s_ledc_fade_rec[channel].scale + ); + ESP_LOGD(LEDC_TAG, "cur duty: %d; target: %d, step: %d, cycle: %d; scale: %d\n", + LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM, + target_duty, + step_num, + s_ledc_fade_rec[channel].cycle_num, + s_ledc_fade_rec[channel].scale + ); + LEDC.int_clr.val |= BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel); + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_FADE_END); + return ESP_OK; +} + +esp_err_t ledc_fade_func_install(int intr_alloc_flags) +{ + LEDC_CHECK(s_ledc_fade_rec == NULL, LEDC_FADE_INSTALLED_ERR_STR, ESP_ERR_INVALID_STATE); + s_ledc_fade_rec = (ledc_fade_t*) calloc(LEDC_CHANNEL_MAX, sizeof(ledc_fade_t)); + if (s_ledc_fade_rec == NULL) { + return ESP_ERR_NO_MEM; + } + int i = 0; + for (i = 0; i < LEDC_CHANNEL_MAX; i++) { + s_ledc_fade_rec[i].ledc_fade_sem = xSemaphoreCreateBinary(); + s_ledc_fade_rec[i].ledc_fade_mux = xSemaphoreCreateMutex(); + if (s_ledc_fade_rec[i].ledc_fade_sem == NULL || s_ledc_fade_rec[i].ledc_fade_mux == NULL) { + ledc_fade_func_uninstall(); + return ESP_ERR_NO_MEM; + } + } + //OR intr_alloc_flags with ESP_INTR_FLAG_IRAM because the fade isr is in IRAM + ledc_isr_register(ledc_fade_isr, NULL, intr_alloc_flags | ESP_INTR_FLAG_IRAM, &s_ledc_fade_isr_handle); + return ESP_OK; +} + +void ledc_fade_func_uninstall() +{ + if (s_ledc_fade_rec == NULL) { + return; + } + if(s_ledc_fade_isr_handle) { + esp_intr_free(s_ledc_fade_isr_handle); + s_ledc_fade_isr_handle = NULL; + } + int i; + for (i = 0; i < LEDC_CHANNEL_MAX; i++) { + if (s_ledc_fade_rec[i].ledc_fade_sem) { + xSemaphoreHandle sem_tmp = s_ledc_fade_rec[i].ledc_fade_sem; + s_ledc_fade_rec[i].ledc_fade_sem = NULL; + vSemaphoreDelete(sem_tmp); + } + if (s_ledc_fade_rec[i].ledc_fade_mux) { + xSemaphoreHandle mux_tmp = s_ledc_fade_rec[i].ledc_fade_mux; + s_ledc_fade_rec[i].ledc_fade_mux = NULL; + vSemaphoreDelete(mux_tmp); + } + } + free(s_ledc_fade_rec); + s_ledc_fade_rec = NULL; + return; +} + +esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done) +{ + LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE); + LEDC_CHECK(wait_done < LEDC_FADE_MAX, LEDC_FADE_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + int speed_mode = s_ledc_fade_rec[channel].speed_mode; + xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_mux, portMAX_DELAY); + if (wait_done == LEDC_FADE_WAIT_DONE) { + s_ledc_fade_rec[channel].mode = LEDC_FADE_WAIT_DONE; + ledc_update_duty(speed_mode, channel); + xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_sem, portMAX_DELAY); + } else { + s_ledc_fade_rec[channel].mode = LEDC_FADE_NO_WAIT; + ledc_update_duty(speed_mode, channel); + } + xSemaphoreGive(s_ledc_fade_rec[channel].ledc_fade_mux); + return ESP_OK; +} diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst index 855f8221633..40855ddb470 100644 --- a/docs/api/ledc.rst +++ b/docs/api/ledc.rst @@ -4,20 +4,19 @@ LED Control Overview -------- -`Instructions`_ +The LED control module is primarily designed to control the intensity of LEDs, although it can be used to generate PWM signals for other purposes as well. +It has 16 channels which can generate independent waveforms that can be used to drive e.g. RGB LED devices. For maximum flexibility, the high-speed as well +as the low-speed channels can be driven from one of four high-speed/low-speed timers. The PWM controller also has the ability to automatically increase or +decrease the duty cycle gradually, allowing for fades without any processor interference. Application Example ------------------- -`Instructions`_ +LEDC change duty cycle and fading control example: `examples/29_ledc `_. API Reference ------------- -`Instructions`_ - -.. _Instructions: template.html - Header Files ^^^^^^^^^^^^ @@ -38,6 +37,11 @@ Macros .. doxygendefine:: LEDC_APB_CLK_HZ .. doxygendefine:: LEDC_REF_CLK_HZ +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: ledc_isr_handle_t + Enumerations ^^^^^^^^^^^^ @@ -49,6 +53,16 @@ Enumerations .. doxygenenum:: ledc_channel_t .. doxygenenum:: ledc_timer_bit_t +Structures +^^^^^^^^^^ + +.. doxygenstruct:: ledc_channel_config_t + :members: + +.. doxygenstruct:: ledc_timer_config_t + :members: + + Functions ^^^^^^^^^ @@ -67,3 +81,9 @@ Functions .. doxygenfunction:: ledc_timer_pause .. doxygenfunction:: ledc_timer_resume .. doxygenfunction:: ledc_bind_channel_timer +.. doxygenfunction:: ledc_set_fade_with_step +.. doxygenfunction:: ledc_set_fade_with_time +.. doxygenfunction:: ledc_fade_func_install +.. doxygenfunction:: ledc_fade_func_uninstall +.. doxygenfunction:: ledc_fade_start + diff --git a/examples/29_ledc/Makefile b/examples/29_ledc/Makefile new file mode 100644 index 00000000000..675fc8a38b6 --- /dev/null +++ b/examples/29_ledc/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ledc + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/29_ledc/README.md b/examples/29_ledc/README.md new file mode 100644 index 00000000000..9feea6b25b4 --- /dev/null +++ b/examples/29_ledc/README.md @@ -0,0 +1,16 @@ +# LEDC(LED control) Example + +###This example shows: + + * init LEDC module: + + a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM. + + b. You need to set the LEDC channel you want to use, and bind the channel with one of the timers. + + * You can install a default fade function, then you can use fade APIs. + + * You can also set a target duty directly without fading. + + * This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly. + diff --git a/examples/29_ledc/main/component.mk b/examples/29_ledc/main/component.mk new file mode 100644 index 00000000000..44bd2b5273f --- /dev/null +++ b/examples/29_ledc/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/29_ledc/main/ledc_fade.c b/examples/29_ledc/main/ledc_fade.c new file mode 100644 index 00000000000..dcdb0462f60 --- /dev/null +++ b/examples/29_ledc/main/ledc_fade.c @@ -0,0 +1,131 @@ +/* Ledc fade example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/xtensa_api.h" +#include "freertos/queue.h" +#include "driver/ledc.h" +#include "esp_attr.h" +#include "esp_err.h" + +/* + * About this example + * 1. init LEDC module: + * a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM. + * b. You need to set the LEDC channel you want to use, and bind with one of the timers. + * + * 2. You can install a default fade function, then you can use fade APIs. + * + * 3. You can also set a target duty directly without fading. + * + * 4. This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly. + * + * + */ + +#define LEDC_IO_0 (18) +#define LEDC_IO_1 (19) +#define LEDC_IO_2 (4) +#define LEDC_IO_3 (5) + +esp_err_t app_main() +{ + ledc_timer_config_t ledc_timer = { + //set timer counter bit number + .bit_num = LEDC_TIMER_13_BIT, + //set frequency of pwm + .freq_hz = 5000, + //timer mode, + .speed_mode = LEDC_HIGH_SPEED_MODE, + //timer index + .timer_num = LEDC_TIMER_0 + }; + ledc_timer_config(&ledc_timer); + + ledc_channel_config_t ledc_channel = { + //set LEDC channel 0 + .channel = LEDC_CHANNEL_0, + //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) + .duty = 100, + //GPIO number + .gpio_num = LEDC_IO_0, + //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. + .intr_type = LEDC_INTR_FADE_END, + //set LEDC mode, from ledc_mode_t + .speed_mode = LEDC_HIGH_SPEED_MODE, + //set LEDC timer source, if different channel use one timer, + //the frequency and bit_num of these channels should be the same + .timer_sel = LEDC_TIMER_0 + }; + //set the configuration + ledc_channel_config(&ledc_channel); + + //config ledc channel1 + ledc_channel.channel = LEDC_CHANNEL_1; + ledc_channel.gpio_num = LEDC_IO_1; + ledc_channel_config(&ledc_channel); + //config ledc channel2 + ledc_channel.channel = LEDC_CHANNEL_2; + ledc_channel.gpio_num = LEDC_IO_2; + ledc_channel_config(&ledc_channel); + //config ledc channel3 + ledc_channel.channel = LEDC_CHANNEL_3; + ledc_channel.gpio_num = LEDC_IO_3; + ledc_channel_config(&ledc_channel); + + //initialize fade service. + ledc_fade_func_install(0); + + while(1) { + printf("LEDC fade up\n"); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000, 2000); + ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT); + vTaskDelay(3000 / portTICK_PERIOD_MS); + + printf("LEDC fade down\n"); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 100, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 300, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 500, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 700, 2000); + ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT); + vTaskDelay(3000 / portTICK_PERIOD_MS); + + printf("LEDC set duty without fade\n"); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3); + vTaskDelay(2000 / portTICK_PERIOD_MS); + + printf("LEDC set duty without fade\n"); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3); + vTaskDelay(2000 / portTICK_PERIOD_MS); + } +} From 5677149833b824e89054cd2ac3d42cf7a24c86c6 Mon Sep 17 00:00:00 2001 From: qiyueixa Date: Fri, 30 Dec 2016 21:22:06 +0800 Subject: [PATCH 062/167] esp32: add wifi low rate feature 1. add low_rate_enable flag to scan results to identify if AP low rate is enabled. 2. add WIFI_PROTOCOL_LR for users to enable low rate feature. --- components/esp32/include/esp_wifi_types.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index be3183ba5d1..88ad3dcf5b6 100755 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -109,6 +109,8 @@ typedef struct { wifi_second_chan_t second; /**< second channel of AP */ int8_t rssi; /**< signal strength of AP */ wifi_auth_mode_t authmode; /**< authmode of AP */ + uint32_t low_rate_enable:1; /**< bit: 0 flag to identify if low rate is enabled or not */ + uint32_t reserved:31; /**< bit: 1..31 reserved */ } wifi_ap_record_t; typedef enum { @@ -119,6 +121,7 @@ typedef enum { #define WIFI_PROTOCOL_11B 1 #define WIFI_PROTOCOL_11G 2 #define WIFI_PROTOCOL_11N 4 +#define WIFI_PROTOCOL_LR 8 typedef enum { WIFI_BW_HT20 = 1, /* Bandwidth is HT20 */ From 7778273314ef83f12df7c37dc490bccc40c4e8c4 Mon Sep 17 00:00:00 2001 From: qiyueixa Date: Thu, 5 Jan 2017 18:43:09 +0800 Subject: [PATCH 063/167] esp32: update wifi libs 1. add wifi low rate feature --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 074303d74fc..edad9748406 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 074303d74fc9c68823adee0a38fc1e8de42943b6 +Subproject commit edad9748406d06bfd2dfba6cf1a0735c3982460f From 0feb3633fc3953babb314ead1ea8c83b55fad7fa Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Thu, 5 Jan 2017 13:17:52 +0200 Subject: [PATCH 064/167] add menuconfig option to enable SO_RCVBUF --- components/lwip/Kconfig | 6 ++++++ components/lwip/include/lwip/port/lwipopts.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 9e2b0316747..760e035c8ce 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -40,6 +40,12 @@ config LWIP_SO_REUSE Enabling this option allows binding to a port which remains in TIME_WAIT. +config LWIP_SO_RCVBUF + bool "Enable SO_RCVBUF option" + default 0 + help + Enabling this option allows checking for available data on a netconn. + config LWIP_DHCP_MAX_NTP_SERVERS int "Maximum number of NTP servers" default 1 diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 933d55a5103..14ec0e67a4f 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -440,7 +440,7 @@ /** * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. */ -#define LWIP_SO_RCVBUF 0 +#define LWIP_SO_RCVBUF CONFIG_LWIP_SO_RCVBUF /** * SO_REUSE==1: Enable SO_REUSEADDR option. From 2e7748d625ee02c3e183a7e6d37371d3a213a846 Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Tue, 3 Jan 2017 15:53:06 +0800 Subject: [PATCH 065/167] component/bt: modify bluetooth API 1. VHCI api and doxygen 2. Controller api and doxygen 3. bluedroid init/enable api and doxygen 4. cleanup demo codes --- components/bt/bluedroid/api/esp_bt_main.c | 8 +- .../bt/bluedroid/api/include/esp_bt_main.h | 12 +-- components/bt/bluedroid/hci/hci_hal_h4.c | 8 +- components/bt/bluedroid/hci/hci_layer.c | 2 +- components/bt/bt.c | 26 ++++- components/bt/include/bt.h | 22 ++-- components/esp32/include/esp_phy_init.h | 2 +- docs/api/bt.rst | 2 +- docs/api/{vhci.rst => controller_vhci.rst} | 26 +++-- docs/api/esp_bt_main.rst | 8 +- examples/05_ble_adv/main/app_bt.c | 16 +-- examples/12_blufi/components/blufi/blufi.c | 4 +- examples/12_blufi/main/demo_main.c | 8 +- examples/14_gatt_server/main/gatts_demo.c | 66 +++++------- examples/15_gatt_client/main/gattc_demo.c | 100 +++++++++--------- 15 files changed, 166 insertions(+), 144 deletions(-) rename docs/api/{vhci.rst => controller_vhci.rst} (66%) diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index f185999287a..2dd9166d229 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -21,7 +21,7 @@ static bool esp_already_enable = false; static bool esp_already_init = false; -esp_err_t esp_enable_bluetooth(void) +esp_err_t esp_bluedroid_enable(void) { btc_msg_t msg; future_t **future_p; @@ -53,7 +53,7 @@ esp_err_t esp_enable_bluetooth(void) return ESP_OK; } -esp_err_t esp_disable_bluetooth(void) +esp_err_t esp_bluedroid_disable(void) { btc_msg_t msg; future_t **future_p; @@ -85,7 +85,7 @@ esp_err_t esp_disable_bluetooth(void) return ESP_OK; } -esp_err_t esp_init_bluetooth(void) +esp_err_t esp_bluedroid_init(void) { btc_msg_t msg; future_t **future_p; @@ -120,7 +120,7 @@ esp_err_t esp_init_bluetooth(void) } -esp_err_t esp_deinit_bluetooth(void) +esp_err_t esp_bluedroid_deinit(void) { btc_msg_t msg; future_t **future_p; diff --git a/components/bt/bluedroid/api/include/esp_bt_main.h b/components/bt/bluedroid/api/include/esp_bt_main.h index 859da092dd2..b24daf33cd4 100644 --- a/components/bt/bluedroid/api/include/esp_bt_main.h +++ b/components/bt/bluedroid/api/include/esp_bt_main.h @@ -22,22 +22,22 @@ extern "C" { #endif /** - * @brief Enable bluetooth, must after esp_init_bluetooth() + * @brief Enable bluetooth, must after esp_bluedroid_init() * * @return * - ESP_OK : Succeed * - Other : Failed */ -esp_err_t esp_enable_bluetooth(void); +esp_err_t esp_bluedroid_enable(void); /** - * @brief Disable bluetooth, must prior to esp_deinit_bluetooth() + * @brief Disable bluetooth, must prior to esp_bluedroid_deinit() * * @return * - ESP_OK : Succeed * - Other : Failed */ -esp_err_t esp_disable_bluetooth(void); +esp_err_t esp_bluedroid_disable(void); /** * @brief Init and alloc the resource for bluetooth, must be prior to every bluetooth stuff @@ -46,7 +46,7 @@ esp_err_t esp_disable_bluetooth(void); * - ESP_OK : Succeed * - Other : Failed */ -esp_err_t esp_init_bluetooth(void); +esp_err_t esp_bluedroid_init(void); /** * @brief Deinit and free the resource for bluetooth, must be after every bluetooth stuff @@ -55,7 +55,7 @@ esp_err_t esp_init_bluetooth(void); * - ESP_OK : Succeed * - Other : Failed */ -esp_err_t esp_deinit_bluetooth(void); +esp_err_t esp_bluedroid_deinit(void); #ifdef __cplusplus } diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 237226266ae..eb78cab5d31 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -57,7 +57,7 @@ typedef struct { static hci_hal_env_t hci_hal_env; static const hci_hal_t interface; static const hci_hal_callbacks_t *callbacks; -static const vhci_host_callback_t vhci_host_cb; +static const esp_vhci_host_callback_t vhci_host_cb; static xTaskHandle xHciH4TaskHandle; static xQueueHandle xHciH4Queue; @@ -105,7 +105,7 @@ static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) xTaskCreate(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle); //register vhci host cb - API_vhci_host_register_callback(&vhci_host_cb); + esp_vhci_host_register_callback(&vhci_host_cb); return true; @@ -148,7 +148,7 @@ static uint16_t transmit_data(serial_data_type_t type, BTTRC_DUMP_BUFFER("Transmit Pkt", data, length); // TX Data to target - API_vhci_host_send_packet(data, length); + esp_vhci_host_send_packet(data, length); // Be nice and restore the old value of that byte *(data) = previous_byte; @@ -278,7 +278,7 @@ static int host_recv_pkt_cb(uint8_t *data, uint16_t len) return 0; } -static const vhci_host_callback_t vhci_host_cb = { +static const esp_vhci_host_callback_t vhci_host_cb = { .notify_host_send_available = host_send_pkt_available_cb, .notify_host_recv = host_recv_pkt_cb, }; diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index d71690d4a63..747a5829531 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -228,7 +228,7 @@ static void hci_host_thread_handler(void *arg) if (pdTRUE == xQueueReceive(xHciHostQueue, &e, (portTickType)portMAX_DELAY)) { if (e.sig == 0xff) { - if (API_vhci_host_check_send_available()) { + if (esp_vhci_host_check_send_available()) { /*Now Target only allowed one packet per TX*/ BT_HDR *pkt = packet_fragmenter->fragment_current_packet(); if (pkt != NULL) { diff --git a/components/bt/bt.c b/components/bt/bt.c index 8d1fecb777a..5bad45ea2c2 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -35,6 +35,15 @@ extern void btdm_osi_funcs_register(void *osi_funcs); extern void btdm_controller_init(void); +/* VHCI function interface */ +typedef struct vhci_host_callback { + void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ + int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ +} vhci_host_callback_t; + +extern bool API_vhci_host_check_send_available(void); +extern void API_vhci_host_send_packet(uint8_t *data, uint16_t len); +extern void API_vhci_host_register_callback(const vhci_host_callback_t *callback); #define BT_DEBUG(...) #define BT_API_CALL_CHECK(info, api_call, ret) \ @@ -118,13 +127,28 @@ static struct osi_funcs_t osi_funcs = { ._read_efuse_mac = esp_efuse_read_mac, }; +bool esp_vhci_host_check_send_available(void) +{ + return API_vhci_host_check_send_available(); +} + +void esp_vhci_host_send_packet(uint8_t *data, uint16_t len) +{ + API_vhci_host_send_packet(data, len); +} + +void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback) +{ + API_vhci_host_register_callback((const vhci_host_callback_t *)callback); +} + static void bt_controller_task(void *pvParam) { btdm_osi_funcs_register(&osi_funcs); btdm_controller_init(); } -void bt_controller_init() +void esp_bt_controller_init() { xTaskCreatePinnedToCore(bt_controller_task, "btController", ESP_TASK_BT_CONTROLLER_STACK, NULL, diff --git a/components/bt/include/bt.h b/components/bt/include/bt.h index 310fdcb9213..926ecfadcdd 100644 --- a/components/bt/include/bt.h +++ b/components/bt/include/bt.h @@ -29,35 +29,35 @@ extern "C" { * * This function should be called only once, before any other BT functions are called. */ -void bt_controller_init(void); +void esp_bt_controller_init(void); -/** @brief vhci_host_callback +/** @brief esp_vhci_host_callback * used for vhci call host function to notify what host need to do */ -typedef struct vhci_host_callback { +typedef struct esp_vhci_host_callback { void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ -} vhci_host_callback_t; +} esp_vhci_host_callback_t; -/** @brief API_vhci_host_check_send_available +/** @brief esp_vhci_host_check_send_available * used for check actively if the host can send packet to controller or not. * @return true for ready to send, false means cannot send packet */ -bool API_vhci_host_check_send_available(void); +bool esp_vhci_host_check_send_available(void); -/** @brief API_vhci_host_send_packet +/** @brief esp_vhci_host_send_packet * host send packet to controller * @param data the packet point *,@param len the packet length */ -void API_vhci_host_send_packet(uint8_t *data, uint16_t len); +void esp_vhci_host_send_packet(uint8_t *data, uint16_t len); -/** @brief API_vhci_host_register_callback +/** @brief esp_vhci_host_register_callback * register the vhci referece callback, the call back * struct defined by vhci_host_callback structure. - * @param callback vhci_host_callback type variable + * @param callback esp_vhci_host_callback type variable */ -void API_vhci_host_register_callback(const vhci_host_callback_t *callback); +void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback); #ifdef __cplusplus } diff --git a/components/esp32/include/esp_phy_init.h b/components/esp32/include/esp_phy_init.h index 7bc0536103d..e669a44151d 100644 --- a/components/esp32/include/esp_phy_init.h +++ b/components/esp32/include/esp_phy_init.h @@ -229,7 +229,7 @@ esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_da * * Applications which don't need to enable PHY on every start up should * disable this menuconfig option and call esp_phy_init before calling - * esp_wifi_init or bt_controller_init. See do_phy_init function in + * esp_wifi_init or esp_bt_controller_init. See do_phy_init function in * cpu_start.c for an example of using this function. * * @param init_data PHY parameters. Default set of parameters can diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 924e855c2f1..6a14d40684a 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -4,7 +4,7 @@ Bluetooth .. toctree:: :caption: Bluetooth APIs - Bluetooth VHCI + Bluetooth Controller && VHCI Bluetooth Common Bluetooth Classic Bluetooth LE diff --git a/docs/api/vhci.rst b/docs/api/controller_vhci.rst similarity index 66% rename from docs/api/vhci.rst rename to docs/api/controller_vhci.rst index 0d5d8b5fffe..a9c1a79289e 100644 --- a/docs/api/vhci.rst +++ b/docs/api/controller_vhci.rst @@ -1,5 +1,5 @@ -VHCI -==== +Controller && VHCI +================== Overview -------- @@ -30,12 +30,24 @@ Header Files Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygenstruct:: vhci_host_callback +.. doxygentypedef:: esp_vhci_host_callback_t + +Enumerations +^^^^^^^^^^^^ + + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: esp_vhci_host_callback + :members: + Functions ^^^^^^^^^ -.. doxygenfunction:: API_vhci_host_check_send_available -.. doxygenfunction:: API_vhci_host_register_callback -.. doxygenfunction:: API_vhci_host_send_packet -.. doxygenfunction:: bt_controller_init +.. doxygenfunction:: esp_bt_controller_init +.. doxygenfunction:: esp_vhci_host_check_send_available +.. doxygenfunction:: esp_vhci_host_send_packet +.. doxygenfunction:: esp_vhci_host_register_callback + diff --git a/docs/api/esp_bt_main.rst b/docs/api/esp_bt_main.rst index dc317c584f3..cc6e80b2e51 100644 --- a/docs/api/esp_bt_main.rst +++ b/docs/api/esp_bt_main.rst @@ -42,8 +42,8 @@ Structures Functions ^^^^^^^^^ -.. doxygenfunction:: esp_enable_bluetooth -.. doxygenfunction:: esp_disable_bluetooth -.. doxygenfunction:: esp_init_bluetooth -.. doxygenfunction:: esp_deinit_bluetooth +.. doxygenfunction:: esp_bluedroid_enable +.. doxygenfunction:: esp_bluedroid_disable +.. doxygenfunction:: esp_bluedroid_init +.. doxygenfunction:: esp_bluedroid_deinit diff --git a/examples/05_ble_adv/main/app_bt.c b/examples/05_ble_adv/main/app_bt.c index 5bdbfd5c736..f0780c950c9 100644 --- a/examples/05_ble_adv/main/app_bt.c +++ b/examples/05_ble_adv/main/app_bt.c @@ -73,7 +73,7 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len) return 0; } -static vhci_host_callback_t vhci_host_cb = { +static esp_vhci_host_callback_t vhci_host_cb = { controller_rcv_pkt_ready, host_rcv_pkt }; @@ -139,13 +139,13 @@ static uint16_t make_cmd_ble_set_adv_data(uint8_t *buf, uint8_t data_len, uint8_ static void hci_cmd_send_reset(void) { uint16_t sz = make_cmd_reset (hci_cmd_buf); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } static void hci_cmd_send_ble_adv_start(void) { uint16_t sz = make_cmd_ble_set_adv_enable (hci_cmd_buf, 1); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } static void hci_cmd_send_ble_set_adv_param(void) @@ -168,7 +168,7 @@ static void hci_cmd_send_ble_set_adv_param(void) peer_addr, adv_chn_map, adv_filter_policy); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } static void hci_cmd_send_ble_set_adv_data(void) @@ -185,7 +185,7 @@ static void hci_cmd_send_ble_set_adv_data(void) adv_data_len = 5 + name_len; uint16_t sz = make_cmd_ble_set_adv_data(hci_cmd_buf, adv_data_len, (uint8_t *)adv_data); - API_vhci_host_send_packet(hci_cmd_buf, sz); + esp_vhci_host_send_packet(hci_cmd_buf, sz); } /* @@ -195,11 +195,11 @@ void bleAdvtTask(void *pvParameters) { int cmd_cnt = 0; bool send_avail = false; - API_vhci_host_register_callback(&vhci_host_cb); + esp_vhci_host_register_callback(&vhci_host_cb); printf("BLE advt task start\n"); while (1) { vTaskDelay(1000 / portTICK_PERIOD_MS); - send_avail = API_vhci_host_check_send_available(); + send_avail = esp_vhci_host_check_send_available(); if (send_avail) { switch (cmd_cnt) { case 0: hci_cmd_send_reset(); ++cmd_cnt; break; @@ -214,7 +214,7 @@ void bleAdvtTask(void *pvParameters) void app_main() { - bt_controller_init(); + esp_bt_controller_init(); xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0); } diff --git a/examples/12_blufi/components/blufi/blufi.c b/examples/12_blufi/components/blufi/blufi.c index 2523ebf0658..8e2b5d1f394 100644 --- a/examples/12_blufi/components/blufi/blufi.c +++ b/examples/12_blufi/components/blufi/blufi.c @@ -107,7 +107,7 @@ esp_err_t blufi_enable(void *arg) { esp_err_t err; - err = esp_enable_bluetooth(); + err = esp_bluedroid_enable(); if (err) { LOG_ERROR("%s failed\n", __func__); return err; @@ -122,7 +122,7 @@ esp_err_t blufi_disable(void *arg) { esp_err_t err; - err = esp_disable_bluetooth(); + err = esp_bluedroid_disable(); if (arg) { ((void (*)(void))arg)(); diff --git a/examples/12_blufi/main/demo_main.c b/examples/12_blufi/main/demo_main.c index 90992313c22..abb47aedb03 100644 --- a/examples/12_blufi/main/demo_main.c +++ b/examples/12_blufi/main/demo_main.c @@ -68,8 +68,8 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); esp_blufi_send_config_state(ESP_BLUFI_CONFIG_OK); - esp_disable_bluetooth(); //close bluetooth function - //esp_deinit_bluetooth(); //free bluetooth resource + esp_bluedroid_disable(); //close bluetooth function + //esp_bluedroid_deinit(); //free bluetooth resource break; case SYSTEM_EVENT_STA_DISCONNECTED: /* This is a workaround as ESP32 WiFi libs don't currently @@ -131,11 +131,11 @@ void app_main() //vTaskDelay(3000 / portTICK_PERIOD_MS); - bt_controller_init(); + esp_bt_controller_init(); xTaskCreatePinnedToCore(&wifiTestTask, "wifiTestTask", 2048, NULL, 20, NULL, 0); LOG_ERROR("%s init bluetooth\n", __func__); - ret = esp_init_bluetooth(); + ret = esp_bluedroid_init(); if (ret) { LOG_ERROR("%s init bluetooth failed\n", __func__); return; diff --git a/examples/14_gatt_server/main/gatts_demo.c b/examples/14_gatt_server/main/gatts_demo.c index cf9a5789b4e..9b3b6cc802a 100644 --- a/examples/14_gatt_server/main/gatts_demo.c +++ b/examples/14_gatt_server/main/gatts_demo.c @@ -2,7 +2,7 @@ // // 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 +// You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // @@ -30,6 +30,8 @@ #include "esp_bt_main.h" #include "esp_bt_main.h" +#define GATTS_TAG "GATTS_DEMO" + ///Declare the static function static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); @@ -115,8 +117,6 @@ static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = { static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - LOG_ERROR("GAP_EVT, event %d\n", event); - switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: esp_ble_gap_start_advertising(&test_adv_params); @@ -129,23 +129,19 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { switch (event) { case ESP_GATTS_REG_EVT: - LOG_INFO("REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true; gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; 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; - LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gap_set_device_name(TEST_DEVICE_NAME); - LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gap_config_adv_data(&test_adv_data); - LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A); - LOG_INFO("%s %d\n", __func__, __LINE__); break; case ESP_GATTS_READ_EVT: { - LOG_INFO("GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); + ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); esp_gatt_rsp_t rsp; memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); rsp.attr_value.handle = param->read.handle; @@ -159,8 +155,8 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i break; } case ESP_GATTS_WRITE_EVT: { - LOG_INFO("GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); - LOG_INFO("GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); break; } @@ -170,7 +166,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_UNREG_EVT: break; case ESP_GATTS_CREATE_EVT: - LOG_INFO("CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A; @@ -184,7 +180,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_ADD_INCL_SRVC_EVT: break; case ESP_GATTS_ADD_CHAR_EVT: - LOG_INFO("ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; @@ -194,19 +190,19 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); break; case ESP_GATTS_ADD_CHAR_DESCR_EVT: - LOG_INFO("ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); break; case ESP_GATTS_DELETE_EVT: break; case ESP_GATTS_START_EVT: - LOG_INFO("SERVICE_START_EVT, status %d, service_handle %d\n", + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n", param->start.status, param->start.service_handle); break; case ESP_GATTS_STOP_EVT: break; case ESP_GATTS_CONNECT_EVT: - LOG_INFO("SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", 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], @@ -227,23 +223,19 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { switch (event) { case ESP_GATTS_REG_EVT: - LOG_INFO("REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true; gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; 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_B; - LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gap_set_device_name(TEST_DEVICE_NAME); - LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gap_config_adv_data(&test_adv_data); - LOG_INFO("%s %d\n", __func__, __LINE__); esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_B); - LOG_INFO("%s %d\n", __func__, __LINE__); break; case ESP_GATTS_READ_EVT: { - LOG_INFO("GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); + ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); esp_gatt_rsp_t rsp; memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); rsp.attr_value.handle = param->read.handle; @@ -257,8 +249,8 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i break; } case ESP_GATTS_WRITE_EVT: { - LOG_INFO("GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); - LOG_INFO("GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value %08x\n", param->write.len, *(uint32_t *)param->write.value); esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); break; } @@ -268,7 +260,7 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_UNREG_EVT: break; case ESP_GATTS_CREATE_EVT: - LOG_INFO("CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_B; @@ -282,7 +274,7 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_ADD_INCL_SRVC_EVT: break; case ESP_GATTS_ADD_CHAR_EVT: - LOG_INFO("ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; @@ -292,19 +284,19 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); break; case ESP_GATTS_ADD_CHAR_DESCR_EVT: - LOG_INFO("ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); break; case ESP_GATTS_DELETE_EVT: break; case ESP_GATTS_START_EVT: - LOG_INFO("SERVICE_START_EVT, status %d, service_handle %d\n", + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n", param->start.status, param->start.service_handle); break; case ESP_GATTS_STOP_EVT: break; case ESP_GATTS_CONNECT_EVT: - LOG_INFO("SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", + ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n", 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], @@ -324,14 +316,12 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { - LOG_INFO("EVT %d, gatts if %d\n", event, gatts_if); - /* If event is register event, store the gatts_if for each profile */ if (event == ESP_GATTS_REG_EVT) { if (param->reg.status == ESP_GATT_OK) { gl_profile_tab[param->reg.app_id].gatts_if = gatts_if; } else { - LOG_INFO("Reg app failed, app_id %04x, status %d\n", + ESP_LOGI(GATTS_TAG, "Reg app failed, app_id %04x, status %d\n", param->reg.app_id, param->reg.status); return; @@ -357,16 +347,16 @@ void app_main() { esp_err_t ret; - bt_controller_init(); - LOG_INFO("%s init bluetooth\n", __func__); - ret = esp_init_bluetooth(); + esp_bt_controller_init(); + + ret = esp_bluedroid_init(); if (ret) { - LOG_ERROR("%s init bluetooth failed\n", __func__); + ESP_LOGE(GATTS_TAG, "%s init bluetooth failed\n", __func__); return; } - ret = esp_enable_bluetooth(); + ret = esp_bluedroid_enable(); if (ret) { - LOG_ERROR("%s enable bluetooth failed\n", __func__); + ESP_LOGE(GATTS_TAG, "%s enable bluetooth failed\n", __func__); return; } diff --git a/examples/15_gatt_client/main/gattc_demo.c b/examples/15_gatt_client/main/gattc_demo.c index 50faaa7c98b..e6ee8672937 100644 --- a/examples/15_gatt_client/main/gattc_demo.c +++ b/examples/15_gatt_client/main/gattc_demo.c @@ -37,6 +37,8 @@ #include "esp_gatt_defs.h" #include "esp_bt_main.h" +#define GATTC_TAG "GATTC_DEMO" + ///Declare static functions static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); @@ -108,16 +110,16 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i switch (event) { case ESP_GATTC_REG_EVT: - LOG_INFO("REG_EVT\n"); + ESP_LOGI(GATTC_TAG, "REG_EVT\n"); esp_ble_gap_set_scan_params(&ble_scan_params); break; case ESP_GATTC_OPEN_EVT: conn_id = p_data->open.conn_id; memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->open.remote_bda, sizeof(esp_bd_addr_t)); - LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); - LOG_INFO("REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", + ESP_LOGI(GATTC_TAG, "REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", gl_profile_tab[PROFILE_A_APP_ID].remote_bda[0], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[1], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[2], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[3], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[4], gl_profile_tab[PROFILE_A_APP_ID].remote_bda[5] @@ -128,37 +130,37 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i case ESP_GATTC_SEARCH_RES_EVT: { esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; conn_id = p_data->search_res.conn_id; - LOG_INFO("SEARCH RES: conn_id = %x\n", conn_id); + ESP_LOGI(GATTC_TAG, "SEARCH RES: conn_id = %x\n", conn_id); if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { - LOG_INFO("UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); + ESP_LOGI(GATTC_TAG, "UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { - LOG_INFO("UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); + ESP_LOGI(GATTC_TAG, "UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { - LOG_INFO("UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], + ESP_LOGI(GATTC_TAG, "UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); } else { - LOG_ERROR("UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + ESP_LOGE(GATTC_TAG, "UNKNOWN LEN %d\n", srvc_id->id.uuid.len); } break; } case ESP_GATTC_SEARCH_CMPL_EVT: conn_id = p_data->search_cmpl.conn_id; - LOG_INFO("SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); + ESP_LOGI(GATTC_TAG, "SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, NULL); break; case ESP_GATTC_GET_CHAR_EVT: if (p_data->get_char.status != ESP_GATT_OK) { break; } - LOG_INFO("GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); - LOG_INFO("GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); + ESP_LOGI(GATTC_TAG, "GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); + ESP_LOGI(GATTC_TAG, "GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); if (p_data->get_char.char_id.uuid.uuid.uuid16 == 0x2a46) { - LOG_INFO("register notify\n"); + ESP_LOGI(GATTC_TAG, "register notify\n"); esp_ble_gattc_register_for_notify(gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, &alert_service_id, &p_data->get_char.char_id); } @@ -166,8 +168,8 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i break; case ESP_GATTC_REG_FOR_NOTIFY_EVT: { uint16_t notify_en = 1; - LOG_INFO("REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); - LOG_INFO("REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); + ESP_LOGI(GATTC_TAG, "REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); + ESP_LOGI(GATTC_TAG, "REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); esp_ble_gattc_write_char_descr( gattc_if, @@ -182,10 +184,10 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i break; } case ESP_GATTC_NOTIFY_EVT: - LOG_INFO("NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); + ESP_LOGI(GATTC_TAG, "NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); break; case ESP_GATTC_WRITE_DESCR_EVT: - LOG_INFO("WRITE: status %d\n", p_data->write.status); + ESP_LOGI(GATTC_TAG, "WRITE: status %d\n", p_data->write.status); break; default: break; @@ -199,16 +201,15 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i switch (event) { case ESP_GATTC_REG_EVT: - LOG_INFO("REG_EVT\n"); - //esp_ble_gap_set_scan_params(&ble_scan_params); + ESP_LOGI(GATTC_TAG, "REG_EVT\n"); break; case ESP_GATTC_OPEN_EVT: conn_id = p_data->open.conn_id; memcpy(gl_profile_tab[PROFILE_B_APP_ID].remote_bda, p_data->open.remote_bda, sizeof(esp_bd_addr_t)); - LOG_INFO("ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d\n", conn_id, gattc_if, p_data->open.status, p_data->open.mtu); - LOG_INFO("REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", + ESP_LOGI(GATTC_TAG, "REMOTE BDA %02x:%02x:%02x:%02x:%02x:%02x\n", gl_profile_tab[PROFILE_B_APP_ID].remote_bda[0], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[1], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[2], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[3], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[4], gl_profile_tab[PROFILE_B_APP_ID].remote_bda[5] @@ -219,37 +220,37 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i case ESP_GATTC_SEARCH_RES_EVT: { esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id; conn_id = p_data->search_res.conn_id; - LOG_INFO("SEARCH RES: conn_id = %x\n", conn_id); + ESP_LOGI(GATTC_TAG, "SEARCH RES: conn_id = %x\n", conn_id); if (srvc_id->id.uuid.len == ESP_UUID_LEN_16) { - LOG_INFO("UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); + ESP_LOGI(GATTC_TAG, "UUID16: %x\n", srvc_id->id.uuid.uuid.uuid16); } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_32) { - LOG_INFO("UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); + ESP_LOGI(GATTC_TAG, "UUID32: %x\n", srvc_id->id.uuid.uuid.uuid32); } else if (srvc_id->id.uuid.len == ESP_UUID_LEN_128) { - LOG_INFO("UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], + ESP_LOGI(GATTC_TAG, "UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", srvc_id->id.uuid.uuid.uuid128[0], srvc_id->id.uuid.uuid.uuid128[1], srvc_id->id.uuid.uuid.uuid128[2], srvc_id->id.uuid.uuid.uuid128[3], srvc_id->id.uuid.uuid.uuid128[4], srvc_id->id.uuid.uuid.uuid128[5], srvc_id->id.uuid.uuid.uuid128[6], srvc_id->id.uuid.uuid.uuid128[7], srvc_id->id.uuid.uuid.uuid128[8], srvc_id->id.uuid.uuid.uuid128[9], srvc_id->id.uuid.uuid.uuid128[10], srvc_id->id.uuid.uuid.uuid128[11], srvc_id->id.uuid.uuid.uuid128[12], srvc_id->id.uuid.uuid.uuid128[13], srvc_id->id.uuid.uuid.uuid128[14], srvc_id->id.uuid.uuid.uuid128[15]); } else { - LOG_ERROR("UNKNOWN LEN %d\n", srvc_id->id.uuid.len); + ESP_LOGE(GATTC_TAG, "UNKNOWN LEN %d\n", srvc_id->id.uuid.len); } break; } case ESP_GATTC_SEARCH_CMPL_EVT: conn_id = p_data->search_cmpl.conn_id; - LOG_INFO("SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); + ESP_LOGI(GATTC_TAG, "SEARCH_CMPL: conn_id = %x, status %d\n", conn_id, p_data->search_cmpl.status); esp_ble_gattc_get_characteristic(gattc_if, conn_id, &alert_service_id, NULL); break; case ESP_GATTC_GET_CHAR_EVT: if (p_data->get_char.status != ESP_GATT_OK) { break; } - LOG_INFO("GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); - LOG_INFO("GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); + ESP_LOGI(GATTC_TAG, "GET CHAR: conn_id = %x, status %d\n", p_data->get_char.conn_id, p_data->get_char.status); + ESP_LOGI(GATTC_TAG, "GET CHAR: srvc_id = %04x, char_id = %04x\n", p_data->get_char.srvc_id.id.uuid.uuid.uuid16, p_data->get_char.char_id.uuid.uuid.uuid16); if (p_data->get_char.char_id.uuid.uuid.uuid16 == 0x2a46) { - LOG_INFO("register notify\n"); + ESP_LOGI(GATTC_TAG, "register notify\n"); esp_ble_gattc_register_for_notify(gattc_if, gl_profile_tab[PROFILE_B_APP_ID].remote_bda, &alert_service_id, &p_data->get_char.char_id); } @@ -257,8 +258,8 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i break; case ESP_GATTC_REG_FOR_NOTIFY_EVT: { uint16_t notify_en = 1; - LOG_INFO("REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); - LOG_INFO("REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); + ESP_LOGI(GATTC_TAG, "REG FOR NOTIFY: status %d\n", p_data->reg_for_notify.status); + ESP_LOGI(GATTC_TAG, "REG FOR_NOTIFY: srvc_id = %04x, char_id = %04x\n", p_data->reg_for_notify.srvc_id.id.uuid.uuid.uuid16, p_data->reg_for_notify.char_id.uuid.uuid.uuid16); esp_ble_gattc_write_char_descr( gattc_if, @@ -273,10 +274,10 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i break; } case ESP_GATTC_NOTIFY_EVT: - LOG_INFO("NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); + ESP_LOGI(GATTC_TAG, "NOTIFY: len %d, value %08x\n", p_data->notify.value_len, *(uint32_t *)p_data->notify.value); break; case ESP_GATTC_WRITE_DESCR_EVT: - LOG_INFO("WRITE: status %d\n", p_data->write.status); + ESP_LOGI(GATTC_TAG, "WRITE: status %d\n", p_data->write.status); break; default: break; @@ -299,27 +300,22 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par switch (scan_result->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: for (int i = 0; i < 6; i++) { - LOG_INFO("%x:", scan_result->scan_rst.bda[i]); + ESP_LOGI(GATTC_TAG, "%x:", scan_result->scan_rst.bda[i]); } - LOG_INFO("\n"); + ESP_LOGI(GATTC_TAG, "\n"); adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); - LOG_INFO("adv_name_len=%x\n", adv_name_len); + ESP_LOGI(GATTC_TAG, "Searched Device Name Len %d\n", adv_name_len); for (int j = 0; j < adv_name_len; j++) { - LOG_INFO("%c", adv_name[j]); + ESP_LOGI(GATTC_TAG, "%c", adv_name[j]); } - LOG_INFO("\n"); - for (int j = 0; j < adv_name_len; j++) { - LOG_INFO("%c", device_name[j]); - } - LOG_INFO("\n"); if (adv_name != NULL) { if (strcmp((char *)adv_name, device_name) == 0) { - LOG_INFO("the name equal to Heart Rate\n"); + ESP_LOGI(GATTC_TAG, "Searched device %s\n", device_name); if (connect == false) { connect = true; - LOG_INFO("Connect to the remote device.\n"); + ESP_LOGI(GATTC_TAG, "Connect to the remote device.\n"); esp_ble_gap_stop_scanning(); esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, scan_result->scan_rst.bda, true); esp_ble_gattc_open(gl_profile_tab[PROFILE_B_APP_ID].gattc_if, scan_result->scan_rst.bda, true); @@ -341,14 +337,14 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - LOG_INFO("EVT %d, gattc if %d\n", event, gattc_if); + ESP_LOGI(GATTC_TAG, "EVT %d, gattc if %d\n", event, gattc_if); /* If event is register event, store the gattc_if for each profile */ if (event == ESP_GATTC_REG_EVT) { if (param->reg.status == ESP_GATT_OK) { gl_profile_tab[param->reg.app_id].gattc_if = gattc_if; } else { - LOG_INFO("Reg app failed, app_id %04x, status %d\n", + ESP_LOGI(GATTC_TAG, "Reg app failed, app_id %04x, status %d\n", param->reg.app_id, param->reg.status); return; @@ -374,17 +370,17 @@ void ble_client_appRegister(void) { esp_err_t status; - LOG_INFO("register callback\n"); + ESP_LOGI(GATTC_TAG, "register callback\n"); //register the scan callback function to the gap moudule if ((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK) { - LOG_ERROR("gap register error, error code = %x\n", status); + ESP_LOGE(GATTC_TAG, "gap register error, error code = %x\n", status); return; } //register the callback function to the gattc module if ((status = esp_ble_gattc_register_callback(esp_gattc_cb)) != ESP_OK) { - LOG_ERROR("gattc register error, error code = %x\n", status); + ESP_LOGE(GATTC_TAG, "gattc register error, error code = %x\n", status); return; } esp_ble_gattc_app_register(PROFILE_A_APP_ID); @@ -393,14 +389,14 @@ void ble_client_appRegister(void) void gattc_client_test(void) { - esp_init_bluetooth(); - esp_enable_bluetooth(); + esp_bluedroid_init(); + esp_bluedroid_enable(); ble_client_appRegister(); } void app_main() { - bt_controller_init(); + esp_bt_controller_init(); gattc_client_test(); } From 24af07fd13e69f1e82620b5608feb14dc50283f9 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Wed, 28 Dec 2016 12:02:43 +0800 Subject: [PATCH 066/167] component/bt : new blufi 1. new blufi protocol 2. new blufi demo 3. support security 4. support sta/ap/sta_ap 5. support wpa-enterprise --- components/bt/bluedroid/api/esp_blufi_api.c | 20 +- .../bt/bluedroid/api/include/esp_blufi_api.h | 276 ++++- components/bt/bluedroid/bta/dm/bta_dm_api.c | 4 +- .../btc/profile/esp/blufi/blufi_adv.c | 164 --- .../btc/profile/esp/blufi/blufi_prf.c | 940 ++++++++++++++---- .../btc/profile/esp/blufi/blufi_protocol.c | 240 +++++ .../btc/profile/esp/blufi/include/blufi_adv.h | 39 - .../btc/profile/esp/blufi/include/blufi_int.h | 172 +++- .../btc/profile/esp/include/btc_blufi_prf.h | 30 +- .../btc/profile/std/gap/btc_gap_ble.c | 4 +- .../bt/bluedroid/hci/packet_fragmenter.c | 16 +- components/bt/bluedroid/include/bt_trace.h | 13 +- docs/api/bt_le.rst | 1 + docs/api/esp_blufi.rst | 128 +++ examples/12_blufi/components/blufi/blufi.c | 132 --- .../12_blufi/components/blufi/blufi_task.c | 110 -- .../12_blufi/components/blufi/component.mk | 13 - .../12_blufi/components/blufi/include/blufi.h | 39 - examples/12_blufi/main/blufi_demo.h | 17 + examples/12_blufi/main/blufi_main.c | 336 +++++++ examples/12_blufi/main/blufi_security.c | 205 ++++ examples/12_blufi/main/demo_main.c | 144 --- 22 files changed, 2090 insertions(+), 953 deletions(-) delete mode 100644 components/bt/bluedroid/btc/profile/esp/blufi/blufi_adv.c create mode 100644 components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c delete mode 100644 components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_adv.h create mode 100644 docs/api/esp_blufi.rst delete mode 100644 examples/12_blufi/components/blufi/blufi.c delete mode 100644 examples/12_blufi/components/blufi/blufi_task.c delete mode 100644 examples/12_blufi/components/blufi/component.mk delete mode 100644 examples/12_blufi/components/blufi/include/blufi.h create mode 100644 examples/12_blufi/main/blufi_demo.h create mode 100644 examples/12_blufi/main/blufi_main.c create mode 100644 examples/12_blufi/main/blufi_security.c delete mode 100644 examples/12_blufi/main/demo_main.c diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index 2697f2cbf93..5493824d2a5 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -22,22 +22,30 @@ #include "btc_main.h" #include "future.h" -esp_err_t esp_blufi_register_callback(esp_blufi_cb_t callback) +esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) { - return (btc_profile_cb_set(BTC_PID_BLUFI, callback) == 0 ? ESP_OK : ESP_FAIL); + if (callbacks == NULL) { + return ESP_FAIL; + } + + btc_blufi_set_callbacks(callbacks); + return (btc_profile_cb_set(BTC_PID_BLUFI, callbacks->event_cb) == 0 ? ESP_OK : ESP_FAIL); } -esp_err_t esp_blufi_send_config_state(esp_blufi_config_state_t state) +esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn_state_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *extra_info) { btc_msg_t msg; btc_blufi_args_t arg; msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; - msg.act = BTC_BLUFI_ACT_SEND_CFG_STATE; - arg.cfg_state.state = state; + msg.act = BTC_BLUFI_ACT_SEND_CFG_REPORT; + arg.wifi_conn_report.opmode = opmode; + arg.wifi_conn_report.sta_conn_state = sta_conn_state; + arg.wifi_conn_report.softap_conn_num = softap_conn_num; + arg.wifi_conn_report.extra_info = extra_info; - return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), btc_blufi_call_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } diff --git a/components/bt/bluedroid/api/include/esp_blufi_api.h b/components/bt/bluedroid/api/include/esp_blufi_api.h index 2644bd5244e..5490fec05a5 100644 --- a/components/bt/bluedroid/api/include/esp_blufi_api.h +++ b/components/bt/bluedroid/api/include/esp_blufi_api.h @@ -18,24 +18,44 @@ #include "esp_bt_defs.h" #include "esp_gatt_defs.h" #include "esp_err.h" +#include "esp_wifi_types.h" #ifdef __cplusplus extern "C" { #endif -#define ESP_BLUFI_RECV_DATA_LEN_MAX (64+1) - typedef enum { - ESP_BLUFI_EVENT_INIT_FINISH = 0, - ESP_BLUFI_EVENT_DEINIT_FINISH = 1, - ESP_BLUFI_EVENT_RECV_DATA = 2, + ESP_BLUFI_EVENT_INIT_FINISH = 0, /*adv_name != NULL) { - data_mask |= BTM_BLE_AD_BIT_DEV_NAME; - BTA_DmSetDeviceName(adv_data->adv_name); - } - if (adv_data->ble_adv_data.int_range.low != 0 || - adv_data->ble_adv_data.int_range.hi != 0) { - data_mask |= BTM_BLE_AD_BIT_INT_RANGE; - } - - if (adv_data->ble_adv_data.p_manu != NULL) { - data_mask |= BTM_BLE_AD_BIT_MANU; - } - - if (adv_data->ble_adv_data.p_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE; - } - - if (adv_data->ble_adv_data.p_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32; - } - - if (adv_data->ble_adv_data.p_services_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128; - } - - if (adv_data->ble_adv_data.p_sol_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_SOL; - } - - if (adv_data->ble_adv_data.p_sol_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32SOL; - } - - if (adv_data->ble_adv_data.p_sol_service_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128SOL; - } - - if (adv_data->ble_adv_data.p_service_data != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_DATA; - } - - if (adv_data->ble_adv_data.appearance != 0) { - data_mask |= BTM_BLE_AD_BIT_APPEARANCE; - } - - if (adv_data->ble_adv_data.p_proprietary != NULL) { - data_mask |= BTM_BLE_AD_BIT_PROPRIETARY; - } - - if (adv_data->ble_adv_data.tx_power != 0) { - data_mask |= BTM_BLE_AD_BIT_TX_PWR; - } - - BTA_DmBleSetAdvConfig(data_mask, &(adv_data->ble_adv_data), p_adv_data_cback); -} - - -/******************************************************************************* -** -** Function BLUFI_BleSetScanRsp -** -** Description This function is called to override the app scan response. -** -** Parameters Pointer to User defined ADV data structure -** -** Returns None -** -*******************************************************************************/ -void BlufiBleSetScanRsp(tBLUFI_BLE_ADV_DATA *scan_rsp_data, - tBTA_SET_ADV_DATA_CMPL_CBACK *p_scan_rsp_data_cback) -{ - tBTA_BLE_AD_MASK data_mask = 0; - if (scan_rsp_data->adv_name != NULL) { - data_mask |= BTM_BLE_AD_BIT_DEV_NAME; - BTA_DmSetDeviceName(scan_rsp_data->adv_name); - } - if (scan_rsp_data->ble_adv_data.int_range.low != 0 || - scan_rsp_data->ble_adv_data.int_range.hi != 0) { - data_mask |= BTM_BLE_AD_BIT_INT_RANGE; - } - - if (scan_rsp_data->ble_adv_data.p_manu != NULL) { - data_mask |= BTM_BLE_AD_BIT_MANU; - } - - if (scan_rsp_data->ble_adv_data.p_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE; - } - - if (scan_rsp_data->ble_adv_data.p_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32; - } - - if (scan_rsp_data->ble_adv_data.p_services_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128; - } - - if (scan_rsp_data->ble_adv_data.p_sol_services != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_SOL; - } - - if (scan_rsp_data->ble_adv_data.p_sol_service_32b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_32SOL; - } - - if (scan_rsp_data->ble_adv_data.p_sol_service_128b != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_128SOL; - } - - if (scan_rsp_data->ble_adv_data.p_service_data != NULL) { - data_mask |= BTM_BLE_AD_BIT_SERVICE_DATA; - } - - if (scan_rsp_data->ble_adv_data.appearance != 0) { - data_mask |= BTM_BLE_AD_BIT_APPEARANCE; - } - - if (scan_rsp_data->ble_adv_data.p_proprietary != NULL) { - data_mask |= BTM_BLE_AD_BIT_PROPRIETARY; - } - - if (scan_rsp_data->ble_adv_data.tx_power != 0) { - data_mask |= BTM_BLE_AD_BIT_TX_PWR; - } - - BTA_DmBleSetScanRsp(data_mask, &(scan_rsp_data->ble_adv_data), p_scan_rsp_data_cback); -} - - diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index d6e23e63a33..d480350453b 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -31,255 +31,240 @@ #include "btc_task.h" #include "btc_manage.h" -#include "blufi_adv.h" #include "blufi_int.h" -const char success_msg[] = "BLUFI_CONFIG_OK"; -const char failed_msg[] = "BLUFI_CONFIG_FAILED"; +#include "esp_blufi_api.h" -#define BTC_BLUFI_CB_TO_APP(event, param) ((esp_blufi_cb_t)btc_profile_cb_get(BTC_PID_BLUFI))((event), (param)) +#define BTC_BLUFI_CB_TO_APP(event, param) ((esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI))((event), (param)) #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] +//define the blufi serivce uuid +#define BLUFI_SERVICE_UUID 0xFFFF +//define the blufi Char uuid (PHONE to ESP32) +#define BLUFI_CHAR_P2E_UUID 0xFF01 +//define the blufi Char uuid (ESP32 to PHONE) +#define BLUFI_CHAR_E2P_UUID 0xFF02 +//define the blufi Descriptor uuid (ESP32 to PHONE) +#define BLUFI_DESCR_E2P_UUID GATT_UUID_CHAR_CLIENT_CONFIG +//define the blufi APP ID +#define BLUFI_APP_UUID 0xFFFF -UINT16 esp32_uuid = SVC_BLUFI_UUID; -UINT8 esp32_manu[17] = {0xff, 0x20, 0x14, 0x07, 0x22, 0x00, 0x02, 0x5B, 0x00, 0x33, 0x49, 0x31, 0x30, 0x4a, 0x30, 0x30, 0x31}; -tBTA_BLE_MANU p_esp32_manu = {sizeof(esp32_manu), esp32_manu}; /* manufacturer data */ +#define BLUFI_HDL_NUM 6 -tBTA_BLE_SERVICE esp32_service = { - 0x01, //only one service in the ijiazu button profile - false, - &esp32_uuid -}; /* 16 bits services */ +tBLUFI_ENV blufi_env; +static /* const */ tBT_UUID blufi_srvc_uuid = {LEN_UUID_16, {BLUFI_SERVICE_UUID}}; +static /* const */ tBT_UUID blufi_char_uuid_p2e = {LEN_UUID_16, {BLUFI_CHAR_P2E_UUID}}; +static /* const */ tBT_UUID blufi_char_uuid_e2p = {LEN_UUID_16, {BLUFI_CHAR_E2P_UUID}}; +static /* const */ tBT_UUID blufi_descr_uuid_e2p = {LEN_UUID_16, {BLUFI_DESCR_E2P_UUID}}; +static /* const */ tBT_UUID blufi_app_uuid = {LEN_UUID_16, {BLUFI_APP_UUID}}; -tBLUFI_BLE_ADV_DATA esp32_adv_data[ADV_SCAN_IDX_MAX] = { - [BLE_ADV_DATA_IDX] = { - .adv_name = "Espressif_008", - { - {0, 0}, - NULL, //no manufature data to be setting in the esp32 adervetisiing datas - &esp32_service, - NULL, //the 128 bits service uuid set to null(not used) - NULL, //the 32 bits Service UUID set to null(not used) - NULL, //16 bits services Solicitation UUIDs set to null(not used) - NULL, //List of 32 bit Service Solicitation UUIDs set to null(not used) - NULL, //List of 128 bit Service Solicitation UUIDs set to null(not used) - NULL, //proprietary data set to null(not used) - NULL, //service data set not null(no service data to be sent) - 0x0200, //device type : generic display - BTA_DM_GENERAL_DISC, // General discoverable. - 0xFE //the tx power value,defult value is 0 - }, - }, - - [BLE_SCAN_RSP_DATA_IDX] = { - .adv_name = NULL, - { - {0, 0}, - &p_esp32_manu, - NULL, - NULL, //the 128 bits service uuid set to null(not used) - NULL, //the 32 bits Service UUID set to null(not used) - NULL, //16 bits services Solicitation UUIDs set to null(not used) - NULL, //List of 32 bit Service Solicitation UUIDs set to null(not used) - NULL, //List of 128 bit Service Solicitation UUIDs set to null(not used) - NULL, //proprietary data set to null(not used) - NULL, //service data set not null(no service data to be sent) - 0x0000, //device type : generic display - 0x00, // General discoverable. - 0x00 - }, //the tx power value,defult value is 0 - } -}; - - - -static tBLUFI_CB_ENV blufi_cb_env; - - - -/***************************************************************************** -** Constants -*****************************************************************************/ +// static functions declare static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data); +static void btc_blufi_recv_handler(uint8_t *data, int len); +static void btc_blufi_send_ack(uint8_t seq); - -/******************************************************************************* -** -** Function blufi_create_service -** -** Description Create a Service for the blufi profile -** -** Returns NULL -** -*******************************************************************************/ static void blufi_create_service(void) { - tBTA_GATTS_IF server_if ; - tBT_UUID uuid = {LEN_UUID_16, {SVC_BLUFI_UUID}}; - UINT16 num_handle = BLUFI_HDL_NUM; - UINT8 inst = 0x00; - server_if = blufi_cb_env.gatt_if; - blufi_cb_env.inst_id = inst; - if (!blufi_cb_env.enabled) { + if (!blufi_env.enabled) { LOG_ERROR("blufi service added error."); return; } - BTA_GATTS_CreateService(server_if, &uuid, inst, num_handle, true); + blufi_env.srvc_inst = 0x00; + BTA_GATTS_CreateService(blufi_env.gatt_if, &blufi_srvc_uuid, blufi_env.srvc_inst, BLUFI_HDL_NUM, true); } - -/******************************************************************************* -** -** Function blufi_profile_cb -** -** Description the callback function after the profile has been register to the BTA manager module -** -** Returns NULL -** -*******************************************************************************/ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) { tBTA_GATTS_RSP rsp; - tBT_UUID uuid = {LEN_UUID_16, {SVC_BLUFI_UUID}}; - UINT8 *p_rec_data = NULL; - tBTA_GATT_STATUS status; - tBTA_DM_DISC disc_mode = BTA_DM_BLE_GENERAL_DISCOVERABLE; - tBTA_DM_CONN conn_mode = BTA_DM_BLE_CONNECTABLE; LOG_DEBUG("blufi profile cb event = %x\n", event); switch (event) { case BTA_GATTS_REG_EVT: - status = p_data->reg_oper.status; + LOG_DEBUG("REG: status %d, app_uuid %04x, gatt_if %d\n", p_data->reg_oper.status, p_data->reg_oper.uuid.uu.uuid16, p_data->reg_oper.server_if); - LOG_DEBUG("p_data->reg_oper.status = %x\n", p_data->reg_oper.status); - LOG_DEBUG("(p_data->reg_oper.uuid.uu.uuid16=%x\n", p_data->reg_oper.uuid.uu.uuid16); if (p_data->reg_oper.status != BTA_GATT_OK) { - LOG_ERROR("blufi profile register failed\n"); + LOG_ERROR("BLUFI profile register failed\n"); return; } - blufi_cb_env.gatt_if = p_data->reg_oper.server_if; - blufi_cb_env.enabled = true; - LOG_DEBUG("register complete: event=%d, status=%d, server_if=%d\n", - event, status, blufi_cb_env.gatt_if); - - LOG_DEBUG("set advertising parameters\n"); - //set connectable,discoverable, pairable and paired only modes of local device - BTA_DmSetVisibility(disc_mode, conn_mode, (uint8_t)BTA_DM_NON_PAIRABLE, (uint8_t)BTA_DM_CONN_ALL); - //set the advertising data to the btm layer - BlufiBleConfigadvData(&esp32_adv_data[BLE_ADV_DATA_IDX], NULL); - //set the adversting data to the btm layer - BlufiBleSetScanRsp(&esp32_adv_data[BLE_SCAN_RSP_DATA_IDX], NULL); - BTA_GATTS_Listen(blufi_cb_env.gatt_if, true, NULL); + blufi_env.gatt_if = p_data->reg_oper.server_if; + blufi_env.enabled = true; //create the blufi service to the service data base. - if (p_data->reg_oper.uuid.uu.uuid16 == SVC_BLUFI_UUID) { + if (p_data->reg_oper.uuid.uu.uuid16 == BLUFI_APP_UUID) { + LOG_DEBUG("%s %d\n", __func__, __LINE__); blufi_create_service(); } break; case BTA_GATTS_READ_EVT: memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP)); rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; - rsp.attr_value.len = 2; - //rsp.attr_value.value[0] = 0xde; - //rsp.attr_value.value[1] = 0xed; - //rsp.attr_value.value[2] = 0xbe; - //rsp.attr_value.value[3] = 0xef; + rsp.attr_value.len = 1; + rsp.attr_value.value[0] = 0x00; BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, p_data->req_data.status, &rsp); break; - case BTA_GATTS_WRITE_EVT: - BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + case BTA_GATTS_WRITE_EVT: { + if(p_data->req_data.p_data->write_req.is_prep) { + tBTA_GATT_STATUS status = GATT_SUCCESS; + + if (blufi_env.prepare_buf == NULL) { + blufi_env.prepare_buf = GKI_getbuf(BLUFI_PREPAIR_BUF_MAX_SIZE); + if (blufi_env.prepare_buf == NULL) { + LOG_ERROR("Blufi prep no mem\n"); + status = GATT_NO_RESOURCES; + } + } else { + if(p_data->req_data.p_data->write_req.offset > BLUFI_PREPAIR_BUF_MAX_SIZE) { + status = GATT_INVALID_OFFSET; + } else if ((p_data->req_data.p_data->write_req.offset + p_data->req_data.p_data->write_req.len) > BLUFI_PREPAIR_BUF_MAX_SIZE) { + status = GATT_INVALID_ATTR_LEN; + } + } + + memset(&rsp, 0, sizeof(tGATTS_RSP)); + rsp.attr_value.handle = p_data->req_data.p_data->write_req.handle; + rsp.attr_value.len = p_data->req_data.p_data->write_req.len; + rsp.attr_value.offset = p_data->req_data.p_data->write_req.offset; + memcpy(rsp.attr_value.value, p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len); + + LOG_DEBUG("prep write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset); + + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + status, &rsp); + + memcpy(blufi_env.prepare_buf + p_data->req_data.p_data->write_req.offset, + p_data->req_data.p_data->write_req.value, + p_data->req_data.p_data->write_req.len); + blufi_env.prepare_len += p_data->req_data.p_data->write_req.len; + + return; + } else { + LOG_DEBUG("norm write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset); + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, p_data->req_data.status, NULL); - - LOG_DEBUG("Received blufi data:"); - for (int i = 0; i < p_data->req_data.p_data->write_req.len; i++) { - LOG_DEBUG("%x", p_data->req_data.p_data->write_req.value[i]); } - LOG_DEBUG("\n"); - - if (p_data->req_data.p_data->write_req.handle == blufi_cb_env.blufi_inst.blufi_hdl) { - btc_msg_t msg; - struct blufi_recv_evt_param recv_data; - - memset(&recv_data, 0x00, sizeof(struct blufi_recv_evt_param)); - - p_rec_data = &p_data->req_data.p_data->write_req.value[0]; - recv_data.data_len = p_data->req_data.p_data->write_req.len; - memcpy(recv_data.data, p_rec_data, recv_data.data_len); - - msg.sig = BTC_SIG_API_CB; - msg.pid = BTC_PID_BLUFI; - msg.act = BTC_BLUFI_CB_ACT_RECV_DATA; - - btc_transfer_context(&msg, &recv_data, sizeof(struct blufi_recv_evt_param), NULL); + + if (p_data->req_data.p_data->write_req.handle == blufi_env.handle_char_p2e) { + btc_blufi_recv_handler(&p_data->req_data.p_data->write_req.value[0], + p_data->req_data.p_data->write_req.len); } + break; + } + case BTA_GATTS_EXEC_WRITE_EVT: + LOG_DEBUG("exec write exec %d\n", p_data->req_data.p_data->exec_write); + + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + GATT_SUCCESS, NULL); + + if (p_data->req_data.p_data->exec_write == GATT_PREP_WRITE_EXEC) { + btc_blufi_recv_handler(blufi_env.prepare_buf, blufi_env.prepare_len); + } + + if (blufi_env.prepare_buf) { + GKI_freebuf(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + } + break; case BTA_GATTS_CONF_EVT: + LOG_DEBUG("CONIRM EVT\n"); /* Nothing */ break; case BTA_GATTS_CREATE_EVT: - uuid.uu.uuid16 = CHAR_BLUFI_UUID; - blufi_cb_env.cur_srvc_id = p_data->create.service_id; - blufi_cb_env.is_primery = p_data->create.is_primary; - //start the blufi service after created - BTA_GATTS_StartService(p_data->create.service_id, BTA_GATT_TRANSPORT_LE); + blufi_env.handle_srvc = p_data->create.service_id; + //add the frist blufi characteristic --> write characteristic - BTA_GATTS_AddCharacteristic(blufi_cb_env.cur_srvc_id, &uuid, - (GATT_PERM_WRITE | GATT_PERM_READ), - (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE | GATT_CHAR_PROP_BIT_NOTIFY)); + BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_p2e, + (GATT_PERM_WRITE), + (GATT_CHAR_PROP_BIT_WRITE)); break; case BTA_GATTS_ADD_CHAR_EVT: - if (p_data->add_result.char_uuid.uu.uuid16 == CHAR_BLUFI_UUID) { - //save the att handle to the env - blufi_cb_env.blufi_inst.blufi_hdl = p_data->add_result.attr_id; + switch (p_data->add_result.char_uuid.uu.uuid16) { + case BLUFI_CHAR_P2E_UUID: /* Phone to ESP32 */ + blufi_env.handle_char_p2e = p_data->add_result.attr_id; - uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; - BTA_GATTS_AddCharDescriptor (blufi_cb_env.cur_srvc_id, - (GATT_PERM_WRITE | GATT_PERM_WRITE), - &uuid); + BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_e2p, + (GATT_PERM_READ), + (GATT_PERM_READ | GATT_CHAR_PROP_BIT_NOTIFY)); + break; + case BLUFI_CHAR_E2P_UUID: /* ESP32 to Phone */ + blufi_env.handle_char_e2p = p_data->add_result.attr_id; + + BTA_GATTS_AddCharDescriptor (blufi_env.handle_srvc, + (GATT_PERM_READ | GATT_PERM_WRITE), + &blufi_descr_uuid_e2p); + break; + default: + break; } break; case BTA_GATTS_ADD_CHAR_DESCR_EVT: { /* call init finish */ + esp_blufi_cb_param_t param; btc_msg_t msg; + blufi_env.handle_descr_e2p = p_data->add_result.attr_id; + //start the blufi service after created + BTA_GATTS_StartService(blufi_env.handle_srvc, BTA_GATT_TRANSPORT_LE); + msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; - msg.act = BTC_BLUFI_CB_ACT_INIT_FINISH; - btc_transfer_context(&msg, NULL, 0, NULL); + msg.act = ESP_BLUFI_EVENT_INIT_FINISH; + param.init_finish.state = ESP_BLUFI_INIT_OK; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); break; } - case BTA_GATTS_CONNECT_EVT: + case BTA_GATTS_CONNECT_EVT: { + btc_msg_t msg; + esp_blufi_cb_param_t param; + //set the connection flag to true - LOG_ERROR("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", + LOG_INFO("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if, p_data->conn.reason, p_data->conn.conn_id); - blufi_cb_env.conn_id = p_data->conn.conn_id; + memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + blufi_env.conn_id = p_data->conn.conn_id; + blufi_env.is_connected = true; - /*return whether the remote device is currently connected*/ - int is_connected = BTA_DmGetConnectionState(p_data->conn.remote_bda); - LOG_DEBUG("is_connected=%d\n", is_connected); - BTA_DmBleBroadcast(0); //stop adv + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_BLE_CONNECT; + memcpy(param.connect.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); break; - case BTA_GATTS_DISCONNECT_EVT: + } + case BTA_GATTS_DISCONNECT_EVT: { + btc_msg_t msg; + esp_blufi_cb_param_t param; + + blufi_env.is_connected = false; //set the connection flag to true - blufi_cb_env.connected = false; + LOG_INFO("\ndevice is disconnected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", + BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if, + p_data->conn.reason, p_data->conn.conn_id); + + memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + blufi_env.conn_id = p_data->conn.conn_id; + blufi_env.is_connected = false; + blufi_env.recv_seq = blufi_env.send_seq = 0; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT; + memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, sizeof(esp_bd_addr_t)); + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); break; + } case BTA_GATTS_OPEN_EVT: break; case BTA_GATTS_CLOSE_EVT: - if (blufi_cb_env.connected && (blufi_cb_env.conn_id == p_data->conn.conn_id)) { - //set the connection channal congested flag to true - blufi_cb_env.congest = p_data->congest.congested; - } - break; - case BTA_GATTS_LISTEN_EVT: break; case BTA_GATTS_CONGEST_EVT: break; @@ -290,72 +275,594 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) static tGATT_STATUS btc_blufi_profile_init(void) { - tBT_UUID app_uuid = {LEN_UUID_16, {SVC_BLUFI_UUID}}; + esp_blufi_callbacks_t *store_p = blufi_env.cbs; - - if (blufi_cb_env.enabled) { - LOG_ERROR("blufi svc already initaliezd"); + if (blufi_env.enabled) { + LOG_ERROR("BLUFI already initialized"); return GATT_ERROR; - } else { - memset(&blufi_cb_env, 0, sizeof(tBLUFI_CB_ENV)); } + memset(&blufi_env, 0x0, sizeof(blufi_env)); + blufi_env.cbs = store_p; /* if set callback prior, restore the point */ - /* register the blufi profile to the BTA_GATTS module*/ - BTA_GATTS_AppRegister(&app_uuid, blufi_profile_cb); + /* register the BLUFI profile to the BTA_GATTS module*/ + BTA_GATTS_AppRegister(&blufi_app_uuid, blufi_profile_cb); return GATT_SUCCESS; } -static void blufi_msg_notify(UINT8 *blufi_msg, UINT8 len) +static tGATT_STATUS btc_blufi_profile_deinit(void) { - BOOLEAN conn_status = blufi_cb_env.connected; - UINT16 conn_id = blufi_cb_env.conn_id; - UINT16 attr_id = blufi_cb_env.blufi_inst.blufi_hdl; - //notify rsp==false; indicate rsp==true. - BOOLEAN rsp = false; - if (!conn_status && blufi_cb_env.congest) { - LOG_ERROR("the conneciton for blufi profile has been loss"); + esp_blufi_cb_param_t param; + btc_msg_t msg; + + if (!blufi_env.enabled) { + LOG_ERROR("BLUFI already initialized"); + return GATT_ERROR; + } + + BTA_GATTS_StopService(blufi_env.handle_srvc); + BTA_GATTS_DeleteService(blufi_env.handle_srvc); + /* register the BLUFI profile to the BTA_GATTS module*/ + BTA_GATTS_AppDeregister(blufi_env.gatt_if); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_DEINIT_FINISH; + param.deinit_finish.state = ESP_BLUFI_DEINIT_OK; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + + return GATT_SUCCESS; +} + +static void btc_blufi_send_notify(uint8_t *pkt, int pkt_len) +{ + UINT16 conn_id = blufi_env.conn_id; + UINT16 attr_id = blufi_env.handle_char_e2p; + bool rsp = false; + + BTA_GATTS_HandleValueIndication(conn_id, attr_id, pkt_len, + pkt, rsp); +} + +static void btc_blufi_recv_handler(uint8_t *data, int len) +{ + struct blufi_hdr *hdr = (struct blufi_hdr *)data; + uint16_t checksum; + int ret; + + if (hdr->seq != blufi_env.recv_seq) { + LOG_ERROR("%s seq %d is not expect %d\n", __func__, hdr->seq, blufi_env.recv_seq + 1); return; } - BTA_GATTS_HandleValueIndication (conn_id, attr_id, len, - blufi_msg, rsp); + blufi_env.recv_seq++; + + // first step, decrypt + if (BLUFI_FC_IS_ENC(hdr->fc) + && (blufi_env.cbs && blufi_env.cbs->decrypt_func)) { + ret = blufi_env.cbs->decrypt_func(hdr->seq, hdr->data, hdr->data_len); + if (ret != hdr->data_len) { /* enc must be success and enc len must equal to plain len */ + LOG_ERROR("%s decrypt error %d\n", __func__, ret); + return; + } + } + + // second step, check sum + if (BLUFI_FC_IS_CHECK(hdr->fc) + && (blufi_env.cbs && blufi_env.cbs->checksum_func)) { + checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2); + if (memcmp(&checksum, &hdr->data[hdr->data_len], 2) != 0) { + LOG_ERROR("%s checksum error %04x, pkt %04x\n", __func__, checksum, *(uint16_t *)&hdr->data[hdr->data_len]); + return; + } + } + + if (BLUFI_FC_IS_REQ_ACK(hdr->fc)) { + btc_blufi_send_ack(hdr->seq); + } + + if (BLUFI_FC_IS_FRAG(hdr->fc)) { + if (blufi_env.offset == 0) { + blufi_env.total_len = *(uint16_t *)(hdr->data); + blufi_env.aggr_buf = GKI_getbuf(blufi_env.total_len); + if (blufi_env.aggr_buf == NULL) { + LOG_ERROR("%s no mem, len %d\n", __func__, blufi_env.total_len); + return; + } + } + memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, hdr->data_len - 2); + blufi_env.offset += (hdr->data_len - 2); + } else { + if (blufi_env.offset > 0) { /* if previous pkt is frag */ + memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data, hdr->data_len); + + btc_blufi_protocol_handler(hdr->type, blufi_env.aggr_buf, blufi_env.total_len); + blufi_env.offset = 0; + GKI_freebuf(blufi_env.aggr_buf); + blufi_env.aggr_buf = NULL; + } else { + btc_blufi_protocol_handler(hdr->type, hdr->data, hdr->data_len); + blufi_env.offset = 0; + } + } +} +void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) +{ + struct blufi_hdr *hdr = NULL; + int remain_len = total_data_len; + uint16_t checksum; + int ret; + + while (remain_len > 0) { + if (remain_len > BLUFI_FRAG_DATA_MAX_LEN) { + hdr = GKI_getbuf(sizeof(struct blufi_hdr) + 2 + BLUFI_FRAG_DATA_MAX_LEN + 2); + if (hdr == NULL) { + LOG_ERROR("%s no mem\n", __func__); + return; + } + hdr->fc = 0x0; + hdr->data_len = BLUFI_FRAG_DATA_MAX_LEN + 2; + *(uint16_t *)hdr->data = remain_len; + memcpy(hdr->data + 2, &data[total_data_len - remain_len], BLUFI_FRAG_DATA_MAX_LEN); //copy first, easy for check sum + hdr->fc |= BLUFI_FC_FRAG; + } else { + hdr = GKI_getbuf(sizeof(struct blufi_hdr) + remain_len + 2); + if (hdr == NULL) { + LOG_ERROR("%s no mem\n", __func__); + return; + } + hdr->fc = 0x0; + hdr->data_len = remain_len; + memcpy(hdr->data, &data[total_data_len - remain_len], hdr->data_len); //copy first, easy for check sum + } + + hdr->type = type; + hdr->fc |= BLUFI_FC_DIR_E2P; + hdr->seq = blufi_env.send_seq++; + + if (BLUFI_TYPE_IS_CTRL(hdr->type)) { + if ((blufi_env.sec_mode & BLUFI_CTRL_SEC_MODE_CHECK_MASK) + && (blufi_env.cbs && blufi_env.cbs->checksum_func)) { + hdr->fc |= BLUFI_FC_CHECK; + checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2); + memcpy(&hdr->data[hdr->data_len], &checksum, 2); + } + } else if (!BLUFI_TYPE_IS_DATA_NEG(hdr->type)) { + if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_CHECK_MASK) + && (blufi_env.cbs && blufi_env.cbs->checksum_func)) { + hdr->fc |= BLUFI_FC_CHECK; + checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2); + memcpy(&hdr->data[hdr->data_len], &checksum, 2); + } + + if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_ENC_MASK) + && (blufi_env.cbs && blufi_env.cbs->encrypt_func)) { + ret = blufi_env.cbs->encrypt_func(hdr->seq, hdr->data, hdr->data_len); + if (ret == hdr->data_len) { /* enc must be success and enc len must equal to plain len */ + hdr->fc |= BLUFI_FC_ENC; + } else { + LOG_ERROR("%s encrypt error %d\n", __func__, ret); + GKI_freebuf(hdr); + return; + } + } + } + + if (hdr->fc & BLUFI_FC_FRAG) { + remain_len -= (hdr->data_len - 2); + } else { + remain_len -= hdr->data_len; + } + + btc_blufi_send_notify((uint8_t *)hdr, + ((hdr->fc & BLUFI_FC_CHECK) ? + hdr->data_len + sizeof(struct blufi_hdr) + 2 : + hdr->data_len + sizeof(struct blufi_hdr))); + + GKI_freebuf(hdr); + hdr = NULL; + } } -static void btc_blufi_config_success(void) +static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *info, int info_len) { - LOG_DEBUG("config success\n"); - blufi_msg_notify((uint8_t *)success_msg, strlen(success_msg)); + uint8_t type; + uint8_t *data; + int data_len; + uint8_t *p; + + data_len = info_len + 3; + p = data = GKI_getbuf(data_len); + if (data == NULL) { + return; + } + + type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP); + *p++ = opmode; + *p++ = sta_conn_state; + *p++ = softap_conn_num; + + if (info) { + if (info->sta_bssid_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID; + *p++ = 6; + memcpy(p, info->sta_bssid, 6); + p += 6; + } + if (info->sta_ssid) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_SSID; + *p++ = info->sta_ssid_len; + memcpy(p, info->sta_ssid, info->sta_ssid_len); + p += info->sta_ssid_len; + } + if (info->sta_passwd) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD; + *p++ = info->sta_passwd_len; + memcpy(p, info->sta_passwd, info->sta_passwd_len); + p += info->sta_passwd_len; + } + if (info->softap_ssid) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID; + *p++ = info->softap_ssid_len; + memcpy(p, info->softap_ssid, info->softap_ssid_len); + p += info->softap_ssid_len; + } + if (info->softap_passwd) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD; + *p++ = info->softap_passwd_len; + memcpy(p, info->softap_passwd, info->softap_passwd_len); + p += info->softap_passwd_len; + } + if (info->softap_authmode_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE; + *p++ = 1; + *p++ = info->softap_authmode; + } + if (info->softap_max_conn_num_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM; + *p++ = 1; + *p++ = info->softap_max_conn_num; + } + if (info->softap_channel_set) { + *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM; + *p++ = 1; + *p++ = info->softap_channel; + } + } + if (p - data > data_len) { + LOG_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len); + } + + btc_blufi_send_encap(type, data, data_len); + GKI_freebuf(data); } -static void btc_blufi_config_failed(void) +static void btc_blufi_send_ack(uint8_t seq) { - LOG_DEBUG("config faield\n"); - blufi_msg_notify((uint8_t *)failed_msg, strlen(failed_msg)); + uint8_t type; + uint8_t data; + + type = BLUFI_BUILD_TYPE(BLUFI_TYPE_CTRL, BLUFI_TYPE_CTRL_SUBTYPE_ACK); + data = seq; + + btc_blufi_send_encap(type, &data, 1); +} + +void btc_blufi_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_blufi_cb_param_t *dst = (esp_blufi_cb_param_t *) p_dest; + esp_blufi_cb_param_t *src = (esp_blufi_cb_param_t *) p_src; + + switch (msg->act) { + case ESP_BLUFI_EVENT_RECV_STA_SSID: + dst->sta_ssid.ssid = GKI_getbuf(src->sta_ssid.ssid_len); + if (dst->sta_ssid.ssid == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->sta_ssid.ssid, src->sta_ssid.ssid, src->sta_ssid.ssid_len); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + dst->sta_passwd.passwd = GKI_getbuf(src->sta_passwd.passwd_len); + if (dst->sta_passwd.passwd == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->sta_passwd.passwd, src->sta_passwd.passwd, src->sta_passwd.passwd_len); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + dst->softap_ssid.ssid = GKI_getbuf(src->softap_ssid.ssid_len); + if (dst->softap_ssid.ssid == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->softap_ssid.ssid, src->softap_ssid.ssid, src->softap_ssid.ssid_len); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + dst->softap_passwd.passwd = GKI_getbuf(src->softap_passwd.passwd_len); + if (dst->softap_passwd.passwd == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->softap_passwd.passwd, src->softap_passwd.passwd, src->softap_passwd.passwd_len); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + dst->username.name = GKI_getbuf(src->username.name_len); + if (dst->username.name == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->username.name, src->username.name, src->username.name_len); + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + dst->ca.cert = GKI_getbuf(src->ca.cert_len); + if (dst->ca.cert == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->ca.cert, src->ca.cert, src->ca.cert_len); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + dst->client_cert.cert = GKI_getbuf(src->client_cert.cert_len); + if (dst->client_cert.cert == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->client_cert.cert, src->client_cert.cert, src->client_cert.cert_len); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + dst->server_cert.cert = GKI_getbuf(src->server_cert.cert_len); + if (dst->server_cert.cert == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->server_cert.cert, src->server_cert.cert, src->server_cert.cert_len); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + dst->client_pkey.pkey = GKI_getbuf(src->client_pkey.pkey_len); + if (dst->client_pkey.pkey == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->client_pkey.pkey, src->client_pkey.pkey, src->client_pkey.pkey_len); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + dst->server_pkey.pkey = GKI_getbuf(src->server_pkey.pkey_len); + if (dst->server_pkey.pkey == NULL) { + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + memcpy(dst->server_pkey.pkey, src->server_pkey.pkey, src->server_pkey.pkey_len); + break; + default: + break; + } +} + +void btc_blufi_cb_deep_free(btc_msg_t *msg) +{ + esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg; + + switch (msg->act) { + case ESP_BLUFI_EVENT_RECV_STA_SSID: + GKI_freebuf(param->sta_ssid.ssid); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + GKI_freebuf(param->sta_passwd.passwd); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + GKI_freebuf(param->softap_ssid.ssid); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + GKI_freebuf(param->softap_passwd.passwd); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + GKI_freebuf(param->username.name); + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + GKI_freebuf(param->ca.cert); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + GKI_freebuf(param->client_cert.cert); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + GKI_freebuf(param->server_cert.cert); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + GKI_freebuf(param->client_pkey.pkey); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + GKI_freebuf(param->server_pkey.pkey); + break; + default: + break; + } } void btc_blufi_cb_handler(btc_msg_t *msg) { - esp_blufi_cb_param_t param; + esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg; switch (msg->act) { - case BTC_BLUFI_CB_ACT_INIT_FINISH: - param.init_finish.state = ESP_BLUFI_INIT_OK; - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_INIT_FINISH, ¶m); + case ESP_BLUFI_EVENT_INIT_FINISH: { + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_INIT_FINISH, param); break; - case BTC_BLUFI_CB_ACT_DEINIT_FINISH: - /* TODO: but now nothing */ + } + case ESP_BLUFI_EVENT_DEINIT_FINISH: { + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_DEINIT_FINISH, param); break; - case BTC_BLUFI_CB_ACT_RECV_DATA: - memcpy(¶m.recv_data, msg->arg, sizeof(struct blufi_recv_evt_param)); - - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_DATA, ¶m); + } + case ESP_BLUFI_EVENT_BLE_CONNECT: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_BLE_CONNECT, param); + break; + case ESP_BLUFI_EVENT_BLE_DISCONNECT: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_BLE_DISCONNECT, param); + break; + case ESP_BLUFI_EVENT_SET_WIFI_OPMODE: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_SET_WIFI_OPMODE, param); + break; + case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP, NULL); + break; + case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP, NULL); + break; + case ESP_BLUFI_EVENT_GET_WIFI_STATUS: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_GET_WIFI_STATUS, NULL); + break; + case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_DEAUTHENTICATE_STA, NULL); + break; + case ESP_BLUFI_EVENT_RECV_STA_BSSID: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_STA_BSSID, param); + break; + case ESP_BLUFI_EVENT_RECV_STA_SSID: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_STA_SSID, param); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_STA_PASSWD, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_SSID, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE, param); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL, param); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_USERNAME, param); + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_CA_CERT, param); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_CLIENT_CERT, param); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SERVER_CERT, param); + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY, param); + break; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY, param); break; default: LOG_ERROR("%s UNKNOWN %d\n", __func__, msg->act); break; } + + btc_blufi_cb_deep_free(msg); +} + +void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_blufi_args_t *dst = (btc_blufi_args_t *) p_dest; + btc_blufi_args_t *src = (btc_blufi_args_t *) p_src; + + switch (msg->act) { + case BTC_BLUFI_ACT_SEND_CFG_REPORT: { + esp_blufi_extra_info_t *src_info = src->wifi_conn_report.extra_info; + dst->wifi_conn_report.extra_info_len = 0; + dst->wifi_conn_report.extra_info = NULL; + + if (src_info == NULL) { + return; + } + + dst->wifi_conn_report.extra_info = GKI_getbuf(sizeof(esp_blufi_extra_info_t)); + if (dst->wifi_conn_report.extra_info == NULL) { + return; + } + + if (src_info->sta_bssid_set) { + memcpy(dst->wifi_conn_report.extra_info->sta_bssid, src_info->sta_bssid, 6); + dst->wifi_conn_report.extra_info->sta_bssid_set = src_info->sta_bssid_set; + dst->wifi_conn_report.extra_info_len += (6 + 2); + } + if (src_info->sta_ssid) { + dst->wifi_conn_report.extra_info->sta_ssid = GKI_getbuf(src_info->sta_ssid_len); + if (dst->wifi_conn_report.extra_info->sta_ssid) { + memcpy(dst->wifi_conn_report.extra_info->sta_ssid, src_info->sta_ssid, src_info->sta_ssid_len); + dst->wifi_conn_report.extra_info->sta_ssid_len = src_info->sta_ssid_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_ssid_len + 2); + } + } + if (src_info->sta_passwd) { + dst->wifi_conn_report.extra_info->sta_passwd = GKI_getbuf(src_info->sta_passwd_len); + if (dst->wifi_conn_report.extra_info->sta_passwd) { + memcpy(dst->wifi_conn_report.extra_info->sta_passwd, src_info->sta_passwd, src_info->sta_passwd_len); + dst->wifi_conn_report.extra_info->sta_passwd_len = src_info->sta_passwd_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_passwd_len + 2); + } + } + if (src_info->softap_ssid) { + dst->wifi_conn_report.extra_info->softap_ssid = GKI_getbuf(src_info->softap_ssid_len); + if (dst->wifi_conn_report.extra_info->softap_ssid) { + memcpy(dst->wifi_conn_report.extra_info->softap_ssid, src_info->softap_ssid, src_info->softap_ssid_len); + dst->wifi_conn_report.extra_info->softap_ssid_len = src_info->softap_ssid_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_ssid_len + 2); + } + } + if (src_info->softap_passwd) { + dst->wifi_conn_report.extra_info->softap_passwd = GKI_getbuf(src_info->softap_passwd_len); + if (dst->wifi_conn_report.extra_info->softap_passwd) { + memcpy(dst->wifi_conn_report.extra_info->softap_passwd, src_info->softap_passwd, src_info->softap_passwd_len); + dst->wifi_conn_report.extra_info->softap_passwd_len = src_info->softap_passwd_len; + dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_passwd_len + 2); + } + } + if (src_info->softap_authmode_set) { + dst->wifi_conn_report.extra_info->softap_authmode_set = src_info->softap_authmode_set; + dst->wifi_conn_report.extra_info->softap_authmode = src_info->softap_authmode; + dst->wifi_conn_report.extra_info_len += (1 + 2); + } + if (src_info->softap_max_conn_num_set) { + dst->wifi_conn_report.extra_info->softap_max_conn_num_set = src_info->softap_max_conn_num_set; + dst->wifi_conn_report.extra_info->softap_max_conn_num = src_info->softap_max_conn_num; + dst->wifi_conn_report.extra_info_len += (1 + 2); + } + if (src_info->softap_channel_set) { + dst->wifi_conn_report.extra_info->softap_channel_set = src_info->softap_channel_set; + dst->wifi_conn_report.extra_info->softap_channel = src_info->softap_channel; + dst->wifi_conn_report.extra_info_len += (1 + 2); + } + break; + } + default: + break; + } +} + +void btc_blufi_call_deep_free(btc_msg_t *msg) +{ + btc_blufi_args_t *arg = (btc_blufi_args_t *)msg->arg; + + switch (msg->act) { + case BTC_BLUFI_ACT_SEND_CFG_REPORT: { + esp_blufi_extra_info_t *info = (esp_blufi_extra_info_t *)arg->wifi_conn_report.extra_info; + + if (info == NULL) { + return; + } + if (info->sta_ssid) { + GKI_freebuf(info->sta_ssid); + } + if (info->sta_passwd) { + GKI_freebuf(info->sta_passwd); + } + if (info->softap_ssid) { + GKI_freebuf(info->softap_ssid); + } + if (info->softap_passwd) { + GKI_freebuf(info->softap_passwd); + } + GKI_freebuf(info); + break; + } + default: + break; + } } void btc_blufi_call_handler(btc_msg_t *msg) @@ -367,17 +874,24 @@ void btc_blufi_call_handler(btc_msg_t *msg) btc_blufi_profile_init(); break; case BTC_BLUFI_ACT_DEINIT: - /* TODO: but now nothing */ + btc_blufi_profile_deinit(); break; - case BTC_BLUFI_ACT_SEND_CFG_STATE: - if (arg->cfg_state.state == ESP_BLUFI_CONFIG_OK) { - btc_blufi_config_success(); - } else { - btc_blufi_config_failed(); - } + case BTC_BLUFI_ACT_SEND_CFG_REPORT: + btc_blufi_wifi_conn_report(arg->wifi_conn_report.opmode, + arg->wifi_conn_report.sta_conn_state, + arg->wifi_conn_report.softap_conn_num, + arg->wifi_conn_report.extra_info, + arg->wifi_conn_report.extra_info_len); break; default: LOG_ERROR("%s UNKNOWN %d\n", __func__, msg->act); break; } + btc_blufi_call_deep_free(msg); } + +void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks) +{ + blufi_env.cbs = callbacks; +} + diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c new file mode 100644 index 00000000000..2f92a43806d --- /dev/null +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c @@ -0,0 +1,240 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include + + +#include "bt_target.h" +#include "bt_trace.h" +#include "bt_types.h" +#include "gatt_api.h" +#include "bta_api.h" +#include "bta_gatt_api.h" +#include "bta_gatts_int.h" + +#include "btc_blufi_prf.h" +#include "btc_task.h" +#include "btc_manage.h" + +#include "blufi_int.h" + +#include "esp_wifi.h" + +extern tBLUFI_ENV blufi_env; + +void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) +{ + btc_msg_t msg; + esp_blufi_cb_param_t param; + uint8_t *output_data = NULL; + int output_len = 0; + bool need_free = false; + + switch (BLUFI_GET_TYPE(type)) { + case BLUFI_TYPE_CTRL: + switch (BLUFI_GET_SUBTYPE(type)) { + case BLUFI_TYPE_CTRL_SUBTYPE_ACK: + /* TODO: check sequence */ + break; + case BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE: + blufi_env.sec_mode = data[0]; + break; + case BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_SET_WIFI_OPMODE; + param.wifi_mode.op_mode = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_GET_WIFI_STATUS; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + case BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_DEAUTHENTICATE_STA; + + btc_transfer_context(&msg, NULL, 0, NULL); + break; + default: + LOG_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type); + break; + } + break; + case BLUFI_TYPE_DATA: + switch (BLUFI_GET_SUBTYPE(type)) { + case BLUFI_TYPE_DATA_SUBTYPE_NEG: + if (blufi_env.cbs && blufi_env.cbs->negotiate_data_handler) { + blufi_env.cbs->negotiate_data_handler(data, len, &output_data, &output_len, &need_free); + } + + if (output_data && output_len > 0) { + btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG), + output_data, output_len); + } + break; + case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_STA_BSSID; + memcpy(param.sta_bssid.bssid, &data[0], 6); + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_STA_SSID: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_STA_SSID; + param.sta_ssid.ssid = &data[0]; + param.sta_ssid.ssid_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_STA_PASSWD; + param.sta_passwd.passwd = &data[0]; + param.sta_passwd.passwd_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_SSID; + param.softap_ssid.ssid = &data[0]; + param.softap_ssid.ssid_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD; + param.softap_passwd.passwd = &data[0]; + param.softap_passwd.passwd_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM; + param.softap_max_conn_num.max_conn_num = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE; + param.softap_auth_mode.auth_mode = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL; + param.softap_channel.channel = data[0]; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL); + break; + case BLUFI_TYPE_DATA_SUBTYPE_USERNAME: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_USERNAME; + param.username.name = &data[0]; + param.username.name_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_CA: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_CA_CERT; + param.ca.cert = &data[0]; + param.ca.cert_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_CERT; + param.client_cert.cert = &data[0]; + param.client_cert.cert_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SERVER_CERT; + param.client_cert.cert = &data[0]; + param.client_cert.cert_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY; + param.client_pkey.pkey = &data[0]; + param.client_pkey.pkey_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + case BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_BLUFI; + msg.act = ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY; + param.client_pkey.pkey = &data[0]; + param.client_pkey.pkey_len = len; + + btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy); + break; + default: + LOG_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type); + break; + } + break; + default: + break; + } +} diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_adv.h b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_adv.h deleted file mode 100644 index 39869ec9e43..00000000000 --- a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_adv.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef __BLUFI_ADV_H__ -#define __BLUFI_ADV_H__ - -#include "bta_api.h" -#include "btm_ble_api.h" -#include "esp_bt_defs.h" - -typedef enum { - BLE_ADV_DATA_IDX = 0, - BLE_SCAN_RSP_DATA_IDX = 1, - ADV_SCAN_IDX_MAX, -} ADV_SCAN_IDX_t; - -typedef struct { - char *adv_name; //set the device name to be sent on the advertising - tBTA_BLE_ADV_DATA ble_adv_data; -} tBLUFI_BLE_ADV_DATA; - -extern void BlufiBleConfigadvData(tBLUFI_BLE_ADV_DATA *adv_data, - tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback); - -extern void BlufiBleSetScanRsp(tBLUFI_BLE_ADV_DATA *scan_rsp_data, - tBTA_SET_ADV_DATA_CMPL_CBACK *p_scan_rsp_data_cback); - -#endif /* __BLUFI_ADV_H__ */ diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h index f46a41a2e48..0de66b8e77a 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h +++ b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h @@ -15,38 +15,156 @@ #ifndef __BLUFI_INT_H__ #define __BLUFI_INT_H__ -//define the blufi serivce uuid -#define SVC_BLUFI_UUID 0xFFFF -//define the blufi Char uuid -#define CHAR_BLUFI_UUID 0xFF01 - -#define BLUFI_HDL_NUM 4 - -#define BLUFI_VAL_MAX_LEN (128) - -#define BLUFI_MAX_STRING_DATA 128 - - -typedef struct { - UINT8 app_id; - UINT16 blufi_hdl; -} tBLUFI_INST; - - /* service engine control block */ typedef struct { - BOOLEAN enabled; - BOOLEAN is_primery; - UINT8 inst_id; + /* Protocol reference */ tGATT_IF gatt_if; - tBLUFI_INST blufi_inst; - BOOLEAN in_use; - BOOLEAN congest; + UINT8 srvc_inst; + UINT16 handle_srvc; + UINT16 handle_char_p2e; + UINT16 handle_char_e2p; + UINT16 handle_descr_e2p; UINT16 conn_id; - BOOLEAN connected; + BOOLEAN is_connected; BD_ADDR remote_bda; UINT32 trans_id; - UINT8 cur_srvc_id; -} tBLUFI_CB_ENV; + UINT8 congest; +#define BLUFI_PREPAIR_BUF_MAX_SIZE 1024 + uint8_t *prepare_buf; + int prepare_len; + /* Control reference */ + esp_blufi_callbacks_t *cbs; + BOOLEAN enabled; + uint8_t send_seq; + uint8_t recv_seq; + uint8_t sec_mode; + uint8_t *aggr_buf; + uint16_t total_len; + uint16_t offset; +} tBLUFI_ENV; + +/* BLUFI protocol */ +struct blufi_hdr{ + uint8_t type; + uint8_t fc; + uint8_t seq; + uint8_t data_len; + uint8_t data[0]; +}; +typedef struct blufi_hdr blufi_hd_t; + +struct blufi_frag_hdr { + uint8_t type; + uint8_t fc; + uint8_t seq; + uint8_t data_len; + uint16_t total_len; + uint8_t content[0]; +}; +typedef struct blufi_frag_hdr blufi_frag_hdr_t; + +#define BLUFI_DATA_SEC_MODE_CHECK_MASK 0x01 +#define BLUFI_DATA_SEC_MODE_ENC_MASK 0x02 +#define BLUFI_CTRL_SEC_MODE_CHECK_MASK 0x10 +#define BLUFI_CTRL_SEC_MODE_ENC_MASK 0x20 + +// packet type +#define BLUFI_TYPE_MASK 0x03 +#define BLUFI_TYPE_SHIFT 0 +#define BLUFI_SUBTYPE_MASK 0xFC +#define BLUFI_SUBTYPE_SHIFT 2 + +#define BLUFI_GET_TYPE(type) ((type) & BLUFI_TYPE_MASK) +#define BLUFI_GET_SUBTYPE(type) (((type) & BLUFI_SUBTYPE_MASK) >>BLUFI_SUBTYPE_SHIFT) +#define BLUFI_BUILD_TYPE(type, subtype) (((type) & BLUFI_TYPE_MASK) | ((subtype)<p_services->p_uuid) { - LOG_ERROR("%s - In 16-UUID_data", __FUNCTION__); + LOG_DEBUG("%s - In 16-UUID_data", __FUNCTION__); mask |= BTM_BLE_AD_BIT_SERVICE; ++bta_adv_data->p_services->num_service; *p_uuid_out16++ = bt_uuid.uu.uuid16; @@ -221,7 +221,7 @@ static void btc_to_bta_adv_data(esp_ble_adv_data_t *p_adv_data, tBTA_BLE_ADV_DAT } if (NULL != bta_adv_data->p_service_32b->p_uuid) { - LOG_ERROR("%s - In 32-UUID_data", __FUNCTION__); + LOG_DEBUG("%s - In 32-UUID_data", __FUNCTION__); mask |= BTM_BLE_AD_BIT_SERVICE_32; ++bta_adv_data->p_service_32b->num_service; *p_uuid_out32++ = bt_uuid.uu.uuid32; diff --git a/components/bt/bluedroid/hci/packet_fragmenter.c b/components/bt/bluedroid/hci/packet_fragmenter.c index 42663a249a4..bd46041c3b7 100644 --- a/components/bt/bluedroid/hci/packet_fragmenter.c +++ b/components/bt/bluedroid/hci/packet_fragmenter.c @@ -147,16 +147,16 @@ static void reassemble_and_dispatch(BT_HDR *packet) if (boundary_flag == START_PACKET_BOUNDARY) { if (partial_packet) { - LOG_ERROR("%s found unfinished packet for handle with start packet. Dropping old.\n", __func__); - LOG_ERROR("partial_packet->len = %x, offset = %x\n", partial_packet->len, partial_packet->len); + LOG_DEBUG("%s found unfinished packet for handle with start packet. Dropping old.\n", __func__); + LOG_DEBUG("partial_packet->len = %x, offset = %x\n", partial_packet->len, partial_packet->len); - for (int i = 0; i < partial_packet->len; i++) { - LOG_ERROR("%x", partial_packet->data[i]); - } - LOG_ERROR("\n"); + //for (int i = 0; i < partial_packet->len; i++) { + // LOG_ERROR("%x", partial_packet->data[i]); + //} + //LOG_ERROR("\n"); hash_map_erase(partial_packets, (void *)(uintptr_t)handle); //buffer_allocator->free(partial_packet); - LOG_ERROR("+++++++++++++++++++\n"); + //LOG_ERROR("+++++++++++++++++++\n"); } uint16_t full_length = l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE; @@ -214,7 +214,7 @@ static void reassemble_and_dispatch(BT_HDR *packet) STREAM_TO_UINT16(handle, stream); STREAM_TO_UINT16(acl_length, stream); STREAM_TO_UINT16(l2cap_length, stream); - LOG_ERROR("partial_packet->offset = %x\n", partial_packet->offset); + LOG_DEBUG("partial_packet->offset = %x\n", partial_packet->offset); hash_map_erase(partial_packets, (void *)(uintptr_t)handle); partial_packet->offset = 0; diff --git a/components/bt/bluedroid/include/bt_trace.h b/components/bt/bluedroid/include/bt_trace.h index fd92f6e8c52..787c4c8aec4 100644 --- a/components/bt/bluedroid/include/bt_trace.h +++ b/components/bt/bluedroid/include/bt_trace.h @@ -32,7 +32,7 @@ #define assert(x) do { if (!(x)) BT_PRINTF("bt host error %s %u\n", __FILE__, __LINE__); } while (0) #endif -inline void trc_dump_buffer(uint8_t *prefix, uint8_t *data, uint16_t len) +inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t len) { uint16_t i; @@ -41,14 +41,13 @@ inline void trc_dump_buffer(uint8_t *prefix, uint8_t *data, uint16_t len) } if (prefix) { - BT_PRINTF("%s:\t", prefix); + BT_PRINTF("%s: len %d\n", prefix, len); } - for (i = 0; i < len; i++) { - BT_PRINTF(" %02x", *(data + i)); - if (!((i + 1) & 0xf)) { - BT_PRINTF("\n"); - } + for (i = 0; i < len; i+=16) { + BT_PRINTF("%02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x\n", + *(data + i), *(data + i + 1), *(data + i + 2), *(data + i + 3), *(data + i + 4), *(data + i + 5), *(data + i + 6), *(data + i + 7), + *(data + i + 8), *(data + i + 9), *(data + i + 10), *(data + i + 11), *(data + i + 12), *(data + i + 13), *(data + i + 14), *(data + i + 15)); } BT_PRINTF("\n"); } diff --git a/docs/api/bt_le.rst b/docs/api/bt_le.rst index 6ae48693726..11d09809fe5 100644 --- a/docs/api/bt_le.rst +++ b/docs/api/bt_le.rst @@ -8,3 +8,4 @@ BT COMMON BLE GATT DEFINE BLE GATT SERVER BLE GATT CLIENT + BLE BLUFI diff --git a/docs/api/esp_blufi.rst b/docs/api/esp_blufi.rst new file mode 100644 index 00000000000..1ffb67bec98 --- /dev/null +++ b/docs/api/esp_blufi.rst @@ -0,0 +1,128 @@ +BLUFI API +========= + +Overview +-------- +BLUFI is a profile based GATT to config ESP32 WIFI to connect/disconnect AP or setup a softap and etc. +Use should concern these things: + 1. The event sent from profile. Then you need to do something as the event indicate. + 2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. + Even you can define the "Key Exchange/Negotiation" procedure. + +Application Example +------------------- + +Check `/examples `_ folder of `espressif/esp-idf `_ repository, that contains the following example: + +`12_blufi `_ + +This is a BLUFI demo. This demo can set ESP32's wifi to softap/station/softap&station mode and config wifi connections. + + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `bt/bluedroid/api/include/esp_blufi_api.h `_ + +Macros +^^^^^^ + + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: esp_blufi_event_cb_t +.. doxygentypedef:: esp_blufi_negotiate_data_handler_t +.. doxygentypedef:: esp_blufi_encrypt_func_t +.. doxygentypedef:: esp_blufi_decrypt_func_t +.. doxygentypedef:: esp_blufi_checksum_func_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: esp_blufi_cb_event_t +.. doxygenenum:: esp_blufi_sta_conn_state_t +.. doxygenenum:: esp_blufi_init_state_t +.. doxygenenum:: esp_blufi_deinit_state_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: esp_blufi_extra_info_t + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_init_finish_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_deinit_finish_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_set_wifi_mode_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_connect_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_disconnect_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_sta_bssid_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_sta_ssid_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_sta_passwd_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_ssid_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_passwd_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_max_conn_num_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_auth_mode_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_softap_channel_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_username_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_ca_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_client_cert_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_server_cert_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_client_pkey_evt_param + :members: + +.. doxygenstruct:: esp_blufi_cb_param_t::blufi_recv_server_pkey_evt_param + :members: + +.. doxygenstruct:: esp_blufi_callbacks_t + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_blufi_register_callbacks +.. doxygenfunction:: esp_blufi_profile_init +.. doxygenfunction:: esp_blufi_profile_deinit +.. doxygenfunction:: esp_blufi_send_wifi_conn_report + diff --git a/examples/12_blufi/components/blufi/blufi.c b/examples/12_blufi/components/blufi/blufi.c deleted file mode 100644 index 8e2b5d1f394..00000000000 --- a/examples/12_blufi/components/blufi/blufi.c +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/*************************************************************** -* -* This file is for gatt server device. It instantiates BATTERY -* sevice. It can be scanned and connected by central device, -* and the client will get the BAS value. It calls the API bta -* layer provides. -* -****************************************************************/ - -#include -#include -#include -#include - -#include "controller.h" - -#include "bt_trace.h" -#include "bt_types.h" -#include "bta_api.h" - -#include "blufi.h" - -#include "esp_bt_defs.h" -#include "esp_bt_main.h" -#include "esp_blufi_api.h" - -extern void wifi_set_blue_config(char *ssid, char *passwd); - -#define HEADER_SSID "ssid" -#define HEADER_PASSWD "passwd" -#define HEADER_CONFIRM "confirm" - -static char tmp_ssid[32 + 1]; -static char tmp_passwd[64 + 1]; - -static void blufi_data_recv(uint8_t *data, int len) -{ - char *p = NULL; - LOG_DEBUG("the data is:%s\n", data); - - p = strstr((char *)data, HEADER_SSID); - if (p) { - LOG_ERROR("SSID: %s\n", p + strlen(HEADER_SSID) + 1); - strcpy(tmp_ssid, p + strlen(HEADER_SSID) + 1); - } - p = strstr((char *)data, HEADER_PASSWD); - if (p) { - LOG_ERROR("PASSWORD: %s\n", p + strlen(HEADER_PASSWD) + 1); - strcpy(tmp_passwd, p + strlen(HEADER_PASSWD) + 1); - } - p = strstr((char *)data, HEADER_CONFIRM); - if (p) { - LOG_ERROR("CONFIRM\n"); - wifi_set_blue_config(tmp_ssid, tmp_passwd); - } - -} - -static void blufi_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) -{ - /* actually, should post to blufi_task handle the procedure, - * now, as a demo, we do simplely */ - switch (event) { - case ESP_BLUFI_EVENT_INIT_FINISH: - LOG_ERROR("blufi init finish\n"); - break; - case ESP_BLUFI_EVENT_RECV_DATA: { - LOG_DEBUG("blufi recv data\n"); - esp_blufi_cb_param_t *blufi_param = (esp_blufi_cb_param_t *)param; - blufi_data_recv(blufi_param->recv_data.data, blufi_param->recv_data.data_len); - break; - } - default: - break; - } -} - -static esp_err_t blufi_startup_in_blufi_task(void *arg) -{ - esp_blufi_register_callback(blufi_callback); - esp_blufi_profile_init(); - - return ESP_OK; -} - - -static void blufi_startup(void) -{ - blufi_transfer_context(blufi_startup_in_blufi_task, NULL); -} - -esp_err_t blufi_enable(void *arg) -{ - esp_err_t err; - - err = esp_bluedroid_enable(); - if (err) { - LOG_ERROR("%s failed\n", __func__); - return err; - } - blufi_startup(); - vTaskDelay(1000 / portTICK_PERIOD_MS); - - return err; -} - -esp_err_t blufi_disable(void *arg) -{ - esp_err_t err; - - err = esp_bluedroid_disable(); - - if (arg) { - ((void (*)(void))arg)(); - } - - return err; -} diff --git a/examples/12_blufi/components/blufi/blufi_task.c b/examples/12_blufi/components/blufi/blufi_task.c deleted file mode 100644 index 75547c64328..00000000000 --- a/examples/12_blufi/components/blufi/blufi_task.c +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include "gki.h" -#include "bt_defs.h" -#include "bt_trace.h" -#include "bt_types.h" -#include "allocator.h" - -#include "bta_api.h" -#include "bta_gatt_api.h" - -#include "controller.h" - -#include "hash_map.h" -#include "hash_functions.h" -#include "alarm.h" -#include "thread.h" - -#include "blufi.h" -#include "blufi_adv.h" - -xQueueHandle xBlufiTaskQueue; -xTaskHandle xBlufiTaskHandle; - -extern void ble_server_test(void); - -static void blufi_task(void *arg) -{ - BtTaskEvt_t e; - - for (;;) { - if (pdTRUE == xQueueReceive(xBlufiTaskQueue, &e, (portTickType)portMAX_DELAY)) { - switch (e.sig) { - case BLUFI_SIG_SWITCH_CONTEXT: - if (e.cb) { - ((BtTaskCb_t)e.cb)(e.arg); - } - break; - default: - break; - } - } - } -} - -static esp_err_t blufi_task_post(uint32_t sig, void *par, void *cb, void *arg) -{ - BtTaskEvt_t evt; - - evt.sig = sig; - evt.par = par; - evt.cb = cb; - evt.arg = arg; - - if (xQueueSend(xBlufiTaskQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { - LOG_ERROR("Blufi Post failed\n"); - return ESP_FAIL; - } - - return ESP_OK; -} - -esp_err_t blufi_transfer_context(blufi_task_cb_t cb, void *arg) -{ - LOG_DEBUG("%s cb %08x, arg %u\n", __func__, (uint32_t)cb, (uint32_t)arg); - - return blufi_task_post(BLUFI_SIG_SWITCH_CONTEXT, 0, cb, arg); -} - -static void blufi_task_deinit(void) -{ - vTaskDelete(xBlufiTaskHandle); - vQueueDelete(xBlufiTaskQueue); -} - - -static void blufi_task_init(void) -{ - xBlufiTaskQueue = xQueueCreate(5, sizeof(BtTaskEvt_t)); - xTaskCreate(blufi_task, "BlUFI", 4096, NULL, configMAX_PRIORITIES - 3, xBlufiTaskHandle); -} - -void blufi_init(void) -{ - blufi_task_init(); - blufi_transfer_context(blufi_enable, NULL); -} - -void blufi_deinit(void) -{ - blufi_transfer_context(blufi_disable, blufi_task_deinit); -} - diff --git a/examples/12_blufi/components/blufi/component.mk b/examples/12_blufi/components/blufi/component.mk deleted file mode 100644 index 297e63f919c..00000000000 --- a/examples/12_blufi/components/blufi/component.mk +++ /dev/null @@ -1,13 +0,0 @@ -# -# Main Makefile. This is basically the same as a component makefile. -# -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -COMPONENT_SRCDIRS := . - -CFLAGS += -Wno-error=unused-label -Wno-error=return-type -Wno-error=missing-braces -Wno-error=pointer-sign -Wno-error=parentheses -I./include - diff --git a/examples/12_blufi/components/blufi/include/blufi.h b/examples/12_blufi/components/blufi/include/blufi.h deleted file mode 100644 index c79695edcd8..00000000000 --- a/examples/12_blufi/components/blufi/include/blufi.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef __BT_APP_COMMON_H__ -#define __BT_APP_COMMON_H__ - -#include -#include "osi.h" -#include "bt_common_types.h" -#include "esp_err.h" - -enum BLUFI_SIG { - BLUFI_SIG_SWITCH_CONTEXT = 0, - BLUFI_SIG_ENABLE, - BLUFI_SIG_DISABLE, -}; - -typedef esp_err_t (*blufi_task_cb_t)(void *arg); - -void blufi_init(void); -void blufi_deinit(void); - -esp_err_t blufi_enable(void *arg); -esp_err_t blufi_disable(void *arg); - -esp_err_t blufi_transfer_context(blufi_task_cb_t cb, void *arg); - -#endif /* __BT_APP_COMMON_H__ */ diff --git a/examples/12_blufi/main/blufi_demo.h b/examples/12_blufi/main/blufi_demo.h new file mode 100644 index 00000000000..c5bf55c7d5f --- /dev/null +++ b/examples/12_blufi/main/blufi_demo.h @@ -0,0 +1,17 @@ +#ifndef __BLUFI_DEMO_H__ +#define __BLUFI_DEMO_H__ + + +#define BLUFI_DEMO_TAG "BLUFI_DEMO" +#define BLUFI_INFO(fmt, ...) ESP_LOGI(BLUFI_DEMO_TAG, fmt, ##__VA_ARGS__) +#define BLUFI_ERROR(fmt, ...) ESP_LOGE(BLUFI_DEMO_TAG, fmt, ##__VA_ARGS__) + +void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free); +int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len); +int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len); +uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len); + +int blufi_security_init(void); +void blufi_security_deinit(void); + +#endif /* __BLUFI_DEMO_H__ */ diff --git a/examples/12_blufi/main/blufi_main.c b/examples/12_blufi/main/blufi_main.c new file mode 100644 index 00000000000..3f5cd3dcc6f --- /dev/null +++ b/examples/12_blufi/main/blufi_main.c @@ -0,0 +1,336 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "bt.h" + +#include "esp_blufi_api.h" +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" +#include "esp_bt_main.h" +#include "blufi_demo.h" + +static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param); + +#define BLUFI_DEVICE_NAME "BLUFI_DEVICE" +static uint8_t blufi_service_uuid128[32] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, +}; + +//static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x12, 0x23, 0x45, 0x56}; +static esp_ble_adv_data_t blufi_adv_data = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x100, + .max_interval = 0x100, + .appearance = 0x00, + .manufacturer_len = 0, + .p_manufacturer_data = NULL, + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 16, + .p_service_uuid = blufi_service_uuid128, + .flag = 0x6, +}; + +static esp_ble_adv_params_t blufi_adv_params = { + .adv_int_min = 0x100, + .adv_int_max = 0x100, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + //.peer_addr = + //.peer_addr_type = + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +#define WIFI_LIST_NUM 10 + +static wifi_config_t sta_config; +static wifi_config_t ap_config; + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +/* store the station info for send back to phone */ +static bool gl_sta_connected = false; +static uint8_t gl_sta_bssid[6]; +static uint8_t gl_sta_ssid[32]; +static int gl_sta_ssid_len; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + wifi_mode_t mode; + + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: { + esp_blufi_extra_info_t info; + + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + esp_wifi_get_mode(&mode); + + memset(&info, 0, sizeof(esp_blufi_extra_info_t)); + memcpy(info.sta_bssid, gl_sta_bssid, 6); + info.sta_bssid_set = true; + info.sta_ssid = gl_sta_ssid; + info.sta_ssid_len = gl_sta_ssid_len; + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info); + break; + } + case SYSTEM_EVENT_STA_CONNECTED: + gl_sta_connected = true; + memcpy(gl_sta_bssid, event->event_info.connected.bssid, 6); + memcpy(gl_sta_ssid, event->event_info.connected.ssid, event->event_info.connected.ssid_len); + gl_sta_ssid_len = event->event_info.connected.ssid_len; + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + gl_sta_connected = false; + memset(gl_sta_ssid, 0, 32); + memset(gl_sta_bssid, 0, 6); + gl_sta_ssid_len = 0; + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_AP_START: + esp_wifi_get_mode(&mode); + + /* TODO: get config or information of softap, then set to report extra_info */ + if (gl_sta_connected) { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL); + } else { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL); + } + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static esp_blufi_callbacks_t blufi_callbacks = { + .event_cb = blufi_event_callback, + .negotiate_data_handler = blufi_dh_negotiate_data_handler, + .encrypt_func = blufi_aes_encrypt, + .decrypt_func = blufi_aes_decrypt, + .checksum_func = blufi_crc_checksum, +}; + +static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) +{ + /* actually, should post to blufi_task handle the procedure, + * now, as a demo, we do simplely */ + switch (event) { + case ESP_BLUFI_EVENT_INIT_FINISH: + BLUFI_INFO("BLUFI init finish\n"); + + esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME); + esp_ble_gap_config_adv_data(&blufi_adv_data); + break; + case ESP_BLUFI_EVENT_DEINIT_FINISH: + BLUFI_INFO("BLUFI init finish\n"); + break; + case ESP_BLUFI_EVENT_BLE_CONNECT: + BLUFI_INFO("BLUFI ble connect\n"); + esp_ble_gap_stop_advertising(); + blufi_security_deinit(); + blufi_security_init(); + break; + case ESP_BLUFI_EVENT_BLE_DISCONNECT: + BLUFI_INFO("BLUFI ble disconnect\n"); + esp_ble_gap_start_advertising(&blufi_adv_params); + break; + case ESP_BLUFI_EVENT_SET_WIFI_OPMODE: + BLUFI_INFO("BLUFI Set WIFI opmode %d\n", param->wifi_mode.op_mode); + ESP_ERROR_CHECK( esp_wifi_set_mode(param->wifi_mode.op_mode) ); + break; + case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP: + BLUFI_INFO("BLUFI requset wifi connect to AP\n"); + esp_wifi_connect(); + break; + case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: + BLUFI_INFO("BLUFI requset wifi disconnect from AP\n"); + esp_wifi_disconnect(); + break; + case ESP_BLUFI_EVENT_GET_WIFI_STATUS: { + wifi_mode_t mode; + esp_blufi_extra_info_t info; + + esp_wifi_get_mode(&mode); + + if (gl_sta_connected ) { + memset(&info, 0, sizeof(esp_blufi_extra_info_t)); + memcpy(info.sta_bssid, gl_sta_bssid, 6); + info.sta_bssid_set = true; + info.sta_ssid = gl_sta_ssid; + info.sta_ssid_len = gl_sta_ssid_len; + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info); + } else { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL); + } + BLUFI_INFO("BLUFI get wifi status from AP\n"); + + break; + } + case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA: + /* TODO */ + break; + case ESP_BLUFI_EVENT_RECV_STA_BSSID: + memcpy(sta_config.sta.bssid, param->sta_bssid.bssid, 6); + sta_config.sta.bssid_set = 1; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA BSSID %s\n", sta_config.sta.ssid); + break; + case ESP_BLUFI_EVENT_RECV_STA_SSID: + strncpy((char *)sta_config.sta.ssid, (char *)param->sta_ssid.ssid, param->sta_ssid.ssid_len); + sta_config.sta.ssid[param->sta_ssid.ssid_len] = '\0'; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA SSID %s\n", sta_config.sta.ssid); + break; + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + strncpy((char *)sta_config.sta.password, (char *)param->sta_passwd.passwd, param->sta_passwd.passwd_len); + sta_config.sta.password[param->sta_passwd.passwd_len] = '\0'; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA PASSWORD %s\n", sta_config.sta.password); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + strncpy((char *)ap_config.ap.ssid, (char *)param->softap_ssid.ssid, param->softap_ssid.ssid_len); + ap_config.ap.ssid_len = param->softap_ssid.ssid_len; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP SSID %s, ssid len %d\n", ap_config.ap.ssid, ap_config.ap.ssid_len); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + strncpy((char *)ap_config.ap.password, (char *)param->softap_passwd.passwd, param->softap_passwd.passwd_len); + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP PASSWORD %s\n", ap_config.ap.password); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM: + if (param->softap_max_conn_num.max_conn_num > 4) { + return; + } + ap_config.ap.max_connection = param->softap_max_conn_num.max_conn_num; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP MAX CONN NUM %d\n", ap_config.ap.max_connection); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE: + if (param->softap_auth_mode.auth_mode >= WIFI_AUTH_MAX) { + return; + } + ap_config.ap.authmode = param->softap_auth_mode.auth_mode; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP AUTH MODE %d\n", ap_config.ap.authmode); + break; + case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL: + if (param->softap_channel.channel > 13) { + return; + } + ap_config.ap.channel = param->softap_channel.channel; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP CHANNEL %d\n", ap_config.ap.channel); + break; + case ESP_BLUFI_EVENT_RECV_USERNAME: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + /* Not handle currently */ + break;; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + /* Not handle currently */ + break; + default: + break; + } +} + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + switch (event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + esp_ble_gap_start_advertising(&blufi_adv_params); + break; + default: + break; + } +} + +void app_main() +{ + esp_err_t ret; + + nvs_flash_init(); + initialise_wifi(); + + esp_bt_controller_init(); + + ret = esp_bluedroid_init(); + if (ret) { + BLUFI_ERROR("%s init bluedroid failed\n", __func__); + return; + } + + ret = esp_bluedroid_enable(); + if (ret) { + BLUFI_ERROR("%s init bluedroid failed\n", __func__); + return; + } + + blufi_security_init(); + esp_ble_gap_register_callback(gap_event_handler); + + esp_blufi_register_callbacks(&blufi_callbacks); + esp_blufi_profile_init(); +} diff --git a/examples/12_blufi/main/blufi_security.c b/examples/12_blufi/main/blufi_security.c new file mode 100644 index 00000000000..94755eaaee6 --- /dev/null +++ b/examples/12_blufi/main/blufi_security.c @@ -0,0 +1,205 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "bt.h" + +#include "esp_blufi_api.h" +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" +#include "esp_bt_main.h" +#include "blufi_demo.h" + +#include "mbedtls/aes.h" +#include "mbedtls/dhm.h" +#include "mbedtls/md5.h" +#include "rom/crc.h" + +/* + The SEC_TYPE_xxx is for self-defined packet data type in the procedure of "BLUFI negotiate key" + If user use other negotiation procedure to exchange(or generate) key, should redefine the type by yourself. + */ +#define SEC_TYPE_DH_PARAM_LEN 0x00 +#define SEC_TYPE_DH_PARAM_DATA 0x01 +#define SEC_TYPE_DH_P 0x02 +#define SEC_TYPE_DH_G 0x03 +#define SEC_TYPE_DH_PUBLIC 0x04 + + +struct blufi_security { +#define DH_SELF_PUB_KEY_LEN 128 +#define DH_SELF_PUB_KEY_BIT_LEN (DH_SELF_PUB_KEY_LEN * 8) + uint8_t self_public_key[DH_SELF_PUB_KEY_LEN]; +#define SHARE_KEY_LEN 128 +#define SHARE_KEY_BIT_LEN (SHARE_KEY_LEN * 8) + uint8_t share_key[SHARE_KEY_LEN]; + size_t share_len; +#define PSK_LEN 16 + uint8_t psk[PSK_LEN]; + uint8_t *dh_param; + int dh_param_len; + uint8_t iv[16]; + mbedtls_dhm_context dhm; + mbedtls_aes_context aes; +}; +static struct blufi_security *blufi_sec; + +static int myrand( void *rng_state, unsigned char *output, size_t len ) +{ + size_t i; + + for( i = 0; i < len; ++i ) + output[i] = esp_random(); + + return( 0 ); +} + +void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free) +{ + int ret; + uint8_t type = data[0]; + + if (blufi_sec == NULL) { + BLUFI_ERROR("BLUFI Security is not initialized"); + return; + } + + switch (type) { + case SEC_TYPE_DH_PARAM_LEN: + blufi_sec->dh_param_len = ((data[1]<<8)|data[2]); + if (blufi_sec->dh_param) { + free(blufi_sec->dh_param); + } + blufi_sec->dh_param = (uint8_t *)malloc(blufi_sec->dh_param_len); + if (blufi_sec->dh_param == NULL) { + return; + } + break; + case SEC_TYPE_DH_PARAM_DATA: + + 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]); + if (ret) { + BLUFI_ERROR("%s read param failed %d\n", __func__, ret); + return; + } + + 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); + return; + } + + mbedtls_dhm_calc_secret( &blufi_sec->dhm, + blufi_sec->share_key, + SHARE_KEY_BIT_LEN, + &blufi_sec->share_len, + NULL, NULL); + + mbedtls_md5(blufi_sec->share_key, blufi_sec->share_len, blufi_sec->psk); + + mbedtls_aes_setkey_enc(&blufi_sec->aes, blufi_sec->psk, 128); + mbedtls_aes_setkey_dec(&blufi_sec->aes, blufi_sec->psk, 128); + + /* alloc output data */ + *output_data = &blufi_sec->self_public_key[0]; + *output_len = blufi_sec->dhm.len; + *need_free = false; + break; + case SEC_TYPE_DH_P: + break; + case SEC_TYPE_DH_G: + break; + case SEC_TYPE_DH_PUBLIC: + break; + } +} + +int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len) +{ + int ret; + size_t iv_offset = 0; + uint8_t iv0[16]; + + memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv)); + iv0[0] = iv8; /* set iv8 as the iv0[0] */ + + ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_ENCRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data); + if (ret) { + return -1; + } + + return crypt_len; +} + +int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len) +{ + int ret; + size_t iv_offset = 0; + uint8_t iv0[16]; + + memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv)); + iv0[0] = iv8; /* set iv8 as the iv0[0] */ + + ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_DECRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data); + if (ret) { + return -1; + } + + return crypt_len; +} + +uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len) +{ + /* This iv8 ignore, not used */ + return crc16_be(0, data, len); +} + +esp_err_t blufi_security_init(void) +{ + blufi_sec = (struct blufi_security *)malloc(sizeof(struct blufi_security)); + if (blufi_sec == NULL) { + return ESP_FAIL; + } + + memset(&blufi_sec, 0x0, sizeof(struct blufi_security)); + + mbedtls_dhm_init(&blufi_sec->dhm); + mbedtls_aes_init(&blufi_sec->aes); + + memset(blufi_sec->iv, 0x0, 16); + return 0; +} + +void blufi_security_deinit(void) +{ + mbedtls_dhm_free(&blufi_sec->dhm); + mbedtls_aes_free(&blufi_sec->aes); + + memset(&blufi_sec, 0x0, sizeof(struct blufi_security)); + + free(blufi_sec); + blufi_sec = NULL; +} diff --git a/examples/12_blufi/main/demo_main.c b/examples/12_blufi/main/demo_main.c deleted file mode 100644 index abb47aedb03..00000000000 --- a/examples/12_blufi/main/demo_main.c +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" -#include "nvs_flash.h" -#include "bt.h" -#include "bta_api.h" - -#include "esp_blufi_api.h" -#include "esp_bt_defs.h" -#include "esp_bt_main.h" -#include "blufi.h" - -#define WIFI_LIST_NUM 10 - -/* FreeRTOS event group to signal when we are connected & ready to make a request */ -static EventGroupHandle_t wifi_event_group; - -/* The event group allows multiple bits for each event, - but we only care about one event - are we connected - to the AP with an IP? */ -const int CONNECTED_BIT = BIT0; - - - -static wifi_config_t sta_config; - -static char tmp_ssid[33]; -static char tmp_passwd[65]; -static bool confirm = false; - -void wifi_set_blue_config(char *ssid, char *passwd) -{ - memset(tmp_ssid, 0, sizeof(tmp_ssid)); - memset(tmp_passwd, 0, sizeof(tmp_passwd)); - strlcpy(tmp_ssid, ssid, sizeof(tmp_ssid)); - strlcpy(tmp_passwd, passwd, sizeof(tmp_passwd)); - confirm = true; - LOG_DEBUG("confirm true\n"); -} - -static esp_err_t event_handler(void *ctx, system_event_t *event) -{ - switch (event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - esp_blufi_send_config_state(ESP_BLUFI_CONFIG_OK); - esp_bluedroid_disable(); //close bluetooth function - //esp_bluedroid_deinit(); //free bluetooth resource - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); - break; - default: - break; - } - return ESP_OK; -} - -static void initialise_wifi(void) -{ - tcpip_adapter_init(); - wifi_event_group = xEventGroupCreate(); - ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - - -void wifiTestTask(void *pvParameters) -{ - esp_err_t ret; - - while (1) { - vTaskDelay(1000 / portTICK_PERIOD_MS); - if (confirm) { - confirm = false; - - memcpy(sta_config.sta.ssid, tmp_ssid, sizeof(sta_config.sta.ssid)); - memcpy(sta_config.sta.password, tmp_passwd, sizeof(sta_config.sta.password)); - sta_config.sta.bssid_set = 0; - - ret = esp_wifi_disconnect(); - LOG_INFO("esp_wifi config\n"); - esp_wifi_set_config(WIFI_IF_STA, &sta_config); - LOG_INFO("esp_wifi connect\n"); - ret = esp_wifi_connect(); - if (ret != ESP_OK) { - LOG_ERROR("esp_wifi connect failed\n"); - esp_blufi_send_config_state(ESP_BLUFI_CONFIG_FAILED); - } - } - } -} - -void app_main() -{ - esp_err_t ret; - - nvs_flash_init(); - initialise_wifi(); - - //vTaskDelay(3000 / portTICK_PERIOD_MS); - - esp_bt_controller_init(); - xTaskCreatePinnedToCore(&wifiTestTask, "wifiTestTask", 2048, NULL, 20, NULL, 0); - - LOG_ERROR("%s init bluetooth\n", __func__); - ret = esp_bluedroid_init(); - if (ret) { - LOG_ERROR("%s init bluetooth failed\n", __func__); - return; - } - blufi_init(); -} From 9017c408ac5ddfb87acd4f4015def51b6bc07a81 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 5 Jan 2017 20:41:15 +0800 Subject: [PATCH 067/167] component/bt : use new lib, optimize BT power --- components/bt/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bt/lib b/components/bt/lib index 91657e0c402..566acfd8c61 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 91657e0c4025f5a694b0a89f449c347b0f2fdf79 +Subproject commit 566acfd8c61a4ba0fb6b9026c89488b01af0fff0 From 7853893731021f6d8f841bf287a1d7ae2175879c Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Thu, 5 Jan 2017 17:57:41 +0800 Subject: [PATCH 068/167] wifi: add wifi rx buffer number config in menuconfig --- components/esp32/Kconfig | 9 +++++++++ components/esp32/include/esp_wifi.h | 2 ++ components/esp32/lib | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 6ee5313b9bf..90cd734bf15 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -508,5 +508,14 @@ config ESP32_PHY_MAX_TX_POWER help Set maximum transmit power. Actual transmit power for high data rates may be lower than this setting. + +config ESP32_WIFI_RX_BUFFER_NUM + int "Max number of WiFi RX buffers" + range 2 25 + default 25 + help + Set the number of WiFi rx buffers. Each buffer takes approximately 1.6KB of RAM. + Larger number for higher throughput but more memory. Smaller number for lower + throughput but less memory. endmenu diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index ac49764f1f2..c835c071c78 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -94,11 +94,13 @@ extern "C" { */ typedef struct { system_event_handler_t event_handler; /**< WiFi event handler */ + uint32_t rx_buf_num; /**< WiFi RX buffer number */ } wifi_init_config_t; #define WIFI_INIT_CONFIG_DEFAULT() { \ .event_handler = &esp_event_send, \ + .rx_buf_num = CONFIG_ESP32_WIFI_RX_BUFFER_NUM, \ }; /** diff --git a/components/esp32/lib b/components/esp32/lib index edad9748406..21e433b8277 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit edad9748406d06bfd2dfba6cf1a0735c3982460f +Subproject commit 21e433b8277adc1d65894ec0a65c60f78dc84f7c From 99d698480098858883272439b09096586c8b611b Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Fri, 6 Jan 2017 12:24:37 +0800 Subject: [PATCH 069/167] component/bt : blufi fix security init bug --- examples/12_blufi/main/blufi_security.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/12_blufi/main/blufi_security.c b/examples/12_blufi/main/blufi_security.c index 94755eaaee6..59d889a52de 100644 --- a/examples/12_blufi/main/blufi_security.c +++ b/examples/12_blufi/main/blufi_security.c @@ -184,7 +184,7 @@ esp_err_t blufi_security_init(void) return ESP_FAIL; } - memset(&blufi_sec, 0x0, sizeof(struct blufi_security)); + memset(blufi_sec, 0x0, sizeof(struct blufi_security)); mbedtls_dhm_init(&blufi_sec->dhm); mbedtls_aes_init(&blufi_sec->aes); @@ -198,7 +198,7 @@ void blufi_security_deinit(void) mbedtls_dhm_free(&blufi_sec->dhm); mbedtls_aes_free(&blufi_sec->aes); - memset(&blufi_sec, 0x0, sizeof(struct blufi_security)); + memset(blufi_sec, 0x0, sizeof(struct blufi_security)); free(blufi_sec); blufi_sec = NULL; From 1b38494df4887306653d0ccf444c1192c983f2fd Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 6 Jan 2017 12:23:11 +0800 Subject: [PATCH 070/167] bootloader: modify bootloader dram start address to 0x3fff0000 Modify bootloader dram_seg from address 0x3ffc0000 to 0x3fff0000, len from 0x20000 to 0x10000. Please be notified that this is just a workaround for fixing app data overwrite bootloader data issue! --- components/bootloader/src/main/esp32.bootloader.ld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bootloader/src/main/esp32.bootloader.ld b/components/bootloader/src/main/esp32.bootloader.ld index 6a77eb6adeb..0c57bdf48d9 100644 --- a/components/bootloader/src/main/esp32.bootloader.ld +++ b/components/bootloader/src/main/esp32.bootloader.ld @@ -17,7 +17,7 @@ MEMORY dport0_seg (RW) : org = 0x3FF00000, len = 0x10 /* IO */ iram_seg (RWX) : org = 0x40080000, len = 0x400 /* 1k of IRAM used by bootloader functions which need to flush/enable APP CPU cache */ iram_pool_1_seg (RWX) : org = 0x40078000, len = 0x8000 /* IRAM POOL1, used for APP CPU cache. We can abuse it in bootloader because APP CPU is still held in reset, until we enable APP CPU cache */ - dram_seg (RW) : org = 0x3FFC0000, len = 0x20000 /* Shared RAM, minus rom bss/data/stack.*/ + dram_seg (RW) : org = 0x3FFF0000, len = 0x10000 /* Shared RAM, minus rom bss/data/stack.*/ } /* Default entry point: */ From 61c6ce86d200428985f0539c0255899d58d46330 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 6 Jan 2017 13:03:07 +0800 Subject: [PATCH 071/167] esp32: put .data before .bss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change reduces chances that a large .bss segment will push .data all the way into 0x3ffe1320 — 0x3ffe5320 range where the bootloader stack is, creating a problem when bootloader will be loading application into memory. With this change, .data would need to be at least 200k big to cause problems. --- components/esp32/ld/esp32.common.ld | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index 09b7634445c..c199a41d3d2 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -85,6 +85,27 @@ SECTIONS _iram_text_end = ABSOLUTE(.); } > iram0_0_seg + .dram0.data : + { + _data_start = ABSOLUTE(.); + KEEP(*(.data)) + KEEP(*(.data.*)) + KEEP(*(.gnu.linkonce.d.*)) + KEEP(*(.data1)) + KEEP(*(.sdata)) + KEEP(*(.sdata.*)) + KEEP(*(.gnu.linkonce.s.*)) + KEEP(*(.sdata2)) + KEEP(*(.sdata2.*)) + KEEP(*(.gnu.linkonce.s2.*)) + KEEP(*(.jcr)) + *(.dram1 .dram1.*) + *libesp32.a:panic.o(.rodata .rodata.*) + _data_end = ABSOLUTE(.); + . = ALIGN(4); + _heap_start = ABSOLUTE(.); + } >dram0_0_seg + /* Shared RAM */ .dram0.bss (NOLOAD) : { @@ -108,27 +129,6 @@ SECTIONS _bss_end = ABSOLUTE(.); } >dram0_0_seg - .dram0.data : - { - _data_start = ABSOLUTE(.); - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - *(.data1) - *(.sdata) - *(.sdata.*) - *(.gnu.linkonce.s.*) - *(.sdata2) - *(.sdata2.*) - *(.gnu.linkonce.s2.*) - *(.jcr) - *(.dram1 .dram1.*) - *libesp32.a:panic.o(.rodata .rodata.*) - _data_end = ABSOLUTE(.); - . = ALIGN(4); - _heap_start = ABSOLUTE(.); - } >dram0_0_seg - .flash.rodata : { _rodata_start = ABSOLUTE(.); From 0b264f4f7b38a10d30ab864cbd5fe1a9b9b909fa Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 6 Jan 2017 13:47:53 +0800 Subject: [PATCH 072/167] bootloader: update ld script comment --- components/bootloader/src/main/esp32.bootloader.ld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bootloader/src/main/esp32.bootloader.ld b/components/bootloader/src/main/esp32.bootloader.ld index 0c57bdf48d9..500478814c7 100644 --- a/components/bootloader/src/main/esp32.bootloader.ld +++ b/components/bootloader/src/main/esp32.bootloader.ld @@ -17,7 +17,7 @@ MEMORY dport0_seg (RW) : org = 0x3FF00000, len = 0x10 /* IO */ iram_seg (RWX) : org = 0x40080000, len = 0x400 /* 1k of IRAM used by bootloader functions which need to flush/enable APP CPU cache */ iram_pool_1_seg (RWX) : org = 0x40078000, len = 0x8000 /* IRAM POOL1, used for APP CPU cache. We can abuse it in bootloader because APP CPU is still held in reset, until we enable APP CPU cache */ - dram_seg (RW) : org = 0x3FFF0000, len = 0x10000 /* Shared RAM, minus rom bss/data/stack.*/ + dram_seg (RW) : org = 0x3FFF0000, len = 0x10000 /* 64k at the end of DRAM, after ROM bootloader stack */ } /* Default entry point: */ From 489701eb2d424790f85fee1d84b2665769274405 Mon Sep 17 00:00:00 2001 From: shangke Date: Fri, 6 Jan 2017 13:49:42 +0800 Subject: [PATCH 073/167] ethernet : fix sometimes ethernet init fail bug --- components/ethernet/emac_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 20d428cf7b3..06e641453b1 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -628,6 +628,7 @@ static void emac_start(void *param) cmd->err = EMAC_CMD_OK; emac_enable_clk(true); + emac_reset(); emac_macaddr_init(); emac_check_mac_addr(); @@ -839,7 +840,9 @@ esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) } } } else { + portENTER_CRITICAL(&g_emac_mux); emac_sig_cnt[sig]++; + portEXIT_CRITICAL(&g_emac_mux); emac_event_t evt; evt.sig = sig; evt.par = par; @@ -898,10 +901,8 @@ esp_err_t esp_eth_init(eth_config_t *config) emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t)); xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl); - esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); - - emac_reset(); emac_enable_clk(false); + esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); emac_config.emac_status = EMAC_RUNTIME_INIT; From 6e1150473e0267a6378708d2cc0a6b87ddfd9d45 Mon Sep 17 00:00:00 2001 From: shangke Date: Fri, 6 Jan 2017 14:16:34 +0800 Subject: [PATCH 074/167] ethernet: update docs --- docs/api/esp_eth.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/api/esp_eth.rst b/docs/api/esp_eth.rst index d6d98e43d8f..1fcb1c23513 100644 --- a/docs/api/esp_eth.rst +++ b/docs/api/esp_eth.rst @@ -21,14 +21,20 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygentypedef:: eth_phy_fun -.. doxygentypedef:: eth_tcpip_input_fun +.. doxygentypedef:: eth_phy_check_link_func +.. doxygentypedef:: eth_phy_check_init_func +.. doxygentypedef:: eth_phy_get_speed_mode_func +.. doxygentypedef:: eth_phy_get_duplex_mode_func +.. doxygentypedef:: eth_phy_func +.. doxygentypedef:: eth_tcpip_input_func .. doxygentypedef:: eth_gpio_config_func Enumerations ^^^^^^^^^^^^ .. doxygenenum:: eth_mode_t +.. doxygenenum:: eth_speed_mode_t +.. doxygenenum:: eth_duplex_mode_t .. doxygenenum:: eth_phy_base_t Structures @@ -48,4 +54,4 @@ Functions .. doxygenfunction:: esp_eth_get_mac .. doxygenfunction:: esp_eth_smi_write .. doxygenfunction:: esp_eth_smi_read - +.. doxygenfunction:: esp_eth_free_rx_buf From 23455de4c2f6f8308f60d11badf4a95ed644bd57 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 6 Jan 2017 14:20:32 +0800 Subject: [PATCH 075/167] Add SPI Master driver, example, test and docs --- .../driver/include/driver/periph_ctrl.h | 3 + components/driver/include/driver/spi_master.h | 235 ++++++ components/driver/periph_ctrl.c | 24 + components/driver/spi_master.c | 692 ++++++++++++++++++ components/driver/test/component.mk | 5 + components/driver/test/test_spi_master.c | 80 ++ components/esp32/include/soc/gpio_sig_map.h | 2 +- components/esp32/include/soc/spi_reg.h | 20 +- components/esp32/include/soc/spi_struct.h | 21 +- docs/api/spi_master.rst | 156 ++++ docs/index.rst | 3 +- examples/26_spi_master/Makefile | 9 + examples/26_spi_master/main/component.mk | 5 + examples/26_spi_master/main/spi_master.c | 275 +++++++ 14 files changed, 1505 insertions(+), 25 deletions(-) create mode 100644 components/driver/include/driver/spi_master.h create mode 100644 components/driver/spi_master.c create mode 100644 components/driver/test/component.mk create mode 100644 components/driver/test/test_spi_master.c create mode 100644 docs/api/spi_master.rst create mode 100644 examples/26_spi_master/Makefile create mode 100644 examples/26_spi_master/main/component.mk create mode 100644 examples/26_spi_master/main/spi_master.c diff --git a/components/driver/include/driver/periph_ctrl.h b/components/driver/include/driver/periph_ctrl.h index 8c404e5b136..0aab55088d7 100644 --- a/components/driver/include/driver/periph_ctrl.h +++ b/components/driver/include/driver/periph_ctrl.h @@ -41,6 +41,9 @@ typedef enum { PERIPH_UHCI1_MODULE, PERIPH_RMT_MODULE, PERIPH_PCNT_MODULE, + PERIPH_SPI_MODULE, + PERIPH_HSPI_MODULE, + PERIPH_VSPI_MODULE, } periph_module_t; /** diff --git a/components/driver/include/driver/spi_master.h b/components/driver/include/driver/spi_master.h new file mode 100644 index 00000000000..4dd8738ad70 --- /dev/null +++ b/components/driver/include/driver/spi_master.h @@ -0,0 +1,235 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#ifndef _DRIVER_SPI_MASTER_H_ +#define _DRIVER_SPI_MASTER_H_ + +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * @brief Enum with the three SPI peripherals that are software-accessible in it + */ +typedef enum { + SPI_HOST=0, ///< SPI1, SPI + HSPI_HOST=1, ///< SPI2, HSPI + VSPI_HOST=2 ///< SPI3, VSPI +} spi_host_device_t; + + +/** + * @brief This is a configuration structure for a SPI bus. + * + * You can use this structure to specify the GPIO pins of the bus. Normally, the driver will use the + * GPIO matrix to route the signals. An exception is made when all signals either can be routed through + * the IO_MUX or are -1. In that case, the IO_MUX is used, allowing for >40MHz speeds. + */ +typedef struct { + int spid_io_num; ///< GPIO pin for spi_d (=MOSI)signal, or -1 if not used. + int spiq_io_num; ///< GPIO pin for spi_q (=MISO) signal, or -1 if not used. + int spiclk_io_num; ///< GPIO pin for spi_clk signal, or -1 if not used. + int spiwp_io_num; ///< GPIO pin for spi_wp signal, or -1 if not used. + int spihd_io_num; ///< GPIO pin for spi_hd signal, or -1 if not used. +} spi_bus_config_t; + + +#define SPI_DEVICE_TXBIT_LSBFIRST (1<<0) ///< Transmit command/address/data LSB first instead of the default MSB first +#define SPI_DEVICE_RXBIT_LSBFIRST (1<<1) ///< Receive data LSB first instead of the default MSB first +#define SPI_DEVICE_BIT_LSBFIRST (SPI_TXBIT_LSBFIRST|SPI_RXBIT_LSBFIRST); ///< Transmit and receive LSB first +#define SPI_DEVICE_3WIRE (1<<2) ///< Use spiq for both sending and receiving data +#define SPI_DEVICE_POSITIVE_CS (1<<3) ///< Make CS positive during a transaction instead of negative +#define SPI_DEVICE_HALFDUPLEX (1<<4) ///< Transmit data before receiving it, instead of simultaneously +#define SPI_DEVICE_CLK_AS_CS (1<<5) ///< Output clock on CS line if CS is active + + +typedef struct spi_transaction_t spi_transaction_t; +typedef void(*transaction_cb_t)(spi_transaction_t *trans); + +/** + * @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses. + */ +typedef struct { + uint8_t command_bits; ///< Amount of bits in command phase (0-16) + uint8_t address_bits; ///< Amount of bits in address phase (0-64) + uint8_t dummy_bits; ///< Amount of dummy bits to insert between address and data phase + uint8_t mode; ///< SPI mode (0-3) + uint8_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128. + uint8_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions. + uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16) + int clock_speed_hz; ///< Clock speed, in Hz + int spics_io_num; ///< CS GPIO pin for this device, or -1 if not used + uint32_t flags; ///< Bitwise OR of SPI_DEVICE_* flags + int queue_size; ///< Transaction queue size + transaction_cb_t pre_cb; ///< Callback to be called before a transmission is started. This callback is called within interrupt context. + transaction_cb_t post_cb; ///< Callback to be called after a transmission has completed. This callback is called within interrupt context. +} spi_device_interface_config_t; + + +#define SPI_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode +#define SPI_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode +#define SPI_MODE_DIOQIO_ADDR (1<<2) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO +#define SPI_USE_RXDATA (1<<2) ///< Receive into rx_data member of spi_transaction_t instead into memory at rx_buffer. +#define SPI_USE_TXDATA (1<<3) ///< Transmit tx_data member of spi_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this. + +/** + * This structure describes one SPI transaction + */ +struct spi_transaction_t { + uint32_t flags; ///< Bitwise OR of SPI_TRANS_* flags + uint16_t command; ///< Command data. Specific length was given when device was added to the bus. + uint64_t address; ///< Address. Specific length was given when device was added to the bus. + size_t length; ///< Total data length, in bits + size_t rxlength; ///< Total data length received, if different from length. (0 defaults this to the value of ``length``) + void *user; ///< User-defined variable. Can be used to store eg transaction ID. + union { + const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase + uint8_t tx_data[4]; ///< If SPI_USE_TXDATA is set, data set here is sent directly from this variable. + }; + union { + void *rx_buffer; ///< Pointer to receive buffer, or NULL for no MISO phase + uint8_t rx_data[4]; ///< If SPI_USE_RXDATA is set, data is received directly to this variable + }; +}; + + +typedef struct spi_device_t* spi_device_handle_t; ///< Handle for a device on a SPI bus + +/** + * @brief Initialize a SPI bus + * + * @warning For now, only supports HSPI and VSPI. + * + * @param host SPI peripheral that controls this bus + * @param bus_config Pointer to a spi_bus_config_t struct specifying how the host should be initialized + * @param dma_chan Either 1 or 2. A SPI bus used by this driver must have a DMA channel associated with + * it. The SPI hardware has two DMA channels to share. This parameter indicates which + * one to use. + * @return + * - ESP_ERR_INVALID_ARG if configuration is invalid + * - ESP_ERR_INVALID_STATE if host already is in use + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on success + */ +esp_err_t spi_bus_initialize(spi_host_device_t host, spi_bus_config_t *bus_config, int dma_chan); + +/** + * @brief Free a SPI bus + * + * @warning In order for this to succeed, all devices have to be removed first. + * + * @param host SPI peripheral to free + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_STATE if not all devices on the bus are freed + * - ESP_OK on success + */ +esp_err_t spi_bus_free(spi_host_device_t host); + +/** + * @brief Allocate a device on a SPI bus + * + * This initializes the internal structures for a device, plus allocates a CS pin on the indicated SPI master + * peripheral and routes it to the indicated GPIO. All SPI master devices have three CS pins and can thus control + * up to three devices. + * + * @param host SPI peripheral to allocate device on + * @param dev_config SPI interface protocol config for the device + * @param handle Pointer to variable to hold the device handle + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_NOT_FOUND if host doesn't have any free CS slots + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on success + */ +esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config_t *dev_config, spi_device_handle_t *handle); + + +/** + * @brief Remove a device from the SPI bus + * + * @param handle Device handle to free + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_STATE if device already is freed + * - ESP_OK on success + */ +esp_err_t spi_bus_remove_device(spi_device_handle_t handle); + + +/** + * @brief Queue a SPI transaction for execution + * + * @param handle Device handle obtained using spi_host_add_dev + * @param trans_desc Description of transaction to execute + * @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to + * never time out. + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait); + + +/** + * @brief Get the result of a SPI transaction queued earlier + * + * This routine will wait until a transaction to the given device (queued earlier with + * spi_device_queue_trans) has succesfully completed. It will then return the description of the + * completed transaction so software can inspect the result and e.g. free the memory or + * re-use the buffers. + * + * @param handle Device handle obtained using spi_host_add_dev + * @param trans_desc Pointer to variable able to contain a pointer to the description of the + * transaction that is executed + * @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time + out. + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transaction_t **trans_desc, TickType_t ticks_to_wait); + + +/** + * @brief Do a SPI transaction + * + * Essentially does the same as spi_device_queue_trans followed by spi_device_get_trans_result. Do + * not use this when there is still a transaction queued that hasn't been finalized + * using spi_device_get_trans_result. + * + * @param handle Device handle obtained using spi_host_add_dev + * @param trans_desc Pointer to variable able to contain a pointer to the description of the + * transaction that is executed + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/driver/periph_ctrl.c b/components/driver/periph_ctrl.c index 7fc4091aa58..d90fa595f91 100644 --- a/components/driver/periph_ctrl.c +++ b/components/driver/periph_ctrl.c @@ -97,6 +97,18 @@ void periph_module_enable(periph_module_t periph) SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_PCNT_CLK_EN); CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_PCNT_RST); break; + case PERIPH_SPI_MODULE: + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_1); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_1); + break; + case PERIPH_HSPI_MODULE: + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST); + break; + case PERIPH_VSPI_MODULE: + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_2); + break; default: break; } @@ -179,6 +191,18 @@ void periph_module_disable(periph_module_t periph) CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_PCNT_CLK_EN); SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_PCNT_RST); break; + case PERIPH_SPI_MODULE: + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_1); + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_1); + break; + case PERIPH_HSPI_MODULE: + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN); + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST); + break; + case PERIPH_VSPI_MODULE: + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2); + SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_2); + break; default: break; } diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c new file mode 100644 index 00000000000..fd4c5b2e299 --- /dev/null +++ b/components/driver/spi_master.c @@ -0,0 +1,692 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* +Architecture: + +We can initialize a SPI driver, but we don't talk to the SPI driver itself, we address a device. A device essentially +is a combination of SPI port and CS pin, plus some information about the specifics of communication to the device +(timing, command/address length etc) + +The essence of the interface to a device is a set of queues; one per device. The idea is that to send something to a SPI +device, you allocate a transaction descriptor. It contains some information about the transfer like the lenghth, address, +command etc, plus pointers to transmit and receive buffer. The address of this block gets pushed into the transmit queue. +The SPI driver does its magic, and sends and retrieves the data eventually. The data gets written to the receive buffers, +if needed the transaction descriptor is modified to indicate returned parameters and the entire thing goes into the return +queue, where whatever software initiated the transaction can retrieve it. + +The entire thing is run from the SPI interrupt handler. If SPI is done transmitting/receiving but nothing is in the queue, +it will not clear the SPI interrupt but just disable it. This way, when a new thing is sent, pushing the packet into the send +queue and re-enabling the interrupt will trigger the interrupt again, which can then take care of the sending. +*/ + + + +#include +#include "driver/spi_master.h" +#include "soc/gpio_sig_map.h" +#include "soc/spi_reg.h" +#include "soc/dport_reg.h" +#include "soc/spi_struct.h" +#include "soc/rtc_cntl_reg.h" +#include "rom/ets_sys.h" +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_intr_alloc.h" +#include "esp_log.h" +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/soc.h" +#include "soc/dport_reg.h" +#include "soc/uart_struct.h" +#include "rom/lldesc.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" +#include "esp_heap_alloc_caps.h" + +typedef struct spi_device_t spi_device_t; + +#define NO_CS 3 //Number of CS pins per SPI host + +typedef struct { + spi_device_t *device[NO_CS]; + intr_handle_t intr; + spi_dev_t *hw; + spi_transaction_t *cur_trans; + int cur_cs; + lldesc_t dmadesc_tx, dmadesc_rx; + bool no_gpio_matrix; +} spi_host_t; + +struct spi_device_t { + QueueHandle_t trans_queue; + QueueHandle_t ret_queue; + spi_device_interface_config_t cfg; + spi_host_t *host; +}; + +static spi_host_t *spihost[3]; + + +static const char *SPI_TAG = "spi_master"; +#define SPI_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } + +/* + Stores a bunch of per-spi-peripheral data. +*/ +typedef struct { + const uint8_t spiclk_out; //GPIO mux output signals + const uint8_t spid_out; + const uint8_t spiq_out; + const uint8_t spiwp_out; + const uint8_t spihd_out; + const uint8_t spid_in; //GPIO mux input signals + const uint8_t spiq_in; + const uint8_t spiwp_in; + const uint8_t spihd_in; + const uint8_t spics_out[3]; // /CS GPIO output mux signals + const uint8_t spiclk_native; //IO pins of IO_MUX muxed signals + const uint8_t spid_native; + const uint8_t spiq_native; + const uint8_t spiwp_native; + const uint8_t spihd_native; + const uint8_t spics0_native; + const uint8_t irq; //irq source for interrupt mux + const uint8_t irq_dma; //dma irq source for interrupt mux + const periph_module_t module; //peripheral module, for enabling clock etc + spi_dev_t *hw; //Pointer to the hardware registers +} spi_signal_conn_t; + +/* + Bunch of constants for every SPI peripheral: GPIO signals, irqs, hw addr of registers etc +*/ +static const spi_signal_conn_t io_signal[3]={ + { + .spiclk_out=SPICLK_OUT_IDX, + .spid_out=SPID_OUT_IDX, + .spiq_out=SPIQ_OUT_IDX, + .spiwp_out=SPIWP_OUT_IDX, + .spihd_out=SPIHD_OUT_IDX, + .spid_in=SPID_IN_IDX, + .spiq_in=SPIQ_IN_IDX, + .spiwp_in=SPIWP_IN_IDX, + .spihd_in=SPIHD_IN_IDX, + .spics_out={SPICS0_OUT_IDX, SPICS1_OUT_IDX, SPICS2_OUT_IDX}, + .spiclk_native=6, + .spid_native=8, + .spiq_native=7, + .spiwp_native=10, + .spihd_native=9, + .spics0_native=11, + .irq=ETS_SPI1_INTR_SOURCE, + .irq_dma=ETS_SPI1_DMA_INTR_SOURCE, + .module=PERIPH_SPI_MODULE, + .hw=&SPI1 + }, { + .spiclk_out=HSPICLK_OUT_IDX, + .spid_out=HSPID_OUT_IDX, + .spiq_out=HSPIQ_OUT_IDX, + .spiwp_out=HSPIWP_OUT_IDX, + .spihd_out=HSPIHD_OUT_IDX, + .spid_in=HSPID_IN_IDX, + .spiq_in=HSPIQ_IN_IDX, + .spiwp_in=HSPIWP_IN_IDX, + .spihd_in=HSPIHD_IN_IDX, + .spics_out={HSPICS0_OUT_IDX, HSPICS1_OUT_IDX, HSPICS2_OUT_IDX}, + .spiclk_native=14, + .spid_native=13, + .spiq_native=12, + .spiwp_native=2, + .spihd_native=4, + .spics0_native=15, + .irq=ETS_SPI2_INTR_SOURCE, + .irq_dma=ETS_SPI2_DMA_INTR_SOURCE, + .module=PERIPH_HSPI_MODULE, + .hw=&SPI2 + }, { + .spiclk_out=VSPICLK_OUT_IDX, + .spid_out=VSPID_OUT_IDX, + .spiq_out=VSPIQ_OUT_IDX, + .spiwp_out=VSPIWP_OUT_IDX, + .spihd_out=VSPIHD_OUT_IDX, + .spid_in=VSPID_IN_IDX, + .spiq_in=VSPIQ_IN_IDX, + .spiwp_in=VSPIWP_IN_IDX, + .spihd_in=VSPIHD_IN_IDX, + .spics_out={VSPICS0_OUT_IDX, VSPICS1_OUT_IDX, VSPICS2_OUT_IDX}, + .spiclk_native=18, + .spid_native=23, + .spiq_native=19, + .spiwp_native=22, + .spihd_native=21, + .spics0_native=5, + .irq=ETS_SPI3_INTR_SOURCE, + .irq_dma=ETS_SPI3_DMA_INTR_SOURCE, + .module=PERIPH_VSPI_MODULE, + .hw=&SPI3 + } +}; + +static void spi_intr(void *arg); + + +esp_err_t spi_bus_initialize(spi_host_device_t host, spi_bus_config_t *bus_config, int dma_chan) +{ + bool native=true; + /* ToDo: remove this when we have flash operations cooperating with this */ + SPI_CHECK(host!=SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED); + + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]==NULL, "host already in use", ESP_ERR_INVALID_STATE); + + SPI_CHECK(bus_config->spid_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spid_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->spiclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spiclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->spiq_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->spiq_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->spiwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spiwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->spihd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spihd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG); + + //The host struct contains two dma descriptors, so we need DMA'able memory for this. + spihost[host]=pvPortMallocCaps(sizeof(spi_host_t), MALLOC_CAP_DMA); + if (spihost[host]==NULL) return ESP_ERR_NO_MEM; + memset(spihost[host], 0, sizeof(spi_host_t)); + + //Check if the selected pins correspond to the native pins of the peripheral + if (bus_config->spid_io_num >= 0 && bus_config->spid_io_num!=io_signal[host].spid_native) native=false; + if (bus_config->spiq_io_num >= 0 && bus_config->spiq_io_num!=io_signal[host].spiq_native) native=false; + if (bus_config->spiclk_io_num >= 0 && bus_config->spiclk_io_num!=io_signal[host].spiclk_native) native=false; + if (bus_config->spiwp_io_num >= 0 && bus_config->spiwp_io_num!=io_signal[host].spiwp_native) native=false; + if (bus_config->spihd_io_num >= 0 && bus_config->spihd_io_num!=io_signal[host].spihd_native) native=false; + + spihost[host]->no_gpio_matrix=native; + if (native) { + //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure + //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway. + if (bus_config->spid_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spid_io_num], 1); + if (bus_config->spiq_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiq_io_num], 1); + if (bus_config->spiwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiwp_io_num], 1); + if (bus_config->spihd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spihd_io_num], 1); + if (bus_config->spiclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiclk_io_num], 1); + } else { + //Use GPIO + if (bus_config->spid_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spid_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->spid_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->spid_io_num, io_signal[host].spid_out, false, false); + gpio_matrix_in(bus_config->spid_io_num, io_signal[host].spid_in, false); + } + if (bus_config->spiq_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiq_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->spiq_io_num, GPIO_MODE_INPUT); + gpio_matrix_out(bus_config->spiq_io_num, io_signal[host].spiq_out, false, false); + gpio_matrix_in(bus_config->spiq_io_num, io_signal[host].spiq_in, false); + } + if (bus_config->spiwp_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiwp_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->spiwp_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->spiwp_io_num, io_signal[host].spiwp_out, false, false); + gpio_matrix_in(bus_config->spiwp_io_num, io_signal[host].spiwp_in, false); + } + if (bus_config->spihd_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spihd_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->spihd_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->spihd_io_num, io_signal[host].spihd_out, false, false); + gpio_matrix_in(bus_config->spihd_io_num, io_signal[host].spihd_in, false); + } + if (bus_config->spiclk_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiclk_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->spiclk_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->spiclk_io_num, io_signal[host].spiclk_out, false, false); + } + } + periph_module_enable(io_signal[host].module); + esp_intr_alloc(io_signal[host].irq, ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void*)spihost[host], &spihost[host]->intr); + spihost[host]->hw=io_signal[host].hw; + + //Reset DMA + spihost[host]->hw->dma_conf.val|=SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + spihost[host]->hw->dma_out_link.start=0; + spihost[host]->hw->dma_in_link.start=0; + spihost[host]->hw->dma_conf.val&=~(SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + + //Disable unneeded ints + spihost[host]->hw->slave.rd_buf_done=0; + spihost[host]->hw->slave.wr_buf_done=0; + spihost[host]->hw->slave.rd_sta_done=0; + spihost[host]->hw->slave.wr_sta_done=0; + spihost[host]->hw->slave.rd_buf_inten=0; + spihost[host]->hw->slave.wr_buf_inten=0; + spihost[host]->hw->slave.rd_sta_inten=0; + spihost[host]->hw->slave.wr_sta_inten=0; + + //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as + //disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling + //any transactions that are queued. + spihost[host]->hw->slave.trans_inten=1; + spihost[host]->hw->slave.trans_done=1; + + //Select DMA channel. + SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, dma_chan, (host * 2)); + + return ESP_OK; +} + +esp_err_t spi_bus_free(spi_host_device_t host) +{ + int x; + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE); + for (x=0; xdevice[x]==NULL, "not all CSses freed", ESP_ERR_INVALID_STATE); + } + spihost[host]->hw->slave.trans_inten=0; + spihost[host]->hw->slave.trans_done=0; + esp_intr_free(spihost[host]->intr); + periph_module_disable(io_signal[host].module); + free(spihost[host]); + spihost[host]=NULL; + return ESP_OK; +} + +/* + Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks + up the CS pin to whatever is specified. +*/ +esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config_t *dev_config, spi_device_handle_t *handle) +{ + int freecs; + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE); + SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG); + for (freecs=0; freecsdevice[freecs], NULL, (spi_device_t *)1)) break; + } + SPI_CHECK(freecs!=NO_CS, "no free cs pins for host", ESP_ERR_NOT_FOUND); + //The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full + //duplex mode does absolutely nothing on the ESP32. + SPI_CHECK(dev_config->cs_ena_pretrans==0 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay incompatible with full-duplex", ESP_ERR_INVALID_ARG); + + //Allocate memory for device + spi_device_t *dev=malloc(sizeof(spi_device_t)); + if (dev==NULL) return ESP_ERR_NO_MEM; + memset(dev, 0, sizeof(spi_device_t)); + spihost[host]->device[freecs]=dev; + + //Allocate queues, set defaults + dev->trans_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_transaction_t *)); + dev->ret_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_transaction_t *)); + if (dev_config->duty_cycle_pos==0) dev_config->duty_cycle_pos=128; + dev->host=spihost[host]; + + //We want to save a copy of the dev config in the dev struct. + memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t)); + + //Set CS pin, CS options + if (dev_config->spics_io_num > 0) { + if (spihost[host]->no_gpio_matrix &&dev_config->spics_io_num == io_signal[host].spics0_native && freecs==0) { + //Again, the cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define. + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], 1); + } else { + //Use GPIO matrix + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], PIN_FUNC_GPIO); + gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(dev_config->spics_io_num, io_signal[host].spics_out[freecs], false, false); + } + } + if (dev_config->flags&SPI_DEVICE_CLK_AS_CS) { + spihost[host]->hw->pin.master_ck_sel |= (1<hw->pin.master_ck_sel &= (1<flags&SPI_DEVICE_POSITIVE_CS) { + spihost[host]->hw->pin.master_cs_pol |= (1<hw->pin.master_cs_pol &= (1<trans_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE); + SPI_CHECK(handle->host->cur_trans==0 || handle->host->device[handle->host->cur_cs]!=handle, "Have unfinished transactions", ESP_ERR_INVALID_STATE); + SPI_CHECK(uxQueueMessagesWaiting(handle->ret_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE); + + //Kill queues + vQueueDelete(handle->trans_queue); + vQueueDelete(handle->ret_queue); + //Remove device from list of csses and free memory + for (x=0; xhost->device[x] == handle) handle->host->device[x]=NULL; + } + free(handle); + return ESP_OK; +} + +static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) { + int pre, n, h, l; + //In hw, n, h and l are 1-32, pre is 0-8K. Value written to register is one lower than used value. + if (hz>(fapb/2)) { + //Can only solve this using fapb directly. + hw->clock.clkcnt_l=0; + hw->clock.clkcnt_h=0; + hw->clock.clkcnt_n=0; + hw->clock.clkdiv_pre=0; + hw->clock.clk_equ_sysclk=1; + } else { + //For best duty cycle resolution, we want n to be as close to 32 as possible. + //ToDo: + //This algo could use some tweaking; at the moment it either fixes n to 32 and + //uses the prescaler to get a suitable division factor, or sets the prescaler to 0 + //and uses n to set a value. In practice, sometimes a better result can be + //obtained by setting both n and pre to well-chosen valued... ToDo: fix up some algo to + //do this automatically (worst-case: bruteforce n/pre combo's) - JD + //Also ToDo: + //The ESP32 has a SPI_CK_OUT_HIGH_MODE and SPI_CK_OUT_LOW_MODE register; it looks like we can + //use those to specify the duty cycle in a more precise way. Figure out how to use these. - JD + n=(fapb/(hz*32)); + if (n>32) { + //Need to use prescaler + n=32; + } + if (n<32) { + //No need for prescaler. + n=(fapb/hz); + } + pre=(fapb/n)/hz; + h=n; + l=(((256-duty_cycle)*n+127)/256); + hw->clock.clk_equ_sysclk=0; + hw->clock.clkcnt_n=n-1; + hw->clock.clkdiv_pre=pre-1; + hw->clock.clkcnt_h=h-1; + hw->clock.clkcnt_l=l-1; + } +} + + +//If a transaction is smaller than or equal to of bits, we do not use DMA; instead, we directly copy/paste +//bits from/to the work registers. Keep between 32 and (8*32) please. +#define THRESH_DMA_TRANS (8*32) + +//This is run in interrupt context and apart from initialization and destruction, this is the only code +//touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are +//no muxes in this code. +static void IRAM_ATTR spi_intr(void *arg) +{ + int i; + int prevCs=-1; + BaseType_t r; + BaseType_t do_yield=pdFALSE; + spi_transaction_t *trans=NULL; + spi_host_t *host=(spi_host_t*)arg; + + //Ignore all but the trans_done int. + if (!host->hw->slave.trans_done) return; + + if (host->cur_trans) { + //Okay, transaction is done. + if ((host->cur_trans->rx_buffer || (host->cur_trans->flags & SPI_USE_RXDATA)) && host->cur_trans->rxlength<=THRESH_DMA_TRANS) { + //Need to copy from SPI regs to result buffer. + uint32_t *data; + if (host->cur_trans->flags & SPI_USE_RXDATA) { + data=(uint32_t*)&host->cur_trans->rx_data[0]; + } else { + data=(uint32_t*)host->cur_trans->rx_buffer; + } + for (int x=0; x < host->cur_trans->rxlength; x+=32) { + //Do a memcpy to get around possible alignment issues in rx_buffer + uint32_t word=host->hw->data_buf[x/32]; + memcpy(&data[x/32], &word, 4); + } + } + //Call post-transaction callback, if any + if (host->device[host->cur_cs]->cfg.post_cb) host->device[host->cur_cs]->cfg.post_cb(host->cur_trans); + //Return transaction descriptor. + xQueueSendFromISR(host->device[host->cur_cs]->ret_queue, &host->cur_trans, &do_yield); + host->cur_trans=NULL; + prevCs=host->cur_cs; + } + //ToDo: This is a stupidly simple low-cs-first priority scheme. Make this configurable somehow. - JD + for (i=0; idevice[i]) { + r=xQueueReceiveFromISR(host->device[i]->trans_queue, &trans, &do_yield); + //Stop looking if we have a transaction to send. + if (r) break; + } + } + if (i==NO_CS) { + //No packet waiting. Disable interrupt. + esp_intr_disable(host->intr); + } else { + host->hw->slave.trans_done=0; //clear int bit + //We have a transaction. Send it. + spi_device_t *dev=host->device[i]; + host->cur_trans=trans; + //We should be done with the transmission. + assert(host->hw->cmd.usr == 0); + + //Default rxlength to be the same as length, if not filled in. + if (trans->rxlength==0) { + trans->rxlength=trans->length; + } + + //Reconfigure accoding to device settings, but only if we change CSses. + if (i!=prevCs) { + //Assumes a hardcoded 80MHz Fapb for now. ToDo: figure out something better once we have + //clock scaling working. + int apbclk=APB_CLK_FREQ; + spi_set_clock(host->hw, apbclk, dev->cfg.clock_speed_hz, dev->cfg.duty_cycle_pos); + //Configure bit order + host->hw->ctrl.rd_bit_order=(dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0; + host->hw->ctrl.wr_bit_order=(dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0; + + //Configure polarity + //SPI iface needs to be configured for a delay unless it is not routed through GPIO and clock is >=apb/2 + int nodelay=(host->no_gpio_matrix && dev->cfg.clock_speed_hz >= (apbclk/2)); + if (dev->cfg.mode==0) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } else if (dev->cfg.mode==1) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (dev->cfg.mode==2) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (dev->cfg.mode==3) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } + + //Configure bit sizes, load addr and command + host->hw->user.usr_dummy=(dev->cfg.dummy_bits)?1:0; + host->hw->user.usr_addr=(dev->cfg.address_bits)?1:0; + host->hw->user.usr_command=(dev->cfg.command_bits)?1:0; + host->hw->user1.usr_addr_bitlen=dev->cfg.address_bits-1; + host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits-1; + host->hw->user2.usr_command_bitlen=dev->cfg.command_bits-1; + //Configure misc stuff + host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1; + host->hw->user.sio=(dev->cfg.flags & SPI_DEVICE_3WIRE)?1:0; + + host->hw->ctrl2.setup_time=dev->cfg.cs_ena_pretrans-1; + host->hw->user.cs_setup=dev->cfg.cs_ena_pretrans?1:0; + host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans-1; + host->hw->user.cs_hold=(dev->cfg.cs_ena_posttrans)?1:0; + + //Configure CS pin + host->hw->pin.cs0_dis=(i==0)?0:1; + host->hw->pin.cs1_dis=(i==1)?0:1; + host->hw->pin.cs2_dis=(i==2)?0:1; + } + //Reset DMA + host->hw->dma_conf.val |= SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + host->hw->dma_out_link.start=0; + host->hw->dma_in_link.start=0; + host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + //QIO/DIO + host->hw->ctrl.val &= ~(SPI_FREAD_DUAL|SPI_FREAD_QUAD|SPI_FREAD_DIO|SPI_FREAD_QIO); + host->hw->user.val &= ~(SPI_FWRITE_DUAL|SPI_FWRITE_QUAD|SPI_FWRITE_DIO|SPI_FWRITE_QIO); + if (trans->flags & SPI_MODE_DIO) { + if (trans->flags & SPI_MODE_DIOQIO_ADDR) { + host->hw->ctrl.fread_dio=1; + host->hw->user.fwrite_dio=1; + } else { + host->hw->ctrl.fread_dual=1; + host->hw->user.fwrite_dual=1; + } + host->hw->ctrl.fastrd_mode=1; + } else if (trans->flags & SPI_MODE_QIO) { + if (trans->flags & SPI_MODE_DIOQIO_ADDR) { + host->hw->ctrl.fread_qio=1; + host->hw->user.fwrite_qio=1; + } else { + host->hw->ctrl.fread_quad=1; + host->hw->user.fwrite_quad=1; + } + host->hw->ctrl.fastrd_mode=1; + } + + + //Fill DMA descriptors + if (trans->rx_buffer || (trans->flags & SPI_USE_RXDATA)) { + uint32_t *data; + if (trans->flags & SPI_USE_RXDATA) { + data=(uint32_t *)&trans->rx_data[0]; + } else { + data=trans->rx_buffer; + } + if (trans->rxlengthhw->user.usr_miso_highpart=0; + host->dmadesc_rx.size=(trans->rxlength+7)/8; + host->dmadesc_rx.length=(trans->rxlength+7)/8; + host->dmadesc_rx.buf=(uint8_t*)data; + host->dmadesc_rx.eof=1; + host->dmadesc_rx.sosf=0; + host->dmadesc_rx.owner=1; + host->hw->dma_in_link.addr=(int)(&host->dmadesc_rx)&0xFFFFF; + host->hw->dma_in_link.start=1; + } + host->hw->user.usr_miso=1; + } else { + host->hw->user.usr_miso=0; + } + + if (trans->tx_buffer || (trans->flags & SPI_USE_TXDATA)) { + uint32_t *data; + if (trans->flags & SPI_USE_TXDATA) { + data=(uint32_t *)&trans->tx_data[0]; + } else { + data=(uint32_t *)trans->tx_buffer; + } + if (trans->rxlength < 8*32) { + //No need for DMA. + for (int x=0; x < trans->rxlength; x+=32) { + //Use memcpy to get around alignment issues for txdata + uint32_t word; + memcpy(&word, &data[x/32], 4); + host->hw->data_buf[(x/32)+8]=word; + } + host->hw->user.usr_mosi_highpart=1; + } else { + host->hw->user.usr_mosi_highpart=0; + host->dmadesc_tx.size=(trans->length+7)/8; + host->dmadesc_tx.length=(trans->length+7)/8; + host->dmadesc_tx.buf=(uint8_t*)data; + host->dmadesc_tx.eof=1; + host->dmadesc_tx.sosf=0; + host->dmadesc_tx.owner=1; + host->hw->dma_out_link.addr=(int)(&host->dmadesc_tx) & 0xFFFFF; + host->hw->dma_out_link.start=1; + } + } + host->hw->mosi_dlen.usr_mosi_dbitlen=trans->length-1; + host->hw->miso_dlen.usr_miso_dbitlen=trans->rxlength-1; + host->hw->user2.usr_command_value=trans->command; + if (dev->cfg.address_bits>32) { + host->hw->addr=trans->address >> 32; + host->hw->slv_wr_status=trans->address & 0xffffffff; + } else { + host->hw->addr=trans->address & 0xffffffff; + } + host->hw->user.usr_mosi=(trans->tx_buffer==NULL)?0:1; + host->hw->user.usr_miso=(trans->tx_buffer==NULL)?0:1; + + //Call pre-transmission callback, if any + if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans); + //Kick off transfer + host->hw->cmd.usr=1; + } + if (do_yield) portYIELD_FROM_ISR(); +} + + +esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait) +{ + BaseType_t r; + SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_USE_RXDATA)==0 ||trans_desc->length <= 32, "rxdata transfer > 32bytes", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32bytes", ESP_ERR_INVALID_ARG); + SPI_CHECK(!((trans_desc->flags & (SPI_MODE_DIO|SPI_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG); + SPI_CHECK(!((trans_desc->flags & (SPI_MODE_DIO|SPI_MODE_QIO)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG); + r=xQueueSend(handle->trans_queue, (void*)&trans_desc, ticks_to_wait); + if (!r) return ESP_ERR_TIMEOUT; + esp_intr_enable(handle->host->intr); + return ESP_OK; +} + +esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transaction_t **trans_desc, TickType_t ticks_to_wait) +{ + BaseType_t r; + SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); + r=xQueueReceive(handle->ret_queue, (void*)trans_desc, ticks_to_wait); + if (!r) return ESP_ERR_TIMEOUT; + return ESP_OK; +} + +//Porcelain to do one blocking transmission. +esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc) +{ + esp_err_t ret; + spi_transaction_t *ret_trans; + //ToDo: check if any spi transfers in flight + ret=spi_device_queue_trans(handle, trans_desc, portMAX_DELAY); + if (ret!=ESP_OK) return ret; + ret=spi_device_get_trans_result(handle, &ret_trans, portMAX_DELAY); + if (ret!=ESP_OK) return ret; + assert(ret_trans==trans_desc); + return ESP_OK; +} + diff --git a/components/driver/test/component.mk b/components/driver/test/component.mk new file mode 100644 index 00000000000..5dd172bdb74 --- /dev/null +++ b/components/driver/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c new file mode 100644 index 00000000000..0fd47cbdbae --- /dev/null +++ b/components/driver/test/test_spi_master.c @@ -0,0 +1,80 @@ +/* + Tests for the spi_master device driver +*/ + +#include +#include +#include +#include +#include +#include "rom/ets_sys.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "driver/spi_master.h" + + + +TEST_CASE("SPI Master test", "[spi]") +{ + spi_bus_config_t buscfg={ + .spid_io_num=4, + .spiq_io_num=16, + .spiclk_io_num=25, + .spiwp_io_num=-1, + .spihd_io_num=-1 + }; + spi_device_interface_config_t devcfg={ + .command_bits=8, + .address_bits=64, + .dummy_bits=0, + .clock_speed_hz=8000, + .duty_cycle_pos=128, + .cs_ena_pretrans=7, + .cs_ena_posttrans=7, + .mode=0, + .spics_io_num=21, + .queue_size=3 + }; + + esp_err_t ret; + spi_device_handle_t handle; + printf("THIS TEST NEEDS A JUMPER BETWEEN IO4 AND IO16\n"); + + ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1); + TEST_ASSERT(ret==ESP_OK); + ret=spi_bus_add_device(HSPI_HOST, &devcfg, &handle); + TEST_ASSERT(ret==ESP_OK); + printf("Bus/dev inited.\n"); + spi_transaction_t t; + char sendbuf[16]="Hello World!"; + char recvbuf[16]="UUUUUUUUUUUUUUU"; + memset(&t, 0, sizeof(t)); + + t.length=16*8; + t.tx_buffer=sendbuf; + t.rx_buffer=recvbuf; + t.address=0xA00000000000000FL; + t.command=0x55; + printf("Transmit...\n"); + ret=spi_device_transmit(handle, &t); + TEST_ASSERT(ret==ESP_OK); + printf("Send vs recv:\n"); + for (int x=0; x<16; x++) printf("%02X ", (int)sendbuf[x]); + printf("`_ + +Macros +^^^^^^ + +.. doxygendefine:: SPI_DEVICE_TXBIT_LSBFIRST +.. doxygendefine:: SPI_DEVICE_RXBIT_LSBFIRST +.. doxygendefine:: SPI_DEVICE_BIT_LSBFIRST +.. doxygendefine:: SPI_DEVICE_3WIRE +.. doxygendefine:: SPI_DEVICE_POSITIVE_CS +.. doxygendefine:: SPI_DEVICE_HALFDUPLEX +.. doxygendefine:: SPI_DEVICE_CLK_AS_CS + +.. doxygendefine:: SPI_MODE_DIO +.. doxygendefine:: SPI_MODE_QIO +.. doxygendefine:: SPI_MODE_DIOQIO_ADDR +.. doxygendefine:: SPI_USE_RXDATA +.. doxygendefine:: SPI_USE_TXDATA + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: spi_device_handle_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: spi_host_device_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: spi_transaction_t + :members: + +.. doxygenstruct:: spi_bus_config_t + :members: + +.. doxygenstruct:: spi_device_interface_config_t + :members: + + + +Functions +--------- + +.. doxygenfunction:: spi_bus_initialize +.. doxygenfunction:: spi_bus_free +.. doxygenfunction:: spi_bus_add_device +.. doxygenfunction:: spi_bus_remove_device +.. doxygenfunction:: spi_device_queue_trans +.. doxygenfunction:: spi_device_get_trans_result +.. doxygenfunction:: spi_device_transmit + diff --git a/docs/index.rst b/docs/index.rst index 9c2b0643f4a..3161db345b8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -70,7 +70,7 @@ Contents: 6.4. UART 6.5. I2C - TBA 6.6. I2S - TBA - 6.7. SPI - TBA + 6.7. SPI - 6.8. CAN - TBA 6.9. SD Controller - TBA 6.10. Infrared - TBA @@ -111,6 +111,7 @@ Contents: Pulse Counter Sigma-delta Modulation SPI Flash and Partition APIs + SPI Master API Logging Non-Volatile Storage Virtual Filesystem diff --git a/examples/26_spi_master/Makefile b/examples/26_spi_master/Makefile new file mode 100644 index 00000000000..7ca171bb526 --- /dev/null +++ b/examples/26_spi_master/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := spi_master + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/26_spi_master/main/component.mk b/examples/26_spi_master/main/component.mk new file mode 100644 index 00000000000..4d3b30caf37 --- /dev/null +++ b/examples/26_spi_master/main/component.mk @@ -0,0 +1,5 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/26_spi_master/main/spi_master.c b/examples/26_spi_master/main/spi_master.c new file mode 100644 index 00000000000..1cdbf72ad55 --- /dev/null +++ b/examples/26_spi_master/main/spi_master.c @@ -0,0 +1,275 @@ +/* SPI Master example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "driver/spi_master.h" +#include "soc/gpio_struct.h" +#include "driver/gpio.h" + + +/* + This code displays some fancy graphics on the ILI9341-based 320x240 LCD on an ESP-WROVER_KIT board. + It is not very fast, even when the SPI transfer itself happens at 8MHz and with DMA, because + the rest of the code is not very optimized. Especially calculating the image line-by-line + is inefficient; it would be quicker to send an entire screenful at once. This example does, however, + demonstrate the use of both spi_device_transmit as well as spi_device_queue_trans/spi_device_get_trans_result + as well as pre-transmit callbacks. + + Some info about the ILI9341: It has an C/D line, which is connected to a GPIO here. It expects this + line to be low for a command and high for data. We use a pre-transmit callback here to control that + line: every transaction has as the user-definable argument the needed state of the D/C line and just + before the transaction is sent, the callback will set this line to the correct state. +*/ + +#define PIN_NUM_MISO 25 +#define PIN_NUM_MOSI 23 +#define PIN_NUM_CLK 19 +#define PIN_NUM_CS 22 + +#define PIN_NUM_DC 21 +#define PIN_NUM_RST 18 +#define PIN_NUM_BCKL 5 + + +/* + The ILI9341 needs a bunch of command/argument values to be initialized. They are stored in this struct. +*/ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} ili_init_cmd_t; + +static const ili_init_cmd_t ili_init_cmds[]={ + {0xCF, {0x00, 0x83, 0X30}, 3}, + {0xED, {0x64, 0x03, 0X12, 0X81}, 4}, + {0xE8, {0x85, 0x01, 0x79}, 3}, + {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, + {0xF7, {0x20}, 1}, + {0xEA, {0x00, 0x00}, 2}, + {0xC0, {0x26}, 1}, + {0xC1, {0x11}, 1}, + {0xC5, {0x35, 0x3E}, 2}, + {0xC7, {0xBE}, 1}, + {0x36, {0x28}, 1}, + {0x3A, {0x55}, 1}, + {0xB1, {0x00, 0x1B}, 2}, + {0xF2, {0x08}, 1}, + {0x26, {0x01}, 1}, + {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15}, + {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15}, + {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4}, + {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4}, + {0x2C, {0}, 0}, + {0xB7, {0x07}, 1}, + {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4}, + {0x11, {0}, 0x80}, + {0x29, {0}, 0x80}, + {0, {0}, 0xff}, +}; + +//Send a command to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete. +void ili_cmd(spi_device_handle_t spi, const uint8_t cmd) +{ + esp_err_t ret; + spi_transaction_t t; + memset(&t, 0, sizeof(t)); //Zero out the transaction + t.length=8; //Command is 8 bits + t.tx_buffer=&cmd; //The data is the cmd itself + t.user=(void*)0; //D/C needs to be set to 0 + ret=spi_device_transmit(spi, &t); //Transmit! + assert(ret==ESP_OK); //Should have had no issues. +} + +//Send data to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete. +void ili_data(spi_device_handle_t spi, const uint8_t *data, int len) +{ + esp_err_t ret; + spi_transaction_t t; + if (len==0) return; //no need to send anything + memset(&t, 0, sizeof(t)); //Zero out the transaction + t.length=len*8; //Len is in bytes, transaction length is in bits. + t.tx_buffer=data; //Data + t.user=(void*)1; //D/C needs to be set to 1 + ret=spi_device_transmit(spi, &t); //Transmit! + assert(ret==ESP_OK); //Should have had no issues. +} + +//This function is called (in irq context!) just before a transmission starts. It will +//set the D/C line to the value indicated in the user field. +void ili_spi_pre_transfer_callback(spi_transaction_t *t) +{ + int dc=(int)t->user; + gpio_set_level(PIN_NUM_DC, dc); +} + +//Initialize the display +void ili_init(spi_device_handle_t spi) +{ + int cmd=0; + //Initialize non-SPI GPIOs + gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT); + + //Reset the display + gpio_set_level(PIN_NUM_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(PIN_NUM_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + //Send all the commands + while (ili_init_cmds[cmd].databytes!=0xff) { + ili_cmd(spi, ili_init_cmds[cmd].cmd); + ili_data(spi, ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F); + if (ili_init_cmds[cmd].databytes&0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + ///Enable backlight + gpio_set_level(PIN_NUM_BCKL, 0); +} + + +//To send a line we have to send a command, 2 data bytes, another command, 2 more data bytes and another command +//before sending the line data itself; a total of 6 transactions. (We can't put all of this in just one transaction +//because the D/C line needs to be toggled in the middle.) +//This routine queues these commands up so they get sent as quickly as possible. +void send_line(spi_device_handle_t spi, int ypos, uint16_t *line) +{ + esp_err_t ret; + int x; + //Transaction descriptors. Declared static so they're not allocated on the stack; we need this memory even when this + //function is finished because the SPI driver needs access to it even while we're already calculating the next line. + static spi_transaction_t trans[6]; + + //In theory, it's better to initialize trans and data only once and hang on to the initialized + //variables. We allocate them on the stack, so we need to re-init them each call. + for (x=0; x<6; x++) { + memset(&trans[x], 0, sizeof(spi_transaction_t)); + if ((x&1)==0) { + //Even transfers are commands + trans[x].length=8; + trans[x].user=(void*)0; + } else { + //Odd transfers are data + trans[x].length=8*4; + trans[x].user=(void*)1; + } + trans[x].flags=SPI_USE_TXDATA; + } + trans[0].tx_data[0]=0x2A; //Column Address Set + trans[1].tx_data[0]=0; //Start Col High + trans[1].tx_data[1]=0; //Start Col Low + trans[1].tx_data[2]=(320)>>8; //End Col High + trans[1].tx_data[3]=(320)&0xff; //End Col Low + trans[2].tx_data[0]=0x2B; //Page address set + trans[3].tx_data[0]=ypos>>8; //Start page high + trans[3].tx_data[1]=ypos&0xff; //start page low + trans[3].tx_data[2]=(ypos+1)>>8; //end page high + trans[3].tx_data[3]=(ypos+1)&0xff; //end page low + trans[4].tx_data[0]=0x2C; //memory write + trans[5].tx_buffer=line; //finally send the line data + trans[5].length=320*2*8; //Data length, in bits + trans[5].flags=0; //undo SPI_USE_TXDATA flag + + //Queue all transactions. + for (x=0; x<6; x++) { + ret=spi_device_queue_trans(spi, &trans[x], portMAX_DELAY); + assert(ret==ESP_OK); + } + + //When we are here, the SPI driver is busy (in the background) getting the transactions sent. That happens + //mostly using DMA, so the CPU doesn't have much to do here. We're not going to wait for the transaction to + //finish because we may as well spend the time calculating the next line. When that is done, we can call + //send_line_finish, which will wait for the transfers to be done and check their status. +} + + +void send_line_finish(spi_device_handle_t spi) +{ + spi_transaction_t *rtrans; + esp_err_t ret; + //Wait for all 6 transactions to be done and get back the results. + for (int x=0; x<6; x++) { + ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY); + assert(ret==ESP_OK); + //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though. + } +} + + +//Simple routine to generate some patterns and send them to the LCD. Don't expect anything too +//impressive. Because the SPI driver handles transactions in the background, we can calculate the next line +//while the previous one is being sent. +void display_pretty_colors(spi_device_handle_t spi) +{ + uint16_t line[2][320]; + int x, y, frame=0; + //Indexes of the line currently being sent to the LCD and the line we're calculating. + int sending_line=-1; + int calc_line=0; + + while(1) { + frame++; + for (y=0; y<240; y++) { + //Calculate a line. + for (x=0; x<320; x++) { + line[calc_line][x]=((x<<3)^(y<<3)^(frame+x*y)); + } + //Finish up the sending process of the previous line, if any + if (sending_line!=-1) send_line_finish(spi); + //Swap sending_line and calc_line + sending_line=calc_line; + calc_line=(calc_line==1)?0:1; + //Send the line we currently calculated. + send_line(spi, y, line[sending_line]); + //The line is queued up for sending now; the actual sending happens in the + //background. We can go on to calculate the next line as long as we do not + //touch line[sending_line]; the SPI sending process is still reading from that. + } + } +} + + +void app_main() +{ + esp_err_t ret; + spi_device_handle_t spi; + spi_bus_config_t buscfg={ + .spiq_io_num=PIN_NUM_MISO, + .spid_io_num=PIN_NUM_MOSI, + .spiclk_io_num=PIN_NUM_CLK, + .spiwp_io_num=-1, + .spihd_io_num=-1 + }; + spi_device_interface_config_t devcfg={ + .clock_speed_hz=10000000, //Clock out at 10 MHz + .mode=0, //SPI mode 0 + .spics_io_num=PIN_NUM_CS, //CS pin + .queue_size=7, //We want to be able to queue 7 transactions at a time + .pre_cb=ili_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line + }; + //Initialize the SPI bus + ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1); + assert(ret==ESP_OK); + //Attach the LCD to the SPI bus + ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi); + assert(ret==ESP_OK); + //Initialize the LCD + ili_init(spi); + //Go do nice stuff. + display_pretty_colors(spi); +} From 845cd09570aed9b1602481494ce4184a7f29b099 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 5 Jan 2017 00:27:59 +0800 Subject: [PATCH 076/167] cxx: link against libstdc++, remove abi.cpp --- components/esp32/abi.cpp | 114 --------------------------------------- make/project.mk | 1 + 2 files changed, 1 insertion(+), 114 deletions(-) delete mode 100644 components/esp32/abi.cpp diff --git a/components/esp32/abi.cpp b/components/esp32/abi.cpp deleted file mode 100644 index c40dba85e0d..00000000000 --- a/components/esp32/abi.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include -#include -#include -#include - -// using __cxxabiv1::__guard; - -void *operator new(size_t size) -{ - return malloc(size); -} - -void *operator new[](size_t size) -{ - return malloc(size); -} - -void operator delete(void * ptr) -{ - free(ptr); -} - -void operator delete[](void * ptr) -{ - free(ptr); -} - -extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__)); -extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); - -void __cxa_pure_virtual(void) -{ - abort(); -} - -void __cxa_deleted_virtual(void) -{ - abort(); -} - -#if 0 -typedef struct { - uint8_t guard; - uint8_t ps; -} guard_t; - -extern "C" int __cxa_guard_acquire(__guard* pg) -{ - uint8_t ps = xt_rsil(15); - if (reinterpret_cast(pg)->guard) { - xt_wsr_ps(ps); - return 0; - } - reinterpret_cast(pg)->ps = ps; - return 1; -} - -extern "C" void __cxa_guard_release(__guard* pg) -{ - reinterpret_cast(pg)->guard = 1; - xt_wsr_ps(reinterpret_cast(pg)->ps); -} - -extern "C" void __cxa_guard_abort(__guard* pg) -{ - xt_wsr_ps(reinterpret_cast(pg)->ps); -} -#endif - -extern "C" void __cxa_throw_bad_array_new_length() -{ - abort(); -} - -namespace std -{ -void __throw_bad_function_call() -{ - abort(); -} - -void __throw_length_error(char const*) -{ - abort(); -} - -void __throw_bad_alloc() -{ - abort(); -} - -void __throw_logic_error(const char* str) -{ - abort(); -} - -void __throw_out_of_range(const char* str) -{ - abort(); -} -} diff --git a/make/project.mk b/make/project.mk index e2a93ce1190..2b8eedeeb50 100644 --- a/make/project.mk +++ b/make/project.mk @@ -187,6 +187,7 @@ LDFLAGS ?= -nostdlib \ -Wl,--start-group \ $(COMPONENT_LDFLAGS) \ -lgcc \ + -lstdc++ \ -Wl,--end-group \ -Wl,-EL From 5b2888e1135311b9a5abc1334af2a26dc2d2c87e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 5 Jan 2017 00:37:56 +0800 Subject: [PATCH 077/167] unity: fix testcase initializer for compiling with C++ --- .../components/unity/include/unity_config.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/unit-test-app/components/unity/include/unity_config.h b/tools/unit-test-app/components/unity/include/unity_config.h index b8e56a79c1d..07df5b3058c 100644 --- a/tools/unit-test-app/components/unity/include/unity_config.h +++ b/tools/unit-test-app/components/unity/include/unity_config.h @@ -61,11 +61,20 @@ void unity_run_all_tests(); .desc = desc_, \ .fn = &UNITY_TEST_UID(test_func_), \ .file = __FILE__, \ - .line = __LINE__ \ + .line = __LINE__, \ + .next = NULL \ }; \ unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ }\ static void UNITY_TEST_UID(test_func_) (void) +/** + * Note: initialization of test_desc_t fields above has to be done exactly + * in the same order as the fields are declared in the structure. + * Otherwise the initializer will not be valid in C++ (which doesn't + * support designated initializers). G++ can parse the syntax, but + * field names are treated as annotations and don't affect initialization + * order. Also make sure all the fields are initialized. + */ // shorthand to check esp_err_t return code #define TEST_ESP_OK(rc) TEST_ASSERT_EQUAL_INT32(ESP_OK, rc) From 1cbc2fa046f4c1afd96054eb2987859c265ec361 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 5 Jan 2017 22:55:38 +0800 Subject: [PATCH 078/167] esp32: ets_update_cpu_frequency should set tick scale for both CPUs --- components/esp32/cpu_freq.c | 8 +++++ components/esp32/ld/esp32.rom.ld | 4 ++- components/esp32/test/test_delay.c | 58 ++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 components/esp32/test/test_delay.c diff --git a/components/esp32/cpu_freq.c b/components/esp32/cpu_freq.c index 257639cf90e..7618f147af8 100644 --- a/components/esp32/cpu_freq.c +++ b/components/esp32/cpu_freq.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include "esp_attr.h" #include "rom/ets_sys.h" #include "rom/uart.h" #include "sdkconfig.h" @@ -66,3 +67,10 @@ void esp_set_cpu_freq(void) ets_update_cpu_frequency(freq_mhz); } +void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) +{ + extern uint32_t g_ticks_per_us_pro; // g_ticks_us defined in ROM for PRO CPU + extern uint32_t g_ticks_per_us_app; // same defined for APP CPU + g_ticks_per_us_pro = ticks_per_us; + g_ticks_per_us_app = ticks_per_us; +} diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 6241ff840eb..8b5ed5f8dcd 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -202,7 +202,7 @@ PROVIDE ( ets_timer_init = 0x400084e8 ); PROVIDE ( ets_timer_setfn = 0x40008350 ); PROVIDE ( ets_unpack_flash_code = 0x40007018 ); PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); -PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); +/* PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); */ /* Updates g_ticks_per_us on the current CPU only; not on the other core */ PROVIDE ( ets_waiti0 = 0x400067d8 ); PROVIDE ( exc_cause_table = 0x3ff991d0 ); PROVIDE ( _exit_r = 0x4000bd28 ); @@ -1725,6 +1725,8 @@ PROVIDE ( xthal_memcpy = 0x4000c0bc ); PROVIDE ( xthal_set_ccompare = 0x4000c058 ); PROVIDE ( xthal_set_intclear = 0x4000c1ec ); PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); +PROVIDE ( g_ticks_per_us_pro = 0x3ffe01e0 ); +PROVIDE ( g_ticks_per_us_app = 0x3ffe40f0 ); /* These functions are xtos-related (or call xtos-related functions) and do not play well with multicore FreeRTOS. Where needed, we provide alternatives that are multicore diff --git a/components/esp32/test/test_delay.c b/components/esp32/test/test_delay.c new file mode 100644 index 00000000000..0b6c4aaf97c --- /dev/null +++ b/components/esp32/test/test_delay.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include "unity.h" +#include "rom/ets_sys.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +typedef struct { + int delay_us; + int method; +} delay_test_arg_t; + +static void test_delay_task(void* p) +{ + const delay_test_arg_t* arg = (delay_test_arg_t*) p; + struct timeval tv_start, tv_stop; + gettimeofday(&tv_start, NULL); + switch (arg->method) { + case 0: + ets_delay_us(arg->delay_us); + break; + case 1: + vTaskDelay(arg->delay_us / portTICK_PERIOD_MS / 1000); + break; + default: + TEST_FAIL(); + } + gettimeofday(&tv_stop, NULL); + int real_delay_us = (tv_stop.tv_sec - tv_start.tv_sec) * 1000000 + + tv_stop.tv_usec - tv_start.tv_usec; + printf("%s core=%d expected=%d actual=%d\n", arg->method ? "vTaskDelay" : "ets_delay_us", + xPortGetCoreID(), arg->delay_us, real_delay_us); + TEST_ASSERT_TRUE(abs(real_delay_us - arg->delay_us) < 1000); + vTaskDelay(1); + vTaskDelete(NULL); +} + +TEST_CASE("ets_delay produces correct delay on both CPUs", "[delay]") +{ + int delay_ms = 50; + const delay_test_arg_t args = { .delay_us = delay_ms * 1000, .method = 0 }; + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); +} + +TEST_CASE("vTaskDelay produces correct delay on both CPUs", "[delay]") +{ + int delay_ms = 50; + const delay_test_arg_t args = { .delay_us = delay_ms * 1000, .method = 1 }; + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1); + vTaskDelay(delay_ms / portTICK_PERIOD_MS + 1); +} From 079138201da75a40c5d06a7d5f7ce7a46512f42a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 5 Jan 2017 00:25:55 +0800 Subject: [PATCH 079/167] cxx: implement static initialization guards --- components/cxx/component.mk | 3 + components/cxx/cxx_guards.cpp | 216 +++++++++++++++++++++++++++++++ components/cxx/test/component.mk | 1 + components/cxx/test/test_cxx.cpp | 203 +++++++++++++++++++++++++++++ 4 files changed, 423 insertions(+) create mode 100644 components/cxx/component.mk create mode 100644 components/cxx/cxx_guards.cpp create mode 100644 components/cxx/test/component.mk create mode 100644 components/cxx/test/test_cxx.cpp diff --git a/components/cxx/component.mk b/components/cxx/component.mk new file mode 100644 index 00000000000..3ce43fc3284 --- /dev/null +++ b/components/cxx/component.mk @@ -0,0 +1,3 @@ +# Mark __cxa_guard_dummy as undefined so that implementation of static guards +# is taken from cxx_guards.o instead of libstdc++.a +COMPONENT_ADD_LDFLAGS := -l$(COMPONENT_NAME) -u __cxa_guard_dummy diff --git a/components/cxx/cxx_guards.cpp b/components/cxx/cxx_guards.cpp new file mode 100644 index 00000000000..288ff3ea03c --- /dev/null +++ b/components/cxx/cxx_guards.cpp @@ -0,0 +1,216 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +using __cxxabiv1::__guard; + +static SemaphoreHandle_t s_static_init_mutex = NULL; //!< lock used for the critical section +static SemaphoreHandle_t s_static_init_wait_sem = NULL; //!< counting semaphore used by the waiting tasks +static portMUX_TYPE s_init_spinlock = portMUX_INITIALIZER_UNLOCKED; //!< spinlock used to guard initialization of the above two primitives +static size_t s_static_init_waiting_count = 0; //!< number of tasks which are waiting for static init guards +#ifndef _NDEBUG +static size_t s_static_init_max_waiting_count = 0; //!< maximum ever value of the above; can be inspected using GDB for debugging purposes +#endif + + +/** + * Layout of the guard object (defined by the ABI). + * + * Compiler will check lower byte before calling guard functions. + */ +typedef struct { + uint8_t ready; //!< nonzero if initialization is done + uint8_t pending; //!< nonzero if initialization is in progress +} guard_t; + +static void static_init_prepare() +{ + portENTER_CRITICAL(&s_init_spinlock); + if (s_static_init_mutex == NULL) { + s_static_init_mutex = xSemaphoreCreateMutex(); + s_static_init_wait_sem = xSemaphoreCreateCounting(INT_MAX, 0); + if (s_static_init_mutex == NULL || s_static_init_wait_sem == NULL) { + // no way to bail out of static initialization without these + abort(); + } + } + portEXIT_CRITICAL(&s_init_spinlock); +} + +/** + * Use s_static_init_wait_sem to wait until guard->pending == 0. + * Preconditions: + * - s_static_init_mutex taken + * - guard.pending == 1 + * Postconditions: + * - s_static_init_mutex taken + * - guard.pending == 0 + */ +static void wait_for_guard_obj(guard_t* g) +{ + s_static_init_waiting_count++; +#ifndef _NDEBUG + s_static_init_max_waiting_count = std::max(s_static_init_waiting_count, + s_static_init_max_waiting_count); +#endif + + do { + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + /* Task may be preempted here, but this isn't a problem, + * as the semaphore will be given exactly the s_static_init_waiting_count + * number of times; eventually the current task will execute next statement, + * which will immediately succeed. + */ + result = xSemaphoreTake(s_static_init_wait_sem, portMAX_DELAY); + assert(result); + /* At this point the semaphore was given, so all waiting tasks have woken up. + * We take s_static_init_mutex before accessing the state of the guard + * object again. + */ + result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + /* Semaphore may have been given because some other guard object became ready. + * Check the guard object we need and wait again if it is still pending. + */ + } while(g->pending); + s_static_init_waiting_count--; +} + +/** + * Unblock tasks waiting for static initialization to complete. + * Preconditions: + * - s_static_init_mutex taken + * Postconditions: + * - s_static_init_mutex taken + */ +static void signal_waiting_tasks() +{ + auto count = s_static_init_waiting_count; + while (count--) { + xSemaphoreGive(s_static_init_wait_sem); + } +} + +extern "C" int __cxa_guard_acquire(__guard* pg) +{ + guard_t* g = reinterpret_cast(pg); + const auto scheduler_started = xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED; + if (!scheduler_started) { + if (g->pending) { + /* Before the scheduler has started, there we don't support simultaneous + * static initialization. This may be implemented using a spinlock and a + * s32c1i instruction, though. + */ + abort(); + } + } else { + if (s_static_init_mutex == NULL) { + static_init_prepare(); + } + + /* We don't need to use double-checked locking pattern here, as the compiler + * must generate code to check if the first byte of *pg is non-zero, before + * calling __cxa_guard_acquire. + */ + auto result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + if (g->pending) { + /* Another task is doing initialization at the moment; wait until it calls + * __cxa_guard_release or __cxa_guard_abort + */ + wait_for_guard_obj(g); + /* At this point there are two scenarios: + * - the task which was doing static initialization has called __cxa_guard_release, + * which means that g->ready is set. We need to return 0. + * - the task which was doing static initialization has called __cxa_guard_abort, + * which means that g->ready is not set; we should acquire the guard and return 1, + * same as for the case if we didn't have to wait. + * Note: actually the second scenario is unlikely to occur in the current + * configuration because exception support is disabled. + */ + } + } + int ret; + if (g->ready) { + /* Static initialization has been done by another task; nothing to do here */ + ret = 0; + } else { + /* Current task can start doing static initialization */ + g->pending = 1; + ret = 1; + } + if (scheduler_started) { + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + } + return ret; +} + +extern "C" void __cxa_guard_release(__guard* pg) +{ + guard_t* g = reinterpret_cast(pg); + const auto scheduler_started = xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED; + if (scheduler_started) { + auto result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + } + assert(g->pending && "tried to release a guard which wasn't acquired"); + g->pending = 0; + /* Initialization was successful */ + g->ready = 1; + if (scheduler_started) { + /* Unblock the tasks waiting for static initialization to complete */ + signal_waiting_tasks(); + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + } +} + +extern "C" void __cxa_guard_abort(__guard* pg) +{ + guard_t* g = reinterpret_cast(pg); + const auto scheduler_started = xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED; + if (scheduler_started) { + auto result = xSemaphoreTake(s_static_init_mutex, portMAX_DELAY); + assert(result); + } + assert(!g->ready && "tried to abort a guard which is ready"); + assert(g->pending && "tried to release a guard which is not acquired"); + g->pending = 0; + if (scheduler_started) { + /* Unblock the tasks waiting for static initialization to complete */ + signal_waiting_tasks(); + auto result = xSemaphoreGive(s_static_init_mutex); + assert(result); + } +} + +/** + * Dummy function used to force linking this file instead of the same one in libstdc++. + * This works via -u __cxa_guard_dummy flag in component.mk + */ +extern "C" void __cxa_guard_dummy() +{ +} diff --git a/components/cxx/test/component.mk b/components/cxx/test/component.mk new file mode 100644 index 00000000000..ce464a212a6 --- /dev/null +++ b/components/cxx/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/cxx/test/test_cxx.cpp b/components/cxx/test/test_cxx.cpp new file mode 100644 index 00000000000..8b790783ed0 --- /dev/null +++ b/components/cxx/test/test_cxx.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include "unity.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +static const char* TAG = "cxx"; + +TEST_CASE("can use new and delete", "[cxx]") +{ + int* int_p = new int(10); + delete int_p; + int* int_array = new int[10]; + delete[] int_array; +} + +class Base +{ +public: + virtual ~Base() {} + virtual void foo() = 0; +}; + +class Derived : public Base +{ +public: + virtual void foo() { } +}; + +TEST_CASE("can call virtual functions", "[cxx]") +{ + Derived d; + Base& b = static_cast(d); + b.foo(); +} + +class NonPOD +{ +public: + NonPOD(int a_) : a(a_) { } + int a; +}; + +static int non_pod_test_helper(int new_val) +{ + static NonPOD non_pod(42); + int ret = non_pod.a; + non_pod.a = new_val; + return ret; +} + +TEST_CASE("can use static initializers for non-POD types", "[cxx]") +{ + TEST_ASSERT_EQUAL(42, non_pod_test_helper(1)); + TEST_ASSERT_EQUAL(1, non_pod_test_helper(0)); +} + +TEST_CASE("can call std::function and bind", "[cxx]") +{ + int outer = 1; + std::function fn = [&outer](int x) -> int { + return x + outer; + }; + outer = 5; + TEST_ASSERT_EQUAL(6, fn(1)); + + auto bound = std::bind(fn, outer); + outer = 10; + TEST_ASSERT_EQUAL(15, bound()); +} + +TEST_CASE("can use std::vector", "[cxx]") +{ + std::vector v(10, 1); + v[0] = 42; + TEST_ASSERT_EQUAL(51, std::accumulate(std::begin(v), std::end(v), 0)); +} + +/* + * This test exercises static initialization guards for two objects. + * For each object, 4 tasks are created which attempt to perform static initialization. + * We check that constructor runs only once for each object. + */ + +static SemaphoreHandle_t s_slow_init_sem = NULL; + +template +class SlowInit +{ +public: + SlowInit(int arg) { + ESP_LOGD(TAG, "init obj=%d start, arg=%d\n", obj, arg); + vTaskDelay(300/portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(-1, mInitBy); + TEST_ASSERT_EQUAL(0, mInitCount); + mInitBy = arg; + ++mInitCount; + ESP_LOGD(TAG, "init obj=%d done\n", obj); + } + + static void task(void* arg) { + int taskId = reinterpret_cast(arg); + ESP_LOGD(TAG, "obj=%d before static init, task=%d\n", obj, taskId); + static SlowInit slowinit(taskId); + ESP_LOGD(TAG, "obj=%d after static init, task=%d\n", obj, taskId); + xSemaphoreGive(s_slow_init_sem); + vTaskDelay(10); + vTaskDelete(NULL); + } +private: + static int mInitBy; + static int mInitCount; +}; + +template<> int SlowInit<1>::mInitBy = -1; +template<> int SlowInit<1>::mInitCount = 0; +template<> int SlowInit<2>::mInitBy = -1; +template<> int SlowInit<2>::mInitCount = 0; + +template +static void start_slow_init_task(int id, int affinity) +{ + xTaskCreatePinnedToCore(&SlowInit::task, "slow_init", 2048, + reinterpret_cast(id), 3, NULL, affinity); +} + +TEST_CASE("static initialization guards work as expected", "[cxx]") +{ + s_slow_init_sem = xSemaphoreCreateCounting(10, 0); + TEST_ASSERT_NOT_NULL(s_slow_init_sem); + // four tasks competing for static initialization of one object + start_slow_init_task<1>(0, PRO_CPU_NUM); + start_slow_init_task<1>(1, APP_CPU_NUM); + start_slow_init_task<1>(2, PRO_CPU_NUM); + start_slow_init_task<1>(3, tskNO_AFFINITY); + + // four tasks competing for static initialization of another object + start_slow_init_task<2>(0, PRO_CPU_NUM); + start_slow_init_task<2>(1, APP_CPU_NUM); + start_slow_init_task<2>(2, PRO_CPU_NUM); + start_slow_init_task<2>(3, tskNO_AFFINITY); + + // All tasks should + for (int i = 0; i < 8; ++i) { + TEST_ASSERT_TRUE(xSemaphoreTake(s_slow_init_sem, 500/portTICK_PERIOD_MS)); + } + vSemaphoreDelete(s_slow_init_sem); +} + +struct GlobalInitTest +{ + GlobalInitTest() : index(order++) { + } + int index; + static int order; +}; + +int GlobalInitTest::order = 0; + +GlobalInitTest g_init_test1; +GlobalInitTest g_init_test2; +GlobalInitTest g_init_test3; + +TEST_CASE("global initializers run in the correct order", "[cxx]") +{ + TEST_ASSERT_EQUAL(0, g_init_test1.index); + TEST_ASSERT_EQUAL(1, g_init_test2.index); + TEST_ASSERT_EQUAL(2, g_init_test3.index); +} + +struct StaticInitTestBeforeScheduler +{ + StaticInitTestBeforeScheduler() + { + static int first_init_order = getOrder(); + index = first_init_order; + } + + int getOrder() + { + return order++; + } + + int index; + static int order; +}; + +int StaticInitTestBeforeScheduler::order = 1; + +StaticInitTestBeforeScheduler g_static_init_test1; +StaticInitTestBeforeScheduler g_static_init_test2; +StaticInitTestBeforeScheduler g_static_init_test3; + +TEST_CASE("before scheduler has started, static initializers work correctly", "[cxx]") +{ + TEST_ASSERT_EQUAL(1, g_static_init_test1.index); + TEST_ASSERT_EQUAL(1, g_static_init_test2.index); + TEST_ASSERT_EQUAL(1, g_static_init_test3.index); + TEST_ASSERT_EQUAL(2, StaticInitTestBeforeScheduler::order); +} From e4b9563dacf5daee27ba8c6ff21d1b003fb37ae9 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Fri, 6 Jan 2017 17:40:46 +0800 Subject: [PATCH 080/167] component/bt : blufi add version --- components/bt/bluedroid/api/esp_blufi_api.c | 4 ++++ components/bt/bluedroid/api/include/esp_blufi_api.h | 9 +++++++++ components/bt/bluedroid/bta/gatt/bta_gatts_act.c | 2 -- .../bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c | 4 ++++ .../bluedroid/btc/profile/esp/blufi/blufi_protocol.c | 9 +++++++++ .../btc/profile/esp/blufi/include/blufi_int.h | 12 +++++++++--- .../btc/profile/esp/include/btc_blufi_prf.h | 3 +++ docs/api/esp_blufi.rst | 1 + examples/12_blufi/main/blufi_main.c | 2 ++ 9 files changed, 41 insertions(+), 5 deletions(-) diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index 5493824d2a5..094fbfae5fd 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -71,4 +71,8 @@ esp_err_t esp_blufi_profile_deinit(void) return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } +uint16_t esp_blufi_get_version(void) +{ + return btc_blufi_get_version(); +} diff --git a/components/bt/bluedroid/api/include/esp_blufi_api.h b/components/bt/bluedroid/api/include/esp_blufi_api.h index 5490fec05a5..6da65121631 100644 --- a/components/bt/bluedroid/api/include/esp_blufi_api.h +++ b/components/bt/bluedroid/api/include/esp_blufi_api.h @@ -344,6 +344,15 @@ esp_err_t esp_blufi_profile_deinit(void); */ esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn_state_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *extra_info); +/** + * + * @brief Get BLUFI profile version + * + * @return Most 8bit significant is Great version, Least 8bit is Sub version + * + */ +uint16_t esp_blufi_get_version(void); + #ifdef __cplusplus } #endif diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index aa00bf287db..04bb3ee7cba 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -233,8 +233,6 @@ void bta_gatts_register(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) if (p_msg->api_reg.p_cback) { (*p_msg->api_reg.p_cback)(BTA_GATTS_REG_EVT, &cb_data); } - - LOG_ERROR("status=%x\n", status); } diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index d480350453b..c126273a723 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -895,3 +895,7 @@ void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks) blufi_env.cbs = callbacks; } +uint16_t btc_blufi_get_version(void) +{ + return BTC_BLUFI_VERSION; +} diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c index 2f92a43806d..d4d617f0000 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c @@ -90,6 +90,15 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_transfer_context(&msg, NULL, 0, NULL); break; + case BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION: { + uint8_t type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION); + uint8_t data[2]; + + data[0] = BTC_BLUFI_GREAT_VER; + data[1] = BTC_BLUFI_SUB_VER; + btc_blufi_send_encap(type, &data[0], sizeof(data)); + break; + } default: LOG_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type); break; diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h index 0de66b8e77a..c21b41c4cab 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h +++ b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h @@ -15,6 +15,10 @@ #ifndef __BLUFI_INT_H__ #define __BLUFI_INT_H__ +#define BTC_BLUFI_GREAT_VER 0x01 //Version + Subversion +#define BTC_BLUFI_SUB_VER 0x00 //Version + Subversion +#define BTC_BLUFI_VERSION ((BTC_BLUFI_GREAT_VER<<8)|BTC_BLUFI_SUB_VER) //Version + Subversion + /* service engine control block */ typedef struct { /* Protocol reference */ @@ -85,7 +89,8 @@ typedef struct blufi_frag_hdr blufi_frag_hdr_t; #define BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP 0x03 #define BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP 0x04 #define BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS 0x05 -#define BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA 0x06 +#define BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA 0x06 +#define BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION 0x07 #define BLUFI_TYPE_DATA 0x1 #define BLUFI_TYPE_DATA_SUBTYPE_NEG 0x00 @@ -104,7 +109,7 @@ typedef struct blufi_frag_hdr blufi_frag_hdr_t; #define BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY 0x0d #define BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY 0x0e #define BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP 0x0f - +#define BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION 0x10 #define BLUFI_TYPE_IS_CTRL(type) (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_CTRL) #define BLUFI_TYPE_IS_DATA(type) (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_DATA) @@ -115,7 +120,8 @@ typedef struct blufi_frag_hdr blufi_frag_hdr_t; #define BLUFI_TYPE_IS_CTRL_CONN_WIFI(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP) #define BLUFI_TYPE_IS_CTRL_DISCONN_WIFI(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP) #define BLUFI_TYPE_IS_CTRL_GET_WIFI_STATUS(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS) -#define BLUFI_TYPE_IS_CTRL_DEAUTHENTICATE_STA(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA) +#define BLUFI_TYPE_IS_CTRL_DEAUTHENTICATE_STA(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA) +#define BLUFI_TYPE_IS_CTRL_GET_VERSION(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION) #define BLUFI_TYPE_IS_DATA_NEG(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_NEG) #define BLUFI_TYPE_IS_DATA_STA_BSSID(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID) diff --git a/components/bt/bluedroid/btc/profile/esp/include/btc_blufi_prf.h b/components/bt/bluedroid/btc/profile/esp/include/btc_blufi_prf.h index eb909e3a2ee..1d82d0c9a5b 100644 --- a/components/bt/bluedroid/btc/profile/esp/include/btc_blufi_prf.h +++ b/components/bt/bluedroid/btc/profile/esp/include/btc_blufi_prf.h @@ -41,4 +41,7 @@ void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks); void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); void btc_blufi_call_deep_free(btc_msg_t *msg); + +uint16_t btc_blufi_get_version(void); + #endif /* __BTC_BLUFI_PRF_H__ */ diff --git a/docs/api/esp_blufi.rst b/docs/api/esp_blufi.rst index 1ffb67bec98..f620edeb83e 100644 --- a/docs/api/esp_blufi.rst +++ b/docs/api/esp_blufi.rst @@ -125,4 +125,5 @@ Functions .. doxygenfunction:: esp_blufi_profile_init .. doxygenfunction:: esp_blufi_profile_deinit .. doxygenfunction:: esp_blufi_send_wifi_conn_report +.. doxygenfunction:: esp_blufi_get_version diff --git a/examples/12_blufi/main/blufi_main.c b/examples/12_blufi/main/blufi_main.c index 3f5cd3dcc6f..9e2b1da0ab3 100644 --- a/examples/12_blufi/main/blufi_main.c +++ b/examples/12_blufi/main/blufi_main.c @@ -328,6 +328,8 @@ void app_main() return; } + BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version()); + blufi_security_init(); esp_ble_gap_register_callback(gap_event_handler); From 9c630b759f3b0f5be4abf556fef509592818a3f0 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Fri, 6 Jan 2017 21:19:58 +0800 Subject: [PATCH 081/167] component/bt : add bt device api 1. add get bd_addr function --- components/bt/bluedroid/api/esp_bt_device.c | 22 +++++++++ .../bt/bluedroid/api/include/esp_bt_defs.h | 3 ++ .../bt/bluedroid/api/include/esp_bt_device.h | 38 +++++++++++++++ docs/api/bt_common.rst | 1 + docs/api/esp_bt_device.rst | 48 +++++++++++++++++++ examples/12_blufi/main/blufi_main.c | 3 ++ 6 files changed, 115 insertions(+) create mode 100644 components/bt/bluedroid/api/esp_bt_device.c create mode 100644 components/bt/bluedroid/api/include/esp_bt_device.h create mode 100644 docs/api/esp_bt_device.rst diff --git a/components/bt/bluedroid/api/esp_bt_device.c b/components/bt/bluedroid/api/esp_bt_device.c new file mode 100644 index 00000000000..745e446c232 --- /dev/null +++ b/components/bt/bluedroid/api/esp_bt_device.c @@ -0,0 +1,22 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include "esp_bt_device.h" +#include "controller.h" + +const uint8_t *esp_bt_dev_get_address(void) +{ + return controller_get_interface()->get_address()->address; +} diff --git a/components/bt/bluedroid/api/include/esp_bt_defs.h b/components/bt/bluedroid/api/include/esp_bt_defs.h index cba8fbe74f4..cdb56a49b81 100644 --- a/components/bt/bluedroid/api/include/esp_bt_defs.h +++ b/components/bt/bluedroid/api/include/esp_bt_defs.h @@ -96,6 +96,9 @@ typedef enum { /// Maximum of the application id #define ESP_APP_ID_MAX 0x7fff +#define ESP_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define ESP_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] + #ifdef __cplusplus } #endif diff --git a/components/bt/bluedroid/api/include/esp_bt_device.h b/components/bt/bluedroid/api/include/esp_bt_device.h new file mode 100644 index 00000000000..51c24f28812 --- /dev/null +++ b/components/bt/bluedroid/api/include/esp_bt_device.h @@ -0,0 +1,38 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_BT_DEVICE_H__ +#define __ESP_BT_DEVICE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @brief Get bluetooth device address. Must use after "esp_bluedroid_enable". + * + * @return bluetooth device address (six bytes) + */ +const uint8_t *esp_bt_dev_get_address(void); + +#ifdef __cplusplus +} +#endif + + +#endif /* __ESP_BT_DEVICE_H__ */ diff --git a/docs/api/bt_common.rst b/docs/api/bt_common.rst index eaa6b8d31f6..f2ce6bae9de 100644 --- a/docs/api/bt_common.rst +++ b/docs/api/bt_common.rst @@ -6,3 +6,4 @@ BT COMMON Bluetooth DEFINE Bluetooth MAIN + Bluetooth DEVICE diff --git a/docs/api/esp_bt_device.rst b/docs/api/esp_bt_device.rst new file mode 100644 index 00000000000..c344a5e633f --- /dev/null +++ b/docs/api/esp_bt_device.rst @@ -0,0 +1,48 @@ +BT DEVICE APIs +=============== + +Overview +-------- + +Bluetooth device reference APIs. + +`Instructions`_ + +Application Example +------------------- + +`Instructions`_ + +.. _Instructions: template.html + + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `bt/bluedroid/api/include/esp_bt_device.h `_ + + +Macros +^^^^^^ + + +Type Definitions +^^^^^^^^^^^^^^^^ + + +Enumerations +^^^^^^^^^^^^ + + +Structures +^^^^^^^^^^ + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_bt_dev_get_address + diff --git a/examples/12_blufi/main/blufi_main.c b/examples/12_blufi/main/blufi_main.c index 9e2b1da0ab3..a95db66eb59 100644 --- a/examples/12_blufi/main/blufi_main.c +++ b/examples/12_blufi/main/blufi_main.c @@ -29,6 +29,7 @@ #include "esp_bt_defs.h" #include "esp_gap_ble_api.h" #include "esp_bt_main.h" +#include "esp_bt_device.h" #include "blufi_demo.h" static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param); @@ -328,6 +329,8 @@ void app_main() return; } + BLUFI_INFO("BD ADDR: "ESP_BD_ADDR_STR"\n", ESP_BD_ADDR_HEX(esp_bt_dev_get_address())); + BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version()); blufi_security_init(); From 6ca2934843d4e0d07d45e481d663d56e34b54716 Mon Sep 17 00:00:00 2001 From: Wu Jian Gang Date: Fri, 6 Jan 2017 18:36:11 +0800 Subject: [PATCH 082/167] example: fix CI error of coap client demo --- .../23_coap_client/main/Kconfig.projbuild | 13 -- examples/23_coap_client/main/coap_client.c | 183 ++++++++++-------- examples/23_coap_client/main/coap_client.h | 43 ---- 3 files changed, 106 insertions(+), 133 deletions(-) delete mode 100644 examples/23_coap_client/main/coap_client.h diff --git a/examples/23_coap_client/main/Kconfig.projbuild b/examples/23_coap_client/main/Kconfig.projbuild index ba3e0d458ba..045137a0e94 100644 --- a/examples/23_coap_client/main/Kconfig.projbuild +++ b/examples/23_coap_client/main/Kconfig.projbuild @@ -1,24 +1,11 @@ menu "Example Configuration" -config TARGET_DOMAIN - string "Target Domain" - default "californium.eclipse.org" - help - Target domain for the example to connect to. - config TARGET_DOMAIN_URI string "Target Uri" default "coap://californium.eclipse.org" help Target uri for the example to use. -config TARGET_PORT_NUMBER - int "Target port number" - range 0 65535 - default 5683 - help - Target port number for the example to connect to. - config WIFI_SSID string "WiFi SSID" default "myssid" diff --git a/examples/23_coap_client/main/coap_client.c b/examples/23_coap_client/main/coap_client.c index cef24f4c6e2..7c0df1afec8 100644 --- a/examples/23_coap_client/main/coap_client.c +++ b/examples/23_coap_client/main/coap_client.c @@ -6,9 +6,10 @@ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ -#include "coap_client.h" #include +#include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -19,12 +20,28 @@ #include "esp_event_loop.h" #include "nvs_flash.h" -#include -#include "coap_config.h" -#include "resource.h" #include "coap.h" +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define COAP_DEFAULT_TIME_SEC 5 +#define COAP_DEFAULT_TIME_USEC 0 + +/* The examples use uri "coap://californium.eclipse.org" that + you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define COAP_DEFAULT_DEMO_URI "coap://californium.eclipse.org" +*/ +#define COAP_DEFAULT_DEMO_URI CONFIG_TARGET_DOMAIN_URI static EventGroupHandle_t wifi_event_group; @@ -33,98 +50,111 @@ static EventGroupHandle_t wifi_event_group; to the AP with an IP? */ const static int CONNECTED_BIT = BIT0; -const static char *TAG = "CoAP_demo"; +const static char *TAG = "CoAP_client"; static void message_handler(struct coap_context_t *ctx, const coap_endpoint_t *local_interface, const coap_address_t *remote, coap_pdu_t *sent, coap_pdu_t *received, const coap_tid_t id) { - unsigned char* data = NULL; - size_t data_len; - if (COAP_RESPONSE_CLASS(received->hdr->code) == 2) { - if (coap_get_data(received, &data_len, &data)) { - printf("Received: %s\n", data); - } - } + unsigned char* data = NULL; + size_t data_len; + if (COAP_RESPONSE_CLASS(received->hdr->code) == 2) { + if (coap_get_data(received, &data_len, &data)) { + printf("Received: %s\n", data); + } + } } static void coap_demo_thread(void *p) { - coap_context_t* ctx = NULL; - coap_address_t dst_addr, src_addr; - static coap_uri_t uri; - fd_set readfds; - struct timeval tv; - int flags, result; - coap_pdu_t* request = NULL; - const char* server_uri = COAP_DEFAULT_DEMO_URI; - uint8_t get_method = 1; + struct hostent *hp; + struct ip4_addr *ip4_addr; - coap_address_init(&src_addr); - src_addr.addr.sin.sin_family = AF_INET; - src_addr.addr.sin.sin_port = htons(0); - src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + coap_context_t* ctx = NULL; + coap_address_t dst_addr, src_addr; + static coap_uri_t uri; + fd_set readfds; + struct timeval tv; + int flags, result; + coap_pdu_t* request = NULL; + const char* server_uri = COAP_DEFAULT_DEMO_URI; + uint8_t get_method = 1; - ctx = coap_new_context(&src_addr); - if (ctx) { - coap_address_init(&dst_addr); - dst_addr.addr.sin.sin_family = AF_INET; - dst_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); - dst_addr.addr.sin.sin_addr.s_addr = inet_addr(COAP_DEFAULT_DEMO_ADDR); + while (1) { + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connected to AP"); - coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri); - request = coap_new_pdu(); - if (request){ - request->hdr->type = COAP_MESSAGE_CON; - request->hdr->id = coap_new_message_id(ctx); - request->hdr->code = get_method; - coap_add_option(request, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s); + if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) { + ESP_LOGE(TAG, "CoAP server uri error"); + break; + } - coap_register_response_handler(ctx, message_handler); - coap_send_confirmed(ctx, ctx->endpoint, &dst_addr, request); + hp = gethostbyname((const char *)uri.host.s); - flags = fcntl(ctx->sockfd, F_GETFL, 0); - fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); + if (hp == NULL) { + ESP_LOGE(TAG, "DNS lookup failed"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + continue; + } - tv.tv_usec = COAP_DEFAULT_TIME_USEC; - tv.tv_sec = COAP_DEFAULT_TIME_SEC; + /* Code to print the resolved IP. - for(;;) { - FD_ZERO(&readfds); - FD_CLR( ctx->sockfd, &readfds ); - FD_SET( ctx->sockfd, &readfds ); - result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); - if (result > 0) { - if (FD_ISSET( ctx->sockfd, &readfds )) - coap_read(ctx); - } else if (result < 0) { - break; - } else { - printf("select timeout\n"); - } - } - } - coap_free_context(ctx); - } + Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */ + ip4_addr = (struct ip4_addr *)hp->h_addr; + ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*ip4_addr)); - vTaskDelete(NULL); -} + coap_address_init(&src_addr); + src_addr.addr.sin.sin_family = AF_INET; + src_addr.addr.sin.sin_port = htons(0); + src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; -static void coap_server_init(void) -{ - int ret = pdPASS; - xTaskHandle coap_handle = NULL; + ctx = coap_new_context(&src_addr); + if (ctx) { + coap_address_init(&dst_addr); + dst_addr.addr.sin.sin_family = AF_INET; + dst_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr; - ret = xTaskCreate(coap_demo_thread, - COAP_DEMO_THREAD_NAME, - COAP_DEMO_THREAD_STACK_WORDS, - NULL, - COAP_DEMO_THREAD_PRORIOTY, - &coap_handle); + request = coap_new_pdu(); + if (request){ + request->hdr->type = COAP_MESSAGE_CON; + request->hdr->id = coap_new_message_id(ctx); + request->hdr->code = get_method; + coap_add_option(request, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s); - if (ret != pdPASS) { - ESP_LOGI(TAG, "create thread %s failed", COAP_DEMO_THREAD_NAME); + coap_register_response_handler(ctx, message_handler); + coap_send_confirmed(ctx, ctx->endpoint, &dst_addr, request); + + flags = fcntl(ctx->sockfd, F_GETFL, 0); + fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); + + tv.tv_usec = COAP_DEFAULT_TIME_USEC; + tv.tv_sec = COAP_DEFAULT_TIME_SEC; + + for(;;) { + FD_ZERO(&readfds); + FD_CLR( ctx->sockfd, &readfds ); + FD_SET( ctx->sockfd, &readfds ); + result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); + if (result > 0) { + if (FD_ISSET( ctx->sockfd, &readfds )) + coap_read(ctx); + } else if (result < 0) { + break; + } else { + ESP_LOGE(TAG, "select timeout"); + } + } + } + coap_free_context(ctx); + } } + + vTaskDelete(NULL); } static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) @@ -135,7 +165,6 @@ static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - coap_server_init(); break; case SYSTEM_EVENT_STA_DISCONNECTED: /* This is a workaround as ESP32 WiFi libs don't currently @@ -165,7 +194,6 @@ static void wifi_conn_init(void) }; ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_ERROR_CHECK( esp_wifi_start() ); } @@ -173,4 +201,5 @@ void app_main(void) { nvs_flash_init(); wifi_conn_init(); + xTaskCreate(coap_demo_thread, "coap", 2048, NULL, 5, NULL); } diff --git a/examples/23_coap_client/main/coap_client.h b/examples/23_coap_client/main/coap_client.h deleted file mode 100644 index 50f4ccd16fe..00000000000 --- a/examples/23_coap_client/main/coap_client.h +++ /dev/null @@ -1,43 +0,0 @@ -/* CoAP client Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ - -#ifndef _COAP_CLIENT_H_ -#define _COAP_CLIENT_H_ - -#include - -/* The examples use simple WiFi configuration that you can set via - 'make menuconfig'. - - If you'd rather not, just change the below entries to strings with - the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" -*/ -#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID -#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD - -#define COAP_DEMO_THREAD_NAME "CoAP_demo" -#define COAP_DEMO_THREAD_STACK_WORDS 10240 -#define COAP_DEMO_THREAD_PRORIOTY 8 - -#define COAP_DEFAULT_TIME_SEC 5 -#define COAP_DEFAULT_TIME_USEC 0 - -/* The examples use domain of "californium.eclipse.org",uri "coap://californium.eclipse.org" and port number of 5683 that - you can set via 'make menuconfig'. - - If you'd rather not, just change the below entries to strings with - the config you want - ie #define COAP_DEFAULT_DEMO_ADDR "californium.eclipse.org" - , ie #define COAP_DEFAULT_DEMO_URI "coap://californium.eclipse.org" and ie #define COAP_DEFAULT_PORT 5683 -*/ -#define COAP_DEFAULT_PORT CONFIG_TARGET_PORT_NUMBER -#define COAP_DEFAULT_DEMO_ADDR CONFIG_TARGET_DOMAIN -#define COAP_DEFAULT_DEMO_URI CONFIG_TARGET_DOMAIN_URI - -#endif - From 4491dd0e2a2024e5c3fcc3e877aa459079179d87 Mon Sep 17 00:00:00 2001 From: Wu Jian Gang Date: Fri, 6 Jan 2017 18:42:46 +0800 Subject: [PATCH 083/167] example: fix CI error of coap server demo --- .../24_coap_server/main/Kconfig.projbuild | 7 - examples/24_coap_server/main/coap_server.c | 168 +++++++++--------- examples/24_coap_server/main/coap_server.h | 38 ---- 3 files changed, 85 insertions(+), 128 deletions(-) delete mode 100644 examples/24_coap_server/main/coap_server.h diff --git a/examples/24_coap_server/main/Kconfig.projbuild b/examples/24_coap_server/main/Kconfig.projbuild index 4926bfb2002..7a9cb97a0e0 100644 --- a/examples/24_coap_server/main/Kconfig.projbuild +++ b/examples/24_coap_server/main/Kconfig.projbuild @@ -1,12 +1,5 @@ menu "Example Configuration" -config LOCAL_PORT_NUMBER - int "Local port number" - range 0 65535 - default 5683 - help - Local port number for the example to use. - config WIFI_SSID string "WiFi SSID" default "myssid" diff --git a/examples/24_coap_server/main/coap_server.c b/examples/24_coap_server/main/coap_server.c index 4e066689bb7..75e3296f79a 100644 --- a/examples/24_coap_server/main/coap_server.c +++ b/examples/24_coap_server/main/coap_server.c @@ -6,9 +6,9 @@ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ -#include "coap_server.h" #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -19,12 +19,20 @@ #include "esp_event_loop.h" #include "nvs_flash.h" -#include -#include "coap_config.h" -#include "resource.h" #include "coap.h" +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define COAP_DEFAULT_TIME_SEC 5 +#define COAP_DEFAULT_TIME_USEC 0 static EventGroupHandle_t wifi_event_group; @@ -33,32 +41,32 @@ static EventGroupHandle_t wifi_event_group; to the AP with an IP? */ const static int CONNECTED_BIT = BIT0; -const static char *TAG = "CoAP_demo"; +const static char *TAG = "CoAP_server"; static coap_async_state_t *async = NULL; static void send_async_response(coap_context_t *ctx, const coap_endpoint_t *local_if) { - coap_pdu_t *response; - unsigned char buf[3]; - const char* response_data = "Hello World!"; - size_t size = sizeof(coap_hdr_t) + 20; - response = coap_pdu_init(async->flags & COAP_MESSAGE_CON, COAP_RESPONSE_CODE(205), 0, size); - response->hdr->id = coap_new_message_id(ctx); - if (async->tokenlen) - coap_add_token(response, async->tokenlen, async->token); - coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf); - coap_add_data (response, strlen(response_data), (unsigned char *)response_data); + coap_pdu_t *response; + unsigned char buf[3]; + const char* response_data = "Hello World!"; + size_t size = sizeof(coap_hdr_t) + 20; + response = coap_pdu_init(async->flags & COAP_MESSAGE_CON, COAP_RESPONSE_CODE(205), 0, size); + response->hdr->id = coap_new_message_id(ctx); + if (async->tokenlen) + coap_add_token(response, async->tokenlen, async->token); + coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf); + coap_add_data (response, strlen(response_data), (unsigned char *)response_data); - if (coap_send(ctx, local_if, &async->peer, response) == COAP_INVALID_TID) { + if (coap_send(ctx, local_if, &async->peer, response) == COAP_INVALID_TID) { - } - coap_delete_pdu(response); - coap_async_state_t *tmp; - coap_remove_async(ctx, async->id, &tmp); - coap_free_async(async); - async = NULL; + } + coap_delete_pdu(response); + coap_async_state_t *tmp; + coap_remove_async(ctx, async->id, &tmp); + coap_free_async(async); + async = NULL; } /* @@ -69,76 +77,70 @@ async_handler(coap_context_t *ctx, struct coap_resource_t *resource, const coap_endpoint_t *local_interface, coap_address_t *peer, coap_pdu_t *request, str *token, coap_pdu_t *response) { - async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM, (void*)"no data"); + async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM, (void*)"no data"); } static void coap_demo_thread(void *p) { - coap_context_t* ctx = NULL; - coap_address_t serv_addr; - coap_resource_t* resource = NULL; - fd_set readfds; - struct timeval tv; - int flags = 0; - /* Prepare the CoAP server socket */ - coap_address_init(&serv_addr); - serv_addr.addr.sin.sin_family = AF_INET; - serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; - serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); - ctx = coap_new_context(&serv_addr); - if (ctx) { - flags = fcntl(ctx->sockfd, F_GETFL, 0); - fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); + coap_context_t* ctx = NULL; + coap_address_t serv_addr; + coap_resource_t* resource = NULL; + fd_set readfds; + struct timeval tv; + int flags = 0; - tv.tv_usec = COAP_DEFAULT_TIME_USEC; - tv.tv_sec = COAP_DEFAULT_TIME_SEC; - /* Initialize the resource */ - resource = coap_resource_init((unsigned char *)"Espressif", 9, 0); - if (resource){ - coap_register_handler(resource, COAP_REQUEST_GET, async_handler); - coap_add_resource(ctx, resource); - /*For incoming connections*/ - for (;;) { - FD_ZERO(&readfds); - FD_CLR( ctx->sockfd, &readfds); - FD_SET( ctx->sockfd, &readfds); + while (1) { + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connected to AP"); - int result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); - if (result > 0){ - if (FD_ISSET( ctx->sockfd, &readfds )) - coap_read(ctx); - } else if (result < 0){ - break; - } else { - printf("select timeout\n"); - } + /* Prepare the CoAP server socket */ + coap_address_init(&serv_addr); + serv_addr.addr.sin.sin_family = AF_INET; + serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + ctx = coap_new_context(&serv_addr); + if (ctx) { + flags = fcntl(ctx->sockfd, F_GETFL, 0); + fcntl(ctx->sockfd, F_SETFL, flags|O_NONBLOCK); - if (async) - send_async_response(ctx, ctx->endpoint); - } - } + tv.tv_usec = COAP_DEFAULT_TIME_USEC; + tv.tv_sec = COAP_DEFAULT_TIME_SEC; + /* Initialize the resource */ + resource = coap_resource_init((unsigned char *)"Espressif", 9, 0); + if (resource){ + coap_register_handler(resource, COAP_REQUEST_GET, async_handler); + coap_add_resource(ctx, resource); + /*For incoming connections*/ + for (;;) { + FD_ZERO(&readfds); + FD_CLR( ctx->sockfd, &readfds); + FD_SET( ctx->sockfd, &readfds); - coap_free_context(ctx); - } + int result = select( FD_SETSIZE, &readfds, 0, 0, &tv ); + if (result > 0){ + if (FD_ISSET( ctx->sockfd, &readfds )) + coap_read(ctx); + } else if (result < 0){ + break; + } else { + ESP_LOGE(TAG, "select timeout"); + } - vTaskDelete(NULL); -} + if (async) { + send_async_response(ctx, ctx->endpoint); + } + } + } -static void coap_server_init(void) -{ - int ret = pdPASS; - xTaskHandle coap_handle = NULL; - - ret = xTaskCreate(coap_demo_thread, - COAP_DEMO_THREAD_NAME, - COAP_DEMO_THREAD_STACK_WORDS, - NULL, - COAP_DEMO_THREAD_PRORIOTY, - &coap_handle); - - if (ret != pdPASS) { - ESP_LOGI(TAG, "create thread %s failed", COAP_DEMO_THREAD_NAME); + coap_free_context(ctx); + } } + + vTaskDelete(NULL); } static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) @@ -149,7 +151,6 @@ static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - coap_server_init(); break; case SYSTEM_EVENT_STA_DISCONNECTED: /* This is a workaround as ESP32 WiFi libs don't currently @@ -179,7 +180,6 @@ static void wifi_conn_init(void) }; ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_ERROR_CHECK( esp_wifi_start() ); } @@ -187,4 +187,6 @@ void app_main(void) { nvs_flash_init(); wifi_conn_init(); + + xTaskCreate(coap_demo_thread, "coap", 2048, NULL, 5, NULL); } diff --git a/examples/24_coap_server/main/coap_server.h b/examples/24_coap_server/main/coap_server.h deleted file mode 100644 index e9e8728fc37..00000000000 --- a/examples/24_coap_server/main/coap_server.h +++ /dev/null @@ -1,38 +0,0 @@ -/* CoAP server Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ - -#ifndef _COAP_SERVER_H_ -#define _COAP_SERVER_H_ - -#include - -/* The examples use simple WiFi configuration that you can set via - 'make menuconfig'. - - If you'd rather not, just change the below entries to strings with - the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" -*/ -#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID -#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD - -#define COAP_DEMO_THREAD_NAME "CoAP_demo" -#define COAP_DEMO_THREAD_STACK_WORDS 10240 -#define COAP_DEMO_THREAD_PRORIOTY 8 - -/* The examples use local port number of 5683 that you can set via 'make menuconfig'. - - If you'd rather not, just change the below entries to strings with - the config you want - ie #define OPENSSL_DEMO_TARGET_TCP_PORT 5683 -*/ -#define COAP_DEFAULT_PORT CONFIG_LOCAL_PORT_NUMBER -#define COAP_DEFAULT_TIME_SEC 5 -#define COAP_DEFAULT_TIME_USEC 0 - -#endif - From ed01eb2df16c871ba8e67746953d5602867bc1a0 Mon Sep 17 00:00:00 2001 From: Wu Jian Gang Date: Fri, 6 Jan 2017 18:52:58 +0800 Subject: [PATCH 084/167] example: fix CI error of ota demo --- examples/26_ota/main/ota.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/26_ota/main/ota.c b/examples/26_ota/main/ota.c index 8c8caf2d376..4956129e5dc 100644 --- a/examples/26_ota/main/ota.c +++ b/examples/26_ota/main/ota.c @@ -7,6 +7,9 @@ CONDITIONS OF ANY KIND, either express or implied. */ #include +#include +#include + #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" @@ -19,11 +22,6 @@ #include "esp_partition.h" #include "nvs_flash.h" -#include "lwip/err.h" -#include "lwip/sockets.h" -#include "lwip/sys.h" -#include "lwip/netdb.h" -#include "lwip/dns.h" #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD @@ -226,6 +224,10 @@ void __attribute__((noreturn)) task_fatal_error() ESP_LOGE(TAG, "Exiting task due to fatal error..."); close(socket_id); (void)vTaskDelete(NULL); + + while (1) { + ; + } } void main_task(void *pvParameter) From 1b9f477b152c843fe1e6215d5aac3a5966c33674 Mon Sep 17 00:00:00 2001 From: Wu Jian Gang Date: Fri, 6 Jan 2017 21:39:32 +0800 Subject: [PATCH 085/167] example: Reindex ota demo --- examples/{26_ota => 25_ota}/Makefile | 0 examples/{26_ota => 25_ota}/OTA_workflow.png | Bin examples/{26_ota => 25_ota}/README.md | 0 examples/{26_ota => 25_ota}/main/Kconfig.projbuild | 0 examples/{26_ota => 25_ota}/main/component.mk | 0 examples/{26_ota => 25_ota}/main/ota.c | 0 examples/{26_ota => 25_ota}/sdkconfig.defaults | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename examples/{26_ota => 25_ota}/Makefile (100%) rename examples/{26_ota => 25_ota}/OTA_workflow.png (100%) rename examples/{26_ota => 25_ota}/README.md (100%) rename examples/{26_ota => 25_ota}/main/Kconfig.projbuild (100%) rename examples/{26_ota => 25_ota}/main/component.mk (100%) rename examples/{26_ota => 25_ota}/main/ota.c (100%) rename examples/{26_ota => 25_ota}/sdkconfig.defaults (100%) diff --git a/examples/26_ota/Makefile b/examples/25_ota/Makefile similarity index 100% rename from examples/26_ota/Makefile rename to examples/25_ota/Makefile diff --git a/examples/26_ota/OTA_workflow.png b/examples/25_ota/OTA_workflow.png similarity index 100% rename from examples/26_ota/OTA_workflow.png rename to examples/25_ota/OTA_workflow.png diff --git a/examples/26_ota/README.md b/examples/25_ota/README.md similarity index 100% rename from examples/26_ota/README.md rename to examples/25_ota/README.md diff --git a/examples/26_ota/main/Kconfig.projbuild b/examples/25_ota/main/Kconfig.projbuild similarity index 100% rename from examples/26_ota/main/Kconfig.projbuild rename to examples/25_ota/main/Kconfig.projbuild diff --git a/examples/26_ota/main/component.mk b/examples/25_ota/main/component.mk similarity index 100% rename from examples/26_ota/main/component.mk rename to examples/25_ota/main/component.mk diff --git a/examples/26_ota/main/ota.c b/examples/25_ota/main/ota.c similarity index 100% rename from examples/26_ota/main/ota.c rename to examples/25_ota/main/ota.c diff --git a/examples/26_ota/sdkconfig.defaults b/examples/25_ota/sdkconfig.defaults similarity index 100% rename from examples/26_ota/sdkconfig.defaults rename to examples/25_ota/sdkconfig.defaults From a575b9e893ca649008cba5df97b0c0e107c6e08b Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 6 Jan 2017 19:35:22 +0800 Subject: [PATCH 086/167] esp32: modify ld file to fix a crash issue --- components/esp32/heap_alloc_caps.c | 4 ++-- components/esp32/ld/esp32.common.ld | 2 +- components/esp32/ld/esp32.rom.ld | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/esp32/heap_alloc_caps.c b/components/esp32/heap_alloc_caps.c index 7bb3b408791..a89455835c3 100644 --- a/components/esp32/heap_alloc_caps.c +++ b/components/esp32/heap_alloc_caps.c @@ -170,7 +170,7 @@ Warning: These variables are assumed to have the start and end of the data and i area used statically by the program, respectively. These variables are defined in the ld file. */ -extern int _bss_start, _heap_start, _init_start, _iram_text_end; +extern int _data_start, _heap_start, _init_start, _iram_text_end; /* Initialize the heap allocator. We pass it a bunch of region descriptors, but we need to modify those first to accommodate for @@ -183,7 +183,7 @@ void heap_alloc_caps_init() { //Compile-time assert to see if we don't have more tags than is set in heap_regions.h _Static_assert((sizeof(tag_desc)/sizeof(tag_desc[0]))-1 <= HEAPREGIONS_MAX_TAGCOUNT, "More than HEAPREGIONS_MAX_TAGCOUNT tags defined!"); //Disable the bits of memory where this code is loaded. - disable_mem_region(&_bss_start, &_heap_start); //DRAM used by bss/data static variables + disable_mem_region(&_data_start, &_heap_start); //DRAM used by bss/data static variables disable_mem_region(&_init_start, &_iram_text_end); //IRAM used by code disable_mem_region((void*)0x3ffae000, (void*)0x3ffb0000); //knock out ROM data region disable_mem_region((void*)0x40070000, (void*)0x40078000); //CPU0 cache region diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index c199a41d3d2..833eb909692 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -103,7 +103,6 @@ SECTIONS *libesp32.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.); . = ALIGN(4); - _heap_start = ABSOLUTE(.); } >dram0_0_seg /* Shared RAM */ @@ -127,6 +126,7 @@ SECTIONS *(COMMON) . = ALIGN (8); _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); } >dram0_0_seg .flash.rodata : diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 6241ff840eb..fef42c9528d 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -87,9 +87,9 @@ PROVIDE ( _ctype_ = 0x3ff96354 ); PROVIDE ( __ctype_ptr__ = 0x3ff96350 ); PROVIDE ( __ctzdi2 = 0x4000ca64 ); PROVIDE ( __ctzsi2 = 0x4000c7f0 ); -PROVIDE ( _data_end = 0x4000d5c8 ); +PROVIDE ( _data_end_rom = 0x4000d5c8 ); PROVIDE ( _data_end_btdm_rom = 0x4000d4f8 ); -PROVIDE ( _data_start = 0x4000d4f8 ); +PROVIDE ( _data_start_rom = 0x4000d4f8 ); PROVIDE ( _data_start_btdm_rom = 0x4000d4f4 ); PROVIDE ( _data_start_btdm = 0x3ffae6e0); PROVIDE ( _data_end_btdm = 0x3ffaff10); From 0e7f7a2112c94b937f99638fe4ae8f50bc133719 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Sat, 7 Jan 2017 16:32:03 +0800 Subject: [PATCH 087/167] esp32: fix wifi auth issue Fix wifi auth failed issue by default phy mode to 11N --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 21e433b8277..23d627498d6 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 21e433b8277adc1d65894ec0a65c60f78dc84f7c +Subproject commit 23d627498d60e524e5d95d649481bd650aff1aad From 0bfe08578b7a30180e357343d12f2bdfcbb7ff53 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 6 Jan 2017 17:19:09 +0800 Subject: [PATCH 088/167] Add sdkconfig include to cpu_util.h so CONFIG_ESP32_DEBUG_OCDAWARE actually resolves --- components/esp32/cpu_util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esp32/cpu_util.c b/components/esp32/cpu_util.c index e3b4ef8f164..ecfcab4baf3 100644 --- a/components/esp32/cpu_util.c +++ b/components/esp32/cpu_util.c @@ -16,6 +16,7 @@ #include "soc/cpu.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" +#include "sdkconfig.h" void IRAM_ATTR esp_cpu_stall(int cpu_id) { From c1b06bf0a2596bbfbddb4ecf9b0eb275d5e0cc45 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 9 Jan 2017 03:08:24 +0800 Subject: [PATCH 089/167] bootloader: export ets_update_cpu_frequency --- components/bootloader/src/main/component.mk | 5 ++++- components/bootloader/src/main/esp32.bootloader.rom.ld | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 components/bootloader/src/main/esp32.bootloader.rom.ld diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index 9bcc5352a06..73cd9287dff 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -5,7 +5,10 @@ # we pull in bootloader-specific linker arguments. # -LINKER_SCRIPTS := esp32.bootloader.ld $(IDF_PATH)/components/esp32/ld/esp32.rom.ld +LINKER_SCRIPTS := \ + esp32.bootloader.ld \ + $(IDF_PATH)/components/esp32/ld/esp32.rom.ld \ + esp32.bootloader.rom.ld COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain $(addprefix -T ,$(LINKER_SCRIPTS)) diff --git a/components/bootloader/src/main/esp32.bootloader.rom.ld b/components/bootloader/src/main/esp32.bootloader.rom.ld new file mode 100644 index 00000000000..70f83bdf54b --- /dev/null +++ b/components/bootloader/src/main/esp32.bootloader.rom.ld @@ -0,0 +1 @@ +PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ From edd924f2735449be266ae5e48f0cbb1ca486067c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 19 Dec 2016 22:19:47 +0800 Subject: [PATCH 090/167] sdmmc: add peripheral driver and protocol layer --- components/driver/include/driver/sdmmc_defs.h | 303 ++++++++++ components/driver/include/driver/sdmmc_host.h | 165 +++++ .../driver/include/driver/sdmmc_types.h | 140 +++++ components/driver/sdmmc_host.c | 463 ++++++++++++++ components/driver/sdmmc_private.h | 44 ++ components/driver/sdmmc_transaction.c | 372 ++++++++++++ components/esp32/include/esp_err.h | 3 +- components/esp32/include/soc/sdmmc_reg.h | 94 +++ components/esp32/include/soc/sdmmc_struct.h | 371 ++++++++++++ components/esp32/include/soc/soc.h | 2 +- components/esp32/ld/esp32.peripherals.ld | 1 + components/sdmmc/component.mk | 0 components/sdmmc/include/sdmmc_cmd.h | 77 +++ components/sdmmc/sdmmc_cmd.c | 571 ++++++++++++++++++ components/sdmmc/test/component.mk | 1 + components/sdmmc/test/test_sd.c | 95 +++ examples/27_sd_card/Makefile | 9 + examples/27_sd_card/README.md | 81 +++ examples/27_sd_card/main/component.mk | 4 + examples/27_sd_card/main/sd_card.c | 107 ++++ 20 files changed, 2901 insertions(+), 2 deletions(-) create mode 100644 components/driver/include/driver/sdmmc_defs.h create mode 100644 components/driver/include/driver/sdmmc_host.h create mode 100644 components/driver/include/driver/sdmmc_types.h create mode 100644 components/driver/sdmmc_host.c create mode 100644 components/driver/sdmmc_private.h create mode 100644 components/driver/sdmmc_transaction.c create mode 100644 components/esp32/include/soc/sdmmc_reg.h create mode 100644 components/esp32/include/soc/sdmmc_struct.h create mode 100755 components/sdmmc/component.mk create mode 100644 components/sdmmc/include/sdmmc_cmd.h create mode 100644 components/sdmmc/sdmmc_cmd.c create mode 100644 components/sdmmc/test/component.mk create mode 100644 components/sdmmc/test/test_sd.c create mode 100644 examples/27_sd_card/Makefile create mode 100644 examples/27_sd_card/README.md create mode 100644 examples/27_sd_card/main/component.mk create mode 100644 examples/27_sd_card/main/sd_card.c diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h new file mode 100644 index 00000000000..9913bfd1757 --- /dev/null +++ b/components/driver/include/driver/sdmmc_defs.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2006 Uwe Stuehler + * Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SDMMC_DEFS_H_ +#define _SDMMC_DEFS_H_ + +#include +#include + +/* MMC commands */ /* response type */ +#define MMC_GO_IDLE_STATE 0 /* R0 */ +#define MMC_SEND_OP_COND 1 /* R3 */ +#define MMC_ALL_SEND_CID 2 /* R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* R1 */ +#define MMC_SWITCH 6 /* R1B */ +#define MMC_SELECT_CARD 7 /* R1 */ +#define MMC_SEND_EXT_CSD 8 /* R1 */ +#define MMC_SEND_CSD 9 /* R2 */ +#define MMC_STOP_TRANSMISSION 12 /* R1B */ +#define MMC_SEND_STATUS 13 /* R1 */ +#define MMC_SET_BLOCKLEN 16 /* R1 */ +#define MMC_READ_BLOCK_SINGLE 17 /* R1 */ +#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */ +#define MMC_SET_BLOCK_COUNT 23 /* R1 */ +#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ +#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */ +#define MMC_APP_CMD 55 /* R1 */ + +/* SD commands */ /* response type */ +#define SD_SEND_RELATIVE_ADDR 3 /* R6 */ +#define SD_SEND_SWITCH_FUNC 6 /* R1 */ +#define SD_SEND_IF_COND 8 /* R7 */ + +/* SD application commands */ /* response type */ +#define SD_APP_SET_BUS_WIDTH 6 /* R1 */ +#define SD_APP_OP_COND 41 /* R3 */ +#define SD_APP_SEND_SCR 51 /* R1 */ + +/* OCR bits */ +#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */ +#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */ +#define MMC_OCR_SECTOR_MODE (1<<30) +#define MMC_OCR_BYTE_MODE (1<<29) +#define MMC_OCR_3_5V_3_6V (1<<23) +#define MMC_OCR_3_4V_3_5V (1<<22) +#define MMC_OCR_3_3V_3_4V (1<<21) +#define MMC_OCR_3_2V_3_3V (1<<20) +#define MMC_OCR_3_1V_3_2V (1<<19) +#define MMC_OCR_3_0V_3_1V (1<<18) +#define MMC_OCR_2_9V_3_0V (1<<17) +#define MMC_OCR_2_8V_2_9V (1<<16) +#define MMC_OCR_2_7V_2_8V (1<<15) +#define MMC_OCR_2_6V_2_7V (1<<14) +#define MMC_OCR_2_5V_2_6V (1<<13) +#define MMC_OCR_2_4V_2_5V (1<<12) +#define MMC_OCR_2_3V_2_4V (1<<11) +#define MMC_OCR_2_2V_2_3V (1<<10) +#define MMC_OCR_2_1V_2_2V (1<<9) +#define MMC_OCR_2_0V_2_1V (1<<8) +#define MMC_OCR_1_65V_1_95V (1<<7) + +#define SD_OCR_SDHC_CAP (1<<30) +#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */ + +/* R1 response type bits */ +#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */ +#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */ + +/* 48-bit response decoding (32 bits w/o CRC) */ +#define MMC_R1(resp) ((resp)[0]) +#define MMC_R3(resp) ((resp)[0]) +#define SD_R6(resp) ((resp)[0]) +#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf) + +/* RCA argument and response */ +#define MMC_ARG_RCA(rca) ((rca) << 16) +#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16) + +/* bus width argument */ +#define SD_ARG_BUS_WIDTH_1 0 +#define SD_ARG_BUS_WIDTH_4 2 + +/* EXT_CSD fields */ +#define EXT_CSD_BUS_WIDTH 183 /* WO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_SEC_COUNT 212 /* RO */ + +/* EXT_CSD field definitions */ +#define EXT_CSD_CMD_SET_NORMAL (1U << 0) +#define EXT_CSD_CMD_SET_SECURE (1U << 1) +#define EXT_CSD_CMD_SET_CPSECURE (1U << 2) + +/* EXT_CSD_HS_TIMING */ +#define EXT_CSD_HS_TIMING_BC 0 +#define EXT_CSD_HS_TIMING_HS 1 +#define EXT_CSD_HS_TIMING_HS200 2 +#define EXT_CSD_HS_TIMING_HS400 3 + +/* EXT_CSD_BUS_WIDTH */ +#define EXT_CSD_BUS_WIDTH_1 0 +#define EXT_CSD_BUS_WIDTH_4 1 +#define EXT_CSD_BUS_WIDTH_8 2 +#define EXT_CSD_BUS_WIDTH_4_DDR 5 +#define EXT_CSD_BUS_WIDTH_8_DDR 6 + +/* EXT_CSD_CARD_TYPE */ +/* The only currently valid values for this field are 0x01, 0x03, 0x07, + * 0x0B and 0x0F. */ +#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) +#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) +#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) +#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) +#define EXT_CSD_CARD_TYPE_26M 0x01 +#define EXT_CSD_CARD_TYPE_52M 0x03 +#define EXT_CSD_CARD_TYPE_52M_V18 0x07 +#define EXT_CSD_CARD_TYPE_52M_V12 0x0b +#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f + +/* MMC_SWITCH access mode */ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* MMC R2 response (CSD) */ +#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define MMC_CSD_CSDVER_1_0 1 +#define MMC_CSD_CSDVER_2_0 2 +#define MMC_CSD_CSDVER_EXT_CSD 3 +#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4) +#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */ +#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */ +#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */ +#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */ +#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */ +#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \ + (MMC_CSD_C_SIZE_MULT((resp))+2)) +#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) + +/* MMC v1 R2 response (CID) */ +#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24) +#define MMC_CID_PNM_V1_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \ + (pnm)[7] = '\0'; \ + } while (0) +#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8) +#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24) +#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8) + +/* MMC v2 R2 response (CID) */ +#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8) +#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16) +#define MMC_CID_PNM_V2_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = '\0'; \ + } while (0) +#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32) + +/* SD R2 response (CSD) */ +#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define SD_CSD_CSDVER_1_0 0 +#define SD_CSD_CSDVER_2_0 1 +#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8) +#define SD_CSD_TAAC_1_5_MSEC 0x26 +#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8) +#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) +#define SD_CSD_SPEED_25_MHZ 0x32 +#define SD_CSD_SPEED_50_MHZ 0x5a +#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) +#define SD_CSD_CCC_BASIC (1 << 0) /* basic */ +#define SD_CSD_CCC_BR (1 << 2) /* block read */ +#define SD_CSD_CCC_BW (1 << 4) /* block write */ +#define SD_CSD_CCC_ERASE (1 << 5) /* erase */ +#define SD_CSD_CCC_WP (1 << 6) /* write protection */ +#define SD_CSD_CCC_LC (1 << 7) /* lock card */ +#define SD_CSD_CCC_AS (1 << 8) /*application specific*/ +#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */ +#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */ +#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1) +#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1) +#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1) +#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1) +#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \ + (SD_CSD_C_SIZE_MULT((resp))+2)) +#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22) +#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10) +#define SD_CSD_V2_BL_LEN 0x9 /* 512 */ +#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3) +#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3) +#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3) +#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3) +#define SD_CSD_VDD_RW_CURR_100mA 0x7 +#define SD_CSD_VDD_RW_CURR_80mA 0x6 +#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) +#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1) +#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */ +#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */ +#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1) +#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3) +#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4) +#define SD_CSD_RW_BL_LEN_2G 0xa +#define SD_CSD_RW_BL_LEN_1G 0x9 +#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1) +#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1) +#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1) +#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1) +#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1) +#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2) + +/* SD R2 response (CID) */ +#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8) +#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16) +#define SD_CID_PNM_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = '\0'; \ + } while (0) +#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8) +#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32) +#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12) + +/* SCR (SD Configuration Register) */ +#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4) +#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */ +#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4) +#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */ +#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */ +#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */ +#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1) +#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3) +#define SCR_SD_SECURITY_NONE 0 /* no security */ +#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */ +#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */ +#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4) +#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */ +#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */ +#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1) +#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4) +#define SCR_SD_SPEC4(scr) MMC_RSP_BITS((scr), 42, 1) +#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 8) +#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1) +#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1) +#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) + +/* Status of Switch Function */ +#define SFUNC_STATUS_GROUP(status, group) \ + (__bitfield((uint32_t *)(status), 400 + (group - 1) * 16, 16)) + +#define SD_ACCESS_MODE_SDR12 0 +#define SD_ACCESS_MODE_SDR25 1 +#define SD_ACCESS_MODE_SDR50 2 +#define SD_ACCESS_MODE_SDR104 3 +#define SD_ACCESS_MODE_DDR50 4 + +static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len) +{ + uint32_t mask = (len % 32 == 0) ? UINT_MAX : UINT_MAX >> (32 - (len % 32)); + size_t word = 3 - start / 32; + size_t shift = start % 32; + uint32_t right = src[word] >> shift; + uint32_t left = (len + shift <= 32) ? 0 : src[word - 1] << ((32 - shift) % 32); + return (left | right) & mask; +} + +#endif //_SDMMC_DEFS_H_ diff --git a/components/driver/include/driver/sdmmc_host.h b/components/driver/include/driver/sdmmc_host.h new file mode 100644 index 00000000000..0f56c266a84 --- /dev/null +++ b/components/driver/include/driver/sdmmc_host.h @@ -0,0 +1,165 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include "esp_err.h" +#include "sdmmc_types.h" +#include "driver/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SDMMC_HOST_SLOT_0 0 ///< SDMMC slot 0 +#define SDMMC_HOST_SLOT_1 1 ///< SDMMC slot 1 + +/** + * @brief Default sdmmc_host_t structure initializer for SDMMC peripheral + * + * Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz + */ +#define SDMMC_HOST_DEFAULT() {\ + .flags = SDMMC_HOST_FLAG_4BIT, \ + .slot = SDMMC_HOST_SLOT_1, \ + .max_freq_khz = SDMMC_FREQ_DEFAULT, \ + .io_voltage = 3.3f, \ + .init = &sdmmc_host_init, \ + .set_bus_width = &sdmmc_host_set_bus_width, \ + .set_card_clk = &sdmmc_host_set_card_clk, \ + .do_transaction = &sdmmc_host_do_transaction, \ + .deinit = &sdmmc_host_deinit, \ +} + +/** + * Extra configuration for SDMMC peripheral slot + */ +typedef struct { + gpio_num_t gpio_cd; ///< GPIO number of card detect signal + gpio_num_t gpio_wp; ///< GPIO number of write protect signal +} sdmmc_slot_config_t; + +#define SDMMC_SLOT_NO_CD ((gpio_num_t) -1) ///< indicates that card detect line is not used +#define SDMMC_SLOT_NO_WP ((gpio_num_t) -1) ///< indicates that write protect line is not used + +/** + * Macro defining default configuration of SDMMC host slot + */ +#define SDMMC_SLOT_CONFIG_DEFAULT() {\ + .gpio_cd = SDMMC_SLOT_NO_CD, \ + .gpio_wp = SDMMC_SLOT_NO_WP, \ +} + +/** + * @brief Initialize SDMMC host peripheral + * + * @note This function is not thread safe + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if sdmmc_host_init was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + */ +esp_err_t sdmmc_host_init(); + +/** + * @brief Initialize given slot of SDMMC peripheral + * + * On the ESP32, SDMMC peripheral has two slots: + * - Slot 0: 8-bit wide, maps to HS1_* signals in PIN MUX + * - Slot 1: 4-bit wide, maps to HS2_* signals in PIN MUX + * + * Card detect and write protect signals can be routed to + * arbitrary GPIOs using GPIO matrix. + * + * @note This function is not thread safe + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param slot_config additional configuration for the slot + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if host has not been initialized using sdmmc_host_init + */ +esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config); + +/** + * @brief Select bus width to be used for data transfer + * + * SD/MMC card must be initialized prior to this command, and a command to set + * bus width has to be sent to the card (e.g. SD_APP_SET_BUS_WIDTH) + * + * @note This function is not thread safe + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param width bus width (1, 4, or 8 for slot 0; 1 or 4 for slot 1) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if slot number or width is not valid + */ +esp_err_t sdmmc_host_set_bus_width(int slot, size_t width); + +/** + * @brief Set card clock frequency + * + * Currently only integer fractions of 40MHz clock can be used. + * For High Speed cards, 40MHz can be used. + * For Default Speed cards, 20MHz can be used. + * + * @note This function is not thread safe + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param freq_khz card clock frequency, in kHz + * @return + * - ESP_OK on success + * - other error codes may be returned in the future + */ +esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz); + +/** + * @brief Send command to the card and get response + * + * This function returns when command is sent and response is received, + * or data is transferred, or timeout occurs. + * + * @note This function is not thread safe w.r.t. init/deinit functions, + * and bus width/clock speed configuration functions. Multiple tasks + * can call sdmmc_host_do_transaction as long as other sdmmc_host_* + * functions are not called. + * + * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1) + * @param cmdinfo pointer to structure describing command and data to transfer + * @return + * - ESP_OK on success + * - ESP_ERR_TIMEOUT if response or data transfer has timed out + * - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed + * - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response + */ +esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo); + +/** + * @brief Disable SDMMC host and release allocated resources + * + * @note This function is not thread safe + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if sdmmc_host_init function has not been called + */ +esp_err_t sdmmc_host_deinit(); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h new file mode 100644 index 00000000000..dfbd2439c69 --- /dev/null +++ b/components/driver/include/driver/sdmmc_types.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2006 Uwe Stuehler + * Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SDMMC_TYPES_H_ +#define _SDMMC_TYPES_H_ + +#include +#include +#include "esp_err.h" + +/** + * Decoded values from SD card Card Specific Data register + */ +typedef struct { + int csd_ver; /*!< CSD structure format */ + int mmc_ver; /*!< MMC version (for CID format) */ + int capacity; /*!< total number of sectors */ + int sector_size; /*!< sector size in bytes */ + int read_block_len; /*!< block length for reads */ + int card_command_class; /*!< Card Command Class for SD */ + int tr_speed; /*!< Max transfer speed */ +} sdmmc_csd_t; + +/** + * Decoded values from SD card Card IDentification register + */ +typedef struct { + int mfg_id; /*!< manufacturer identification number */ + int oem_id; /*!< OEM/product identification number */ + char name[8]; /*!< product name (MMC v1 has the longest) */ + int revision; /*!< product revision */ + int serial; /*!< product serial number */ + int date; /*!< manufacturing date */ +} sdmmc_cid_t; + +/** + * Decoded values from SD Configuration Register + */ +typedef struct { + int sd_spec; /*!< SD Physical layer specification version, reported by card */ + int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */ +} sdmmc_scr_t; + +/** + * SD/MMC command response buffer + */ +typedef uint32_t sdmmc_response_t[4]; + +/** + * SD/MMC command information + */ +typedef struct { + uint32_t opcode; /*!< SD or MMC command index */ + uint32_t arg; /*!< SD/MMC command argument */ + sdmmc_response_t response; /*!< response buffer */ + void* data; /*!< buffer to send or read into */ + size_t datalen; /*!< length of data buffer */ + size_t blklen; /*!< block length */ + int flags; /*!< see below */ +#define SCF_ITSDONE 0x0001 /*!< command is complete */ +#define SCF_CMD(flags) ((flags) & 0x00f0) +#define SCF_CMD_AC 0x0000 +#define SCF_CMD_ADTC 0x0010 +#define SCF_CMD_BC 0x0020 +#define SCF_CMD_BCR 0x0030 +#define SCF_CMD_READ 0x0040 /*!< read command (data expected) */ +#define SCF_RSP_BSY 0x0100 +#define SCF_RSP_136 0x0200 +#define SCF_RSP_CRC 0x0400 +#define SCF_RSP_IDX 0x0800 +#define SCF_RSP_PRESENT 0x1000 +/* response types */ +#define SCF_RSP_R0 0 /*!< none */ +#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136) +#define SCF_RSP_R3 (SCF_RSP_PRESENT) +#define SCF_RSP_R4 (SCF_RSP_PRESENT) +#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) + esp_err_t error; /*!< error returned from transfer */ +} sdmmc_command_t; + +/** + * SD/MMC Host description + * + * This structure defines properties of SD/MMC host and functions + * of SD/MMC host which can be used by upper layers. + */ +typedef struct { + uint32_t flags; /*!< flags defining host properties */ +#define SDMMC_HOST_FLAG_1BIT BIT(0) /*!< host supports 1-line SD and MMC protocol */ +#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */ +#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */ +#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */ + int slot; /*!< slot number, to be passed to host functions */ + int max_freq_khz; /*!< max frequency supported by the host */ +#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */ +#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */ +#define SDMMC_FREQ_PROBING 4000 /*!< SD/MMC probing speed */ + float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */ + esp_err_t (*init)(void); /*!< Host function to initialize the driver */ + esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */ + esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */ + esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */ + esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */ +} sdmmc_host_t; + +/** + * SD/MMC card information structure + */ +typedef struct { + sdmmc_host_t host; /*!< Host with which the card is associated */ + uint32_t ocr; /*!< OCR (Operation Conditions Register) value */ + sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */ + sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */ + sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */ + uint16_t rca; /*!< RCA (Relative Card Address) */ +} sdmmc_card_t; + + + + +#endif // _SDMMC_TYPES_H_ diff --git a/components/driver/sdmmc_host.c b/components/driver/sdmmc_host.c new file mode 100644 index 00000000000..400c85b8899 --- /dev/null +++ b/components/driver/sdmmc_host.c @@ -0,0 +1,463 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "esp_log.h" +#include "esp_intr_alloc.h" +#include "soc/sdmmc_struct.h" +#include "soc/sdmmc_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#include "rom/gpio.h" +#include "driver/gpio.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_private.h" + +#define SDMMC_EVENT_QUEUE_LENGTH 32 + +typedef struct { + uint32_t clk; + uint32_t cmd; + uint32_t d0; + uint32_t d1; + uint32_t d2; + uint32_t d3; + uint32_t d4; + uint32_t d5; + uint32_t d6; + uint32_t d7; + uint8_t card_detect; + uint8_t write_protect; + uint8_t width; +} sdmmc_slot_info_t; + + +static void sdmmc_isr(void* arg); +static void sdmmc_host_dma_init(); + +static const sdmmc_slot_info_t s_slot_info[2] = { + { + .clk = PERIPHS_IO_MUX_SD_CLK_U, + .cmd = PERIPHS_IO_MUX_SD_CMD_U, + .d0 = PERIPHS_IO_MUX_SD_DATA0_U, + .d1 = PERIPHS_IO_MUX_SD_DATA1_U, + .d2 = PERIPHS_IO_MUX_SD_DATA2_U, + .d3 = PERIPHS_IO_MUX_SD_DATA3_U, + .d4 = PERIPHS_IO_MUX_GPIO16_U, + .d5 = PERIPHS_IO_MUX_GPIO17_U, + .d6 = PERIPHS_IO_MUX_GPIO5_U, + .d7 = PERIPHS_IO_MUX_GPIO18_U, + .card_detect = HOST_CARD_DETECT_N_1_IDX, + .write_protect = HOST_CARD_WRITE_PRT_1_IDX, + .width = 8 + }, + { + .clk = PERIPHS_IO_MUX_MTMS_U, + .cmd = PERIPHS_IO_MUX_MTDO_U, + .d0 = PERIPHS_IO_MUX_GPIO2_U, + .d1 = PERIPHS_IO_MUX_GPIO4_U, + .d2 = PERIPHS_IO_MUX_MTDI_U, + .d3 = PERIPHS_IO_MUX_MTCK_U, + .card_detect = HOST_CARD_DETECT_N_2_IDX, + .write_protect = HOST_CARD_WRITE_PRT_2_IDX, + .width = 4 + } +}; + +static const char* TAG = "sdmmc_periph"; +static intr_handle_t s_intr_handle; +static QueueHandle_t s_event_queue; + + +void sdmmc_host_reset() +{ + // Set reset bits + SDMMC.ctrl.controller_reset = 1; + SDMMC.ctrl.dma_reset = 1; + SDMMC.ctrl.fifo_reset = 1; + // Wait for the reset bits to be cleared by hardware + while (SDMMC.ctrl.controller_reset || SDMMC.ctrl.fifo_reset || SDMMC.ctrl.dma_reset) { + ; + } +} + +/* We have two clock divider stages: + * - one is the clock generator which drives SDMMC peripheral, + * it can be configured using SDMMC.clock register. It can generate + * frequencies 160MHz/(N + 1), where 0 < N < 16, I.e. from 10 to 80 MHz. + * - 4 clock dividers inside SDMMC peripheral, which can divide clock + * from the first stage by 2 * M, where 0 < M < 255 + * (they can also be bypassed). + * + * For cards which aren't UHS-1 or UHS-2 cards, which we don't support, + * maximum bus frequency in high speed (HS) mode is 50 MHz. + * Note: for non-UHS-1 cards, HS mode is optional. + * Default speed (DS) mode is mandatory, it works up to 25 MHz. + * Whether the card supports HS or not can be determined using TRAN_SPEED + * field of card's CSD register. + * + * 50 MHz can not be obtained exactly, closest we can get is 53 MHz. + * For now set the first stage divider to generate 40MHz, and then configure + * the second stage dividers to generate the frequency requested. + * + * Of the second stage dividers, div0 is used for card 0, and div1 is used + * for card 1. + */ + +static void sdmmc_host_input_clk_enable() +{ + // Set frequency to 160MHz / (p + 1) = 40MHz, duty cycle (h + 1)/(p + 1) = 1/2 + SDMMC.clock.div_factor_p = 3; + SDMMC.clock.div_factor_h = 1; + SDMMC.clock.div_factor_m = 3; + // Set phases for in/out clocks + SDMMC.clock.phase_dout = 4; + SDMMC.clock.phase_din = 4; + SDMMC.clock.phase_core = 0; + // Wait for the clock to propagate + ets_delay_us(10); +} + +static void sdmmc_host_input_clk_disable() +{ + SDMMC.clock.val = 0; +} + +static void sdmmc_host_clock_update_command(int slot) +{ + // Clock update command (not a real command; just updates CIU registers) + sdmmc_hw_cmd_t cmd_val = { + .card_num = slot, + .update_clk_reg = 1, + .wait_complete = 1 + }; + bool repeat = true; + while(repeat) { + sdmmc_host_start_command(slot, cmd_val, 0); + while (true) { + // Sending clock update command to the CIU can generate HLE error. + // According to the manual, this is okay and we must retry the command. + if (SDMMC.rintsts.hle) { + SDMMC.rintsts.hle = 1; + repeat = true; + break; + } + // When the command is accepted by CIU, start_command bit will be + // cleared in SDMMC.cmd register. + if (SDMMC.cmd.start_command == 0) { + repeat = false; + break; + } + } + } +} + +esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz) +{ + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + const int clk40m = 40000; + + // Disable clock first + SDMMC.clkena.cclk_enable &= ~BIT(slot); + sdmmc_host_clock_update_command(slot); + + // Calculate new dividers + int div = 0; + if (freq_khz < clk40m) { + // round up; extra *2 is because clock divider divides by 2*n + div = (clk40m + freq_khz * 2 - 1) / (freq_khz * 2); + } + ESP_LOGD(TAG, "slot=%d div=%d freq=%dkHz", slot, div, + (div == 0) ? clk40m : clk40m / (2 * div)); + + // Program CLKDIV and CLKSRC, send them to the CIU + switch(slot) { + case 0: + SDMMC.clksrc.card0 = 0; + SDMMC.clkdiv.div0 = div; + break; + case 1: + SDMMC.clksrc.card1 = 1; + SDMMC.clkdiv.div1 = div; + break; + } + sdmmc_host_clock_update_command(slot); + + // Re-enable clocks + SDMMC.clkena.cclk_enable |= BIT(slot); + SDMMC.clkena.cclk_low_power |= BIT(slot); + sdmmc_host_clock_update_command(slot); + return ESP_OK; +} + +esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg) { + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + while (SDMMC.cmd.start_command == 1) { + ; + } + SDMMC.cmdarg = arg; + cmd.card_num = slot; + cmd.start_command = 1; + SDMMC.cmd = cmd; + return ESP_OK; +} + +esp_err_t sdmmc_host_init() +{ + if (s_intr_handle) { + return ESP_ERR_INVALID_STATE; + } + + // Enable clock to peripheral + sdmmc_host_input_clk_enable(); + + // Reset + sdmmc_host_reset(); + ESP_LOGD(TAG, "peripheral version %x, hardware config %08x", SDMMC.verid, SDMMC.hcon); + + // Clear interrupt status and set interrupt mask to known state + SDMMC.rintsts.val = 0xffffffff; + SDMMC.intmask.val = 0; + SDMMC.ctrl.int_enable = 0; + + // Allocate event queue + s_event_queue = xQueueCreate(SDMMC_EVENT_QUEUE_LENGTH, sizeof(sdmmc_event_t)); + if (!s_event_queue) { + return ESP_ERR_NO_MEM; + } + // Attach interrupt handler + esp_err_t ret = esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, 0, &sdmmc_isr, s_event_queue, &s_intr_handle); + if (ret != ESP_OK) { + vQueueDelete(s_event_queue); + s_event_queue = NULL; + return ret; + } + // Enable interrupts + SDMMC.intmask.val = + SDMMC_INTMASK_CD | + SDMMC_INTMASK_CMD_DONE | + SDMMC_INTMASK_DATA_OVER | + SDMMC_INTMASK_RCRC | SDMMC_INTMASK_DCRC | + SDMMC_INTMASK_RTO | SDMMC_INTMASK_DTO | SDMMC_INTMASK_HTO | + SDMMC_INTMASK_SBE | SDMMC_INTMASK_EBE | + SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; + SDMMC.ctrl.int_enable = 1; + + // Enable DMA + sdmmc_host_dma_init(); + + // Initialize transaction handler + ret = sdmmc_host_transaction_handler_init(); + if (ret != ESP_OK) { + vQueueDelete(s_event_queue); + s_event_queue = NULL; + esp_intr_free(s_intr_handle); + s_intr_handle = NULL; + return ret; + } + + return ESP_OK; +} + + +static inline void configure_pin(uint32_t io_mux_reg) +{ + const int sdmmc_func = 3; + const int drive_strength = 3; + PIN_INPUT_ENABLE(io_mux_reg); + PIN_FUNC_SELECT(io_mux_reg, sdmmc_func); + PIN_SET_DRV(io_mux_reg, drive_strength); +} + +esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config) +{ + if (!s_intr_handle) { + return ESP_ERR_INVALID_STATE; + } + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + if (slot_config == NULL) { + return ESP_ERR_INVALID_ARG; + } + int gpio_cd = slot_config->gpio_cd; + int gpio_wp = slot_config->gpio_wp; + + // Configure pins + const sdmmc_slot_info_t* pslot = &s_slot_info[slot]; + configure_pin(pslot->clk); + configure_pin(pslot->cmd); + configure_pin(pslot->d0); + configure_pin(pslot->d1); + configure_pin(pslot->d2); + configure_pin(pslot->d3); + if (pslot->width == 8) { + configure_pin(pslot->d4); + configure_pin(pslot->d5); + configure_pin(pslot->d6); + configure_pin(pslot->d7); + } + if (gpio_cd != -1) { + gpio_set_direction(gpio_cd, GPIO_MODE_INPUT); + gpio_matrix_in(gpio_cd, pslot->card_detect, 0); + } + if (gpio_wp != -1) { + gpio_set_direction(gpio_wp, GPIO_MODE_INPUT); + gpio_matrix_in(gpio_wp, pslot->write_protect, 0); + } + // By default, set probing frequency (400kHz) and 1-bit bus + esp_err_t ret = sdmmc_host_set_card_clk(slot, 400); + if (ret != ESP_OK) { + return ret; + } + ret = sdmmc_host_set_bus_width(slot, 1); + if (ret != ESP_OK) { + return ret; + } + return ESP_OK; +} + +esp_err_t sdmmc_host_deinit() +{ + if (!s_intr_handle) { + return ESP_ERR_INVALID_STATE; + } + esp_intr_free(s_intr_handle); + s_intr_handle = NULL; + vQueueDelete(s_event_queue); + s_event_queue = NULL; + sdmmc_host_input_clk_disable(); + sdmmc_host_transaction_handler_deinit(); + return ESP_OK; +} + +esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t* out_event) +{ + if (!out_event) { + return ESP_ERR_INVALID_ARG; + } + if (!s_event_queue) { + return ESP_ERR_INVALID_STATE; + } + int ret = xQueueReceive(s_event_queue, out_event, tick_count); + if (ret == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + return ESP_OK; +} + +esp_err_t sdmmc_host_set_bus_width(int slot, size_t width) +{ + if (!(slot == 0 || slot == 1)) { + return ESP_ERR_INVALID_ARG; + } + if (s_slot_info[slot].width < width) { + return ESP_ERR_INVALID_ARG; + } + const uint16_t mask = BIT(slot); + if (width == 1) { + SDMMC.ctype.card_width_8 &= ~mask; + SDMMC.ctype.card_width &= ~mask; + } else if (width == 4) { + SDMMC.ctype.card_width_8 &= ~mask; + SDMMC.ctype.card_width |= mask; + } else if (width == 8){ + SDMMC.ctype.card_width_8 |= mask; + } else { + return ESP_ERR_INVALID_ARG; + } + ESP_LOGD(TAG, "slot=%d width=%d", slot, width); + return ESP_OK; +} + +static void sdmmc_host_dma_init() +{ + SDMMC.ctrl.dma_enable = 1; + SDMMC.bmod.val = 0; + SDMMC.bmod.sw_reset = 1; + SDMMC.idinten.ni = 1; + SDMMC.idinten.ri = 1; + SDMMC.idinten.ti = 1; +} + + +void sdmmc_host_dma_stop() +{ + SDMMC.ctrl.use_internal_dma = 0; + SDMMC.ctrl.dma_reset = 1; + SDMMC.bmod.fb = 0; + SDMMC.bmod.enable = 0; +} + +void sdmmc_host_dma_prepare(sdmmc_desc_t* desc, size_t block_size, size_t data_size) +{ + // TODO: set timeout depending on data size + SDMMC.tmout.val = 0xffffffff; + + // Set size of data and DMA descriptor pointer + SDMMC.bytcnt = data_size; + SDMMC.blksiz = block_size; + SDMMC.dbaddr = desc; + + // Enable everything needed to use DMA + SDMMC.ctrl.dma_enable = 1; + SDMMC.ctrl.use_internal_dma = 1; + SDMMC.bmod.enable = 1; + SDMMC.bmod.fb = 1; + sdmmc_host_dma_resume(); +} + +void sdmmc_host_dma_resume() +{ + SDMMC.pldmnd = 1; +} + +/** + * @brief SDMMC interrupt handler + * + * Ignoring SDIO and streaming read/writes for now (and considering just SD memory cards), + * all communication is driven by the master, and the hardware handles things like stop + * commands automatically. So the interrupt handler doesn't need to do much, we just push + * interrupt status into a queue, clear interrupt flags, and let the task currently doing + * communication figure out what to do next. + * + * Card detect interrupts pose a small issue though, because if a card is plugged in and + * out a few times, while there is no task to process the events, event queue can become + * full and some card detect events may be dropped. We ignore this problem for now, since + * the there are no other interesting events which can get lost due to this. + */ +static void sdmmc_isr(void* arg) { + QueueHandle_t queue = (QueueHandle_t) arg; + sdmmc_event_t event; + uint32_t pending = SDMMC.mintsts.val; + SDMMC.rintsts.val = pending; + event.sdmmc_status = pending; + + uint32_t dma_pending = SDMMC.idsts.val; + SDMMC.idsts.val = dma_pending; + event.dma_status = dma_pending & 0x1f; + + int higher_priority_task_awoken = pdFALSE; + xQueueSendFromISR(queue, &event, &higher_priority_task_awoken); + if (higher_priority_task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + diff --git a/components/driver/sdmmc_private.h b/components/driver/sdmmc_private.h new file mode 100644 index 00000000000..5a10a3b672d --- /dev/null +++ b/components/driver/sdmmc_private.h @@ -0,0 +1,44 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "soc/sdmmc_struct.h" + +typedef struct { + uint32_t sdmmc_status; ///< masked SDMMC interrupt status + uint32_t dma_status; ///< masked DMA interrupt status +} sdmmc_event_t; + +void sdmmc_host_reset(); + +esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg); + +esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t* out_event); + +void sdmmc_host_dma_prepare(sdmmc_desc_t* desc, size_t block_size, size_t data_size); + +void sdmmc_host_dma_stop(); + +void sdmmc_host_dma_resume(); + +esp_err_t sdmmc_host_transaction_handler_init(); + +void sdmmc_host_transaction_handler_deinit(); + diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c new file mode 100644 index 00000000000..59b439eec91 --- /dev/null +++ b/components/driver/sdmmc_transaction.c @@ -0,0 +1,372 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp_err.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "soc/sdmmc_reg.h" +#include "soc/sdmmc_struct.h" +#include "driver/sdmmc_types.h" +#include "driver/sdmmc_defs.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_private.h" + + +/* Number of DMA descriptors used for transfer. + * Increasing this value above 4 doesn't improve performance for the usual case + * of SD memory cards (most data transfers are multiples of 512 bytes). + */ +#define SDMMC_DMA_DESC_CNT 4 + +static const char* TAG = "sdmmc_req"; + +typedef enum { + SDMMC_IDLE, + SDMMC_SENDING_CMD, + SDMMC_SENDING_DATA, + SDMMC_BUSY, +} sdmmc_req_state_t; + +typedef struct { + uint8_t* ptr; + size_t size_remaining; + size_t next_desc; + size_t desc_remaining; +} sdmmc_transfer_state_t; + +const uint32_t SDMMC_DATA_ERR_MASK = + SDMMC_INTMASK_DTO | SDMMC_INTMASK_DCRC | + SDMMC_INTMASK_HTO | SDMMC_INTMASK_SBE | + SDMMC_INTMASK_EBE; + +const uint32_t SDMMC_DMA_DONE_MASK = + SDMMC_IDMAC_INTMASK_RI | SDMMC_IDMAC_INTMASK_TI | + SDMMC_IDMAC_INTMASK_NI; + +const uint32_t SDMMC_CMD_ERR_MASK = + SDMMC_INTMASK_RTO | + SDMMC_INTMASK_RCRC | + SDMMC_INTMASK_RESP_ERR; + +static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT]; +static sdmmc_transfer_state_t s_cur_transfer = { 0 }; +static QueueHandle_t s_request_mutex; + +static esp_err_t handle_idle_state_events(); +static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd); +static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* pstate); +static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate); +static void process_command_response(uint32_t status, sdmmc_command_t* cmd); +static void fill_dma_descriptors(size_t num_desc); + +esp_err_t sdmmc_host_transaction_handler_init() +{ + assert(s_request_mutex == NULL); + s_request_mutex = xSemaphoreCreateMutex(); + if (!s_request_mutex) { + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +void sdmmc_host_transaction_handler_deinit() +{ + assert(s_request_mutex); + vSemaphoreDelete(s_request_mutex); + s_request_mutex = NULL; +} + +esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo) +{ + xSemaphoreTake(s_request_mutex, portMAX_DELAY); + // dispose of any events which happened asynchronously + handle_idle_state_events(); + // convert cmdinfo to hardware register value + sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo); + if (cmdinfo->data) { + // these constraints should be handled by upper layer + assert(cmdinfo->datalen >= 4); + assert(cmdinfo->blklen % 4 == 0); + // this clears "owned by IDMAC" bits + memset(s_dma_desc, 0, sizeof(s_dma_desc)); + // initialize first descriptor + s_dma_desc[0].first_descriptor = 1; + // save transfer info + s_cur_transfer.ptr = (uint8_t*) cmdinfo->data; + s_cur_transfer.size_remaining = cmdinfo->datalen; + s_cur_transfer.next_desc = 0; + s_cur_transfer.desc_remaining = (cmdinfo->datalen + SDMMC_DMA_MAX_BUF_LEN - 1) / SDMMC_DMA_MAX_BUF_LEN; + // prepare descriptors + fill_dma_descriptors(SDMMC_DMA_DESC_CNT); + // write transfer info into hardware + sdmmc_host_dma_prepare(&s_dma_desc[0], cmdinfo->blklen, cmdinfo->datalen); + } + // write command into hardware, this also sends the command to the card + esp_err_t ret = sdmmc_host_start_command(slot, hw_cmd, cmdinfo->arg); + if (ret != ESP_OK) { + xSemaphoreGive(s_request_mutex); + return ret; + } + // process events until transfer is complete + cmdinfo->error = ESP_OK; + sdmmc_req_state_t state = SDMMC_SENDING_CMD; + while (state != SDMMC_IDLE) { + ret = handle_event(cmdinfo, &state); + if (ret != ESP_OK) { + break; + } + } + xSemaphoreGive(s_request_mutex); + return ret; +} + +static void fill_dma_descriptors(size_t num_desc) +{ + for (size_t i = 0; i < num_desc; ++i) { + if (s_cur_transfer.size_remaining == 0) { + return; + } + const size_t next = s_cur_transfer.next_desc; + sdmmc_desc_t* desc = &s_dma_desc[next]; + assert(!desc->owned_by_idmac); + size_t size_to_fill = + (s_cur_transfer.size_remaining < SDMMC_DMA_MAX_BUF_LEN) ? + s_cur_transfer.size_remaining : SDMMC_DMA_MAX_BUF_LEN; + bool last = size_to_fill == s_cur_transfer.size_remaining; + desc->last_descriptor = last; + desc->second_address_chained = 1; + desc->owned_by_idmac = 1; + desc->buffer1_ptr = s_cur_transfer.ptr; + desc->next_desc_ptr = (last) ? NULL : &s_dma_desc[(next + 1) % SDMMC_DMA_DESC_CNT]; + desc->buffer1_size = size_to_fill; + + s_cur_transfer.size_remaining -= size_to_fill; + s_cur_transfer.ptr += size_to_fill; + s_cur_transfer.next_desc = (s_cur_transfer.next_desc + 1) % SDMMC_DMA_DESC_CNT; + ESP_LOGV(TAG, "fill %d desc=%d rem=%d next=%d last=%d sz=%d", + num_desc, next, s_cur_transfer.size_remaining, + s_cur_transfer.next_desc, desc->last_descriptor, desc->buffer1_size); + } +} + +static esp_err_t handle_idle_state_events() +{ + /* Handle any events which have happened in between transfers. + * Under current assumptions (no SDIO support) only card detect events + * can happen in the idle state. + */ + sdmmc_event_t evt; + while (sdmmc_host_wait_for_event(0, &evt) == ESP_OK) { + if (evt.sdmmc_status & SDMMC_INTMASK_CD) { + ESP_LOGV(TAG, "card detect event"); + evt.sdmmc_status &= ~SDMMC_INTMASK_CD; + } + if (evt.sdmmc_status != 0 || evt.dma_status != 0) { + ESP_LOGE(TAG, "handle_idle_state_events unhandled: %08x %08x", + evt.sdmmc_status, evt.dma_status); + } + + } + return ESP_OK; +} + + +static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state) +{ + sdmmc_event_t evt; + esp_err_t err = sdmmc_host_wait_for_event(portMAX_DELAY, &evt); + if (err != ESP_OK) { + ESP_LOGE(TAG, "sdmmc_host_wait_for_event returned %d", err); + return err; + } + ESP_LOGV(TAG, "sdmmc_handle_event: evt %08x %08x", evt.sdmmc_status, evt.dma_status); + process_events(evt, cmd, state); + return ESP_OK; +} + +static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd) +{ + sdmmc_hw_cmd_t res = { 0 }; + + res.cmd_index = cmd->opcode; + if (cmd->opcode == MMC_STOP_TRANSMISSION) { + res.stop_abort_cmd = 1; + } else { + res.wait_complete = 1; + } + if (cmd->opcode == SD_APP_SET_BUS_WIDTH) { + res.send_auto_stop = 1; + res.data_expected = 1; + } + if (cmd->flags & SCF_RSP_PRESENT) { + res.response_expect = 1; + if (cmd->flags & SCF_RSP_136) { + res.response_long = 1; + } + } + if (cmd->flags & SCF_RSP_CRC) { + res.check_response_crc = 1; + } + res.use_hold_reg = 1; + if (cmd->data) { + res.data_expected = 1; + if ((cmd->flags & SCF_CMD_READ) == 0) { + res.rw = 1; + } + assert(cmd->datalen % cmd->blklen == 0); + if ((cmd->datalen / cmd->blklen) > 1) { + res.send_auto_stop = 1; + } + } + ESP_LOGV(TAG, "%s: opcode=%d, rexp=%d, crc=%d", __func__, + res.cmd_index, res.response_expect, res.check_response_crc); + return res; +} + +static void process_command_response(uint32_t status, sdmmc_command_t* cmd) +{ + if (cmd->flags & SCF_RSP_PRESENT) { + if (cmd->flags & SCF_RSP_136) { + cmd->response[3] = SDMMC.resp[0]; + cmd->response[2] = SDMMC.resp[1]; + cmd->response[1] = SDMMC.resp[2]; + cmd->response[0] = SDMMC.resp[3]; + + } else { + cmd->response[0] = SDMMC.resp[0]; + cmd->response[1] = 0; + cmd->response[2] = 0; + cmd->response[3] = 0; + } + } + + if ((status & SDMMC_INTMASK_RTO) && + cmd->opcode != MMC_ALL_SEND_CID && + cmd->opcode != MMC_SELECT_CARD && + cmd->opcode != MMC_STOP_TRANSMISSION) { + cmd->error = ESP_ERR_TIMEOUT; + } else if ((cmd->flags & SCF_RSP_CRC) && (status & SDMMC_INTMASK_RCRC)) { + cmd->error = ESP_ERR_INVALID_CRC; + } else if (status & SDMMC_INTMASK_RESP_ERR) { + cmd->error = ESP_ERR_INVALID_RESPONSE; + } + if (cmd->error != 0) { + if (cmd->data) { + sdmmc_host_dma_stop(); + } + ESP_LOGD(TAG, "%s: error %d", __func__, cmd->error); + } +} + +static void process_data_status(uint32_t status, sdmmc_command_t* cmd) +{ + if (status & SDMMC_DATA_ERR_MASK) { + if (status & SDMMC_INTMASK_DTO) { + cmd->error = ESP_ERR_TIMEOUT; + } else if (status & SDMMC_INTMASK_DCRC) { + cmd->error = ESP_ERR_INVALID_CRC; + } else if ((status & SDMMC_INTMASK_EBE) && + (cmd->flags & SCF_CMD_READ) == 0) { + cmd->error = ESP_ERR_TIMEOUT; + } else { + cmd->error = ESP_FAIL; + } + SDMMC.ctrl.fifo_reset = 1; + } + if (cmd->error != 0) { + if (cmd->data) { + sdmmc_host_dma_stop(); + } + ESP_LOGD(TAG, "%s: error %d", __func__, cmd->error); + } + +} + +static inline bool mask_check_and_clear(uint32_t* state, uint32_t mask) { + bool ret = ((*state) & mask) != 0; + *state &= ~mask; + return ret; +} + +static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate) +{ + const char* const s_state_names[] __attribute__((unused)) = { + "IDLE", + "SENDING_CMD", + "SENDIND_DATA", + "BUSY" + }; + sdmmc_event_t orig_evt = evt; + ESP_LOGV(TAG, "%s: state=%s", __func__, s_state_names[*pstate]); + sdmmc_req_state_t next_state = *pstate; + sdmmc_req_state_t state = (sdmmc_req_state_t) -1; + while (next_state != state) { + state = next_state; + switch (state) { + case SDMMC_IDLE: + break; + + case SDMMC_SENDING_CMD: + if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_CMD_ERR_MASK)) { + process_command_response(orig_evt.sdmmc_status, cmd); + break; + } + if (!mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_CMD_DONE)) { + break; + } + process_command_response(orig_evt.sdmmc_status, cmd); + if (cmd->error != ESP_OK || cmd->data == NULL) { + next_state = SDMMC_IDLE; + break; + } + next_state = SDMMC_SENDING_DATA; + break; + + + case SDMMC_SENDING_DATA: + if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_DATA_ERR_MASK)) { + process_data_status(orig_evt.sdmmc_status, cmd); + sdmmc_host_dma_stop(); + } + if (mask_check_and_clear(&evt.dma_status, SDMMC_DMA_DONE_MASK)) { + s_cur_transfer.desc_remaining--; + if (s_cur_transfer.size_remaining) { + fill_dma_descriptors(1); + sdmmc_host_dma_resume(); + } + if (s_cur_transfer.desc_remaining == 0) { + next_state = SDMMC_BUSY; + } + } + break; + + case SDMMC_BUSY: + if (!mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_DATA_OVER)) { + break; + } + process_data_status(orig_evt.sdmmc_status, cmd); + next_state = SDMMC_IDLE; + break; + } + ESP_LOGV(TAG, "%s state=%s next_state=%s", __func__, s_state_names[state], s_state_names[next_state]); + } + *pstate = state; + return ESP_OK; +} + + + diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index a1f4b8f359d..b6a1e8b4215 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -35,7 +35,8 @@ typedef int32_t esp_err_t; #define ESP_ERR_NOT_FOUND 0x105 #define ESP_ERR_NOT_SUPPORTED 0x106 #define ESP_ERR_TIMEOUT 0x107 - +#define ESP_ERR_INVALID_RESPONSE 0x108 +#define ESP_ERR_INVALID_CRC 0x109 #define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ diff --git a/components/esp32/include/soc/sdmmc_reg.h b/components/esp32/include/soc/sdmmc_reg.h new file mode 100644 index 00000000000..d1b452d1d19 --- /dev/null +++ b/components/esp32/include/soc/sdmmc_reg.h @@ -0,0 +1,94 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SOC_SDMMC_REG_H_ +#define _SOC_SDMMC_REG_H_ +#include "soc.h" + +#define SDMMC_CTRL_REG (DR_REG_SDMMC_BASE + 0x00) +#define SDMMC_PWREN_REG (DR_REG_SDMMC_BASE + 0x04) +#define SDMMC_CLKDIV_REG (DR_REG_SDMMC_BASE + 0x08) +#define SDMMC_CLKSRC_REG (DR_REG_SDMMC_BASE + 0x0c) +#define SDMMC_CLKENA_REG (DR_REG_SDMMC_BASE + 0x10) +#define SDMMC_TMOUT_REG (DR_REG_SDMMC_BASE + 0x14) +#define SDMMC_CTYPE_REG (DR_REG_SDMMC_BASE + 0x18) +#define SDMMC_BLKSIZ_REG (DR_REG_SDMMC_BASE + 0x1c) +#define SDMMC_BYTCNT_REG (DR_REG_SDMMC_BASE + 0x20) +#define SDMMC_INTMASK_REG (DR_REG_SDMMC_BASE + 0x24) +#define SDMMC_CMDARG_REG (DR_REG_SDMMC_BASE + 0x28) +#define SDMMC_CMD_REG (DR_REG_SDMMC_BASE + 0x2c) +#define SDMMC_RESP0_REG (DR_REG_SDMMC_BASE + 0x30) +#define SDMMC_RESP1_REG (DR_REG_SDMMC_BASE + 0x34) +#define SDMMC_RESP2_REG (DR_REG_SDMMC_BASE + 0x38) +#define SDMMC_RESP3_REG (DR_REG_SDMMC_BASE + 0x3c) + +#define SDMMC_MINTSTS_REG (DR_REG_SDMMC_BASE + 0x40) +#define SDMMC_RINTSTS_REG (DR_REG_SDMMC_BASE + 0x44) +#define SDMMC_STATUS_REG (DR_REG_SDMMC_BASE + 0x48) +#define SDMMC_FIFOTH_REG (DR_REG_SDMMC_BASE + 0x4c) +#define SDMMC_CDETECT_REG (DR_REG_SDMMC_BASE + 0x50) +#define SDMMC_WRTPRT_REG (DR_REG_SDMMC_BASE + 0x54) +#define SDMMC_GPIO_REG (DR_REG_SDMMC_BASE + 0x58) +#define SDMMC_TCBCNT_REG (DR_REG_SDMMC_BASE + 0x5c) +#define SDMMC_TBBCNT_REG (DR_REG_SDMMC_BASE + 0x60) +#define SDMMC_DEBNCE_REG (DR_REG_SDMMC_BASE + 0x64) +#define SDMMC_USRID_REG (DR_REG_SDMMC_BASE + 0x68) +#define SDMMC_VERID_REG (DR_REG_SDMMC_BASE + 0x6c) +#define SDMMC_HCON_REG (DR_REG_SDMMC_BASE + 0x70) +#define SDMMC_UHS_REG_REG (DR_REG_SDMMC_BASE + 0x74) +#define SDMMC_RST_N_REG (DR_REG_SDMMC_BASE + 0x78) +#define SDMMC_BMOD_REG (DR_REG_SDMMC_BASE + 0x80) +#define SDMMC_PLDMND_REG (DR_REG_SDMMC_BASE + 0x84) +#define SDMMC_DBADDR_REG (DR_REG_SDMMC_BASE + 0x88) +#define SDMMC_DBADDRU_REG (DR_REG_SDMMC_BASE + 0x8c) +#define SDMMC_IDSTS_REG (DR_REG_SDMMC_BASE + 0x8c) +#define SDMMC_IDINTEN_REG (DR_REG_SDMMC_BASE + 0x90) +#define SDMMC_DSCADDR_REG (DR_REG_SDMMC_BASE + 0x94) +#define SDMMC_DSCADDRL_REG (DR_REG_SDMMC_BASE + 0x98) +#define SDMMC_DSCADDRU_REG (DR_REG_SDMMC_BASE + 0x9c) +#define SDMMC_BUFADDRL_REG (DR_REG_SDMMC_BASE + 0xa0) +#define SDMMC_BUFADDRU_REG (DR_REG_SDMMC_BASE + 0xa4) +#define SDMMC_CARDTHRCTL_REG (DR_REG_SDMMC_BASE + 0x100) +#define SDMMC_BACK_END_POWER_REG (DR_REG_SDMMC_BASE + 0x104) +#define SDMMC_UHS_REG_EXT_REG (DR_REG_SDMMC_BASE + 0x108) +#define SDMMC_EMMC_DDR_REG_REG (DR_REG_SDMMC_BASE + 0x10c) +#define SDMMC_ENABLE_SHIFT_REG (DR_REG_SDMMC_BASE + 0x110) + +#define SDMMC_CLOCK_REG (DR_REG_SDMMC_BASE + 0x800) + +#define SDMMC_INTMASK_EBE BIT(15) +#define SDMMC_INTMASK_ACD BIT(14) +#define SDMMC_INTMASK_SBE BIT(13) +#define SDMMC_INTMASK_HLE BIT(12) +#define SDMMC_INTMASK_FRUN BIT(11) +#define SDMMC_INTMASK_HTO BIT(10) +#define SDMMC_INTMASK_DTO BIT(9) +#define SDMMC_INTMASK_RTO BIT(8) +#define SDMMC_INTMASK_DCRC BIT(7) +#define SDMMC_INTMASK_RCRC BIT(6) +#define SDMMC_INTMASK_RXDR BIT(5) +#define SDMMC_INTMASK_TXDR BIT(4) +#define SDMMC_INTMASK_DATA_OVER BIT(3) +#define SDMMC_INTMASK_CMD_DONE BIT(2) +#define SDMMC_INTMASK_RESP_ERR BIT(1) +#define SDMMC_INTMASK_CD BIT(0) + +#define SDMMC_IDMAC_INTMASK_AI BIT(9) +#define SDMMC_IDMAC_INTMASK_NI BIT(8) +#define SDMMC_IDMAC_INTMASK_CES BIT(5) +#define SDMMC_IDMAC_INTMASK_DU BIT(4) +#define SDMMC_IDMAC_INTMASK_FBE BIT(2) +#define SDMMC_IDMAC_INTMASK_RI BIT(1) +#define SDMMC_IDMAC_INTMASK_TI BIT(0) + +#endif /* _SOC_SDMMC_REG_H_ */ diff --git a/components/esp32/include/soc/sdmmc_struct.h b/components/esp32/include/soc/sdmmc_struct.h new file mode 100644 index 00000000000..20bb9d260d8 --- /dev/null +++ b/components/esp32/include/soc/sdmmc_struct.h @@ -0,0 +1,371 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SOC_SDMMC_STRUCT_H_ +#define _SOC_SDMMC_STRUCT_H_ + +#include + +typedef struct { + uint32_t reserved1: 1; + uint32_t disable_int_on_completion: 1; + uint32_t last_descriptor: 1; + uint32_t first_descriptor: 1; + uint32_t second_address_chained: 1; + uint32_t end_of_ring: 1; + uint32_t reserved2: 24; + uint32_t card_error_summary: 1; + uint32_t owned_by_idmac: 1; + uint32_t buffer1_size: 13; + uint32_t buffer2_size: 13; + uint32_t reserved3: 6; + void* buffer1_ptr; + union { + void* buffer2_ptr; + void* next_desc_ptr; + }; +} sdmmc_desc_t; + +#define SDMMC_DMA_MAX_BUF_LEN 4096 + +_Static_assert(sizeof(sdmmc_desc_t) == 16, "invalid size of sdmmc_desc_t structure"); + + +typedef struct { + uint32_t cmd_index: 6; ///< Command index + uint32_t response_expect: 1; ///< set if response is expected + uint32_t response_long: 1; ///< 0: short response expected, 1: long response expected + uint32_t check_response_crc: 1; ///< set if controller should check response CRC + uint32_t data_expected: 1; ///< 0: no data expected, 1: data expected + uint32_t rw: 1; ///< 0: read from card, 1: write to card (don't care if no data expected) + uint32_t stream_mode: 1; ///< 0: block transfer, 1: stream transfer (don't care if no data expected) + uint32_t send_auto_stop: 1; ///< set to send stop at the end of the transfer + uint32_t wait_complete: 1; ///< 0: send command at once, 1: wait for previous command to complete + uint32_t stop_abort_cmd: 1; ///< set if this is a stop or abort command intended to stop current transfer + uint32_t send_init: 1; ///< set to send init sequence (80 clocks of 1) + uint32_t card_num: 5; ///< card number + uint32_t update_clk_reg: 1; ///< 0: normal command, 1: don't send command, just update clock registers + uint32_t read_ceata: 1; ///< set if performing read from CE-ATA device + uint32_t ccs_expected: 1; ///< set if CCS is expected from CE-ATA device + uint32_t enable_boot: 1; ///< set for mandatory boot mode + uint32_t expect_boot_ack: 1; ///< when set along with enable_boot, controller expects boot ack pattern + uint32_t disable_boot: 1; ///< set to terminate boot operation (don't set along with enable_boot) + uint32_t boot_mode: 1; ///< 0: mandatory boot operation, 1: alternate boot operation + uint32_t volt_switch: 1; ///< set to enable voltage switching (for CMD11 only) + uint32_t use_hold_reg: 1; ///< clear to bypass HOLD register + uint32_t reserved: 1; + uint32_t start_command: 1; ///< Start command; once command is sent to the card, bit is cleared. +} sdmmc_hw_cmd_t; ///< command format used in cmd register; this structure is defined to make it easier to build command values + +_Static_assert(sizeof(sdmmc_hw_cmd_t) == 4, "invalid size of sdmmc_cmd_t structure"); + + +typedef volatile struct { + union { + struct { + uint32_t controller_reset: 1; + uint32_t fifo_reset: 1; + uint32_t dma_reset: 1; + uint32_t reserved1: 1; + uint32_t int_enable: 1; + uint32_t dma_enable: 1; + uint32_t read_wait: 1; + uint32_t send_irq_response: 1; + uint32_t abort_read_data: 1; + uint32_t send_ccsd: 1; + uint32_t send_auto_stop_ccsd: 1; + uint32_t ceata_device_interrupt_status: 1; + uint32_t reserved2: 4; + uint32_t card_voltage_a: 4; + uint32_t card_voltage_b: 4; + uint32_t enable_od_pullup: 1; + uint32_t use_internal_dma: 1; + uint32_t reserved3: 6; + }; + uint32_t val; + } ctrl; + + uint32_t pwren; ///< 1: enable power to card, 0: disable power to card + + union { + struct { + uint32_t div0: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + uint32_t div1: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + uint32_t div2: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + uint32_t div3: 8; ///< 0: bypass, 1-255: divide clock by (2*div0). + }; + uint32_t val; + } clkdiv; + + union { + struct { + uint32_t card0: 2; ///< 0-3: select clock divider for card 0 among div0-div3 + uint32_t card1: 2; ///< 0-3: select clock divider for card 1 among div0-div3 + uint32_t reserved: 28; + }; + uint32_t val; + } clksrc; + + union { + struct { + uint32_t cclk_enable: 16; ///< 1: enable clock to card, 0: disable clock + uint32_t cclk_low_power: 16; ///< 1: enable clock gating when card is idle, 0: disable clock gating + }; + uint32_t val; + } clkena; + + union { + struct { + uint32_t response: 8; ///< response timeout, in card output clock cycles + uint32_t data: 24; ///< data read timeout, in card output clock cycles + }; + uint32_t val; + } tmout; + + union { + struct { + uint32_t card_width: 16; ///< one bit for each card: 0: 1-bit mode, 1: 4-bit mode + uint32_t card_width_8: 16; ///< one bit for each card: 0: not 8-bit mode (corresponding card_width bit is used), 1: 8-bit mode (card_width bit is ignored) + }; + uint32_t val; + } ctype; + + uint32_t blksiz: 16; ///< block size, default 0x200 + uint32_t : 16; + + uint32_t bytcnt; ///< number of bytes to be transferred + + union { + struct { + uint32_t cd: 1; ///< Card detect interrupt enable + uint32_t re: 1; ///< Response error interrupt enable + uint32_t cmd_done: 1; ///< Command done interrupt enable + uint32_t dto: 1; ///< Data transfer over interrupt enable + uint32_t txdr: 1; ///< Transmit FIFO data request interrupt enable + uint32_t rxdr: 1; ///< Receive FIFO data request interrupt enable + uint32_t rcrc: 1; ///< Response CRC error interrupt enable + uint32_t dcrc: 1; ///< Data CRC error interrupt enable + uint32_t rto: 1; ///< Response timeout interrupt enable + uint32_t drto: 1; ///< Data read timeout interrupt enable + uint32_t hto: 1; ///< Data starvation-by-host timeout interrupt enable + uint32_t frun: 1; ///< FIFO underrun/overrun error interrupt enable + uint32_t hle: 1; ///< Hardware locked write error interrupt enable + uint32_t sbi_bci: 1; ///< Start bit error / busy clear interrupt enable + uint32_t acd: 1; ///< Auto command done interrupt enable + uint32_t ebe: 1; ///< End bit error / write no CRC interrupt enable + uint32_t sdio: 16; ///< SDIO interrupt enable + }; + uint32_t val; + } intmask; + + uint32_t cmdarg; ///< Command argument to be passed to card + + sdmmc_hw_cmd_t cmd; + + uint32_t resp[4]; ///< Response from card + + union { + struct { + uint32_t cd: 1; ///< Card detect interrupt masked status + uint32_t re: 1; ///< Response error interrupt masked status + uint32_t cmd_done: 1; ///< Command done interrupt masked status + uint32_t dto: 1; ///< Data transfer over interrupt masked status + uint32_t txdr: 1; ///< Transmit FIFO data request interrupt masked status + uint32_t rxdr: 1; ///< Receive FIFO data request interrupt masked status + uint32_t rcrc: 1; ///< Response CRC error interrupt masked status + uint32_t dcrc: 1; ///< Data CRC error interrupt masked status + uint32_t rto: 1; ///< Response timeout interrupt masked status + uint32_t drto: 1; ///< Data read timeout interrupt masked status + uint32_t hto: 1; ///< Data starvation-by-host timeout interrupt masked status + uint32_t frun: 1; ///< FIFO underrun/overrun error interrupt masked status + uint32_t hle: 1; ///< Hardware locked write error interrupt masked status + uint32_t sbi_bci: 1; ///< Start bit error / busy clear interrupt masked status + uint32_t acd: 1; ///< Auto command done interrupt masked status + uint32_t ebe: 1; ///< End bit error / write no CRC interrupt masked status + uint32_t sdio: 16; ///< SDIO interrupt masked status + }; + uint32_t val; + } mintsts; + + union { + struct { + uint32_t cd: 1; ///< Card detect raw interrupt status + uint32_t re: 1; ///< Response error raw interrupt status + uint32_t cmd_done: 1; ///< Command done raw interrupt status + uint32_t dto: 1; ///< Data transfer over raw interrupt status + uint32_t txdr: 1; ///< Transmit FIFO data request raw interrupt status + uint32_t rxdr: 1; ///< Receive FIFO data request raw interrupt status + uint32_t rcrc: 1; ///< Response CRC error raw interrupt status + uint32_t dcrc: 1; ///< Data CRC error raw interrupt status + uint32_t rto: 1; ///< Response timeout raw interrupt status + uint32_t drto: 1; ///< Data read timeout raw interrupt status + uint32_t hto: 1; ///< Data starvation-by-host timeout raw interrupt status + uint32_t frun: 1; ///< FIFO underrun/overrun error raw interrupt status + uint32_t hle: 1; ///< Hardware locked write error raw interrupt status + uint32_t sbi_bci: 1; ///< Start bit error / busy clear raw interrupt status + uint32_t acd: 1; ///< Auto command done raw interrupt status + uint32_t ebe: 1; ///< End bit error / write no CRC raw interrupt status + uint32_t sdio: 16; ///< SDIO raw interrupt status + }; + uint32_t val; + } rintsts; ///< interrupts can be cleared by writing this register + + union { + struct { + uint32_t fifo_rx_watermark: 1; ///< FIFO reached receive watermark level + uint32_t fifo_tx_watermark: 1; ///< FIFO reached transmit watermark level + uint32_t fifo_empty: 1; ///< FIFO is empty + uint32_t fifo_full: 1; ///< FIFO is full + uint32_t cmd_fsm_state: 4; ///< command FSM state + uint32_t data3_status: 1; ///< this bit reads 1 if card is present + uint32_t data_busy: 1; ///< this bit reads 1 if card is busy + uint32_t data_fsm_busy: 1; ///< this bit reads 1 if transmit/receive FSM is busy + uint32_t response_index: 6; ///< index of the previous response + uint32_t fifo_count: 13; ///< number of filled locations in the FIFO + uint32_t dma_ack: 1; ///< DMA acknowledge signal + uint32_t dma_req: 1; ///< DMA request signal + }; + uint32_t val; + } status; + + union { + struct { + uint32_t tx_watermark: 12; ///< FIFO TX watermark level + uint32_t reserved1: 4; + uint32_t rx_watermark: 12; ///< FIFO RX watermark level + uint32_t dw_dma_mts: 3; + uint32_t reserved2: 1; + }; + uint32_t val; + } fifoth; + + union { + struct { + uint32_t cards: 2; ///< bit N reads 1 if card N is present + uint32_t reserved: 30; + }; + uint32_t val; + } cdetect; + + union { + struct { + uint32_t card0: 2; ///< bit N reads 1 if card N is write protected + uint32_t reserved: 30; + }; + uint32_t val; + } wrtprt; + + uint32_t gpio; ///< unused + uint32_t tcbcnt; ///< transferred (to card) byte count + uint32_t tbbcnt; ///< transferred from host to FIFO byte count + + union { + struct { + uint32_t debounce_count: 24; ///< number of host cycles used by debounce filter, typical time should be 5-25ms + uint32_t reserved: 8; + }; + } debnce; + + uint32_t usrid; ///< user ID + uint32_t verid; ///< IP block version + uint32_t hcon; ///< compile-time IP configuration + uint32_t uhs; ///< TBD + + union { + struct { + uint32_t cards: 2; ///< bit N resets card N, active low + uint32_t reserved: 30; + }; + } rst_n; + + uint32_t reserved_7c; + + union { + struct { + uint32_t sw_reset: 1; ///< set to reset DMA controller + uint32_t fb: 1; ///< set if AHB master performs fixed burst transfers + uint32_t dsl: 5; ///< descriptor skip length: number of words to skip between two unchained descriptors + uint32_t enable: 1; ///< set to enable IDMAC + uint32_t pbl: 3; ///< programmable burst length + uint32_t reserved: 21; + }; + uint32_t val; + } bmod; + + uint32_t pldmnd; ///< set any bit to resume IDMAC FSM from suspended state + sdmmc_desc_t* dbaddr; ///< descriptor list base + + union { + struct { + uint32_t ti: 1; ///< transmit interrupt status + uint32_t ri: 1; ///< receive interrupt status + uint32_t fbe: 1; ///< fatal bus error + uint32_t reserved1: 1; + uint32_t du: 1; ///< descriptor unavailable + uint32_t ces: 1; ///< card error summary + uint32_t reserved2: 2; + uint32_t nis: 1; ///< normal interrupt summary + uint32_t fbe_code: 3; ///< code of fatal bus error + uint32_t fsm: 4; ///< DMAC FSM state + uint32_t reserved3: 15; + }; + uint32_t val; + } idsts; + + union { + struct { + uint32_t ti: 1; ///< transmit interrupt enable + uint32_t ri: 1; ///< receive interrupt enable + uint32_t fbe: 1; ///< fatal bus error interrupt enable + uint32_t reserved1: 1; + uint32_t du: 1; ///< descriptor unavailable interrupt enable + uint32_t ces: 1; ///< card error interrupt enable + uint32_t reserved2: 2; + uint32_t ni: 1; ///< normal interrupt interrupt enable + uint32_t ai: 1; ///< abnormal interrupt enable + uint32_t reserved3: 22; + }; + uint32_t val; + } idinten; + + uint32_t dscaddr; ///< current host descriptor address + uint32_t dscaddrl; ///< unused + uint32_t dscaddru; ///< unused + uint32_t bufaddrl; ///< unused + uint32_t bufaddru; ///< unused + uint32_t reserved_a8[22]; + uint32_t cardthrctl; + uint32_t back_end_power; + uint32_t uhs_reg_ext; + uint32_t emmc_ddr_reg; + uint32_t enable_shift; + uint32_t reserved_114[443]; + union { + struct { + uint32_t phase_dout: 3; ///< phase of data output clock (0x0: 0, 0x1: 90, 0x4: 180, 0x6: 270) + uint32_t phase_din: 3; ///< phase of data input clock + uint32_t phase_core: 3; ///< phase of the clock to SDMMC peripheral + uint32_t div_factor_p: 4; ///< controls clock period; it will be (div_factor_p + 1) / 160MHz + uint32_t div_factor_h: 4; ///< controls length of high pulse; it will be (div_factor_h + 1) / 160MHz + uint32_t div_factor_m: 4; ///< should be equal to div_factor_p + }; + uint32_t val; + } clock; +} sdmmc_dev_t; +extern sdmmc_dev_t SDMMC; + +_Static_assert(sizeof(sdmmc_dev_t) == 0x804, "invalid size of sdmmc_dev_t structure"); + + + +#endif //_SOC_SDMMC_STRUCT_H_ diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index b93bae72980..1d447fadb3f 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -153,7 +153,7 @@ #define DR_REG_FRC_TIMER_BASE 0x3ff47000 #define DR_REG_RTCCNTL_BASE 0x3ff48000 #define DR_REG_RTCIO_BASE 0x3ff48400 -#define DR_REG_SENS_BASE 0x3ff48800 +#define DR_REG_SENS_BASE 0x3ff48800 #define DR_REG_IO_MUX_BASE 0x3ff49000 #define DR_REG_RTCMEM0_BASE 0x3ff61000 #define DR_REG_RTCMEM1_BASE 0x3ff62000 diff --git a/components/esp32/ld/esp32.peripherals.ld b/components/esp32/ld/esp32.peripherals.ld index 95aaadcbcc4..8d8d4e8b1bc 100644 --- a/components/esp32/ld/esp32.peripherals.ld +++ b/components/esp32/ld/esp32.peripherals.ld @@ -19,3 +19,4 @@ PROVIDE ( SPI3 = 0x3ff65000 ); PROVIDE ( I2C1 = 0x3ff67000 ); PROVIDE ( I2S1 = 0x3ff6D000 ); PROVIDE ( UART2 = 0x3ff6E000 ); +PROVIDE ( SDMMC = 0x3ff68000 ); diff --git a/components/sdmmc/component.mk b/components/sdmmc/component.mk new file mode 100755 index 00000000000..e69de29bb2d diff --git a/components/sdmmc/include/sdmmc_cmd.h b/components/sdmmc/include/sdmmc_cmd.h new file mode 100644 index 00000000000..58b6f082cc0 --- /dev/null +++ b/components/sdmmc/include/sdmmc_cmd.h @@ -0,0 +1,77 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "esp_err.h" +#include "driver/sdmmc_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Probe and initialize SD/MMC card using given host + * + * @note Only SD cards (SDSC and SDHC/SDXC) are supported now. + * Support for MMC/eMMC cards will be added later. + * + * @param host pointer to structure defining host controller + * @param out_card pointer to structure which will receive information about the card when the function completes + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +esp_err_t sdmmc_card_init(const sdmmc_host_t* host, + sdmmc_card_t* out_card); + +/** + * @brief Print information about the card to a stream + * @param stream stream obtained using fopen or fdopen + * @param card card information structure initialized using sdmmc_card_init + */ +void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card); + +/** + * Write given number of sectors to SD/MMC card + * + * @param card pointer to card information structure previously initialized using sdmmc_card_init + * @param src pointer to data buffer to read data from; data size must be equal to sector_count * card->csd.sector_size + * @param start_sector sector where to start writing + * @param sector_count number of sectors to write + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src, + size_t start_sector, size_t sector_count); + +/** + * Write given number of sectors to SD/MMC card + * + * @param card pointer to card information structure previously initialized using sdmmc_card_init + * @param dst pointer to data buffer to write into; buffer size must be at least sector_count * card->csd.sector_size + * @param start_sector sector where to start reading + * @param sector_count number of sectors to read + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst, + size_t start_sector, size_t sector_count); + +#ifdef __cplusplus +} +#endif diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c new file mode 100644 index 00000000000..659ff5dd6cd --- /dev/null +++ b/components/sdmmc/sdmmc_cmd.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2006 Uwe Stuehler + * Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "esp_log.h" +#include "esp_heap_alloc_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/sdmmc_defs.h" +#include "driver/sdmmc_types.h" +#include "sdmmc_cmd.h" + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +static const char* TAG = "sdmmc_cmd"; + +static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); +static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); +static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card); +static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr); +static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp); +static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid); +static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid); +static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca); +static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd); +static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd); +static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd); +static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card); +static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr); +static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr); +static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width); +static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status); +static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status); +static uint32_t get_host_ocr(float voltage); + + +esp_err_t sdmmc_card_init(const sdmmc_host_t* config, + sdmmc_card_t* card) +{ + ESP_LOGD(TAG, "%s", __func__); + memset(card, 0, sizeof(*card)); + memcpy(&card->host, config, sizeof(*config)); + esp_err_t err = sdmmc_send_cmd_go_idle_state(card); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err); + return err; + } + ets_delay_us(10000); + uint32_t host_ocr = get_host_ocr(config->io_voltage); + err = sdmmc_send_cmd_send_if_cond(card, host_ocr); + if (err == ESP_OK) { + ESP_LOGD(TAG, "SDHC/SDXC card"); + host_ocr |= SD_OCR_SDHC_CAP; + } else if (err == ESP_ERR_TIMEOUT) { + ESP_LOGD(TAG, "CMD8 timeout; not an SDHC/SDXC card"); + } else { + ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err); + return err; + } + host_ocr &= card->ocr; + ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr); + err = sddmc_send_cmd_all_send_cid(card, &card->cid); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_set_relative_addr(card, &card->rca); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_send_csd(card, &card->csd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err); + return err; + } + const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1; + if (!(card->ocr & SD_OCR_SDHC_CAP) && + card->csd.capacity > max_sdsc_capacity) { + ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.", + __func__, card->csd.capacity, max_sdsc_capacity); + card->csd.capacity = max_sdsc_capacity; + } + err = sdmmc_send_cmd_select_card(card); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err); + return err; + } + if ((card->ocr & SD_OCR_SDHC_CAP) == 0) { + err = sdmmc_send_cmd_set_blocklen(card, &card->csd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err); + return err; + } + } + err = sdmmc_send_cmd_send_scr(card, &card->scr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err); + return err; + } + if ((config->flags & SDMMC_HOST_FLAG_4BIT) && + (card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) { + ESP_LOGD(TAG, "switching to 4-bit bus mode"); + err = sdmmc_send_cmd_set_bus_width(card, 4); + if (err != ESP_OK) { + ESP_LOGE(TAG, "set_bus_width failed"); + return err; + } + err = (*config->set_bus_width)(config->slot, 4); + if (err != ESP_OK) { + ESP_LOGE(TAG, "slot->set_bus_width failed"); + return err; + } + uint32_t status; + err = sdmmc_send_cmd_stop_transmission(card, &status); + if (err != ESP_OK) { + ESP_LOGE(TAG, "stop_transmission failed (0x%x)", err); + return err; + } + } + uint32_t status = 0; + while (!(status & MMC_R1_READY_FOR_DATA)) { + // TODO: add some timeout here + uint32_t count = 0; + err = sdmmc_send_cmd_send_status(card, &status); + if (err != ESP_OK) { + return err; + } + if (++count % 10 == 0) { + ESP_LOGV(TAG, "waiting for card to become ready (%d)", count); + } + } + if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED && + card->csd.tr_speed / 1000 >= SDMMC_FREQ_HIGHSPEED) { + ESP_LOGD(TAG, "switching to HS bus mode"); + err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode"); + return err; + } + } else if (config->max_freq_khz >= SDMMC_FREQ_DEFAULT && + card->csd.tr_speed / 1000 >= SDMMC_FREQ_DEFAULT) { + ESP_LOGD(TAG, "switching to DS bus mode"); + err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode"); + return err; + } + } + sdmmc_scr_t scr_tmp; + err = sdmmc_send_cmd_send_scr(card, &scr_tmp); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err); + return err; + } + if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) { + ESP_LOGE(TAG, "data check fail!"); + return ESP_ERR_INVALID_RESPONSE; + } + return ESP_OK; +} + +void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card) +{ + fprintf(stream, "Name: %s\n", card->cid.name); + fprintf(stream, "Type: %s\n", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC"); + fprintf(stream, "Speed: %s\n", (card->csd.tr_speed > 25000000)?"high speed":"default speed"); + fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024)); + fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n", + card->csd.csd_ver, + card->csd.sector_size, card->csd.capacity, card->csd.read_block_len); + fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width); +} + +static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd) +{ + int slot = card->host.slot; + ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d", + slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen); + esp_err_t err = (*card->host.do_transaction)(slot, cmd); + if (err != 0) { + ESP_LOGD(TAG, "sdmmc_req_run returned 0x%x", err); + return err; + } + int state = MMC_R1_CURRENT_STATE(cmd->response); + ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d", + cmd->response[0], + cmd->response[1], + cmd->response[2], + cmd->response[3], + cmd->error, + state); + return cmd->error; +} + +static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd) +{ + sdmmc_command_t app_cmd = { + .opcode = MMC_APP_CMD, + .flags = SCF_CMD_AC | SCF_RSP_R1, + .arg = MMC_ARG_RCA(card->rca), + }; + esp_err_t err = sdmmc_send_cmd(card, &app_cmd); + if (err != ESP_OK) { + return err; + } + if (!(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) { + ESP_LOGW(TAG, "card doesn't support APP_CMD"); + return ESP_ERR_NOT_SUPPORTED; + } + return sdmmc_send_cmd(card, cmd); +} + + +static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card) +{ + sdmmc_command_t cmd = { + .opcode = MMC_GO_IDLE_STATE, + .flags = SCF_CMD_BC | SCF_RSP_R0, + }; + return sdmmc_send_cmd(card, &cmd); +} + + +static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr) +{ + const uint8_t pattern = 0xaa; /* any pattern will do here */ + sdmmc_command_t cmd = { + .opcode = SD_SEND_IF_COND, + .arg = (((ocr & SD_OCR_VOL_MASK) != 0) << 8) | pattern, + .flags = SCF_CMD_BCR | SCF_RSP_R7, + }; + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + uint8_t response = cmd.response[0] & 0xff; + if (response != pattern) { + return ESP_ERR_INVALID_RESPONSE; + } + return ESP_OK; +} + +static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp) +{ + sdmmc_command_t cmd = { + .arg = ocr, + .flags = SCF_CMD_BCR | SCF_RSP_R3, + .opcode = SD_APP_OP_COND + }; + int nretries = 100; // arbitrary, BSD driver uses this value + for (; nretries != 0; --nretries) { + esp_err_t err = sdmmc_send_app_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) || + ocr == 0) { + break; + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } + if (nretries == 0) { + return ESP_ERR_TIMEOUT; + } + if (ocrp) { + *ocrp = MMC_R3(cmd.response); + } + return ESP_OK; +} + +static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid) +{ + out_cid->mfg_id = SD_CID_MID(resp); + out_cid->oem_id = SD_CID_OID(resp); + SD_CID_PNM_CPY(resp, out_cid->name); + out_cid->revision = SD_CID_REV(resp); + out_cid->serial = SD_CID_PSN(resp); + out_cid->date = SD_CID_MDT(resp); + return ESP_OK; +} + +static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid) +{ + assert(out_cid); + sdmmc_command_t cmd = { + .opcode = MMC_ALL_SEND_CID, + .flags = SCF_CMD_BCR | SCF_RSP_R2 + }; + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + return sdmmc_decode_cid(cmd.response, out_cid); +} + + +static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca) +{ + assert(out_rca); + sdmmc_command_t cmd = { + .opcode = SD_SEND_RELATIVE_ADDR, + .flags = SCF_CMD_BCR | SCF_RSP_R6 + }; + + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + *out_rca = SD_R6_RCA(cmd.response); + return ESP_OK; +} + + +static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd) +{ + sdmmc_command_t cmd = { + .opcode = MMC_SET_BLOCKLEN, + .arg = csd->sector_size, + .flags = SCF_CMD_AC | SCF_RSP_R1 + }; + return sdmmc_send_cmd(card, &cmd); +} + +static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd) +{ + out_csd->csd_ver = SD_CSD_CSDVER(response); + switch (out_csd->csd_ver) { + case SD_CSD_CSDVER_2_0: + out_csd->capacity = SD_CSD_V2_CAPACITY(response); + out_csd->read_block_len = SD_CSD_V2_BL_LEN; + break; + case SD_CSD_CSDVER_1_0: + out_csd->capacity = SD_CSD_CAPACITY(response); + out_csd->read_block_len = SD_CSD_READ_BL_LEN(response); + break; + default: + ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver); + return ESP_ERR_NOT_SUPPORTED; + } + out_csd->card_command_class = SD_CSD_CCC(response); + int read_bl_size = 1 << out_csd->read_block_len; + out_csd->sector_size = MIN(read_bl_size, 512); + if (out_csd->sector_size < read_bl_size) { + out_csd->capacity *= read_bl_size / out_csd->sector_size; + } + int speed = SD_CSD_SPEED(response); + if (speed == SD_CSD_SPEED_50_MHZ) { + out_csd->tr_speed = 50000000; + } else { + out_csd->tr_speed = 25000000; + } + return ESP_OK; +} + +static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd) +{ + sdmmc_command_t cmd = { + .opcode = MMC_SEND_CSD, + .arg = MMC_ARG_RCA(card->rca), + .flags = SCF_CMD_AC | SCF_RSP_R2 + }; + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + return sdmmc_decode_csd(cmd.response, out_csd); +} + +static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card) +{ + sdmmc_command_t cmd = { + .opcode = MMC_SELECT_CARD, + .arg = MMC_ARG_RCA(card->rca), + .flags = SCF_CMD_AC | SCF_RSP_R1 + }; + return sdmmc_send_cmd(card, &cmd); +} + +static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr) +{ + sdmmc_response_t resp = {0xabababab, 0xabababab, 0x12345678, 0x09abcdef}; + resp[2] = __builtin_bswap32(raw_scr[0]); + resp[3] = __builtin_bswap32(raw_scr[1]); + int ver = SCR_STRUCTURE(resp); + if (ver != 0) { + return ESP_ERR_NOT_SUPPORTED; + } + out_scr->sd_spec = SCR_SD_SPEC(resp); + out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp); + return ESP_OK; +} + +static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr) +{ + size_t datalen = 8; + uint32_t* buf = (uint32_t*) pvPortMallocCaps(datalen, MALLOC_CAP_DMA); + if (buf == NULL) { + return ESP_ERR_NO_MEM; + } + sdmmc_command_t cmd = { + .data = buf, + .datalen = datalen, + .blklen = datalen, + .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1, + .opcode = SD_APP_SEND_SCR + }; + esp_err_t err = sdmmc_send_app_cmd(card, &cmd); + if (err == ESP_OK) { + buf[0] = (buf[0]); + buf[1] = (buf[1]); + err = sdmmc_decode_scr(buf, out_scr); + } + free(buf); + return err; +} + +static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width) +{ + sdmmc_command_t cmd = { + .opcode = SD_APP_SET_BUS_WIDTH, + .flags = SCF_RSP_R1 | SCF_CMD_AC, + .arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1 + }; + + return sdmmc_send_app_cmd(card, &cmd); +} + +static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status) +{ + sdmmc_command_t cmd = { + .opcode = MMC_STOP_TRANSMISSION, + .arg = 0, + .flags = SCF_RSP_R1B | SCF_CMD_AC + }; + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err == 0) { + *status = MMC_R1(cmd.response); + } + return err; +} + +static uint32_t get_host_ocr(float voltage) +{ + // TODO: report exact voltage to the card + // For now tell that the host has 2.8-3.6V voltage range + (void) voltage; + return SD_OCR_VOL_MASK; +} + +static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status) +{ + sdmmc_command_t cmd = { + .opcode = MMC_SEND_STATUS, + .arg = MMC_ARG_RCA(card->rca), + .flags = SCF_CMD_AC | SCF_RSP_R1 + }; + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + return err; + } + if (out_status) { + *out_status = MMC_R1(cmd.response); + } + return ESP_OK; +} + +esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src, + size_t start_block, size_t block_count) +{ + if (start_block + block_count > card->csd.capacity) { + return ESP_ERR_INVALID_SIZE; + } + size_t block_size = card->csd.sector_size; + sdmmc_command_t cmd = { + .flags = SCF_CMD_ADTC | SCF_RSP_R1, + .blklen = block_size, + .data = (void*) src, + .datalen = block_count * block_size + }; + if (block_count == 1) { + cmd.opcode = MMC_WRITE_BLOCK_SINGLE; + } else { + cmd.opcode = MMC_WRITE_BLOCK_MULTIPLE; + } + if (card->ocr & SD_OCR_SDHC_CAP) { + cmd.arg = start_block; + } else { + cmd.arg = start_block * block_size; + } + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); + return err; + } + uint32_t status = 0; + size_t count = 0; + while (!(status & MMC_R1_READY_FOR_DATA)) { + // TODO: add some timeout here + err = sdmmc_send_cmd_send_status(card, &status); + if (err != ESP_OK) { + return err; + } + if (++count % 10 == 0) { + ESP_LOGV(TAG, "waiting for card to become ready (%d)", count); + } + } + return ESP_OK; +} + +esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst, + size_t start_block, size_t block_count) +{ + if (start_block + block_count > card->csd.capacity) { + return ESP_ERR_INVALID_SIZE; + } + size_t block_size = card->csd.sector_size; + sdmmc_command_t cmd = { + .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1, + .blklen = block_size, + .data = (void*) dst, + .datalen = block_count * block_size + }; + if (block_count == 1) { + cmd.opcode = MMC_READ_BLOCK_SINGLE; + } else { + cmd.opcode = MMC_READ_BLOCK_MULTIPLE; + } + if (card->ocr & SD_OCR_SDHC_CAP) { + cmd.arg = start_block; + } else { + cmd.arg = start_block * block_size; + } + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); + return err; + } + uint32_t status = 0; + size_t count = 0; + while (!(status & MMC_R1_READY_FOR_DATA)) { + // TODO: add some timeout here + err = sdmmc_send_cmd_send_status(card, &status); + if (err != ESP_OK) { + return err; + } + if (++count % 10 == 0) { + ESP_LOGV(TAG, "waiting for card to become ready (%d)", count); + } + } + return ESP_OK; +} diff --git a/components/sdmmc/test/component.mk b/components/sdmmc/test/component.mk new file mode 100644 index 00000000000..ce464a212a6 --- /dev/null +++ b/components/sdmmc/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c new file mode 100644 index 00000000000..e8fbd25451d --- /dev/null +++ b/components/sdmmc/test/test_sd.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include "unity.h" +#include "driver/gpio.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "esp_log.h" +#include "esp_heap_alloc_caps.h" +#include +#include + + +TEST_CASE("can probe SD", "[sd]") +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + sdmmc_host_init(); + sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + TEST_ESP_OK(sdmmc_card_init(&config, card)); + sdmmc_card_print_info(stdout, card); + sdmmc_host_deinit(); + free(card); +} + + +static void do_single_write_read_test(sdmmc_card_t* card, + size_t start_block, size_t block_count) +{ + size_t block_size = card->csd.sector_size; + size_t total_size = block_size * block_count; + printf(" %8d | %3d | %4.1f ", start_block, block_count, total_size / 1024.0f); + uint32_t* buffer = pvPortMallocCaps(total_size, MALLOC_CAP_DMA); + srand(start_block); + for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) { + buffer[i] = rand(); + } + struct timeval t_start_wr; + gettimeofday(&t_start_wr, NULL); + TEST_ESP_OK(sdmmc_write_sectors(card, buffer, start_block, block_count)); + struct timeval t_stop_wr; + gettimeofday(&t_stop_wr, NULL); + float time_wr = 1e3f * (t_stop_wr.tv_sec - t_start_wr.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_wr.tv_usec); + memset(buffer, 0xbb, total_size); + struct timeval t_start_rd; + gettimeofday(&t_start_rd, NULL); + TEST_ESP_OK(sdmmc_read_sectors(card, buffer, start_block, block_count)); + struct timeval t_stop_rd; + gettimeofday(&t_stop_rd, NULL); + float time_rd = 1e3f * (t_stop_rd.tv_sec - t_start_rd.tv_sec) + 1e-3f * (t_stop_rd.tv_usec - t_start_rd.tv_usec); + + printf(" | %6.2f | %.2f | %.2fs | %.2f\n", + time_wr, total_size / (time_wr / 1000) / (1024 * 1024), + time_rd, total_size / (time_rd / 1000) / (1024 * 1024)); + srand(start_block); + for (size_t i = 0; i < total_size / sizeof(buffer[0]); ++i) { + TEST_ASSERT_EQUAL_HEX32(rand(), buffer[i]); + } + free(buffer); +} + +TEST_CASE("can write and read back blocks", "[sd]") +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + config.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_host_init(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + TEST_ESP_OK(sdmmc_card_init(&config, card)); + sdmmc_card_print_info(stdout, card); + printf(" sector | count | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n"); + do_single_write_read_test(card, 0, 1); + do_single_write_read_test(card, 0, 4); + do_single_write_read_test(card, 1, 16); + do_single_write_read_test(card, 16, 32); + do_single_write_read_test(card, 48, 64); + do_single_write_read_test(card, 128, 128); + do_single_write_read_test(card, card->csd.capacity - 64, 32); + do_single_write_read_test(card, card->csd.capacity - 64, 64); + do_single_write_read_test(card, card->csd.capacity - 8, 1); + do_single_write_read_test(card, card->csd.capacity/2, 1); + do_single_write_read_test(card, card->csd.capacity/2, 4); + do_single_write_read_test(card, card->csd.capacity/2, 8); + do_single_write_read_test(card, card->csd.capacity/2, 16); + do_single_write_read_test(card, card->csd.capacity/2, 32); + do_single_write_read_test(card, card->csd.capacity/2, 64); + do_single_write_read_test(card, card->csd.capacity/2, 128); + free(card); + sdmmc_host_deinit(); +} diff --git a/examples/27_sd_card/Makefile b/examples/27_sd_card/Makefile new file mode 100644 index 00000000000..512e40fa674 --- /dev/null +++ b/examples/27_sd_card/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := sd_card + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/27_sd_card/README.md b/examples/27_sd_card/README.md new file mode 100644 index 00000000000..4053ad26092 --- /dev/null +++ b/examples/27_sd_card/README.md @@ -0,0 +1,81 @@ +# SD Card example + +This example demonstrates how to use an SD card with ESP32. Example does the following steps: + +1. Use an "all-in-one" `esp_vfs_fat_sdmmc_mount` function to: + - initialize SDMMC peripheral, + - probe and initialize the card connected to SD/MMC slot 1 (HS2_CMD, HS2_CLK, HS2_D0, HS2_D1, HS2_D2, HS2_D3 lines), + - mount FAT filesystem using FATFS library (and format card, if the filesystem can not be mounted), + - register FAT filesystem in VFS, enabling C standard library and POSIX functions to be used. +2. Print information about the card, such as name, type, capacity, and maximum supported frequency. +3. Create a file using `fopen` and write to it using `fprintf`. +4. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function. +5. Open renamed file for reading, read back the line, and print it to the terminal. + +*Note:* despite the name, `sdmmc` component doesn't support MMC/eMMC cards yet. It is also possible to extend `sdmmc` component to support SPI mode with SD cards via SPI peripheral. + +## Hardware + +To run this example, ESP32 development board needs to be connected to SD card as follows: + +ESP32 pin | SD card pin | Notes +--------------|-------------|------------ +GPIO14 (MTMS) | CLK | 10k pullup +GPIO15 (MTDO) | CMD | 10k pullup +GPIO2 | D0 | 10k pullup, pull low to go into download mode +GPIO4 | D1 | 10k pullup; not used in 1-line mode +GPIO12 (MTDI) | D2 | otherwise 10k pullup (see note below!); not used in 1-line mode +GPIO13 (MTCK) | D3 | 10k pullup needed at card side, even in 1-line mode +N/C | CD | +N/C | WP | + +This example doesn't utilize card detect (CD) and write protect (WP) signals from SD card slot. + +### Note about GPIO2 +GPIO2 pin is used as a bootstrapping pin, and should be low to enter UART download mode. One way to do this is to connect GPIO0 and GPIO2 using a jumper, and then the auto-reset circuit on most development boards will pull GPIO2 low along with GPIO2, when entering download mode. + +### Note about GPIO12 + +GPIO12 is used as a bootstrapping pin to select output voltage of an internal regulator which powers the flash chip. + +- For boards which don't use the internal regulator, GPIO12 can be pulled high. +- On boards which use the internal regulator and a 3.3V flash chip, GPIO12 should be pulled up high, which is compatible with SD card operation. +- For boards which use 1.8V flash chip, GPIO12 needs to be low at reset. + * In this case, internal pullup can be enabled using a `gpio_pullup_en(GPIO_NUM_12);` call. Most SD cards work fine when an internal pullup on GPIO12 line is enabled. Note that if ESP32 experiences a power-on reset while the SD card is sending data, high level on GPIO12 can be latched into the bootstrapping register, and ESP32 will enter a boot loop until external reset with correct GPIO12 level is applied. + * Another option is to program flash voltage selection efuses: set `SDIO_TIEH=0` and `SDIO_FORCE=1`. This will permanently select 1.8v output voltage for the internal regulator, and GPIO12 will not be used as a bootstrapping pin anymore. Then it is safe to connect a pullup resistor to GPIO12. This option is suggested for production use. + +## 4-line and 1-line modes + +By default, example code uses the following initializer for SDMMC host peripheral configuration: + +```c++ +sdmmc_host_t host = SDMMC_HOST_DEFAULT(); +``` + +Among other things, this sets `host.flags` to `SDMMC_HOST_FLAG_4BIT`, which means that SD/MMC driver will switch to 4-line mode when initializing the card (initial communication always happens in 1-line mode). If some of D1, D2, D3 pins are not connected to the card, set `host.flags` to `SDMMC_HOST_FLAG_1BIT` — then the SD/MMC driver will not attempt to switch to 4-line mode. +Note that even if D3 line is not connected to the ESP32, it still has to be pulled up at card side, otherwise the card will go into SPI protocol mode. + + +## Example output + +Here is an example console output. In this case a 128MB SDSC card was connected, and `format_if_mount_failed` parameter was set to `true` in the source code. Card was unformatted, so the initial mount has failed. Card was then partitioned, formatted, and mounted again. + +``` +I (1776) example: Initializing SD card +W (1856) vfs_fat_sdmmc: failed to mount card (13) +W (1856) vfs_fat_sdmmc: partitioning card +W (1856) vfs_fat_sdmmc: formatting card +W (2726) vfs_fat_sdmmc: mounting again +I (2736) example: Card info: +I (2736) example: Name: SU128 +I (2736) example: Type: SDSC +I (2736) example: Capacity: 120 MB +I (2736) example: Max clock speed: 25 MHz +I (2736) example: Opening file +I (2756) example: File written +I (2756) example: Renaming file +I (2756) example: Reading file +I (2756) example: Read from file: 'Hello SU128!' +I (2756) example: Card unmounted +``` + diff --git a/examples/27_sd_card/main/component.mk b/examples/27_sd_card/main/component.mk new file mode 100644 index 00000000000..a98f634eae0 --- /dev/null +++ b/examples/27_sd_card/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/27_sd_card/main/sd_card.c b/examples/27_sd_card/main/sd_card.c new file mode 100644 index 00000000000..03d0ec13107 --- /dev/null +++ b/examples/27_sd_card/main/sd_card.c @@ -0,0 +1,107 @@ +/* SD card and FAT filesystem example. + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" + +static const char* TAG = "example"; + +void app_main(void) +{ + ESP_LOGI(TAG, "Initializing SD card"); + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + + // To use 1-line SD mode, uncomment the following line: + // host.flags = SDMMC_HOST_FLAG_1BIT; + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + // Options for mounting the filesystem. + // If format_if_mount_failed is set to true, SD card will be partitioned and formatted + // in case when mounting fails. + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5 + }; + + // Use settings defined above to initialize SD card and mount FAT filesystem. + // Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function. + // Please check its source code and implement error recovery when developing + // production applications. + sdmmc_card_t* card; + esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + ESP_LOGE(TAG, "Failed to mount filesystem. If you want the card to be formatted, set format_if_mount_failed = true."); + } else { + ESP_LOGE(TAG, "Failed to initialize the card (%d). Make sure SD card lines have pull-up resistors in place.", ret); + } + return; + } + + // Card has been initialized, print its properties + sdmmc_card_print_info(stdout, card); + + // Use POSIX and C standard library functions to work with files. + // First create a file. + ESP_LOGI(TAG, "Opening file"); + FILE* f = fopen("/sdcard/hello.txt", "w"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + fprintf(f, "Hello %s!\n", card->cid.name); + fclose(f); + ESP_LOGI(TAG, "File written"); + + // Check if destination file exists before renaming + struct stat st; + if (stat("/sdcard/foo.txt", &st) == 0) { + // Delete it if it exists + unlink("/sdcard/foo.txt"); + } + + // Rename original file + ESP_LOGI(TAG, "Renaming file"); + if (rename("/sdcard/hello.txt", "/sdcard/foo.txt") != 0) { + ESP_LOGE(TAG, "Rename failed"); + return; + } + + // Open renamed file for reading + ESP_LOGI(TAG, "Reading file"); + f = fopen("/sdcard/foo.txt", "r"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for reading"); + return; + } + char line[64]; + fgets(line, sizeof(line), f); + fclose(f); + // strip newline + char* pos = strchr(line, '\n'); + if (pos) { + *pos = '\0'; + } + ESP_LOGI(TAG, "Read from file: '%s'", line); + + // All done, unmount partition and disable SDMMC host peripheral + esp_vfs_fat_sdmmc_unmount(); + ESP_LOGI(TAG, "Card unmounted"); +} From 3f889de5ab72e69a54d0076df394797ac87f4500 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 29 Dec 2016 22:21:29 +0800 Subject: [PATCH 091/167] vfs: implement vfs_unregister --- components/vfs/include/esp_vfs.h | 9 +++++++++ components/vfs/vfs.c | 34 ++++++++++++++++++++++++++------ docs/api/vfs.rst | 1 + 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 7dd273fb002..5d9236b54c6 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -131,6 +131,15 @@ typedef struct esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx); +/** + * Unregister a virtual filesystem for given path prefix + * + * @param base_path file prefix previously used in esp_vfs_register call + * @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix + * hasn't been registered + */ +esp_err_t esp_vfs_unregister(const char* base_path); + /** * These functions are to be used in newlib syscall table. They will be called by * newlib when it needs to use any of the syscalls. diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index b60c60a818f..b166a427bb8 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -54,9 +54,6 @@ static size_t s_vfs_count = 0; esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx) { - if (s_vfs_count >= VFS_MAX_COUNT) { - return ESP_ERR_NO_MEM; - } size_t len = strlen(base_path); if (len < 2 || len > ESP_VFS_PATH_MAX) { return ESP_ERR_INVALID_ARG; @@ -68,16 +65,41 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct if (entry == NULL) { return ESP_ERR_NO_MEM; } + size_t index; + for (index = 0; index < s_vfs_count; ++index) { + if (s_vfs[index] == NULL) { + break; + } + } + if (index == s_vfs_count) { + if (s_vfs_count >= VFS_MAX_COUNT) { + free(entry); + return ESP_ERR_NO_MEM; + } + ++s_vfs_count; + } + s_vfs[index] = entry; strcpy(entry->path_prefix, base_path); // we have already verified argument length memcpy(&entry->vfs, vfs, sizeof(esp_vfs_t)); entry->path_prefix_len = len; entry->ctx = ctx; - entry->offset = s_vfs_count; - s_vfs[s_vfs_count] = entry; - ++s_vfs_count; + entry->offset = index; return ESP_OK; } +esp_err_t esp_vfs_unregister(const char* base_path) +{ + for (size_t i = 0; i < s_vfs_count; ++i) { + vfs_entry_t* vfs = s_vfs[i]; + if (memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) { + free(vfs); + s_vfs[i] = NULL; + return ESP_OK; + } + } + return ESP_ERR_INVALID_STATE; +} + static const vfs_entry_t* get_vfs_for_fd(int fd) { int index = ((fd & VFS_INDEX_MASK) >> VFS_INDEX_S); diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst index 798aac54924..71550f111c6 100644 --- a/docs/api/vfs.rst +++ b/docs/api/vfs.rst @@ -31,6 +31,7 @@ Functions ^^^^^^^^^ .. doxygenfunction:: esp_vfs_register +.. doxygenfunction:: esp_vfs_unregister .. doxygenfunction:: esp_vfs_write .. doxygenfunction:: esp_vfs_lseek .. doxygenfunction:: esp_vfs_read From 6fb430f45eb6aa81f2066995442ee83cdeba203f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 3 Jan 2017 03:26:25 +0800 Subject: [PATCH 092/167] vfs: add directory APIs --- components/newlib/include/sys/dirent.h | 13 --- components/vfs/include/esp_vfs.h | 34 +++++++ components/vfs/include/sys/dirent.h | 55 ++++++++++ components/vfs/vfs.c | 135 +++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 13 deletions(-) delete mode 100644 components/newlib/include/sys/dirent.h create mode 100644 components/vfs/include/sys/dirent.h diff --git a/components/newlib/include/sys/dirent.h b/components/newlib/include/sys/dirent.h deleted file mode 100644 index a3fb5c02c57..00000000000 --- a/components/newlib/include/sys/dirent.h +++ /dev/null @@ -1,13 +0,0 @@ -/* includes , which is this file. On a - system which supports , this file is overridden by - dirent.h in the libc/sys/.../sys directory. On a system which does - not support , we will get this file which uses #error to force - an error. */ - -#ifdef __cplusplus -extern "C" { -#endif -#error " not supported" -#ifdef __cplusplus -} -#endif diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 5d9236b54c6..304750aabd6 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -21,6 +21,8 @@ #include #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -106,6 +108,38 @@ typedef struct int (*rename_p)(void* ctx, const char *src, const char *dst); int (*rename)(const char *src, const char *dst); }; + union { + DIR* (*opendir_p)(void* ctx, const char* name); + DIR* (*opendir)(const char* name); + }; + union { + struct dirent* (*readdir_p)(void* ctx, DIR* pdir); + struct dirent* (*readdir)(DIR* pdir); + }; + union { + int (*readdir_r_p)(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + int (*readdir_r)(DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + }; + union { + long (*telldir_p)(void* ctx, DIR* pdir); + long (*telldir)(DIR* pdir); + }; + union { + void (*seekdir_p)(void* ctx, DIR* pdir, long offset); + void (*seekdir)(DIR* pdir, long offset); + }; + union { + int (*closedir_p)(void* ctx, DIR* pdir); + int (*closedir)(DIR* pdir); + }; + union { + int (*mkdir_p)(void* ctx, const char* name, mode_t mode); + int (*mkdir)(const char* name, mode_t mode); + }; + union { + int (*rmdir_p)(void* ctx, const char* name); + int (*rmdir)(const char* name); + }; } esp_vfs_t; diff --git a/components/vfs/include/sys/dirent.h b/components/vfs/include/sys/dirent.h new file mode 100644 index 00000000000..57b5be5eea3 --- /dev/null +++ b/components/vfs/include/sys/dirent.h @@ -0,0 +1,55 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +/** + * This header file provides POSIX-compatible definitions of directory + * access functions and related data types. + * See http://pubs.opengroup.org/onlinepubs/7908799/xsh/dirent.h.html + * for reference. + */ + +/** + * @brief Opaque directory structure + */ +typedef struct { + uint16_t dd_vfs_idx; /*!< VFS index, not to be used by applications */ + uint16_t dd_rsv; /*!< field reserved for future extension */ + /* remaining fields are defined by VFS implementation */ +} DIR; + +/** + * @brief Directory entry structure + */ +struct dirent { + int d_ino; /*!< file number */ + uint8_t d_type; /*!< not defined in POSIX, but present in BSD and Linux */ +#define DT_UNKNOWN 0 +#define DT_REG 1 +#define DT_DIR 2 + char d_name[256]; /*!< zero-terminated file name */ +}; + +DIR* opendir(const char* name); +struct dirent* readdir(DIR* pdir); +long telldir(DIR* pdir); +void seekdir(DIR* pdir, long loc); +void rewinddir(DIR* pdir); +int closedir(DIR* pdir); +int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index b166a427bb8..d80972a533c 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -163,6 +163,28 @@ static const vfs_entry_t* get_vfs_for_path(const char* path) } +#define CHECK_AND_CALLV(r, pvfs, func, ...) \ + if (pvfs->vfs.func == NULL) { \ + __errno_r(r) = ENOSYS; \ + return; \ + } \ + if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + } else { \ + (*pvfs->vfs.func)(__VA_ARGS__);\ + } + +#define CHECK_AND_CALLP(ret, r, pvfs, func, ...) \ + if (pvfs->vfs.func == NULL) { \ + __errno_r(r) = ENOSYS; \ + return NULL; \ + } \ + if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + } else { \ + ret = (*pvfs->vfs.func)(__VA_ARGS__);\ + } + int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode) { const vfs_entry_t* vfs = get_vfs_for_path(path); @@ -309,3 +331,116 @@ int esp_vfs_rename(struct _reent *r, const char *src, const char *dst) CHECK_AND_CALL(ret, r, vfs, rename, src_within_vfs, dst_within_vfs); return ret; } + +DIR* opendir(const char* name) +{ + const vfs_entry_t* vfs = get_vfs_for_path(name); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return NULL; + } + const char* path_within_vfs = translate_path(vfs, name); + DIR* ret; + CHECK_AND_CALLP(ret, r, vfs, opendir, path_within_vfs); + if (ret != NULL) { + ret->dd_vfs_idx = vfs->offset << VFS_INDEX_S; + } + return ret; +} + +struct dirent* readdir(DIR* pdir) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return NULL; + } + struct dirent* ret; + CHECK_AND_CALLP(ret, r, vfs, readdir, pdir); + return ret; +} + +int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return -1; + } + int ret; + CHECK_AND_CALL(ret, r, vfs, readdir_r, pdir, entry, out_dirent); + return ret; +} + +long telldir(DIR* pdir) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return -1; + } + long ret; + CHECK_AND_CALL(ret, r, vfs, telldir, pdir); + return ret; +} + +void seekdir(DIR* pdir, long loc) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return; + } + CHECK_AND_CALLV(r, vfs, seekdir, pdir, loc); +} + +void rewinddir(DIR* pdir) +{ + seekdir(pdir, 0); +} + +int closedir(DIR* pdir) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx); + struct _reent* r = __getreent(); + if (vfs == NULL) { + errno = EBADF; + return -1; + } + int ret; + CHECK_AND_CALL(ret, r, vfs, closedir, pdir); + return ret; +} + +int mkdir(const char* name, mode_t mode) +{ + const vfs_entry_t* vfs = get_vfs_for_path(name); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, name); + int ret; + CHECK_AND_CALL(ret, r, vfs, mkdir, path_within_vfs, mode); + return ret; +} + +int rmdir(const char* name) +{ + const vfs_entry_t* vfs = get_vfs_for_path(name); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, name); + int ret; + CHECK_AND_CALL(ret, r, vfs, rmdir, path_within_vfs); + return ret; +} From 1440c866fc9e0609accd706c750745104d5252c9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 29 Dec 2016 17:28:39 +0800 Subject: [PATCH 093/167] fatfs: import version 0.12b --- components/fatfs/component.mk | 2 + components/fatfs/src/00history.txt | 279 ++ components/fatfs/src/00readme.txt | 21 + components/fatfs/src/diskio.c | 225 + components/fatfs/src/diskio.h | 80 + components/fatfs/src/ff.c | 6041 +++++++++++++++++++++++++ components/fatfs/src/ff.h | 366 ++ components/fatfs/src/ffconf.h | 267 ++ components/fatfs/src/integer.h | 38 + components/fatfs/src/option/syscall.c | 151 + components/fatfs/src/option/unicode.c | 17 + 11 files changed, 7487 insertions(+) create mode 100644 components/fatfs/component.mk create mode 100644 components/fatfs/src/00history.txt create mode 100644 components/fatfs/src/00readme.txt create mode 100644 components/fatfs/src/diskio.c create mode 100644 components/fatfs/src/diskio.h create mode 100644 components/fatfs/src/ff.c create mode 100644 components/fatfs/src/ff.h create mode 100644 components/fatfs/src/ffconf.h create mode 100644 components/fatfs/src/integer.h create mode 100644 components/fatfs/src/option/syscall.c create mode 100644 components/fatfs/src/option/unicode.c diff --git a/components/fatfs/component.mk b/components/fatfs/component.mk new file mode 100644 index 00000000000..f2094e7ab9a --- /dev/null +++ b/components/fatfs/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src src/option diff --git a/components/fatfs/src/00history.txt b/components/fatfs/src/00history.txt new file mode 100644 index 00000000000..49aac282b23 --- /dev/null +++ b/components/fatfs/src/00history.txt @@ -0,0 +1,279 @@ +---------------------------------------------------------------------------- + Revision history of FatFs module +---------------------------------------------------------------------------- + +R0.00 (February 26, 2006) + + Prototype. + + + +R0.01 (April 29, 2006) + + The first release. + + + +R0.02 (June 01, 2006) + + Added FAT12 support. + Removed unbuffered mode. + Fixed a problem on small (<32M) partition. + + + +R0.02a (June 10, 2006) + + Added a configuration option (_FS_MINIMUM). + + + +R0.03 (September 22, 2006) + + Added f_rename(). + Changed option _FS_MINIMUM to _FS_MINIMIZE. + + + +R0.03a (December 11, 2006) + + Improved cluster scan algorithm to write files fast. + Fixed f_mkdir() creates incorrect directory on FAT32. + + + +R0.04 (February 04, 2007) + + Added f_mkfs(). + Supported multiple drive system. + Changed some interfaces for multiple drive system. + Changed f_mountdrv() to f_mount(). + + + +R0.04a (April 01, 2007) + + Supported multiple partitions on a physical drive. + Added a capability of extending file size to f_lseek(). + Added minimization level 3. + Fixed an endian sensitive code in f_mkfs(). + + + +R0.04b (May 05, 2007) + + Added a configuration option _USE_NTFLAG. + Added FSINFO support. + Fixed DBCS name can result FR_INVALID_NAME. + Fixed short seek (<= csize) collapses the file object. + + + +R0.05 (August 25, 2007) + + Changed arguments of f_read(), f_write() and f_mkfs(). + Fixed f_mkfs() on FAT32 creates incorrect FSINFO. + Fixed f_mkdir() on FAT32 creates incorrect directory. + + + +R0.05a (February 03, 2008) + + Added f_truncate() and f_utime(). + Fixed off by one error at FAT sub-type determination. + Fixed btr in f_read() can be mistruncated. + Fixed cached sector is not flushed when create and close without write. + + + +R0.06 (April 01, 2008) + + Added fputc(), fputs(), fprintf() and fgets(). + Improved performance of f_lseek() on moving to the same or following cluster. + + + +R0.07 (April 01, 2009) + + Merged Tiny-FatFs as a configuration option. (_FS_TINY) + Added long file name feature. (_USE_LFN) + Added multiple code page feature. (_CODE_PAGE) + Added re-entrancy for multitask operation. (_FS_REENTRANT) + Added auto cluster size selection to f_mkfs(). + Added rewind option to f_readdir(). + Changed result code of critical errors. + Renamed string functions to avoid name collision. + + + +R0.07a (April 14, 2009) + + Septemberarated out OS dependent code on reentrant cfg. + Added multiple sector size feature. + + + +R0.07c (June 21, 2009) + + Fixed f_unlink() can return FR_OK on error. + Fixed wrong cache control in f_lseek(). + Added relative path feature. + Added f_chdir() and f_chdrive(). + Added proper case conversion to extended character. + + + +R0.07e (November 03, 2009) + + Septemberarated out configuration options from ff.h to ffconf.h. + Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH. + Fixed name matching error on the 13 character boundary. + Added a configuration option, _LFN_UNICODE. + Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. + + + +R0.08 (May 15, 2010) + + Added a memory configuration option. (_USE_LFN = 3) + Added file lock feature. (_FS_SHARE) + Added fast seek feature. (_USE_FASTSEEK) + Changed some types on the API, XCHAR->TCHAR. + Changed .fname in the FILINFO structure on Unicode cfg. + String functions support UTF-8 encoding files on Unicode cfg. + + + +R0.08a (August 16, 2010) + + Added f_getcwd(). (_FS_RPATH = 2) + Added sector erase feature. (_USE_ERASE) + Moved file lock semaphore table from fs object to the bss. + Fixed f_mkfs() creates wrong FAT32 volume. + + + +R0.08b (January 15, 2011) + + Fast seek feature is also applied to f_read() and f_write(). + f_lseek() reports required table size on creating CLMP. + Extended format syntax of f_printf(). + Ignores duplicated directory separators in given path name. + + + +R0.09 (September 06, 2011) + + f_mkfs() supports multiple partition to complete the multiple partition feature. + Added f_fdisk(). + + + +R0.09a (August 27, 2012) + + Changed f_open() and f_opendir() reject null object pointer to avoid crash. + Changed option name _FS_SHARE to _FS_LOCK. + Fixed assertion failure due to OS/2 EA on FAT12/16 volume. + + + +R0.09b (January 24, 2013) + + Added f_setlabel() and f_getlabel(). + + + +R0.10 (October 02, 2013) + + Added selection of character encoding on the file. (_STRF_ENCODE) + Added f_closedir(). + Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO) + Added forced mount feature with changes of f_mount(). + Improved behavior of volume auto detection. + Improved write throughput of f_puts() and f_printf(). + Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write(). + Fixed f_write() can be truncated when the file size is close to 4GB. + Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error. + + + +R0.10a (January 15, 2014) + + Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID) + Added a configuration option of minimum sector size. (_MIN_SS) + 2nd argument of f_rename() can have a drive number and it will be ignored. + Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10) + Fixed f_close() invalidates the file object without volume lock. + Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10) + Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07) + + + +R0.10b (May 19, 2014) + + Fixed a hard error in the disk I/O layer can collapse the directory entry. + Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07) + + + +R0.10c (November 09, 2014) + + Added a configuration option for the platforms without RTC. (_FS_NORTC) + Changed option name _USE_ERASE to _USE_TRIM. + Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b) + Fixed a potential problem of FAT access that can appear on disk error. + Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08) + + + +R0.11 (February 09, 2015) + + Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND) + Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c) + Fixed _FS_NORTC option does not work properly. (appeared at R0.10c) + + + +R0.11a (September 05, 2015) + + Fixed wrong media change can lead a deadlock at thread-safe configuration. + Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE) + Removed some code pages actually not exist on the standard systems. (_CODE_PAGE) + Fixed errors in the case conversion teble of code page 437 and 850 (ff.c). + Fixed errors in the case conversion teble of Unicode (cc*.c). + + + +R0.12 (April 12, 2016) + + Added support for exFAT file system. (_FS_EXFAT) + Added f_expand(). (_USE_EXPAND) + Changed some members in FINFO structure and behavior of f_readdir(). + Added an option _USE_CHMOD. + Removed an option _WORD_ACCESS. + Fixed errors in the case conversion table of Unicode (cc*.c). + + + +R0.12a (July 10, 2016) + + Added support for creating exFAT volume with some changes of f_mkfs(). + Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed. + f_forward() is available regardless of _FS_TINY. + Fixed f_mkfs() creates wrong volume. (appeared at R0.12) + Fixed wrong memory read in create_name(). (appeared at R0.12) + Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD. + + + +R0.12b (September 04, 2016) + + Improved f_rename() to be able to rename objects with the same name but case. + Fixed an error in the case conversion teble of code page 866. (ff.c) + Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12) + Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12) + Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12) + Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12) + Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12) + Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12) + diff --git a/components/fatfs/src/00readme.txt b/components/fatfs/src/00readme.txt new file mode 100644 index 00000000000..42426a4011a --- /dev/null +++ b/components/fatfs/src/00readme.txt @@ -0,0 +1,21 @@ +FatFs Module Source Files R0.12a + + +FILES + + 00readme.txt This file. + history.txt Revision history. + ffconf.h Configuration file for FatFs module. + ff.h Common include file for FatFs and application module. + ff.c FatFs module. + diskio.h Common include file for FatFs and disk I/O module. + diskio.c An example of glue function to attach existing disk I/O module to FatFs. + integer.h Integer type definitions for FatFs. + option Optional external functions. + + + Low level disk I/O module is not included in this archive because the FatFs + module is only a generic file system layer and not depend on any specific + storage device. You have to provide a low level disk I/O module that written + to control the target storage device. + diff --git a/components/fatfs/src/diskio.c b/components/fatfs/src/diskio.c new file mode 100644 index 00000000000..25f5e53bc03 --- /dev/null +++ b/components/fatfs/src/diskio.c @@ -0,0 +1,225 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ +/*-----------------------------------------------------------------------*/ +/* If a working storage control module is available, it should be */ +/* attached to the FatFs via a glue function rather than modifying it. */ +/* This is an example of glue functions to attach various exsisting */ +/* storage control modules to the FatFs module with a defined API. */ +/*-----------------------------------------------------------------------*/ + +#include "diskio.h" /* FatFs lower layer API */ + +/* Definitions of physical drive number for each drive */ +#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */ +#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */ +#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */ + + +/*-----------------------------------------------------------------------*/ +/* Get Drive Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + DSTATUS stat; + int result; + + switch (pdrv) { + case DEV_RAM : + result = RAM_disk_status(); + + // translate the reslut code here + + return stat; + + case DEV_MMC : + result = MMC_disk_status(); + + // translate the reslut code here + + return stat; + + case DEV_USB : + result = USB_disk_status(); + + // translate the reslut code here + + return stat; + } + return STA_NOINIT; +} + + + +/*-----------------------------------------------------------------------*/ +/* Inidialize a Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + DSTATUS stat; + int result; + + switch (pdrv) { + case DEV_RAM : + result = RAM_disk_initialize(); + + // translate the reslut code here + + return stat; + + case DEV_MMC : + result = MMC_disk_initialize(); + + // translate the reslut code here + + return stat; + + case DEV_USB : + result = USB_disk_initialize(); + + // translate the reslut code here + + return stat; + } + return STA_NOINIT; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to read */ +) +{ + DRESULT res; + int result; + + switch (pdrv) { + case DEV_RAM : + // translate the arguments here + + result = RAM_disk_read(buff, sector, count); + + // translate the reslut code here + + return res; + + case DEV_MMC : + // translate the arguments here + + result = MMC_disk_read(buff, sector, count); + + // translate the reslut code here + + return res; + + case DEV_USB : + // translate the arguments here + + result = USB_disk_read(buff, sector, count); + + // translate the reslut code here + + return res; + } + + return RES_PARERR; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_write ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to write */ +) +{ + DRESULT res; + int result; + + switch (pdrv) { + case DEV_RAM : + // translate the arguments here + + result = RAM_disk_write(buff, sector, count); + + // translate the reslut code here + + return res; + + case DEV_MMC : + // translate the arguments here + + result = MMC_disk_write(buff, sector, count); + + // translate the reslut code here + + return res; + + case DEV_USB : + // translate the arguments here + + result = USB_disk_write(buff, sector, count); + + // translate the reslut code here + + return res; + } + + return RES_PARERR; +} + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE pdrv, /* Physical drive nmuber (0..) */ + BYTE cmd, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + int result; + + switch (pdrv) { + case DEV_RAM : + + // Process of the command for the RAM drive + + return res; + + case DEV_MMC : + + // Process of the command for the MMC/SD card + + return res; + + case DEV_USB : + + // Process of the command the USB drive + + return res; + } + + return RES_PARERR; +} + diff --git a/components/fatfs/src/diskio.h b/components/fatfs/src/diskio.h new file mode 100644 index 00000000000..03e8b7c560b --- /dev/null +++ b/components/fatfs/src/diskio.h @@ -0,0 +1,80 @@ +/*-----------------------------------------------------------------------/ +/ Low level disk interface modlue include file (C)ChaN, 2014 / +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + + +DSTATUS disk_initialize (BYTE pdrv); +DSTATUS disk_status (BYTE pdrv); +DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); +DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl fucntion */ + +/* Generic command (Used by FatFs) */ +#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ +#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ +#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ + +/* Generic command (Not used by FatFs) */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ +#define CTRL_FORMAT 8 /* Create physical format on the media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ +#define ISDIO_READ 55 /* Read data form SD iSDIO register */ +#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ +#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/fatfs/src/ff.c b/components/fatfs/src/ff.c new file mode 100644 index 00000000000..ffc5240970e --- /dev/null +++ b/components/fatfs/src/ff.c @@ -0,0 +1,6041 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12b / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2016, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of device I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 68020 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res +#endif + + + +/* Definitions of sector size */ +#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if _MAX_SS == _MIN_SS +#define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp */ +#if _FS_NORTC == 1 +#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 +#error Invalid _FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File lock controls */ +#if _FS_LOCK != 0 +#if _FS_READONLY +#error _FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, directory (0:root) */ + DWORD ofs; /* Object ID 3, directory offset */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + + +/* DBCS code ranges and SBCS upper conversion tables */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 771 /* KBL */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Latin 1 */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \ + 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \ + 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 860 /* Portuguese */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 861 /* Icelandic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 863 /* Canadian-French */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 864 /* Arabic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 865 /* Nordic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 869 /* Greek 2 */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#if _USE_LFN != 0 +#error Cannot enable LFN without valid code page. +#endif +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S != 0 /* Code page is DBCS */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* File attribute bits (internal use) */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* File access control and file status flags (internal use) */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + +/* Name status flags */ +#define NSFLAG 11 /* Index of name status byte in fn[] */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ + + +/* Limits and boundaries (differ from specs but correct for real DOS/Windows) */ +#define MAX_FAT12 0xFF5 /* Maximum number of FAT12 clusters */ +#define MAX_FAT16 0xFFF5 /* Maximum number of FAT16 clusters */ +#define MAX_FAT32 0xFFFFFF5 /* Maximum number of FAT32 clusters */ +#define MAX_EXFAT 0x7FFFFFFD /* Maximum number of exFAT clusters (limited by implementation) */ +#define MAX_DIR 0x200000 /* Maximum size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Maximum size of exFAT directory */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure members because the structure is not binary compatible between +/ different platforms */ + +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* Error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* File system type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ + +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: File system version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: File system version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */ +#define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ + +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN type (BYTE) */ +#define LDIR_Chksum 13 /* Checksum of the SFN entry (BYTE) */ +#define LDIR_FstClusLO 26 /* Must be zero (WORD) */ +#define XDIR_Type 0 /* Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* File attribute (WORD) */ +#define XDIR_CrtTime 8 /* Created time (DWORD) */ +#define XDIR_ModTime 12 /* Modified time (DWORD) */ +#define XDIR_AccTime 16 /* Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* Gneral secondary flags (WORD) */ +#define XDIR_NumName 35 /* Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ + + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ + +/* Remark: Variables here without initial value shall be guaranteed zero/null +/ at start-up. If not, either the linker or start-up routine being used is +/ not compliance with C standard. */ + +#if _VOLUMES < 1 || _VOLUMES > 9 +#error Wrong _VOLUMES setting +#endif +static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +static WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH != 0 && _VOLUMES >= 2 +static BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_LOCK != 0 +static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if _USE_LFN == 0 /* Non-LFN configuration */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#else +#if _MAX_LFN < 12 || _MAX_LFN > 255 +#error Wrong _MAX_LFN setting +#endif + +#if _USE_LFN == 1 /* LFN enabled with static working buffer */ +#if _FS_EXFAT +static BYTE DirBuf[SZDIRE*19]; /* Directory entry block scratchpad buffer (19 entries in size) */ +#endif +static WCHAR LfnBuf[_MAX_LFN+1]; /* LFN enabled with static working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +#elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[SZDIRE*19]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() +#else +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif + +#elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2 + SZDIRE*19); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) +#endif + +#else +#error Wrong _USE_LFN setting +#endif +#endif + +#ifdef _EXCVT +static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended characters */ +#endif + + + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static +WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static +DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if _FS_EXFAT +static +QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !_FS_READONLY +static +void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static +void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if _FS_EXFAT +static +void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + + if (cnt) { + do *d++ = *s++; while (--cnt); + } +} + +/* Fill memory block */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + do *d++ = (BYTE)val; while (--cnt); +} + +/* Compare memory block */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { /* ZR:same, NZ:different */ + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */ + while (*str && *str != chr) str++; + return *str; +} + + + + +#if _FS_REENTRANT +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +static +int lock_fs ( + FATFS* fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS* fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} + +#endif + + + +#if _FS_LOCK != 0 +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == _FS_LOCK) { /* The object is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ + } + + /* The object has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + return (i == _FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->obj.fs && + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } + + if (i == _FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} + +#endif /* _FS_LOCK != 0 */ + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the file system object */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs /* File system object */ +) +{ + DWORD wsect; + UINT nf; + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Write back the sector if it is dirty */ + wsect = fs->winsect; /* Current sector number */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fs->wflag = 0; + if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ + for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } + } + return res; +} +#endif + + +static +FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Synchronize file system and strage device */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ + FATFS* fs /* File system object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { + /* Create FSInfo structure */ + mem_set(fs->win, 0, SS(fs)); + st_word(fs->win + BS_55AA, 0xAA55); + st_dword(fs->win + FSI_LeadSig, 0x41615252); + st_dword(fs->win + FSI_StrucSig, 0x61417272); + st_dword(fs->win + FSI_Free_Count, fs->free_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); + /* Write it into the FSInfo sector */ + fs->winsect = fs->volbase + 1; + disk_write(fs->drv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; + } + + return res; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ +) +{ + UINT wc, bc; + DWORD val; + FATFS *fs = obj->fs; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + val = ld_word(fs->win + clst * 2 % SS(fs)); + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; + break; +#if _FS_EXFAT + case FS_EXFAT : + if (obj->objsize) { + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ + + if (obj->stat == 2) { /* Is there no valid chain on the FAT? */ + if (cofs <= clen) { + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* Generate the value */ + break; + } + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the contiguous part? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + break; + } + } + /* go next */ +#endif + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* Corresponding file system object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res = FR_INT_ERR; + + + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ + switch (fs->fs_type) { + case FS_FAT12 : /* Bitfield items */ + bc = (UINT)clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc++ % SS(fs); + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc % SS(fs); + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + fs->wflag = 1; + break; + + case FS_FAT16 : /* WORD aligned items */ + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); + fs->wflag = 1; + break; + + case FS_FAT32 : /* DWORD aligned items */ +#if _FS_EXFAT + case FS_EXFAT : +#endif + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); + fs->wflag = 1; + break; + } + } + return res; +} + +#endif /* !_FS_READONLY */ + + + + +#if _FS_EXFAT && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* exFAT: Accessing FAT and Allocation Bitmap */ +/*-----------------------------------------------------------------------*/ + +/*---------------------------------------------*/ +/* exFAT: Find a contiguous free cluster block */ +/*---------------------------------------------*/ + +static +DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */ + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = 4096; + } + if (!bv) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check run length */ + } else { + scl = val; ctr = 0; /* Encountered a live cluster, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*------------------------------------*/ +/* exFAT: Set/Clear a block of bitmap */ +/*------------------------------------*/ + +static +FRESULT change_bitmap ( + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + DWORD sect; + + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + i = 0; + } +} + + +/*---------------------------------------------*/ +/* Complement contiguous part of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_fat_chain ( + _FDID* obj /* Pointer to the corresponding object */ +) +{ + FRESULT res; + DWORD cl, n; + + if (obj->stat == 3) { /* Has the object been changed 'fragmented'? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; + } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + +#endif /* _FS_EXFAT && !_FS_READONLY */ + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +static +FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + _FDID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0:an entire chain) */ +) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; +#if _FS_EXFAT || _USE_TRIM + DWORD scl = clst, ecl = clst; +#endif +#if _USE_TRIM + DWORD rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ + + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; + } + + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if _FS_EXFAT || _USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ + if (res != FR_OK) return res; + } +#endif +#if _USE_TRIM + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Inform device the block can be erased */ +#endif + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Does object have no chain? */ + obj->stat = 0; /* Change the object status 'initial' */ + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Did the chain got contiguous? */ + obj->stat = 2; /* Change the object status 'contiguous' */ + } + } + } +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch a chain or Create a new chain */ +/*-----------------------------------------------------------------------*/ +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster# to stretch, 0:Create a new chain */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + FATFS *fs = obj->fs; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Get suggested cluster to start from */ + if (scl == 0 || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch current chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Invalid value */ + if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous chain' */ + } else { /* This is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap-around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* An error occurred */ + if (ncl == scl) return 0; /* No free cluster */ + } + } + + if (_FS_EXFAT && fs->fs_type == FS_EXFAT && obj->stat == 2) { /* Is it a contiguous chain? */ + res = FR_OK; /* FAT does not need to be written */ + } else { + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ + } + } + + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst < fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Create error status */ + } + + return ncl; /* Return new cluster number or error status */ +} + +#endif /* !_FS_READONLY */ + + + + +#if _USE_FASTSEEK +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (ncl == 0) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} + +#endif /* _USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to directory object */ + DWORD ofs /* Offset of directory table */ +) +{ + DWORD csz, clst; + FATFS *fs = dp->obj.fs; + + + if (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ + return FR_INT_ERR; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = fs->dirbase; + if (_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } + + if (clst == 0) { /* Static table (root-directory in FAT12/16) */ + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory in FAT32+) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; + } + dp->sect = clust2sect(fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (!dp->sect) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; +#if !_FS_READONLY + UINT n; +#endif + + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */ + + if (ofs % SS(fs) == 0) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (!dp->clust) { /* Static table */ + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; return FR_NO_FILE; + } + } + else { /* Dynamic table */ + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* Reached end of dynamic table */ +#if !_FS_READONLY + if (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + /* Clean-up the stretched table */ + if (_FS_EXFAT) dp->obj.stat |= 4; /* The directory needs to be updated */ + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ + for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */ + fs->wflag = 1; + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; + } + fs->winsect -= n; /* Restore window offset */ +#else + if (!stretch) dp->sect = 0; /* If no stretch, report EOT (this is to suppress warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(fs, clst); + } + } + } + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve a block of directory entries */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate */ +) +{ + FRESULT res; + UINT n; + FATFS *fs = dp->obj.fs; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; +#if _FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { +#endif + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); + } while (res == FR_OK); /* Next entry with table stretch enabled */ + } + + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT: Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the key entry */ +) +{ + DWORD cl; + + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } + + return cl; +} + + +#if !_FS_READONLY +static +void st_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ + DWORD cl /* Value to be set */ +) +{ + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } +} +#endif + + + +#if _USE_LFN != 0 +/*------------------------------------------------------------------------*/ +/* FAT-LFN: LFN handling */ +/*------------------------------------------------------------------------*/ +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ + + +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN entry */ +/*--------------------------------------------------------*/ +static +int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ + return 0; /* Not matched */ + } + wc = uc; + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ + + return 1; /* The part of LFN matched */ +} + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ +static +int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; /* The part of LFN is valid */ +} +#endif + + +#if !_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ +static +void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set checksum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + st_word(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ + } while (++s < 13); + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LFN != 0 */ + + + +#if _USE_LFN != 0 && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Create a Numbered SFN */ +/*-----------------------------------------------------------------------*/ + +static +void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (BYTE)((seq % 16) + '0'); + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif /* _USE_LFN != 0 && !_FS_READONLY */ + + + +#if _USE_LFN != 0 +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Calculate checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ + +static +BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} + +#endif /* _USE_LFN != 0 */ + + + +#if _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static +WORD xdir_sum ( /* Get checksum of the directoly block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static +WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = ff_wtoupper(chr); /* File name needs to be ignored case */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !_FS_READONLY && _USE_MKFS +static +DWORD xsum32 ( + BYTE dat, /* Data to be sumed */ + DWORD sum /* Previous value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static +void get_xdir_info ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + UINT di, si; + WCHAR w; +#if !_LFN_UNICODE + UINT nc; +#endif + + /* Get file name */ +#if _LFN_UNICODE + if (dirb[XDIR_NumName] <= _MAX_LFN) { + for (si = SZDIRE * 2, di = 0; di < dirb[XDIR_NumName]; si += 2, di++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + fno->fname[di] = w; /* Store it */ + } + } else { + di = 0; /* Buffer overflow and inaccessible object */ + } +#else + for (si = SZDIRE * 2, di = nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) { di = 0; break; } /* Could not be converted and inaccessible object */ + if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ + fno->fname[di++] = (char)(w >> 8); + } + if (di >= _MAX_LFN) { di = 0; break; } /* Buffer overflow and inaccessible object */ + fno->fname[di++] = (char)w; + } +#endif + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object? */ + fno->fname[di] = 0; /* Terminate file name */ + + fno->altname[0] = 0; /* No SFN */ + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static +FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ +) +{ + FRESULT res; + UINT i, nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load 85 entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; + mem_cpy(dirb, dp->dir, SZDIRE); + nent = dirb[XDIR_NumSec] + 1; + + /* Load C0 entry */ + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR; + mem_cpy(dirb + SZDIRE, dp->dir, SZDIRE); + + /* Load C1 entries */ + if (nent < 3 || nent > 19) return FR_NO_FILE; + i = SZDIRE * 2; nent *= SZDIRE; + do { + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR; + mem_cpy(dirb + i, dp->dir, SZDIRE); + i += SZDIRE; + } while (i < nent); + + /* Sanity check */ + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + + return FR_OK; +} + + +#if !_FS_READONLY || _FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ +static +FRESULT load_obj_dir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const _FDID* obj /* Object with containing directory information */ +) +{ + FRESULT res; + + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto the block location */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; +} +#endif + + +#if !_FS_READONLY +/*-----------------------------------------------*/ +/* exFAT: Store the directory block to the media */ +/*-----------------------------------------------*/ +static +FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + + /* Store the set of directory to the volume */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ + +static +void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the nul terminated file name */ +) +{ + UINT i; + BYTE nb, nc; + WCHAR chr; + + + mem_set(dirb, 0, 2 * SZDIRE); /* Initialize 85+C0 entry */ + dirb[XDIR_Type] = 0x85; + dirb[XDIR_Type + SZDIRE] = 0xC0; + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ + + i = SZDIRE * 2; /* C1 offset */ + nc = 0; nb = 1; chr = 1; + do { + dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ + do { /* Fill name field */ + if (chr && (chr = lfn[nc]) != 0) nc++; /* Get a character if exist */ + st_word(dirb + i, chr); i += 2; /* Store it */ + } while (i % SZDIRE); + nb++; + } while (lfn[nc]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nc; /* Set name length */ + dirb[XDIR_NumSec] = nb; /* Set number of C0+C1s */ +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_EXFAT */ + + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE a, c; +#if _USE_LFN != 0 + BYTE ord = 0xFF, sum = 0xFF; +#endif + + while (dp->sect) { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; /* Test for the entry type */ + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of the directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + if (_USE_LABEL && vol) { + if (c == 0x83) break; /* Volume label entry? */ + } else { + if (c == 0x85) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ + } + break; + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if _USE_LFN != 0 /* LFN configuration */ + if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ + break; + } +#endif + } + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ + return res; +} + +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp /* Pointer to the directory object with the file name */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + BYTE c; +#if _USE_LFN != 0 + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + + while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip the comparison if hash value mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* On the FAT12/16/32 volume */ +#if _USE_LFN != 0 + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN != 0 /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + UINT n, nlen, nent; + BYTE sn[12], sum; + + + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + DIR dj; + + nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */ + + if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */ + dp->obj.stat &= 3; + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */ + res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */ + if (res != FR_OK) return res; + res = load_obj_dir(&dj, &dp->obj); + if (res != FR_OK) return res; /* Load the object status */ + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* On the FAT12/16/32 volume */ + mem_cpy(sn, dp->fn, 12); + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + dp->fn[NSFLAG] = sn[NSFLAG]; + } + + /* Create an SFN with/without LFNs. */ + nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - nent * SZDIRE); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } + +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ + +#endif + + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if _USE_LFN != 0 + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + fs->wflag = 1; + } + } + + return res; +} + +#endif /* !_FS_READONLY */ + + + +#if !_FS_READONLY && _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + DWORD last = dp->dptr; + + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ + if (res == FR_OK) { + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + /* Mark an entry 'deleted' */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + dp->dir[XDIR_Type] &= 0x7F; + } else { /* On the FAT12/16/32 volume */ + dp->dir[DIR_Name] = DDEM; + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } +#else /* Non LFN configuration */ + + res = move_window(fs, dp->sect); + if (res == FR_OK) { + dp->dir[DIR_Name] = DDEM; + fs->wflag = 1; + } +#endif + + return res; +} + +#endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */ + + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ + +static +void get_fileinfo ( /* No return code */ + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT i, j; + TCHAR c; + DWORD tm; +#if _USE_LFN != 0 + WCHAR w, lfv; + FATFS *fs = dp->obj.fs; +#endif + + + fno->fname[0] = 0; /* Invaidate file info */ + if (!dp->sect) return; /* Exit if read pointer has reached end of directory */ + +#if _USE_LFN != 0 /* LFN configuration */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xdir_info(fs->dirbuf, fno); + return; + } else +#endif + { /* On the FAT12/16/32 volume */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + i = j = 0; + while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */ + if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ + fno->fname[i++] = (char)(w >> 8); + } +#endif + if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ + fno->fname[i++] = (TCHAR)w; + } + fno->fname[i] = 0; /* Terminate the LFN */ + } + } + + i = j = 0; + lfv = fno->fname[i]; /* LFN is exist if non-zero */ + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) { /* Insert a . if extension is exist */ + if (!lfv) fno->fname[j] = '.'; + fno->altname[j++] = '.'; + } +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) { + c = c << 8 | dp->dir[i++]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif + fno->altname[j] = c; + if (!lfv) { + if (IsUpper(c) && (dp->dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) { + c += 0x20; /* To lower */ + } + fno->fname[j] = c; + } + j++; + } + if (!lfv) { + fno->fname[j] = 0; + if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */ + } + fno->altname[j] = 0; /* Terminate the SFN */ + +#else /* Non-LFN configuration */ + i = j = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */ + fno->fname[j++] = c; + } + fno->fname[j] = 0; +#endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ + fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + + +#if _USE_FIND && _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ + +static +WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ + const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ +) +{ +#if !_LFN_UNICODE + WCHAR chr; + + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ +#ifdef _EXCVT + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#else + if (IsDBCS1(chr) && IsDBCS2(**ptr)) { /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; + } +#endif + return chr; +#else + return ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif +} + + +static +int pattern_matching ( /* 0:not matched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + WCHAR pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (!*pat && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard chars */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} + +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ + + + +/*-----------------------------------------------------------------------*/ +/* Pick a top segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if _USE_LFN != 0 /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ') break; /* Break if end of the path name */ + if (w == '/' || w == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + w = (w << 8) + b; /* Create a DBC */ + if (!IsDBCS2(b)) return FR_INVALID_NAME; /* Reject invalid sequence */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + lfn[di++] = w; /* Store the Unicode character */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ +#if _FS_RPATH != 0 + if ((di == 1 && lfn[di - 1] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ + lfn[di] = 0; + for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ + dp->fn[i] = (i < di) ? '.' : ' '; + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Snip off trailing spaces and dots if exist */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + lfn[di] = 0; /* LFN is created */ + if (di == 0) return FR_INVALID_NAME; /* Reject nul name */ + + /* Create SFN in directory form */ + mem_set(dp->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + i = b = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN character */ + if (!w) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII character */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Is this DBC? (always false at SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dp->fn[i++] = (BYTE)(w >> 8); + } else { /* SBC */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dp->fn[i++] = (BYTE)w; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */ + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* _USE_LFN != 0 : Non-LFN configuration */ + BYTE c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + p = *path; sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = 0; ni = 8; +#if _FS_RPATH != 0 + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or over size? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ + i = 8; ni = 11; /* Goto extension */ + continue; + } + if (c >= 0x80) { /* Extended character? */ +#ifdef _EXCVT + c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else +#if !_DF1S + return FR_INVALID_NAME; /* Reject extended characters (ASCII only cfg) */ +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at SBCS cfg.) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ + sfn[i++] = c; + } + } + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ + + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + + return FR_OK; +#endif /* _USE_LFN != 0 */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE ns; + _FDID *obj = &dp->obj; + FATFS *fs = obj->fs; + + +#if _FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + obj->sclust = fs->cdir; /* Start from the current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + obj->sclust = 0; /* Start from the root directory */ + } +#if _FS_EXFAT && _FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && obj->sclust) { /* Retrieve the sub-directory status if needed */ + DIR dj; + + obj->c_scl = fs->cdc_scl; + obj->c_size = fs->cdc_size; + obj->c_ofs = fs->cdc_ofs; + res = load_obj_dir(&dj, obj); + if (res != FR_OK) return res; + obj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; + res = dir_sdi(dp, 0); + + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the segment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + /* Get into the sub-directory */ + if (!(obj->attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Save containing directory information for next dir */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + } else +#endif + { + obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static +int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + UINT i; + int vol = -1; +#if _STR_VOLUME_ID /* Find string drive id */ + static const char* const str[] = {_VOLUME_STRS}; + const char *sp; + char c; + TCHAR tc; +#endif + + + if (*path) { /* If the pointer is not a null */ + for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */ + if (*tt == ':') { /* If a ':' is exist in the path name */ + tp = *path; + i = *tp++ - '0'; + if (i < 10 && tp == tt) { /* Is there a numeric drive id? */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = ++tt; + } + } +#if _STR_VOLUME_ID + else { /* No numeric drive number, find string drive id */ + i = 0; tt++; + do { + sp = str[i]; tp = *path; + do { /* Compare a string drive id with path name */ + c = *sp++; tc = *tp++; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = tt; + } + } +#endif + return vol; + } +#if _FS_RPATH != 0 && _VOLUMES >= 2 + vol = CurrVol; /* Current drive */ +#else + vol = 0; /* Drive 0 */ +#endif + } + return vol; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT boot sector */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ + FATFS* fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT-VBR or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ + + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ + + if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { + if ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ + if (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0; /* Check "FAT3" string */ + } +#if _FS_EXFAT + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; +#endif + return 2; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Find logical drive and check if the volume is mounted */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + BYTE mode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the file system object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock the volume */ + *rfs = fs; /* Return pointer to the file system object */ + + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ + if (fs->fs_type) { /* If the volume has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + } + return FR_OK; /* The file system object is valid */ + } + } + + /* The file system object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + } + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; + } +#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; +#endif + /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ + } while (!LD2PT(vol) && fmt >= 2 && ++i < 4); + } + if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found. Following code initializes the file system object */ + +#if _FS_EXFAT + if (fmt == 1) { + QWORD maxlba; + + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; + + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */ + + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ + + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ + + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Check if bitmap location is in assumption (at the first cluster) */ + if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; + for (i = 0; i < SS(fs); i += SZDIRE) { + if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ + } + if (i == SS(fs)) return FR_NO_FILESYSTEM; +#if !_FS_READONLY + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* _FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM;/* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ + +#if !_FS_READONLY + /* Get FSINFO if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + fs->fsi_flag = 0x80; +#if (_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (_FS_NOFSINFO & 1) == 0 + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); +#endif +#if (_FS_NOFSINFO & 2) == 0 + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif /* (_FS_NOFSINFO & 3) != 3 */ +#endif /* !_FS_READONLY */ + } + + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ +#if _USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if _FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block working buuffer */ +#endif +#endif +#if _FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ +#endif +#if _FS_LOCK != 0 /* Clear file lock semaphores */ + clear_lock(fs); +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + _FDID* obj, /* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */ + FATFS** fs /* Pointer to pointer to the owner file system object to return */ +) +{ + FRESULT res; + + + if (!obj || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) { + *fs = 0; /* The object is invalid */ + res = FR_INVALID_OBJECT; + } else { + *fs = obj->fs; /* Owner file sytem object */ + ENTER_FF(obj->fs); /* Lock file system */ + res = FR_OK; + } + return res; +} + + + + +/*--------------------------------------------------------------------------- + + Public Functions (FatFs API) + +----------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + /* Get logical drive number */ + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if _FS_LOCK != 0 + clear_lock(cfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&path, &fs, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; +#if !_FS_READONLY + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; +#endif + DEF_NAMBUF + + + if (!fp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; + res = find_volume(&path, &fs, mode); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } +#if _FS_LOCK != 0 + else { + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + } +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_LOCK != 0 + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + } + else { /* Any object is already existing */ + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = GET_FATTIME(); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_CrtTime, dw); /* Set created time */ + fs->dirbuf[XDIR_CrtTime10] = 0; + st_dword(fs->dirbuf + XDIR_ModTime, dw); /* Set modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + fs->dirbuf[XDIR_Attr] = AM_ARC; /* Reset attribute */ + st_dword(fs->dirbuf + XDIR_FstClus, 0); /* Reset file allocation info */ + st_qword(fs->dirbuf + XDIR_FileSize, 0); + st_qword(fs->dirbuf + XDIR_ValidFileSize, 0); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Clean directory info */ + st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */ + st_dword(dj.dir + DIR_ModTime, dw); /* Set modified time */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + cl = ld_clust(fs, dj.dir); /* Get cluster chain */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + + if (cl) { /* Remove the cluster chain if exist */ + dw = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, dw); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Following succeeded */ + if (dj.obj.attr & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */ + res = FR_DENIED; + } + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA_MODIFIED; + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if _FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->obj.lockid) res = FR_INT_ERR; +#endif + } +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } else { + if (dj.obj.attr & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } + } + } +#endif + + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get allocation info */ + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + fp->obj.c_scl = dj.obj.sclust; + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } +#if _USE_FASTSEEK + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !_FS_READONLY +#if !_FS_TINY + mem_set(fp->buf, 0, _MAX_SS); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clust2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !_FS_TINY + if (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } +#endif + } + + FREE_NAMBUF(); + } + + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE*)buff; + + + *br = 0; /* Clear read byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data read */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ + } + } + if (clst < 2) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } +#else + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } +#endif +#endif + rcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY + if (fp->sect != sect) { /* Load data sector if not in cache */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ + } +#endif + fp->sect = sect; + } + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ +#if _FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#else + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#endif + } + + LEAVE_FF(fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void* buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + UINT wcnt, cc, csect; + const BYTE *wbuff = (const BYTE*)buff; + + + *bw = 0; /* Clear write byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + /* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */ + if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } + + for ( ; btw; /* Repeat until all data written */ + wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ + } +#if _FS_TINY + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ +#else + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; + } +#else + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif +#endif + wcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; + } +#else + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } +#endif + fp->sect = sect; + } + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ +#if _FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; +#else + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; +#endif + } + + fp->flag |= FA_MODIFIED; /* Set file change flag */ + + LEAVE_FF(fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD tm; + BYTE *dir; + DEF_NAMBUF + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ +#if !_FS_TINY + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + /* Update the directory entry */ + tm = GET_FATTIME(); /* Modified time */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_fat_chain(&fp->obj); /* Create FAT chain if needed */ + if (res == FR_OK) { + DIR dj; + + INIT_NAMBUF(fs); + res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL* fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + +#if !_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(&fp->obj, &fs); /* Lock volume */ + if (res == FR_OK) { +#if _FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ + if (res == FR_OK) +#endif + { + fp->obj.fs = 0; /* Invalidate file object */ + } +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +#if _FS_RPATH >= 1 +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +#if _VOLUMES >= 2 +FRESULT f_chdrive ( + const TCHAR* path /* Drive number */ +) +{ + int vol; + + + /* Get logical drive number */ + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + + CurrVol = (BYTE)vol; /* Set it as current volume */ + + return FR_OK; +} +#endif + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { + fs->cdir = dj.obj.sclust; /* It is the start directory itself */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif + } else { + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { + res = FR_NO_PATH; /* Reached but a file */ + } + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMBUF + + + *buff = 0; + /* Get logical drive */ + res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + i = len; /* Bottom of buffer (directory stack base) */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ + if (res != FR_OK) break; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read(&dj, 0); + if (res != FR_OK) break; + if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; + buff[--i] = '/'; + } + } + tp = buff; + if (res == FR_OK) { +#if _VOLUMES >= 2 + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; +#endif + if (i == len) { /* Root-directory */ + *tp++ = '/'; + } else { /* Sub-directroy */ + do /* Add stacked path str */ + *tp++ = buff[i++]; + while (i < len); + } + } + *tp = 0; + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File pointer from top of file */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, bcs, nsect; + FSIZE_t ifptr; +#if _USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->obj.sclust; /* Origin of the chain */ + if (cl) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) { + *tbl = 0; /* Terminate table */ + } else { + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + } + } else { /* Fast seek */ + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clust2sect(fs, fp->clust); + if (!dsc) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ +#endif + fp->sect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */ +#endif + if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->obj.sclust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; break; + } + } else +#endif + { + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); + fp->clust = clst; + } + fp->fptr += ofs; + if (ofs % SS(fs)) { + nsect = clust2sect(fs, clst); /* Current sector */ + if (!nsect) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); + } + } + } + if (!_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ +#endif + fp->sect = nsect; + } + } + + LEAVE_FF(fs, res); +} + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS *fs; + _FDID *obj; + DEF_NAMBUF + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + obj = &dp->obj; + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + obj->fs = fs; + INIT_NAMBUF(fs); + res = follow_path(dp, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (obj->attr & AM_DIR) { /* This object is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Save containing directory inforamation */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object location and status */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + obj->sclust = ld_clust(fs, dp->dir); /* Get object location */ + } + } else { /* This object is a file */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + obj->id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if _FS_LOCK != 0 + if (res == FR_OK) { + if (obj->sclust) { + obj->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES; + } else { + obj->lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) obj->fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + + + res = validate(&dp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { +#if _FS_LOCK != 0 + if (dp->obj.lockid) { /* Decrement sub-directory open counter */ + res = dec_lock(dp->obj.lockid); + } + if (res == FR_OK) +#endif + { + dp->obj.fs = 0; /* Invalidate directory object */ + } +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_NAMBUF(fs); + res = dir_read(dp, 0); /* Read an item */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ + } + FREE_NAMBUF(); + } + } + LEAVE_FF(fs, res); +} + + + +#if _USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find Next File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ +#if _USE_LFN != 0 && _USE_FIND == 2 + if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ +#endif + } + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Find First File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) { + res = f_findnext(dp, fno); /* Find the first item */ + } + return res; +} + +#endif /* _USE_FIND */ + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &dj.obj.fs, 0); + if (res == FR_OK) { + INIT_NAMBUF(dj.obj.fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ + res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(dj.obj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Path name of the logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding file system object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD nfree, clst, sect, stat; + UINT i; + BYTE *p; + _FDID obj; + + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full cluster scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; + } else { + /* Get number of free clusters */ + nfree = 0; + if (fs->fs_type == FS_FAT12) { /* FAT12: Sector unalighed FAT entries */ + clst = 2; obj.fs = fs; + do { + stat = get_fat(&obj, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) nfree++; + } while (++clst < fs->n_fatent); + } else { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan bitmap table */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; + sect = fs->database; + i = 0; + do { + if (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break; + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Sector alighed FAT entries */ + clst = fs->n_fatent; sect = fs->fatbase; + i = 0; p = 0; + do { + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(p) == 0) nfree++; + p += 2; i -= 2; + } else { + if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++; + p += 4; i -= 4; + } + } while (--clst); + } + } + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FSInfo is to be updated */ + } + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD ncl; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->obj.objsize > fp->fptr) { + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); + } + } + fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA_MODIFIED; +#if !_FS_TINY + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= (BYTE)~FA_DIRTY; + } + } +#endif + if (res != FR_OK) ABORT(fs, res); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + DWORD dclst = 0; + FATFS *fs; +#if _FS_EXFAT + _FDID obj; +#endif + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; /* Cannot remove dot entry */ + } +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + if (dj.fn[NSFLAG] & NS_NONAME) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dj.obj.attr & AM_RDO) { + res = FR_DENIED; /* Cannot remove R/O object */ + } + } + if (res == FR_OK) { +#if _FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus); + obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory ? */ +#if _FS_RPATH != 0 + if (dclst == fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); + if (res == FR_OK) { + res = dir_read(&sdj, 0); /* Read an item */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */ +#if _FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE *dir; + UINT n; + DWORD dsc, dcl, pcl, tm; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; + } + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */ + dj.obj.objsize = (DWORD)fs->csize * SS(fs); + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) res = sync_window(fs); /* Flush FAT */ + tm = GET_FATTIME(); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(fs, dcl); + dir = fs->win; + mem_set(dir, 0, SS(fs)); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + st_dword(dir + DIR_ModTime, tm); + st_clust(fs, dir, dcl); + mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ + dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0; + st_clust(fs, dir + SZDIRE, pcl); + } + for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + fs->winsect = dsc++; + fs->wflag = 1; + res = sync_window(fs); + if (res != FR_OK) break; + mem_set(dir, 0, SS(fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag (contiguous) */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + dir = dj.dir; + st_dword(dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dir, dcl); /* Table start cluster */ + dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) res = sync_fs(fs); + } else { + remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + FATFS *fs; + BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir; + DWORD dw; + DEF_NAMBUF + + + get_ldnumber(&path_new); /* Ignore drive number of new name */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ + if (res == FR_OK) { + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); +/* Start of critical section where any interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT12/FAT16/FAT32 */ + mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(fs, ld_clust(fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { +/* Start of critical section where any interruption can cause a cross-link */ + res = move_window(fs, dw); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } + } +/* End of critical section */ + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +#if _USE_CHMOD && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else +#endif + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; + } + if (res == FR_OK) res = sync_fs(fs); + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; + } + if (res == FR_OK) res = sync_fs(fs); + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_CHMOD && !_FS_READONLY */ + + + +#if _USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Path name of the logical drive number */ + TCHAR* label, /* Pointer to a buffer to return the volume label */ + DWORD* vsn /* Pointer to a variable to return the volume serial number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT si, di; +#if _LFN_UNICODE || _FS_EXFAT + WCHAR w; +#endif + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Find a volume label entry */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + w = ld_word(dj.dir + XDIR_Label + si * 2); +#if _LFN_UNICODE + label[di++] = w; +#else + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) w = '?'; /* Replace wrong character */ + if (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8); + label[di++] = (char)w; +#endif + } + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */ + do { +#if _LFN_UNICODE + w = (si < 11) ? dj.dir[si++] : ' '; + if (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) { + w = w << 8 | dj.dir[si++]; + } + label[di++] = ff_convert(w, 1); /* OEM -> Unicode */ +#else + label[di++] = dj.dir[si++]; +#endif + } while (di < 11); + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } + } + } + if (res == FR_NO_FILE) { /* No label entry and return nul string */ + label[0] = 0; + res = FR_OK; + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(fs, fs->volbase); + if (res == FR_OK) { + switch (fs->fs_type) { + case FS_EXFAT: di = BPB_VolIDEx; break; + case FS_FAT32: di = BS_VolID32; break; + default: di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); + } + } + + LEAVE_FF(fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Pointer to the volume label to set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE dirvn[22]; + UINT i, j, slen; + WCHAR w; + static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; + + + /* Get logical drive */ + res = find_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + dj.obj.fs = fs; + + /* Get length of given volume label */ + for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ + w = label[i++]; +#if !_LFN_UNICODE + if (IsDBCS1(w)) { + w = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } + w = ff_convert(w, 1); +#endif + if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + j, w); j += 2; + } + slen = j; + } else +#endif + { /* On the FAT12/16/32 volume */ + for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ + if (slen) { /* Is there a volume label to be set? */ + dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ + do { +#if _LFN_UNICODE + w = ff_convert(ff_wtoupper(label[i++]), 0); +#else + w = (BYTE)label[i++]; + if (IsDBCS1(w)) { + w = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } +#if _USE_LFN != 0 + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); +#else + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ +#ifdef _EXCVT + if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else + if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ +#endif +#endif +#endif + if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8); + dirvn[j++] = (BYTE)w; + } while (i < slen); + while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */ + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } + } + + /* Set volume label */ + dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get volume label entry */ + if (res == FR_OK) { + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + if (slen) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } + } + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry is found or error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (slen) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); + } + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LABEL */ + + + +#if _USE_EXPAND && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { + if (opt) { + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { + if (opt) { + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { + lclst = scl - 1; + } + } + } + + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_EXPAND && !_FS_READONLY */ + + + +#if _USE_FORWARD +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; + + + *bf = 0; /* Clear transfer byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + remain = fp->obj.objsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; +#if _FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (!rcnt) ABORT(fs, FR_INT_ERR); + } + + LEAVE_FF(fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create FAT file system on the logical drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit [byte] */ + void* work, /* Pointer to working buffer */ + UINT len /* Size of working buffer */ +) +{ + const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; + DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ + UINT i; + int vol; + DSTATUS stat; +#if _USE_TRIM || _FS_EXFAT + DWORD tbl[3]; +#endif + + + /* Check mounted drive and clear work area */ + vol = get_ldnumber(&path); /* Get target logical drive */ + if (vol < 0) return FR_INVALID_DRIVE; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear mounted volume */ + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ + + /* Check physical drive status */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ +#if _MAX_SS != _MIN_SS /* Get sector size of the medium */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = _MAX_SS; +#endif + if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in unit of sector */ + + /* Get working buffer */ + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + if (!szb_buf) return FR_MKFS_ABORTED; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (_MULTI_PARTITION && part != 0) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ + } else { + /* Create a single-partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) return FR_MKFS_ABORTED; + sz_vol -= b_vol; /* Volume size */ + } + if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */ + + /* Pre-determine the FAT type */ + do { + if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + fmt = FS_EXFAT; break; + } + } + if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ + fmt = FS_FAT16; + } while (0); + +#if _FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nb, cl; + WCHAR ch, si; + UINT j, st; + BYTE b; + + if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (!au) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ + if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ + if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ + + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = si = i = j = szb_case = 0; + do { + switch (st) { + case 0: + ch = ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* continue */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + default: + ch = (WCHAR)j; si += j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ + tbl[2] = 1; /* Number of root dir clusters */ + + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (!nb && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb && i < szb_buf); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, szb_buf); + buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ + buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ + st_dword(buf + SZDIRE * 1 + 20, 2); + st_dword(buf + SZDIRE * 1 + 24, szb_bit); + buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ + st_dword(buf + SZDIRE * 2 + 4, sum); + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); + st_dword(buf + SZDIRE * 2 + 24, szb_case); + sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* File system version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + + } else +#endif /* _FS_EXFAT */ + { /* Create an FAT12/16/32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; + } else { /* FAT12/16 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (!au && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, (UINT)szb_buf); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + } + + /* Initialize root directory (fill with zero) */ + nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + } + + /* Determine system ID in the partition table */ + if (_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ + } else { + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (>=64KS) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */ + } + } + } + + if (_MULTI_PARTITION && part != 0) { + /* Update system ID in the partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system type */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ + } else { + if (!(opt & FM_SFD)) { + /* Create partition table in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS is incorrect) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ + } + } + + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; + + return FR_OK; +} + + + +#if _MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create partition table on the physical drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD* szt, /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + /* Determine the CHS without any care of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = n - 1; + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, _MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; + if (!p_cyl) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; + if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x06; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + st_dword(p + 8, s_part); /* Start sector in LBA */ + st_dword(p + 12, sz_part); /* Partition size */ + + /* Next partition */ + b_cyl += p_cyl; + } + st_word(p, 0xAA55); + + /* Write it to the MBR */ + return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; +} + +#endif /* _MULTI_PARTITION */ +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fp /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read characters until buffer gets filled */ +#if _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequence */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c & 0x1F) << 6 | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequence */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequence */ + c = '?'; + } + } + } +#elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[1] + (s[0] << 8); +#elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[0] + (s[1] << 8); +#else /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (IsDBCS1(c)) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c << 8) + s[0]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#else /* Read a character without conversion */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; +#endif + if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ + +typedef struct { + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of chars written */ + BYTE buf[64]; /* Write buffer */ +} putbuff; + + +static +void putc_bfd ( /* Buffered write with code conversion */ + putbuff* pb, + TCHAR c +) +{ + UINT bw; + int i; + + + if (_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + } + + i = pb->idx; /* Write index of pb->buf[] */ + if (i < 0) return; + +#if _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ + if (c < 0x80) { /* 7-bit */ + pb->buf[i++] = (BYTE)c; + } else { + if (c < 0x800) { /* 11-bit */ + pb->buf[i++] = (BYTE)(0xC0 | c >> 6); + } else { /* 16-bit */ + pb->buf[i++] = (BYTE)(0xE0 | c >> 12); + pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); + } +#elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + pb->buf[i++] = (BYTE)c; + pb->buf[i++] = (BYTE)(c >> 8); +#else /* Write a character in ANSI/OEM */ + c = ff_convert(c, 0); /* Unicode -> OEM */ + if (!c) c = '?'; + if (c >= 0x100) + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#endif +#else /* Write a character without conversion */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &bw); + i = (bw == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr++; +} + + +static +int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return EOF; +} + + +static +void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + pb->fp = fp; + pb->nchr = pb->idx = 0; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; + + + putc_init(&pb, fp); + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *fmt++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, ' '); + } + while (*p) putc_bfd(&pb, *p++); + while (j++ < w) putc_bfd(&pb, ' '); + continue; + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof str[0]); + if (f & 8) str[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + while (!(f & 2) && j++ < w) putc_bfd(&pb, d); + do putc_bfd(&pb, str[--i]); while (i); + while (j++ < w) putc_bfd(&pb, d); + } + + va_end(arp); + + return putc_flush(&pb); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/components/fatfs/src/ff.h b/components/fatfs/src/ff.h new file mode 100644 index 00000000000..981a88634fa --- /dev/null +++ b/components/fatfs/src/ff.h @@ -0,0 +1,366 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12b / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2016, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/----------------------------------------------------------------------------*/ + + +#ifndef _FATFS +#define _FATFS 68020 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#error Wrong configuration file (ffconf.h). +#endif + + + +/* Definitions of volume management */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ +#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */ +#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */ + +#else /* Single partition configuration */ +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ +#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ + +#endif + + + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode (UTF-16) string */ +#if _USE_LFN == 0 +#error _LFN_UNICODE must be 0 at non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif +#endif + + + +/* Type of file size variables */ + +#if _FS_EXFAT +#if _USE_LFN == 0 +#error LFN must be enabled when enable exFAT +#endif +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* File system type (0:N/A) */ + BYTE drv; /* Physical drive number */ + BYTE n_fats; /* Number of FATs (1 or 2) */ + BYTE wflag; /* win[] flag (b0:dirty) */ + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ +#if _MAX_SS != _MIN_SS + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if _USE_LFN != 0 + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if _FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer */ +#endif +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !_FS_READONLY + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ +#endif +#if _FS_RPATH != 0 + DWORD cdir; /* Current directory start cluster (0:root) */ +#if _FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ +#endif +#endif + DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ +} FATFS; + + + +/* Object ID and allocation information (_FDID) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */ + DWORD sclust; /* Object start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if _FS_EXFAT + DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */ +#endif +#if _FS_LOCK != 0 + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} _FDID; + + + +/* File object structure (FIL) */ + +typedef struct { + _FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ + BYTE flag; /* File status flags */ + BYTE err; /* Abort flag (error code) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector number containing the directory entry */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ +#endif +#if _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File private data read/write window */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + _FDID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if _USE_LFN != 0 + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ +#endif +#if _USE_FIND + const TCHAR* pat; /* Pointer to the name matching pattern */ +#endif +} DIR; + + + +/* File information structure (FILINFO) */ + +typedef struct { + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if _USE_LFN != 0 + TCHAR altname[13]; /* Altenative file name */ + TCHAR fname[_MAX_LFN + 1]; /* Primary file name */ +#else + TCHAR fname[13]; /* File name */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_close (FIL* fp); /* Close an open file object */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ +FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (DIR* dp); /* Close an open directory */ +FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ +int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ + +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) +#define f_error(fp) ((fp)->err) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->obj.objsize) +#define f_rewind(fp) f_lseek((fp), 0) +#define f_rewinddir(dp) f_readdir((dp), 0) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY && !_FS_NORTC +DWORD get_fattime (void); +#endif + +/* Unicode support functions */ +#if _USE_LFN != 0 /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access mode and open method flags (3rd argument of f_open) */ +#define FA_READ 0x01 +#define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA_OPEN_APPEND 0x30 + +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) + +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 + +/* Filesystem type (FATFS.fs_type) */ +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 +#define FS_EXFAT 4 + +/* File attribute bits for directory entry (FILINFO.fattrib) */ +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _FATFS */ diff --git a/components/fatfs/src/ffconf.h b/components/fatfs/src/ffconf.h new file mode 100644 index 00000000000..c43321f626c --- /dev/null +++ b/components/fatfs/src/ffconf.h @@ -0,0 +1,267 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file +/---------------------------------------------------------------------------*/ + +#define _FFCONF 68020 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define _FS_READONLY 0 +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + + +#define _FS_MINIMIZE 0 +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: All basic functions are enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + + +#define _USE_STRFUNC 0 +/* This option switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + + +#define _USE_FIND 0 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ + + +#define _USE_MKFS 0 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + + +#define _USE_FASTSEEK 0 +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define _USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define _USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ + + +#define _USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + + +#define _USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define _CODE_PAGE 932 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 1 - ASCII (No extended character. Non-LFN cfg. only) +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +*/ + + +#define _USE_LFN 0 +#define _MAX_LFN 255 +/* The _USE_LFN switches the support of long file name (LFN). +/ +/ 0: Disable support of LFN. _MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added +/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and +/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. +/ It should be set 255 to support full featured LFN operations. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree(), must be added to the project. */ + + +#define _LFN_UNICODE 0 +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) +/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. +/ This option also affects behavior of string I/O functions. */ + + +#define _STRF_ENCODE 3 +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to +/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). +/ +/ 0: ANSI/OEM +/ 1: UTF-16LE +/ 2: UTF-16BE +/ 3: UTF-8 +/ +/ This option has no effect when _LFN_UNICODE == 0. */ + + +#define _FS_RPATH 0 +/* This option configures support of relative path. +/ +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() function is available in addition to 1. +*/ + + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/---------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _STR_VOLUME_ID 0 +#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +/* _STR_VOLUME_ID switches string support of volume ID. +/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive +/ number in the path name. _VOLUME_STRS defines the drive ID strings for each +/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for +/ the drive ID strings are: A-Z and 0-9. */ + + +#define _MULTI_PARTITION 0 +/* This option switches support of multi-partition on a physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When multi-partition is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ funciton will be available. */ + + +#define _MIN_SS 512 +#define _MAX_SS 512 +/* These options configure the range of sector size to be supported. (512, 1024, +/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured +/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the +/ disk_ioctl() function. */ + + +#define _USE_TRIM 0 +/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + + +#define _FS_NOFSINFO 0 +/* If you need to know correct free space on the FAT32 volume, set bit 0 of this +/ option, and f_getfree() function at first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define _FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the file system object (FATFS) is used for the file data transfer. */ + + +#define _FS_EXFAT 0 +/* This option switches support of exFAT file system. (0:Disable or 1:Enable) +/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) +/ Note that enabling exFAT discards C89 compatibility. */ + + +#define _FS_NORTC 0 +#define _NORTC_MON 1 +#define _NORTC_MDAY 1 +#define _NORTC_YEAR 2016 +/* The option _FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. +/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to get current time form real-time clock. _NORTC_MON, +/ _NORTC_MDAY and _NORTC_YEAR have no effect. +/ These options have no effect at read-only configuration (_FS_READONLY = 1). */ + + +#define _FS_LOCK 0 +/* The option _FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when _FS_READONLY +/ is 1. +/ +/ 0: Disable file lock function. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock function. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock control is independent of re-entrancy. */ + + +#define _FS_REENTRANT 0 +#define _FS_TIMEOUT 1000 +#define _SYNC_t HANDLE +/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk() function, are always not re-entrant. Only file/directory access +/ to the same volume is under control of this function. +/ +/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. +/ 1: Enable re-entrancy. Also user provided synchronization handlers, +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() +/ function, must be added to the project. Samples are available in +/ option/syscall.c. +/ +/ The _FS_TIMEOUT defines timeout period in unit of time tick. +/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, +/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.h. */ + +/* #include // O/S definitions */ + + +/*--- End of configuration options ---*/ diff --git a/components/fatfs/src/integer.h b/components/fatfs/src/integer.h new file mode 100644 index 00000000000..4660ed6244f --- /dev/null +++ b/components/fatfs/src/integer.h @@ -0,0 +1,38 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _FF_INTEGER +#define _FF_INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include +typedef unsigned __int64 QWORD; + + +#else /* Embedded platform */ + +/* These types MUST be 16-bit or 32-bit */ +typedef int INT; +typedef unsigned int UINT; + +/* This type MUST be 8-bit */ +typedef unsigned char BYTE; + +/* These types MUST be 16-bit */ +typedef short SHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types MUST be 32-bit */ +typedef long LONG; +typedef unsigned long DWORD; + +/* This type MUST be 64-bit (Remove this for C89 compatibility) */ +typedef unsigned long long QWORD; + +#endif + +#endif diff --git a/components/fatfs/src/option/syscall.c b/components/fatfs/src/option/syscall.c new file mode 100644 index 00000000000..c9d219b7970 --- /dev/null +++ b/components/fatfs/src/option/syscall.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------*/ +/* Sample code of OS dependent controls for FatFs */ +/* (C)ChaN, 2014 */ +/*------------------------------------------------------------------------*/ + + +#include "../ff.h" + + +#if _FS_REENTRANT +/*------------------------------------------------------------------------*/ +/* Create a Synchronization Object +/*------------------------------------------------------------------------*/ +/* This function is called in f_mount() function to create a new +/ synchronization object, such as semaphore and mutex. When a 0 is returned, +/ the f_mount() function fails with FR_INT_ERR. +*/ + +int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ + BYTE vol, /* Corresponding volume (logical drive number) */ + _SYNC_t *sobj /* Pointer to return the created sync object */ +) +{ + int ret; + + + *sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */ + ret = (int)(*sobj != INVALID_HANDLE_VALUE); + +// *sobj = SyncObjects[vol]; /* uITRON (give a static sync object) */ +// ret = 1; /* The initial value of the semaphore must be 1. */ + +// *sobj = OSMutexCreate(0, &err); /* uC/OS-II */ +// ret = (int)(err == OS_NO_ERR); + +// *sobj = xSemaphoreCreateMutex(); /* FreeRTOS */ +// ret = (int)(*sobj != NULL); + + return ret; +} + + + +/*------------------------------------------------------------------------*/ +/* Delete a Synchronization Object */ +/*------------------------------------------------------------------------*/ +/* This function is called in f_mount() function to delete a synchronization +/ object that created with ff_cre_syncobj() function. When a 0 is returned, +/ the f_mount() function fails with FR_INT_ERR. +*/ + +int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to any error */ + _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ +) +{ + int ret; + + + ret = CloseHandle(sobj); /* Win32 */ + +// ret = 1; /* uITRON (nothing to do) */ + +// OSMutexDel(sobj, OS_DEL_ALWAYS, &err); /* uC/OS-II */ +// ret = (int)(err == OS_NO_ERR); + +// vSemaphoreDelete(sobj); /* FreeRTOS */ +// ret = 1; + + return ret; +} + + + +/*------------------------------------------------------------------------*/ +/* Request Grant to Access the Volume */ +/*------------------------------------------------------------------------*/ +/* This function is called on entering file functions to lock the volume. +/ When a 0 is returned, the file function fails with FR_TIMEOUT. +*/ + +int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ + _SYNC_t sobj /* Sync object to wait */ +) +{ + int ret; + + ret = (int)(WaitForSingleObject(sobj, _FS_TIMEOUT) == WAIT_OBJECT_0); /* Win32 */ + +// ret = (int)(wai_sem(sobj) == E_OK); /* uITRON */ + +// OSMutexPend(sobj, _FS_TIMEOUT, &err)); /* uC/OS-II */ +// ret = (int)(err == OS_NO_ERR); + +// ret = (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); /* FreeRTOS */ + + return ret; +} + + + +/*------------------------------------------------------------------------*/ +/* Release Grant to Access the Volume */ +/*------------------------------------------------------------------------*/ +/* This function is called on leaving file functions to unlock the volume. +*/ + +void ff_rel_grant ( + _SYNC_t sobj /* Sync object to be signaled */ +) +{ + ReleaseMutex(sobj); /* Win32 */ + +// sig_sem(sobj); /* uITRON */ + +// OSMutexPost(sobj); /* uC/OS-II */ + +// xSemaphoreGive(sobj); /* FreeRTOS */ +} + +#endif + + + + +#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ +/*------------------------------------------------------------------------*/ +/* Allocate a memory block */ +/*------------------------------------------------------------------------*/ +/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. +*/ + +void* ff_memalloc ( /* Returns pointer to the allocated memory block */ + UINT msize /* Number of bytes to allocate */ +) +{ + return malloc(msize); /* Allocate a new memory block with POSIX API */ +} + + +/*------------------------------------------------------------------------*/ +/* Free a memory block */ +/*------------------------------------------------------------------------*/ + +void ff_memfree ( + void* mblock /* Pointer to the memory block to free */ +) +{ + free(mblock); /* Discard the memory block with POSIX API */ +} + +#endif diff --git a/components/fatfs/src/option/unicode.c b/components/fatfs/src/option/unicode.c new file mode 100644 index 00000000000..170e2e09e0f --- /dev/null +++ b/components/fatfs/src/option/unicode.c @@ -0,0 +1,17 @@ +#include "../ff.h" + +#if _USE_LFN != 0 + +#if _CODE_PAGE == 932 /* Japanese Shift_JIS */ +#include "cc932.c" +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#include "cc936.c" +#elif _CODE_PAGE == 949 /* Korean */ +#include "cc949.c" +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#include "cc950.c" +#else /* Single Byte Character-Set */ +#include "ccsbcs.c" +#endif + +#endif From 9398d1b1cc2e28c8cb16011438c4a823960dcbb8 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 29 Dec 2016 17:33:38 +0800 Subject: [PATCH 094/167] fatfs: add pluggable diskio layer, sdmmc implementation --- components/fatfs/src/diskio.c | 301 +++++++++----------------- components/fatfs/src/diskio.h | 22 +- components/fatfs/src/ffconf.h | 8 +- components/fatfs/src/option/syscall.c | 2 +- 4 files changed, 130 insertions(+), 203 deletions(-) diff --git a/components/fatfs/src/diskio.c b/components/fatfs/src/diskio.c index 25f5e53bc03..b00e9d2ca51 100644 --- a/components/fatfs/src/diskio.c +++ b/components/fatfs/src/diskio.c @@ -7,219 +7,126 @@ /* storage control modules to the FatFs module with a defined API. */ /*-----------------------------------------------------------------------*/ +#include #include "diskio.h" /* FatFs lower layer API */ +#include "ffconf.h" +#include "ff.h" +#include "sdmmc_cmd.h" +#include "esp_log.h" +#include +#include -/* Definitions of physical drive number for each drive */ -#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */ -#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */ -#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */ +static const char* TAG = "ff_diskio"; +static ff_diskio_impl_t s_impls[_VOLUMES] = { { 0 } }; +static sdmmc_card_t* s_cards[_VOLUMES] = { NULL }; +PARTITION VolToPart[] = { + {0, 1}, /* Logical drive 0 ==> Physical drive 0, 1st partition */ + {1, 0} /* Logical drive 1 ==> Physical drive 1, auto detection */ +}; -/*-----------------------------------------------------------------------*/ -/* Get Drive Status */ -/*-----------------------------------------------------------------------*/ - -DSTATUS disk_status ( - BYTE pdrv /* Physical drive nmuber to identify the drive */ -) +void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl) { - DSTATUS stat; - int result; - - switch (pdrv) { - case DEV_RAM : - result = RAM_disk_status(); - - // translate the reslut code here - - return stat; - - case DEV_MMC : - result = MMC_disk_status(); - - // translate the reslut code here - - return stat; - - case DEV_USB : - result = USB_disk_status(); - - // translate the reslut code here - - return stat; - } - return STA_NOINIT; + assert(pdrv < _VOLUMES); + memcpy(&s_impls[pdrv], discio_impl, sizeof(ff_diskio_impl_t)); } - - -/*-----------------------------------------------------------------------*/ -/* Inidialize a Drive */ -/*-----------------------------------------------------------------------*/ - -DSTATUS disk_initialize ( - BYTE pdrv /* Physical drive nmuber to identify the drive */ -) +DSTATUS ff_disk_initialize (BYTE pdrv) { - DSTATUS stat; - int result; - - switch (pdrv) { - case DEV_RAM : - result = RAM_disk_initialize(); - - // translate the reslut code here - - return stat; - - case DEV_MMC : - result = MMC_disk_initialize(); - - // translate the reslut code here - - return stat; - - case DEV_USB : - result = USB_disk_initialize(); - - // translate the reslut code here - - return stat; - } - return STA_NOINIT; + return s_impls[pdrv].init(pdrv); +} +DSTATUS ff_disk_status (BYTE pdrv) +{ + return s_impls[pdrv].status(pdrv); +} +DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) +{ + return s_impls[pdrv].read(pdrv, buff, sector, count); +} +DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) +{ + return s_impls[pdrv].write(pdrv, buff, sector, count); +} +DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff) +{ + return s_impls[pdrv].ioctl(pdrv, cmd, buff); } - - -/*-----------------------------------------------------------------------*/ -/* Read Sector(s) */ -/*-----------------------------------------------------------------------*/ - -DRESULT disk_read ( - BYTE pdrv, /* Physical drive nmuber to identify the drive */ - BYTE *buff, /* Data buffer to store read data */ - DWORD sector, /* Start sector in LBA */ - UINT count /* Number of sectors to read */ -) +DWORD get_fattime(void) { - DRESULT res; - int result; - - switch (pdrv) { - case DEV_RAM : - // translate the arguments here - - result = RAM_disk_read(buff, sector, count); - - // translate the reslut code here - - return res; - - case DEV_MMC : - // translate the arguments here - - result = MMC_disk_read(buff, sector, count); - - // translate the reslut code here - - return res; - - case DEV_USB : - // translate the arguments here - - result = USB_disk_read(buff, sector, count); - - // translate the reslut code here - - return res; - } - - return RES_PARERR; + time_t t = time(NULL); + struct tm *tmr = gmtime(&t); + return ((DWORD)(tmr->tm_year - 80) << 25) + | ((DWORD)(tmr->tm_mon + 1) << 21) + | ((DWORD)tmr->tm_mday << 16) + | (WORD)(tmr->tm_hour << 11) + | (WORD)(tmr->tm_min << 5) + | (WORD)(tmr->tm_sec >> 1); } - - -/*-----------------------------------------------------------------------*/ -/* Write Sector(s) */ -/*-----------------------------------------------------------------------*/ - -DRESULT disk_write ( - BYTE pdrv, /* Physical drive nmuber to identify the drive */ - const BYTE *buff, /* Data to be written */ - DWORD sector, /* Start sector in LBA */ - UINT count /* Number of sectors to write */ -) +DSTATUS ff_sdmmc_initialize (BYTE pdrv) { - DRESULT res; - int result; - - switch (pdrv) { - case DEV_RAM : - // translate the arguments here - - result = RAM_disk_write(buff, sector, count); - - // translate the reslut code here - - return res; - - case DEV_MMC : - // translate the arguments here - - result = MMC_disk_write(buff, sector, count); - - // translate the reslut code here - - return res; - - case DEV_USB : - // translate the arguments here - - result = USB_disk_write(buff, sector, count); - - // translate the reslut code here - - return res; - } - - return RES_PARERR; + return 0; } - - -/*-----------------------------------------------------------------------*/ -/* Miscellaneous Functions */ -/*-----------------------------------------------------------------------*/ - -DRESULT disk_ioctl ( - BYTE pdrv, /* Physical drive nmuber (0..) */ - BYTE cmd, /* Control code */ - void *buff /* Buffer to send/receive control data */ -) +DSTATUS ff_sdmmc_status (BYTE pdrv) { - DRESULT res; - int result; - - switch (pdrv) { - case DEV_RAM : - - // Process of the command for the RAM drive - - return res; - - case DEV_MMC : - - // Process of the command for the MMC/SD card - - return res; - - case DEV_USB : - - // Process of the command the USB drive - - return res; - } - - return RES_PARERR; + return 0; +} + +DRESULT ff_sdmmc_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + esp_err_t err = sdmmc_read_blocks(card, buff, sector, count); + if (err != ESP_OK) { + ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err); + return RES_ERROR; + } + return RES_OK; +} + +DRESULT ff_sdmmc_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + esp_err_t err = sdmmc_write_blocks(card, buff, sector, count); + if (err != ESP_OK) { + ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err); + return RES_ERROR; + } + return RES_OK; +} + +DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + switch(cmd) { + case CTRL_SYNC: + return RES_OK; + case GET_SECTOR_COUNT: + *((uint32_t*) buff) = card->csd.capacity; + return RES_OK; + case GET_SECTOR_SIZE: + *((uint32_t*) buff) = card->csd.sector_size; + return RES_OK; + case GET_BLOCK_SIZE: + return RES_ERROR; + } + return RES_ERROR; +} + +void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card) +{ + static const ff_diskio_impl_t sdmmc_impl = { + .init = &ff_sdmmc_initialize, + .status = &ff_sdmmc_status, + .read = &ff_sdmmc_read, + .write = &ff_sdmmc_write, + .ioctl = &ff_sdmmc_ioctl + }; + s_cards[pdrv] = card; + ff_diskio_register(pdrv, &sdmmc_impl); } diff --git a/components/fatfs/src/diskio.h b/components/fatfs/src/diskio.h index 03e8b7c560b..921ef9836a3 100644 --- a/components/fatfs/src/diskio.h +++ b/components/fatfs/src/diskio.h @@ -10,7 +10,8 @@ extern "C" { #endif #include "integer.h" - +#include "sdmmc_cmd.h" +#include "driver/sdmmc_host.h" /* Status of Disk Functions */ typedef BYTE DSTATUS; @@ -29,12 +30,31 @@ typedef enum { /* Prototypes for disk control functions */ +/* Redefine names of disk IO functions to prevent name collisions */ +#define disk_initialize ff_disk_initialize +#define disk_status ff_disk_status +#define disk_read ff_disk_read +#define disk_write ff_disk_write +#define disk_ioctl ff_disk_ioctl + + DSTATUS disk_initialize (BYTE pdrv); DSTATUS disk_status (BYTE pdrv); DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); +typedef struct { + DSTATUS (*init) (BYTE pdrv); + DSTATUS (*status) (BYTE pdrv); + DRESULT (*read) (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); + DRESULT (*write) (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); + DRESULT (*ioctl) (BYTE pdrv, BYTE cmd, void* buff); +} ff_diskio_impl_t; + +void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); + +void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card); /* Disk Status Bits (DSTATUS) */ diff --git a/components/fatfs/src/ffconf.h b/components/fatfs/src/ffconf.h index c43321f626c..b3a1bf57473 100644 --- a/components/fatfs/src/ffconf.h +++ b/components/fatfs/src/ffconf.h @@ -39,7 +39,7 @@ / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ -#define _USE_MKFS 0 +#define _USE_MKFS 1 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ @@ -69,7 +69,7 @@ / Locale and Namespace Configurations /---------------------------------------------------------------------------*/ -#define _CODE_PAGE 932 +#define _CODE_PAGE 1 /* This option specifies the OEM code page to be used on the target system. / Incorrect setting of the code page can cause a file open failure. / @@ -147,7 +147,7 @@ / Drive/Volume Configurations /---------------------------------------------------------------------------*/ -#define _VOLUMES 1 +#define _VOLUMES 2 /* Number of volumes (logical drives) to be used. */ @@ -160,7 +160,7 @@ / the drive ID strings are: A-Z and 0-9. */ -#define _MULTI_PARTITION 0 +#define _MULTI_PARTITION 1 /* This option switches support of multi-partition on a physical drive. / By default (0), each logical drive number is bound to the same physical drive / number and only an FAT volume found on the physical drive will be mounted. diff --git a/components/fatfs/src/option/syscall.c b/components/fatfs/src/option/syscall.c index c9d219b7970..14140c5a732 100644 --- a/components/fatfs/src/option/syscall.c +++ b/components/fatfs/src/option/syscall.c @@ -9,7 +9,7 @@ #if _FS_REENTRANT /*------------------------------------------------------------------------*/ -/* Create a Synchronization Object +/* Create a Synchronization Object */ /*------------------------------------------------------------------------*/ /* This function is called in f_mount() function to create a new / synchronization object, such as semaphore and mutex. When a 0 is returned, From d4187769820db9fa91d85d7076bfe41f0583c381 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 3 Jan 2017 03:45:11 +0800 Subject: [PATCH 095/167] fatfs: rename DIR to FF_DIR to avoid conflict with dirent.h --- components/fatfs/src/ff.c | 1 + components/fatfs/src/ff.h | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/components/fatfs/src/ff.c b/components/fatfs/src/ff.c index ffc5240970e..0d7d7366bf6 100644 --- a/components/fatfs/src/ff.c +++ b/components/fatfs/src/ff.c @@ -18,6 +18,7 @@ /----------------------------------------------------------------------------*/ +#define FF_DEFINE_DIR #include "ff.h" /* Declarations of FatFs API */ #include "diskio.h" /* Declarations of device I/O functions */ diff --git a/components/fatfs/src/ff.h b/components/fatfs/src/ff.h index 981a88634fa..18bc85b1c7a 100644 --- a/components/fatfs/src/ff.h +++ b/components/fatfs/src/ff.h @@ -32,6 +32,9 @@ extern "C" { #error Wrong configuration file (ffconf.h). #endif +#ifdef FF_DEFINE_DIR +#define FF_DIR DIR +#endif /* Definitions of volume management */ @@ -179,7 +182,7 @@ typedef struct { -/* Directory object structure (DIR) */ +/* Directory object structure (FF_DIR) */ typedef struct { _FDID obj; /* Object identifier */ @@ -194,7 +197,7 @@ typedef struct { #if _USE_FIND const TCHAR* pat; /* Pointer to the name matching pattern */ #endif -} DIR; +} FF_DIR; @@ -252,11 +255,11 @@ FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data t FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ FRESULT f_truncate (FIL* fp); /* Truncate the file */ FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ -FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ -FRESULT f_closedir (DIR* dp); /* Close an open directory */ -FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ -FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ -FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */ +FRESULT f_readdir (FF_DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (FF_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (FF_DIR* dp, FILINFO* fno); /* Find next file */ FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ From 44ce833d76146415537136d4d7ddf5f0f2df2638 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 9 Jan 2017 05:54:04 +0800 Subject: [PATCH 096/167] fatfs: add vfs support --- components/fatfs/component.mk | 2 +- components/fatfs/src/diskio.c | 8 +- components/fatfs/src/diskio.h | 30 +- components/fatfs/src/esp_vfs_fat.h | 107 +++++ components/fatfs/src/ffconf.h | 8 +- components/fatfs/src/option/syscall.c | 55 +-- components/fatfs/src/vfs_fat.c | 538 +++++++++++++++++++++++++ components/fatfs/src/vfs_fat_sdmmc.c | 126 ++++++ components/fatfs/test/component.mk | 1 + components/fatfs/test/test_fatfs.c | 552 ++++++++++++++++++++++++++ components/newlib/test/test_newlib.c | 29 ++ components/sdmmc/test/test_sd.c | 14 + 12 files changed, 1408 insertions(+), 62 deletions(-) create mode 100644 components/fatfs/src/esp_vfs_fat.h create mode 100644 components/fatfs/src/vfs_fat.c create mode 100644 components/fatfs/src/vfs_fat_sdmmc.c create mode 100644 components/fatfs/test/component.mk create mode 100644 components/fatfs/test/test_fatfs.c diff --git a/components/fatfs/component.mk b/components/fatfs/component.mk index f2094e7ab9a..591e080c62b 100644 --- a/components/fatfs/component.mk +++ b/components/fatfs/component.mk @@ -1,2 +1,2 @@ COMPONENT_ADD_INCLUDEDIRS := src -COMPONENT_SRCDIRS := src src/option +COMPONENT_SRCDIRS := src/option src diff --git a/components/fatfs/src/diskio.c b/components/fatfs/src/diskio.c index b00e9d2ca51..004cfc5bea2 100644 --- a/components/fatfs/src/diskio.c +++ b/components/fatfs/src/diskio.c @@ -1,5 +1,6 @@ /*-----------------------------------------------------------------------*/ /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ +/* ESP-IDF port Copyright 2016 Espressif Systems (Shanghai) PTE LTD */ /*-----------------------------------------------------------------------*/ /* If a working storage control module is available, it should be */ /* attached to the FatFs via a glue function rather than modifying it. */ @@ -56,7 +57,8 @@ DWORD get_fattime(void) { time_t t = time(NULL); struct tm *tmr = gmtime(&t); - return ((DWORD)(tmr->tm_year - 80) << 25) + int year = tmr->tm_year < 80 ? 0 : tmr->tm_year - 80; + return ((DWORD)(year) << 25) | ((DWORD)(tmr->tm_mon + 1) << 21) | ((DWORD)tmr->tm_mday << 16) | (WORD)(tmr->tm_hour << 11) @@ -78,7 +80,7 @@ DRESULT ff_sdmmc_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { sdmmc_card_t* card = s_cards[pdrv]; assert(card); - esp_err_t err = sdmmc_read_blocks(card, buff, sector, count); + esp_err_t err = sdmmc_read_sectors(card, buff, sector, count); if (err != ESP_OK) { ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err); return RES_ERROR; @@ -90,7 +92,7 @@ DRESULT ff_sdmmc_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { sdmmc_card_t* card = s_cards[pdrv]; assert(card); - esp_err_t err = sdmmc_write_blocks(card, buff, sector, count); + esp_err_t err = sdmmc_write_sectors(card, buff, sector, count); if (err != ESP_OK) { ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err); return RES_ERROR; diff --git a/components/fatfs/src/diskio.h b/components/fatfs/src/diskio.h index 921ef9836a3..7c224809f93 100644 --- a/components/fatfs/src/diskio.h +++ b/components/fatfs/src/diskio.h @@ -44,16 +44,36 @@ DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); +/** + * Structure of pointers to disk IO driver functions. + * + * See FatFs documentation for details about these functions + */ typedef struct { - DSTATUS (*init) (BYTE pdrv); - DSTATUS (*status) (BYTE pdrv); - DRESULT (*read) (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); - DRESULT (*write) (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); - DRESULT (*ioctl) (BYTE pdrv, BYTE cmd, void* buff); + DSTATUS (*init) (BYTE pdrv); /*!< disk initialization function */ + DSTATUS (*status) (BYTE pdrv); /*!< disk status check function */ + DRESULT (*read) (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); /*!< sector read function */ + DRESULT (*write) (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); /*!< sector write function */ + DRESULT (*ioctl) (BYTE pdrv, BYTE cmd, void* buff); /*!< function to get info about disk and do some misc operations */ } ff_diskio_impl_t; +/** + * Register diskio driver for given drive number. + * + * When FATFS library calls one of disk_xxx functions for driver number pdrv, + * corresponding function in discio_impl for given pdrv will be called. + * + * @param pdrv drive number + * @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions + */ void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); +/** + * Register SD/MMC diskio driver + * + * @param pdrv drive number + * @param card pointer to sdmmc_card_t structure describing a card; card should be initialized before calling f_mount. + */ void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card); /* Disk Status Bits (DSTATUS) */ diff --git a/components/fatfs/src/esp_vfs_fat.h b/components/fatfs/src/esp_vfs_fat.h new file mode 100644 index 00000000000..087d6cf77e9 --- /dev/null +++ b/components/fatfs/src/esp_vfs_fat.h @@ -0,0 +1,107 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include "esp_err.h" +#include "driver/gpio.h" +#include "driver/sdmmc_types.h" +#include "driver/sdmmc_host.h" +#include "ff.h" + +/** + * @brief Register FATFS with VFS component + * + * This function registers given FAT drive in VFS, at the specified base path. + * If only one drive is used, fat_drive argument can be an empty string. + * Refer to FATFS library documentation on how to specify FAT drive. + * This function also allocates FATFS structure which should be used for f_mount + * call. + * + * @note This function doesn't mount the drive into FATFS, it just connects + * POSIX and C standard library IO function with FATFS. You need to mount + * desired drive into FATFS separately. + * + * @param base_path path prefix where FATFS should be registered + * @param fat_drive FATFS drive specification; if only one drive is used, can be an empty string + * @param max_files maximum number of files which can be open at the same time + * @param[out] out_fs pointer to FATFS structure which can be used for FATFS f_mount call is returned via this argument. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_register was already called + * - ESP_ERR_NO_MEM if not enough memory or too many VFSes already registered + */ +esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, + size_t max_files, FATFS** out_fs); + +/** + * @brief Un-register FATFS from VFS + * + * @note FATFS structure returned by esp_vfs_fat_register is destroyed after + * this call. Make sure to call f_mount function to unmount it before + * calling esp_vfs_fat_unregister. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS + */ +esp_err_t esp_vfs_fat_unregister(); + +/** + * @brief Configuration arguments for esp_vfs_fat_sdmmc_mount function + */ +typedef struct { + bool format_if_mount_failed; ///< If FAT partition can not be mounted, and this parameter is true, create partition table and format the filesystem + int max_files; ///< Max number of open files +} esp_vfs_fat_sdmmc_mount_config_t; + +/** + * @brief Convenience function to get FAT filesystem on SD card registered in VFS + * + * This is an all-in-one function which does the following: + * - initializes SD/MMC peripheral with configuration in host_config + * - initializes SD/MMC card with configuration in slot_config + * - mounts FAT partition on SD/MMC card using FATFS library, with configuration in mount_config + * - registers FATFS library with VFS, with prefix given by base_prefix variable + * + * This function is intended to make example code more compact. + * For real world applications, developers should implement the logic of + * probing SD card, locating and mounting partition, and registering FATFS in VFS, + * with proper error checking and handling of exceptional conditions. + * + * @param base_path path where partition should be registered (e.g. "/sdcard") + * @param host_config pointer to structure describing SDMMC host + * @param slot_config pointer to structure with extra SDMMC slot configuration + * @param mount_config pointer to structure with extra parameters for mounting FATFS + * @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from SDMMC host, SDMMC protocol, or FATFS drivers + */ +esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, + const sdmmc_host_t* host_config, + const sdmmc_slot_config_t* slot_config, + const esp_vfs_fat_sdmmc_mount_config_t* mount_config, + sdmmc_card_t** out_card); + +/** + * @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_sdmmc_mount + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount hasn't been called + */ +esp_err_t esp_vfs_fat_sdmmc_unmount(); diff --git a/components/fatfs/src/ffconf.h b/components/fatfs/src/ffconf.h index b3a1bf57473..23b63ea9e9b 100644 --- a/components/fatfs/src/ffconf.h +++ b/components/fatfs/src/ffconf.h @@ -241,9 +241,9 @@ / lock control is independent of re-entrancy. */ -#define _FS_REENTRANT 0 +#define _FS_REENTRANT 1 #define _FS_TIMEOUT 1000 -#define _SYNC_t HANDLE +#define _SYNC_t SemaphoreHandle_t /* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() @@ -261,7 +261,7 @@ / SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be / included somewhere in the scope of ff.h. */ -/* #include // O/S definitions */ - +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" /*--- End of configuration options ---*/ diff --git a/components/fatfs/src/option/syscall.c b/components/fatfs/src/option/syscall.c index 14140c5a732..b6ceeaa3feb 100644 --- a/components/fatfs/src/option/syscall.c +++ b/components/fatfs/src/option/syscall.c @@ -21,22 +21,8 @@ int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object _SYNC_t *sobj /* Pointer to return the created sync object */ ) { - int ret; - - - *sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */ - ret = (int)(*sobj != INVALID_HANDLE_VALUE); - -// *sobj = SyncObjects[vol]; /* uITRON (give a static sync object) */ -// ret = 1; /* The initial value of the semaphore must be 1. */ - -// *sobj = OSMutexCreate(0, &err); /* uC/OS-II */ -// ret = (int)(err == OS_NO_ERR); - -// *sobj = xSemaphoreCreateMutex(); /* FreeRTOS */ -// ret = (int)(*sobj != NULL); - - return ret; + *sobj = xSemaphoreCreateMutex(); + return (*sobj != NULL) ? 1 : 0; } @@ -53,20 +39,8 @@ int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to any erro _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ ) { - int ret; - - - ret = CloseHandle(sobj); /* Win32 */ - -// ret = 1; /* uITRON (nothing to do) */ - -// OSMutexDel(sobj, OS_DEL_ALWAYS, &err); /* uC/OS-II */ -// ret = (int)(err == OS_NO_ERR); - -// vSemaphoreDelete(sobj); /* FreeRTOS */ -// ret = 1; - - return ret; + vSemaphoreDelete(sobj); + return 1; } @@ -82,18 +56,7 @@ int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a gran _SYNC_t sobj /* Sync object to wait */ ) { - int ret; - - ret = (int)(WaitForSingleObject(sobj, _FS_TIMEOUT) == WAIT_OBJECT_0); /* Win32 */ - -// ret = (int)(wai_sem(sobj) == E_OK); /* uITRON */ - -// OSMutexPend(sobj, _FS_TIMEOUT, &err)); /* uC/OS-II */ -// ret = (int)(err == OS_NO_ERR); - -// ret = (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); /* FreeRTOS */ - - return ret; + return (xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE) ? 1 : 0; } @@ -108,13 +71,7 @@ void ff_rel_grant ( _SYNC_t sobj /* Sync object to be signaled */ ) { - ReleaseMutex(sobj); /* Win32 */ - -// sig_sem(sobj); /* uITRON */ - -// OSMutexPost(sobj); /* uC/OS-II */ - -// xSemaphoreGive(sobj); /* FreeRTOS */ + xSemaphoreGive(sobj); } #endif diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c new file mode 100644 index 00000000000..4ef387b43ba --- /dev/null +++ b/components/fatfs/src/vfs_fat.c @@ -0,0 +1,538 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include "esp_vfs.h" +#include "esp_log.h" +#include "ff.h" + +#include "diskio.h" + + +typedef struct { + char fat_drive[8]; + size_t max_files; + FATFS fs; + FIL files[0]; + _lock_t lock; +} vfs_fat_ctx_t; + +typedef struct { + DIR dir; + long offset; + FF_DIR ffdir; + FILINFO filinfo; + struct dirent cur_dirent; +} vfs_fat_dir_t; + + +static const char* TAG = "vfs_fat"; + +static size_t vfs_fat_write(void* p, int fd, const void * data, size_t size); +static off_t vfs_fat_lseek(void* p, int fd, off_t size, int mode); +static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size); +static int vfs_fat_open(void* ctx, const char * path, int flags, int mode); +static int vfs_fat_close(void* ctx, int fd); +static int vfs_fat_fstat(void* ctx, int fd, struct stat * st); +static int vfs_fat_stat(void* ctx, const char * path, struct stat * st); +static int vfs_fat_link(void* ctx, const char* n1, const char* n2); +static int vfs_fat_unlink(void* ctx, const char *path); +static int vfs_fat_rename(void* ctx, const char *src, const char *dst); +static DIR* vfs_fat_opendir(void* ctx, const char* name); +static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir); +static int vfs_fat_readdir_r(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent); +static long vfs_fat_telldir(void* ctx, DIR* pdir); +static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset); +static int vfs_fat_closedir(void* ctx, DIR* pdir); +static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode); +static int vfs_fat_rmdir(void* ctx, const char* name); + + +static char s_base_path[ESP_VFS_PATH_MAX]; +static vfs_fat_ctx_t* s_fat_ctx = NULL; + +esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, size_t max_files, FATFS** out_fs) +{ + if (s_fat_ctx) { + return ESP_ERR_INVALID_STATE; + } + const esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_CONTEXT_PTR, + .write_p = &vfs_fat_write, + .lseek_p = &vfs_fat_lseek, + .read_p = &vfs_fat_read, + .open_p = &vfs_fat_open, + .close_p = &vfs_fat_close, + .fstat_p = &vfs_fat_fstat, + .stat_p = &vfs_fat_stat, + .link_p = &vfs_fat_link, + .unlink_p = &vfs_fat_unlink, + .rename_p = &vfs_fat_rename, + .opendir_p = &vfs_fat_opendir, + .closedir_p = &vfs_fat_closedir, + .readdir_p = &vfs_fat_readdir, + .readdir_r_p = &vfs_fat_readdir_r, + .seekdir_p = &vfs_fat_seekdir, + .telldir_p = &vfs_fat_telldir, + .mkdir_p = &vfs_fat_mkdir, + .rmdir_p = &vfs_fat_rmdir + }; + size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL); + s_fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size); + if (s_fat_ctx == NULL) { + return ESP_ERR_NO_MEM; + } + s_fat_ctx->max_files = max_files; + strncpy(s_fat_ctx->fat_drive, fat_drive, sizeof(s_fat_ctx->fat_drive) - 1); + *out_fs = &s_fat_ctx->fs; + esp_err_t err = esp_vfs_register(base_path, &vfs, s_fat_ctx); + if (err != ESP_OK) { + free(s_fat_ctx); + s_fat_ctx = NULL; + return err; + } + _lock_init(&s_fat_ctx->lock); + strncpy(s_base_path, base_path, sizeof(s_base_path) - 1); + s_base_path[sizeof(s_base_path) - 1] = 0; + return ESP_OK; +} + +esp_err_t esp_vfs_fat_unregister() +{ + if (s_fat_ctx == NULL) { + return ESP_ERR_INVALID_STATE; + } + esp_err_t err = esp_vfs_unregister(s_base_path); + if (err != ESP_OK) { + return err; + } + _lock_close(&s_fat_ctx->lock); + free(s_fat_ctx); + s_fat_ctx = NULL; + return ESP_OK; +} + +static int get_next_fd(vfs_fat_ctx_t* fat_ctx) +{ + for (size_t i = 0; i < fat_ctx->max_files; ++i) { + if (fat_ctx->files[i].obj.fs == NULL) { + return (int) i; + } + } + return -1; +} + +static int fat_mode_conv(int m) +{ + int res = 0; + int acc_mode = m & O_ACCMODE; + if (acc_mode == O_RDONLY) { + res |= FA_READ; + } else if (acc_mode == O_WRONLY) { + res |= FA_WRITE; + } else if (acc_mode == O_RDWR) { + res |= FA_READ | FA_WRITE; + } + if ((m & O_CREAT) && (m & O_EXCL)) { + res |= FA_CREATE_NEW; + } else if (m & O_CREAT) { + res |= FA_CREATE_ALWAYS; + } else if (m & O_APPEND) { + res |= FA_OPEN_ALWAYS; + } else { + res |= FA_OPEN_EXISTING; + } + return res; +} + +static int fresult_to_errno(FRESULT fr) +{ + switch(fr) { + case FR_DISK_ERR: return EIO; + case FR_INT_ERR: + assert(0 && "fatfs internal error"); + return EIO; + case FR_NOT_READY: return ENODEV; + case FR_NO_FILE: return ENOENT; + case FR_NO_PATH: return ENOENT; + case FR_INVALID_NAME: return EINVAL; + case FR_DENIED: return EACCES; + case FR_EXIST: return EEXIST; + case FR_INVALID_OBJECT: return EBADF; + case FR_WRITE_PROTECTED: return EACCES; + case FR_INVALID_DRIVE: return ENXIO; + case FR_NOT_ENABLED: return ENODEV; + case FR_NO_FILESYSTEM: return ENODEV; + case FR_MKFS_ABORTED: return EINTR; + case FR_TIMEOUT: return ETIMEDOUT; + case FR_LOCKED: return EACCES; + case FR_NOT_ENOUGH_CORE: return ENOMEM; + case FR_TOO_MANY_OPEN_FILES: return ENFILE; + case FR_INVALID_PARAMETER: return EINVAL; + case FR_OK: return 0; + } + assert(0 && "unhandled FRESULT"); + return ENOTSUP; +} + +static void file_cleanup(vfs_fat_ctx_t* ctx, int fd) +{ + memset(&ctx->files[fd], 0, sizeof(FIL)); +} + +static int vfs_fat_open(void* ctx, const char * path, int flags, int mode) +{ + ESP_LOGV(TAG, "%s: path=\"%s\", flags=%x, mode=%x", __func__, path, flags, mode); + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + _lock_acquire(&s_fat_ctx->lock); + int fd = get_next_fd(fat_ctx); + if (fd < 0) { + ESP_LOGE(TAG, "open: no free file descriptors"); + errno = ENFILE; + fd = -1; + goto out; + } + FRESULT res = f_open(&fat_ctx->files[fd], path, fat_mode_conv(flags)); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + file_cleanup(fat_ctx, fd); + errno = fresult_to_errno(res); + fd = -1; + goto out; + } +out: + _lock_release(&s_fat_ctx->lock); + return fd; +} + +static size_t vfs_fat_write(void* ctx, int fd, const void * data, size_t size) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + unsigned written = 0; + FRESULT res = f_write(file, data, size, &written); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + if (written == 0) { + return -1; + } + } + return written; +} + +static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + unsigned read = 0; + FRESULT res = f_read(file, dst, size, &read); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + if (read == 0) { + return -1; + } + } + return read; +} + +static int vfs_fat_close(void* ctx, int fd) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + _lock_acquire(&s_fat_ctx->lock); + FIL* file = &fat_ctx->files[fd]; + FRESULT res = f_close(file); + file_cleanup(fat_ctx, fd); + int rc = 0; + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + rc = -1; + } + _lock_release(&s_fat_ctx->lock); + return rc; +} + +static off_t vfs_fat_lseek(void* ctx, int fd, off_t offset, int mode) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + off_t new_pos; + if (mode == SEEK_SET) { + new_pos = offset; + } else if (mode == SEEK_CUR) { + off_t cur_pos = f_tell(file); + new_pos = cur_pos + offset; + } else if (mode == SEEK_END) { + off_t size = f_size(file); + new_pos = size + offset; + } else { + errno = EINVAL; + return -1; + } + FRESULT res = f_lseek(file, new_pos); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return new_pos; +} + +static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) +{ + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + FIL* file = &fat_ctx->files[fd]; + st->st_size = f_size(file); + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; + return 0; +} + +static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) +{ + FILINFO info; + FRESULT res = f_stat(path, &info); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + st->st_size = info.fsize; + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | + ((info.fattrib & AM_DIR) ? S_IFDIR : S_IFREG); + struct tm tm; + uint16_t fdate = info.fdate; + tm.tm_mday = fdate & 0x1f; + fdate >>= 5; + tm.tm_mon = (fdate & 0xf) - 1; + fdate >>=4; + tm.tm_year = fdate + 80; + uint16_t ftime = info.ftime; + tm.tm_sec = (ftime & 0x1f) * 2; + ftime >>= 5; + tm.tm_min = (ftime & 0x3f); + ftime >>= 6; + tm.tm_hour = (ftime & 0x1f); + st->st_mtime = mktime(&tm); + return 0; +} + +static int vfs_fat_unlink(void* ctx, const char *path) +{ + FRESULT res = f_unlink(path); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static int vfs_fat_link(void* ctx, const char* n1, const char* n2) +{ + const size_t copy_buf_size = 4096; + void* buf = malloc(copy_buf_size); + if (buf == NULL) { + errno = ENOMEM; + return -1; + } + FIL f1; + FRESULT res = f_open(&f1, n1, FA_READ | FA_OPEN_EXISTING); + if (res != FR_OK) { + goto fail1; + } + FIL f2; + res = f_open(&f2, n2, FA_WRITE | FA_CREATE_NEW); + if (res != FR_OK) { + goto fail2; + } + size_t size_left = f_size(&f1); + while (size_left > 0) { + size_t will_copy = (size_left < copy_buf_size) ? size_left : copy_buf_size; + size_t read; + res = f_read(&f1, buf, will_copy, &read); + if (res != FR_OK) { + goto fail3; + } else if (read != will_copy) { + res = FR_DISK_ERR; + goto fail3; + } + size_t written; + res = f_write(&f2, buf, will_copy, &written); + if (res != FR_OK) { + goto fail3; + } else if (written != will_copy) { + res = FR_DISK_ERR; + goto fail3; + } + size_left -= will_copy; + } + +fail3: + f_close(&f2); +fail2: + f_close(&f1); +fail1: + free(buf); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static int vfs_fat_rename(void* ctx, const char *src, const char *dst) +{ + FRESULT res = f_rename(src, dst); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static DIR* vfs_fat_opendir(void* ctx, const char* name) +{ + vfs_fat_dir_t* fat_dir = calloc(1, sizeof(vfs_fat_dir_t)); + if (!fat_dir) { + errno = ENOMEM; + return NULL; + } + FRESULT res = f_opendir(&fat_dir->ffdir, name); + if (res != FR_OK) { + free(fat_dir); + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return NULL; + } + return (DIR*) fat_dir; +} + +static int vfs_fat_closedir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + FRESULT res = f_closedir(&fat_dir->ffdir); + free(pdir); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir) +{ + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + struct dirent* out_dirent; + int err = vfs_fat_readdir_r(ctx, pdir, &fat_dir->cur_dirent, &out_dirent); + if (err != 0) { + errno = err; + return NULL; + } + return out_dirent; +} + +static int vfs_fat_readdir_r(void* ctx, DIR* pdir, + struct dirent* entry, struct dirent** out_dirent) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + FRESULT res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + return fresult_to_errno(res); + } + if (fat_dir->filinfo.fname[0] == 0) { + // end of directory + *out_dirent = NULL; + return 0; + } + entry->d_ino = 0; + if (fat_dir->filinfo.fattrib & AM_DIR) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + strlcpy(entry->d_name, fat_dir->filinfo.fname, + sizeof(entry->d_name)); + fat_dir->offset++; + *out_dirent = entry; + return 0; +} + +static long vfs_fat_telldir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + return fat_dir->offset; +} + +static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset) +{ + assert(pdir); + vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir; + FRESULT res; + if (offset < fat_dir->offset) { + res = f_rewinddir(&fat_dir->ffdir); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: rewinddir fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return; + } + fat_dir->offset = 0; + } + while (fat_dir->offset < offset) { + res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: f_readdir fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return; + } + fat_dir->offset++; + } +} + +static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode) +{ + (void) mode; + FRESULT res = f_mkdir(name); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} + +static int vfs_fat_rmdir(void* ctx, const char* name) +{ + FRESULT res = f_unlink(name); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + return -1; + } + return 0; +} diff --git a/components/fatfs/src/vfs_fat_sdmmc.c b/components/fatfs/src/vfs_fat_sdmmc.c new file mode 100644 index 00000000000..e5956dd7b2e --- /dev/null +++ b/components/fatfs/src/vfs_fat_sdmmc.c @@ -0,0 +1,126 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp_log.h" +#include "esp_vfs.h" +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_cmd.h" +#include "diskio.h" + +static const char* TAG = "vfs_fat_sdmmc"; +static sdmmc_card_t* s_card = NULL; + +esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, + const sdmmc_host_t* host_config, + const sdmmc_slot_config_t* slot_config, + const esp_vfs_fat_sdmmc_mount_config_t* mount_config, + sdmmc_card_t** out_card) +{ + const size_t workbuf_size = 4096; + void* workbuf = NULL; + + if (s_card != NULL) { + return ESP_ERR_INVALID_STATE; + } + // enable SDMMC + sdmmc_host_init(); + + // enable card slot + sdmmc_host_init_slot(host_config->slot, slot_config); + s_card = malloc(sizeof(sdmmc_card_t)); + if (s_card == NULL) { + return ESP_ERR_NO_MEM; + } + + // probe and initialize card + esp_err_t err = sdmmc_card_init(host_config, s_card); + if (err != ESP_OK) { + ESP_LOGD(TAG, "sdmmc_card_init failed 0x(%x)", err); + goto fail; + } + if (out_card != NULL) { + *out_card = s_card; + } + + // connect SDMMC driver to FATFS + ff_diskio_register_sdmmc(0, s_card); + + // connect FATFS to VFS + FATFS* fs; + err = esp_vfs_fat_register(base_path, "", mount_config->max_files, &fs); + if (err == ESP_ERR_INVALID_STATE) { + // it's okay, already registered with VFS + } else if (err != ESP_OK) { + ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err); + goto fail; + } + + // Try to mount partition + FRESULT res = f_mount(fs, "", 1); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGW(TAG, "failed to mount card (%d)", res); + if (!(res == FR_NO_FILESYSTEM && mount_config->format_if_mount_failed)) { + goto fail; + } + ESP_LOGW(TAG, "partitioning card"); + DWORD plist[] = {100, 0, 0, 0}; + workbuf = malloc(workbuf_size); + res = f_fdisk(0, plist, workbuf); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_fdisk failed (%d)", res); + goto fail; + } + ESP_LOGW(TAG, "formatting card"); + res = f_mkfs("", FM_ANY, s_card->csd.sector_size, workbuf, workbuf_size); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_mkfs failed (%d)", res); + goto fail; + } + free(workbuf); + ESP_LOGW(TAG, "mounting again"); + res = f_mount(fs, "", 0); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res); + goto fail; + } + } + return ESP_OK; + +fail: + free(workbuf); + esp_vfs_unregister(base_path); + free(s_card); + s_card = NULL; + return err; +} + +esp_err_t esp_vfs_fat_sdmmc_unmount() +{ + if (s_card == NULL) { + return ESP_ERR_INVALID_STATE; + } + // unmount + f_mount(0, "", 0); + // release SD driver + free(s_card); + s_card = NULL; + sdmmc_host_deinit(); + return esp_vfs_fat_unregister(); +} diff --git a/components/fatfs/test/component.mk b/components/fatfs/test/component.mk new file mode 100644 index 00000000000..ce464a212a6 --- /dev/null +++ b/components/fatfs/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/fatfs/test/test_fatfs.c b/components/fatfs/test/test_fatfs.c new file mode 100644 index 00000000000..9ee6606fb87 --- /dev/null +++ b/components/fatfs/test/test_fatfs.c @@ -0,0 +1,552 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include "unity.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_vfs.h" +#include "esp_vfs_fat.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "diskio.h" +#include "ff.h" + +static const char* hello_str = "Hello, World!\n"; + +#define HEAP_SIZE_CAPTURE() \ + size_t heap_size = esp_get_free_heap_size(); + +#define HEAP_SIZE_CHECK(tolerance) \ + do {\ + size_t final_heap_size = esp_get_free_heap_size(); \ + if (final_heap_size < heap_size - tolerance) { \ + printf("Initial heap size: %d, final: %d, diff=%d\n", heap_size, final_heap_size, heap_size - final_heap_size); \ + } \ + } while(0) + +static void create_file_with_text(const char* name, const char* text) +{ + FILE* f = fopen(name, "wb"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_TRUE(fputs(text, f) != EOF); + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +TEST_CASE("can create and write file on sd card", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + create_file_with_text("/sdcard/hello.txt", hello_str); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("can read file on sd card", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + FILE* f = fopen("/sdcard/hello.txt", "r"); + TEST_ASSERT_NOT_NULL(f); + char buf[32]; + int cb = fread(buf, 1, sizeof(buf), f); + TEST_ASSERT_EQUAL(strlen(hello_str), cb); + TEST_ASSERT_EQUAL(0, strcmp(hello_str, buf)); + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +static void speed_test(void* buf, size_t buf_size, size_t file_size, bool write) +{ + const size_t buf_count = file_size / buf_size; + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = write, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + FILE* f = fopen("/sdcard/4mb.bin", (write) ? "wb" : "rb"); + TEST_ASSERT_NOT_NULL(f); + + struct timeval tv_start; + gettimeofday(&tv_start, NULL); + for (size_t n = 0; n < buf_count; ++n) { + if (write) { + TEST_ASSERT_EQUAL(1, fwrite(buf, buf_size, 1, f)); + } else { + if (fread(buf, buf_size, 1, f) != 1) { + printf("reading at n=%d, eof=%d", n, feof(f)); + TEST_FAIL(); + } + } + } + + struct timeval tv_end; + gettimeofday(&tv_end, NULL); + + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + + float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); + printf("%s %d bytes (block size %d) in %.3fms (%.3f MB/s)\n", + (write)?"Wrote":"Read", file_size, buf_size, t_s * 1e3, + (file_size / 1024 / 1024) / t_s); +} + + +TEST_CASE("read speed test", "[fatfs]") +{ + + HEAP_SIZE_CAPTURE(); + const size_t buf_size = 16 * 1024; + uint32_t* buf = (uint32_t*) calloc(1, buf_size); + const size_t file_size = 4 * 1024 * 1024; + speed_test(buf, 4 * 1024, file_size, false); + HEAP_SIZE_CHECK(0); + speed_test(buf, 8 * 1024, file_size, false); + HEAP_SIZE_CHECK(0); + speed_test(buf, 16 * 1024, file_size, false); + HEAP_SIZE_CHECK(0); + free(buf); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("write speed test", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + + const size_t buf_size = 16 * 1024; + uint32_t* buf = (uint32_t*) calloc(1, buf_size); + for (size_t i = 0; i < buf_size / 4; ++i) { + buf[i] = esp_random(); + } + const size_t file_size = 4 * 1024 * 1024; + + speed_test(buf, 4 * 1024, file_size, true); + speed_test(buf, 8 * 1024, file_size, true); + speed_test(buf, 16 * 1024, file_size, true); + + free(buf); + + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("can lseek", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + FILE* f = fopen("/sdcard/seek.txt", "wb+"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n")); + TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR)); + TEST_ASSERT_EQUAL('9', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET)); + TEST_ASSERT_EQUAL('3', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END)); + TEST_ASSERT_EQUAL('8', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_END)); + TEST_ASSERT_EQUAL(14, ftell(f)); + TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n")); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END)); + TEST_ASSERT_EQUAL(18, ftell(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET)); + char buf[20]; + TEST_ASSERT_EQUAL(18, fread(buf, 1, sizeof(buf), f)); + const char ref_buf[] = "0123456789\n\0\0\0abc\n"; + TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1); + + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("stat returns correct values", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + struct tm tm; + tm.tm_year = 2016 - 1900; + tm.tm_mon = 0; + tm.tm_mday = 10; + tm.tm_hour = 16; + tm.tm_min = 30; + tm.tm_sec = 0; + time_t t = mktime(&tm); + printf("Setting time: %s", asctime(&tm)); + struct timeval now = { .tv_sec = t }; + settimeofday(&now, NULL); + + create_file_with_text("/sdcard/stat.txt", "foo\n"); + + struct stat st; + TEST_ASSERT_EQUAL(0, stat("/sdcard/stat.txt", &st)); + time_t mtime = st.st_mtime; + struct tm mtm; + localtime_r(&mtime, &mtm); + printf("File time: %s", asctime(&mtm)); + TEST_ASSERT(abs(mtime - t) < 2); // fatfs library stores time with 2 second precision + + TEST_ASSERT(st.st_mode & S_IFREG); + TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("unlink removes a file", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + create_file_with_text("/sdcard/unlink.txt", "unlink\n"); + + TEST_ASSERT_EQUAL(0, unlink("/sdcard/unlink.txt")); + + TEST_ASSERT_NULL(fopen("/sdcard/unlink.txt", "r")); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("link copies a file, rename moves a file", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + unlink("/sdcard/linkcopy.txt"); + unlink("/sdcard/link_dst.txt"); + unlink("/sdcard/link_src.txt"); + + FILE* f = fopen("/sdcard/link_src.txt", "w+"); + TEST_ASSERT_NOT_NULL(f); + char* str = "0123456789"; + for (int i = 0; i < 4000; ++i) { + TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f)); + } + TEST_ASSERT_EQUAL(0, fclose(f)); + + TEST_ASSERT_EQUAL(0, link("/sdcard/link_src.txt", "/sdcard/linkcopy.txt")); + + FILE* fcopy = fopen("/sdcard/linkcopy.txt", "r"); + TEST_ASSERT_NOT_NULL(fcopy); + TEST_ASSERT_EQUAL(0, fseek(fcopy, 0, SEEK_END)); + TEST_ASSERT_EQUAL(40000, ftell(fcopy)); + TEST_ASSERT_EQUAL(0, fclose(fcopy)); + + TEST_ASSERT_EQUAL(0, rename("/sdcard/linkcopy.txt", "/sdcard/link_dst.txt")); + TEST_ASSERT_NULL(fopen("/sdcard/linkcopy.txt", "r")); + FILE* fdst = fopen("/sdcard/link_dst.txt", "r"); + TEST_ASSERT_NOT_NULL(fdst); + TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END)); + TEST_ASSERT_EQUAL(40000, ftell(fdst)); + TEST_ASSERT_EQUAL(0, fclose(fdst)); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +typedef struct { + const char* filename; + bool write; + size_t word_count; + int seed; + SemaphoreHandle_t done; + int result; +} read_write_test_arg_t; + +#define READ_WRITE_TEST_ARG_INIT(name, seed_) \ + { \ + .filename = name, \ + .seed = seed_, \ + .word_count = 8192, \ + .write = true, \ + .done = xSemaphoreCreateBinary() \ + } + +static void read_write_task(void* param) +{ + read_write_test_arg_t* args = (read_write_test_arg_t*) param; + FILE* f = fopen(args->filename, args->write ? "wb" : "rb"); + if (f == NULL) { + args->result = ESP_ERR_NOT_FOUND; + goto done; + } + + srand(args->seed); + for (size_t i = 0; i < args->word_count; ++i) { + uint32_t val = rand(); + if (args->write) { + int cnt = fwrite(&val, sizeof(val), 1, f); + if (cnt != 1) { + args->result = ESP_FAIL; + goto close; + } + } else { + uint32_t rval; + int cnt = fread(&rval, sizeof(rval), 1, f); + if (cnt != 1 || rval != val) { + ets_printf("E: i=%d, cnt=%d rval=%d val=%d\n\n", i, cnt, rval, val); + args->result = ESP_FAIL; + goto close; + } + } + } + args->result = ESP_OK; + +close: + fclose(f); + +done: + xSemaphoreGive(args->done); + vTaskDelay(1); + vTaskDelete(NULL); +} + + +TEST_CASE("multiple tasks can use same volume", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT("/sdcard/f1", 1); + read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT("/sdcard/f2", 2); + + printf("writing f1 and f2\n"); + + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + + args1.write = false; + args2.write = false; + read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT("/sdcard/f3", 3); + read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT("/sdcard/f4", 4); + + printf("reading f1 and f2, writing f3 and f4\n"); + + xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, 1); + xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, 0); + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + xSemaphoreTake(args3.done, portMAX_DELAY); + printf("f3 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args3.result); + xSemaphoreTake(args4.done, portMAX_DELAY); + printf("f4 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args4.result); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + vSemaphoreDelete(args1.done); + vSemaphoreDelete(args2.done); + vSemaphoreDelete(args3.done); + vSemaphoreDelete(args4.done); + vTaskDelay(10); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("can create and remove directories", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir1", 0755)); + struct stat st; + TEST_ASSERT_EQUAL(0, stat("/sdcard/dir1", &st)); + TEST_ASSERT_TRUE(st.st_mode & S_IFDIR); + TEST_ASSERT_FALSE(st.st_mode & S_IFREG); + TEST_ASSERT_EQUAL(0, rmdir("/sdcard/dir1")); + TEST_ASSERT_EQUAL(-1, stat("/sdcard/dir1", &st)); + + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir2", 0755)); + create_file_with_text("/sdcard/dir2/1.txt", "foo\n"); + TEST_ASSERT_EQUAL(0, stat("/sdcard/dir2", &st)); + TEST_ASSERT_TRUE(st.st_mode & S_IFDIR); + TEST_ASSERT_FALSE(st.st_mode & S_IFREG); + TEST_ASSERT_EQUAL(0, stat("/sdcard/dir2/1.txt", &st)); + TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); + TEST_ASSERT_TRUE(st.st_mode & S_IFREG); + TEST_ASSERT_EQUAL(-1, rmdir("/sdcard/dir2")); + TEST_ASSERT_EQUAL(0, unlink("/sdcard/dir2/1.txt")); + TEST_ASSERT_EQUAL(0, rmdir("/sdcard/dir2")); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} + +TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[fatfs]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5 + }; + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); + + unlink("/sdcard/dir/inner/3.txt"); + rmdir("/sdcard/dir/inner"); + unlink("/sdcard/dir/2.txt"); + unlink("/sdcard/dir/1.txt"); + unlink("/sdcard/dir/boo.bin"); + rmdir("/sdcard/dir"); + + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir", 0755)); + create_file_with_text("/sdcard/dir/2.txt", "1\n"); + create_file_with_text("/sdcard/dir/1.txt", "1\n"); + create_file_with_text("/sdcard/dir/boo.bin", "\01\02\03"); + TEST_ASSERT_EQUAL(0, mkdir("/sdcard/dir/inner", 0755)); + create_file_with_text("/sdcard/dir/inner/3.txt", "3\n"); + + DIR* dir = opendir("/sdcard/dir"); + TEST_ASSERT_NOT_NULL(dir); + int count = 0; + const char* names[4]; + while(count < 4) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + printf("found '%s'\n", de->d_name); + if (strcasecmp(de->d_name, "1.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "1.txt"; + ++count; + } else if (strcasecmp(de->d_name, "2.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "2.txt"; + ++count; + } else if (strcasecmp(de->d_name, "inner") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_DIR); + names[count] = "inner"; + ++count; + } else if (strcasecmp(de->d_name, "boo.bin") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "boo.bin"; + ++count; + } else { + TEST_FAIL_MESSAGE("unexpected directory entry"); + } + } + TEST_ASSERT_EQUAL(count, 4); + + rewinddir(dir); + struct dirent* de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0])); + seekdir(dir, 3); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3])); + seekdir(dir, 1); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1])); + seekdir(dir, 2); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2])); + + TEST_ASSERT_EQUAL(0, closedir(dir)); + + TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); + HEAP_SIZE_CHECK(0); +} diff --git a/components/newlib/test/test_newlib.c b/components/newlib/test/test_newlib.c index 1d86cd4a978..1d2a4bf09f3 100644 --- a/components/newlib/test/test_newlib.c +++ b/components/newlib/test/test_newlib.c @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include "unity.h" #include "sdkconfig.h" @@ -86,6 +88,33 @@ TEST_CASE("test time functions", "[newlib]") } +TEST_CASE("test asctime", "[newlib]") +{ + char buf[64]; + struct tm tm = { 0 }; + tm.tm_year = 2016 - 1900; + tm.tm_mon = 0; + tm.tm_mday = 10; + tm.tm_hour = 16; + tm.tm_min = 30; + tm.tm_sec = 0; + time_t t = mktime(&tm); + const char* time_str = asctime(&tm); + strlcpy(buf, time_str, sizeof(buf)); + printf("Setting time: %s", time_str); + struct timeval now = { .tv_sec = t }; + settimeofday(&now, NULL); + + struct timeval tv; + gettimeofday(&tv, NULL); + time_t mtime = tv.tv_sec; + struct tm mtm; + localtime_r(&mtime, &mtm); + time_str = asctime(&mtm); + printf("Got time: %s", time_str); + TEST_ASSERT_EQUAL_STRING(buf, time_str); +} + static bool fn_in_rom(void *fn, char *name) { const int fnaddr = (int)fn; diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index e8fbd25451d..6c2154817fc 100644 --- a/components/sdmmc/test/test_sd.c +++ b/components/sdmmc/test/test_sd.c @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include #include From 47f4a097049d74d6154c8dedda1850a4e99f45a2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 9 Jan 2017 04:09:09 +0800 Subject: [PATCH 097/167] docs: add sdmmc and fatfs docs --- docs/Doxyfile | 5 ++- docs/api/fatfs.rst | 64 +++++++++++++++++++++++++++++++ docs/api/sdmmc.rst | 95 ++++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 2 + 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 docs/api/fatfs.rst create mode 100644 docs/api/sdmmc.rst diff --git a/docs/Doxyfile b/docs/Doxyfile index 668ebaba026..7dbd1cc8d1c 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -32,7 +32,10 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_heap_alloc_caps.h \ ../components/freertos/include/freertos/heap_regions.h \ ../components/esp32/include/esp_smartconfig.h \ - ../components/esp32/include/esp_deep_sleep.h + ../components/esp32/include/esp_deep_sleep.h \ + ../components/sdmmc/include/sdmmc_cmd.h \ + ../components/fatfs/src/esp_vfs_fat.h \ + ../components/fatfs/src/diskio.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/api/fatfs.rst b/docs/api/fatfs.rst new file mode 100644 index 00000000000..d2efc87abf2 --- /dev/null +++ b/docs/api/fatfs.rst @@ -0,0 +1,64 @@ +FAT Filesystem Support +====================== + +ESP-IDF uses `FatFs `_ library to work with FAT filesystems. FatFs library resides in ``fatfs`` component. Although it can be used directly, many of its features can be accessed via VFS using C standard library and POSIX APIs. + +Additionally, FatFs has been modified to support run-time pluggable disk IO layer. This allows mapping of FatFs drives to physical disks at run-time. + +Using FatFs with VFS +-------------------- + +``esp_vfs_fat.h`` header file defines functions to connect FatFs with VFS. ``esp_vfs_fat_register`` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. ``esp_vfs_fat_unregister`` function deletes the registration with VFS, and frees the ``FATFS`` structure. + +Most applications will use the following flow when working with ``esp_vfs_fat_`` functions: + +1. Call ``esp_vfs_fat_register``, specifying path prefix where the filesystem has to be mounted (e.g. ``"/sdcard"``), FatFs drive number, and a variable which will receive a pointer to ``FATFS`` structure. + +2. Call ``ff_diskio_register`` function to register disk IO driver for the drive number used in step 1. + +3. Call ``f_mount`` function (and optionally ``f_fdisk``, ``f_mkfs``) to mount the filesystem using the same drive number which was passed to ``esp_vfs_fat_register``. See FatFs documentation for more details. + +4. Call POSIX and C standard library functions to open, read, write, erase, copy files, etc. Use paths starting with the prefix passed to ``esp_vfs_register`` (such as ``"/sdcard/hello.txt"``). + +5. Optionally, call FatFs library functions directly. Use paths without a VFS prefix in this case (``"/hello.txt"``). + +6. Close all open files. + +7. Call ``f_mount`` function for the same drive number, with NULL ``FATFS*`` argument, to unmount the filesystem. + +8. Call ``ff_diskio_register`` with NULL ``ff_diskio_impl_t*`` argument and the same drive number. + +9. Call ``esp_vfs_fat_unregister`` to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1. + +Convenience functions, ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmount``, which wrap these steps and also handle SD card initialization, are described in the next section. + +.. doxygenfunction:: esp_vfs_fat_register +.. doxygenfunction:: esp_vfs_fat_unregister + + +Using FatFs with VFS and SD cards +--------------------------------- + +``esp_vfs_fat.h`` header file also provides a convenience function to perform steps 1–3 and 7–9, and also handle SD card initialization: ``esp_vfs_fat_sdmmc_mount``. This function does only limited error handling. Developers are encouraged to look at its source code and incorporate more advanced versions into production applications. ``esp_vfs_fat_sdmmc_unmount`` function unmounts the filesystem and releases resources acquired by ``esp_vfs_fat_sdmmc_mount``. + +.. doxygenfunction:: esp_vfs_fat_sdmmc_mount + +.. doxygenstruct:: esp_vfs_fat_sdmmc_mount_config_t + :members: + +.. doxygenfunction:: esp_vfs_fat_sdmmc_unmount + +FatFS disk IO layer +------------------- + +FatFs has been extended with an API to register disk IO driver at runtime. + +Implementation of disk IO functions for SD/MMC cards is provided. It can be registered for the given FatFs drive number using ``ff_diskio_register_sdmmc`` function. + +.. doxygenfunction:: ff_diskio_register + +.. doxygenstruct:: ff_diskio_impl_t + :members: + +.. doxygenfunction:: ff_diskio_register_sdmmc + diff --git a/docs/api/sdmmc.rst b/docs/api/sdmmc.rst new file mode 100644 index 00000000000..126be86576d --- /dev/null +++ b/docs/api/sdmmc.rst @@ -0,0 +1,95 @@ +SDMMC Host Peripheral +===================== + +Overview +-------- + +SDMMC peripheral supports SD and MMC memory cards and SDIO cards. SDMMC software builds on top of SDMMC driver and consists of the following parts: + +1. SDMMC host driver (``driver/sdmmc_host.h``) — this driver provides APIs to send commands to the slave device(s), send and receive data, and handling error conditions on the bus. + +2. SDMMC protocol layer (``sdmmc_cmd.h``) — this component handles specifics of SD protocol such as card initialization and data transfer commands. Despite the name, only SD (SDSC/SDHC/SDXC) cards are supported at the moment. Support for MCC/eMMC cards can be added in the future. + +Protocol layer works with the host via ``sdmmc_host_t`` structure. This structure contains pointers to various functions of the host. This design makes it possible to implement an SD host using SPI interface later. + +Application Example +------------------- + +An example which combines SDMMC driver with FATFS library is provided in ``examples/24_sd_card`` directory. This example initializes the card, writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. + + +Protocol layer APIs +------------------- + +Protocol layer is given ``sdmmc_host_t`` structure which describes the SD/MMC host driver, lists its capabilites, and provides pointers to functions of the driver. Protocol layer stores card-specific information in ``sdmmc_card_t`` structure. When sending commands to the SD/MMC host driver, protocol layer uses ``sdmmc_command_t`` structure to describe the command, argument, expected return value, and data to transfer, if any. + +Normal usage of the protocol layer is as follows: + +1. Call the host driver functions to initialize the host (e.g. ``sdmmc_host_init``, ``sdmmc_host_init_slot``). +2. Call ``sdmmc_card_init`` to initialize the card, passing it host driver information (``host``) and a pointer to ``sdmmc_card_t`` structure which will be filled in (``card``). +3. To read and write sectors of the card, use ``sdmmc_read_sectors`` and ``sdmmc_write_sectors``, passing the pointer to card information structure (``card``). +4. When card is not used anymore, call the host driver function to disable SDMMC host peripheral and free resources allocated by the driver (e.g. ``sdmmc_host_deinit``). + +Most applications need to use the protocol layer only in one task; therefore the protocol layer doesn't implement any kind of locking on the ``sdmmc_card_t`` structure, or when accessing SDMMC host driver. Such locking has to be implemented in the higher layer, if necessary (e.g. in the filesystem driver). + +.. doxygenstruct:: sdmmc_host_t + :members: + +.. doxygendefine:: SDMMC_HOST_FLAG_1BIT +.. doxygendefine:: SDMMC_HOST_FLAG_4BIT +.. doxygendefine:: SDMMC_HOST_FLAG_8BIT +.. doxygendefine:: SDMMC_HOST_FLAG_SPI +.. doxygendefine:: SDMMC_FREQ_DEFAULT +.. doxygendefine:: SDMMC_FREQ_HIGHSPEED +.. doxygendefine:: SDMMC_FREQ_PROBING + +.. doxygenstruct:: sdmmc_command_t + :members: + +.. doxygenstruct:: sdmmc_card_t + :members: + +.. doxygenstruct:: sdmmc_csd_t + :members: + +.. doxygenstruct:: sdmmc_cid_t + :members: + +.. doxygenstruct:: sdmmc_scr_t + :members: + +.. doxygenfunction:: sdmmc_card_init +.. doxygenfunction:: sdmmc_write_sectors +.. doxygenfunction:: sdmmc_read_sectors + +SDMMC host driver APIs +---------------------- + +On the ESP32, SDMMC host peripheral has two slots: + +- Slot 0 (``SDMMC_HOST_SLOT_0``) is an 8-bit slot. It uses ``HS1_*`` signals in the PIN MUX. +- Slot 1 (``SDMMC_HOST_SLOT_1``) is a 4-bit slot. It uses ``HS2_*`` signals in the PIN MUX. + +Card Detect and Write Protect signals can be routed to arbitrary pins using GPIO matrix. To use these pins, set ``gpio_cd`` and ``gpio_wp`` members of ``sdmmc_slot_config_t`` structure when calling ``sdmmc_host_init_slot``. + +Of all the funtions listed below, only ``sdmmc_host_init``, ``sdmmc_host_init_slot``, and ``sdmmc_host_deinit`` will be used directly by most applications. Other functions, such as ``sdmmc_host_set_bus_width``, ``sdmmc_host_set_card_clk``, and ``sdmmc_host_do_transaction`` will be called by the SD/MMC protocol layer via function pointers in ``sdmmc_host_t`` structure. + +.. doxygenfunction:: sdmmc_host_init + +.. doxygendefine:: SDMMC_HOST_SLOT_0 +.. doxygendefine:: SDMMC_HOST_SLOT_1 +.. doxygendefine:: SDMMC_HOST_DEFAULT + +.. doxygenfunction:: sdmmc_host_init_slot + +.. doxygenstruct:: sdmmc_slot_config_t + :members: + +.. doxygendefine:: SDMMC_SLOT_NO_CD +.. doxygendefine:: SDMMC_SLOT_NO_WP +.. doxygendefine:: SDMMC_SLOT_CONFIG_DEFAULT + +.. doxygenfunction:: sdmmc_host_set_bus_width +.. doxygenfunction:: sdmmc_host_set_card_clk +.. doxygenfunction:: sdmmc_host_do_transaction +.. doxygenfunction:: sdmmc_host_deinit diff --git a/docs/index.rst b/docs/index.rst index 3161db345b8..2d9a62f14b8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -110,11 +110,13 @@ Contents: I2C Pulse Counter Sigma-delta Modulation + SD/MMC SPI Flash and Partition APIs SPI Master API Logging Non-Volatile Storage Virtual Filesystem + FAT Filesystem Ethernet Interrupt Allocation Memory Allocation From 084c14f1e441b589434785632d8d450bd4bb54e4 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 9 Jan 2017 10:09:10 +0800 Subject: [PATCH 098/167] esp32: fix wifi timer thread-safe issue Update wifi lib to fix a wifi timer thread-safe issue --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 23d627498d6..6ccb241d457 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 23d627498d60e524e5d95d649481bd650aff1aad +Subproject commit 6ccb241d457f9efddb9cc6b82307335400d7f8f9 From 9dd5f2a952456d2f996d0be50a518bba21c081b7 Mon Sep 17 00:00:00 2001 From: Wu Jian Gang Date: Sat, 7 Jan 2017 21:20:39 +0800 Subject: [PATCH 099/167] lwip: fix compile issue when autoip option enabled --- components/lwip/core/ipv4/autoip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/lwip/core/ipv4/autoip.c b/components/lwip/core/ipv4/autoip.c index faac4957ca2..f27d28aa245 100755 --- a/components/lwip/core/ipv4/autoip.c +++ b/components/lwip/core/ipv4/autoip.c @@ -273,7 +273,7 @@ autoip_bind(struct netif *netif) #if ESP_LWIP struct dhcp *dhcp = netif->dhcp; if (dhcp->cb != NULL) { - dhcp->cb(); + dhcp->cb(netif); } #endif return ERR_OK; From 11a994d5d8c5d479ab436010ed20db59f11e16d1 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 9 Jan 2017 12:04:21 +0800 Subject: [PATCH 100/167] Calling esp_intr_noniram_[en|dis]able twice is an error, so abort instead of doing an assert which disappears in non-debug mode --- components/esp32/intr_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index 9476a433d44..4fdda2f396f 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -691,7 +691,7 @@ void esp_intr_noniram_disable() int oldint; int cpu=xPortGetCoreID(); int intmask=~non_iram_int_mask[cpu]; - assert(non_iram_int_disabled_flag[cpu]==false); + if (non_iram_int_disabled_flag[cpu]) abort(); non_iram_int_disabled_flag[cpu]=true; asm volatile ( "movi %0,0\n" @@ -709,7 +709,7 @@ void esp_intr_noniram_enable() { int cpu=xPortGetCoreID(); int intmask=non_iram_int_disabled[cpu]; - assert(non_iram_int_disabled_flag[cpu]==true); + if (!non_iram_int_disabled_flag[cpu]) abort(); non_iram_int_disabled_flag[cpu]=false; asm volatile ( "movi a3,0\n" From 6421479dabaea425ce08a705200533f02600b050 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 9 Jan 2017 10:09:05 +1100 Subject: [PATCH 101/167] build system: Fix sdkconfig.defaults file preventing menuconfig changes 'make defconfig' now behaves similarly whether sdkconfig.defaults is present or not, and 'make menuconfig' doesn't trigger a defconfig. --- make/project_config.mk | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/make/project_config.mk b/make/project_config.mk index b8e40f43576..011aa1ff0e4 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -25,7 +25,7 @@ KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfi COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" KCONFIG_CONFIG=$(SDKCONFIG) \ COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" -menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig | defconfig +menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(call prereq_if_explicit,defconfig) $(summary) MENUCONFIG $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig @@ -39,14 +39,11 @@ $(SDKCONFIG): defconfig endif endif -$(wildcard $(PROJECT_PATH)/sdkconfig.defaults): | menuconfig defconfig - cp $< $@ - # defconfig creates a default config, based on SDKCONFIG_DEFAULTS if present defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) $(summary) DEFCONFIG ifneq ("$(wildcard $(SDKCONFIG_DEFAULTS))","") - cp $(SDKCONFIG_DEFAULTS) $(SDKCONFIG) + cat $(SDKCONFIG_DEFAULTS) >> $(SDKCONFIG) # append defaults to sdkconfig, will override existing values endif mkdir -p $(BUILD_DIR_BASE)/include/config $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig From ca57a86f2091cc55ee40c20e7124089ee8813959 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 9 Jan 2017 16:42:45 +0800 Subject: [PATCH 102/167] Add option to automatically set a watchpoint at the end of the swapped-in task --- components/esp32/include/esp_panic.h | 10 ++++++ components/esp32/panic.c | 48 ++++++++++++++++++++++++++++ components/freertos/Kconfig | 16 ++++++++++ components/freertos/tasks.c | 7 ++++ 4 files changed, 81 insertions(+) diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index 6aba6c5f481..ce4ea3c501c 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -14,8 +14,18 @@ #ifndef __ASSEMBLER__ +#include "esp_err.h" + void esp_set_breakpoint_if_jtag(void *fn); +#define ESP_WATCHPOINT_LOAD 0x40000000 +#define ESP_WATCHPOINT_STORE 0x80000000 +#define ESP_WATCHPOINT_ACCESS 0xC0000000 + +esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags); +void esp_clear_watchpoint(int no); + + #endif #endif \ No newline at end of file diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 3cdbfb3e397..b4a84aee8ea 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -32,6 +32,7 @@ #include "esp_gdbstub.h" #include "esp_panic.h" #include "esp_attr.h" +#include "esp_err.h" /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level @@ -353,3 +354,50 @@ void esp_set_breakpoint_if_jtag(void *fn) setFirstBreakpoint((uint32_t)fn); } } + + +esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags) +{ + int x; + if (no<0 || no>1) return ESP_ERR_INVALID_ARG; + if (flags&(~0xC0000000)) return ESP_ERR_INVALID_ARG; + int dbreakc=0x3F; + //We support watching 2^n byte values, from 1 to 64. Calculate the mask for that. + for (x=0; x<6; x++) { + if (size==(1<pxStack, 32, ESP_WATCHPOINT_STORE); +#endif + + } portEXIT_CRITICAL_NESTED(irqstate); } From 5c1c6d2c32245e017646b1cb17e6da8a603dc553 Mon Sep 17 00:00:00 2001 From: Liu Han Date: Mon, 9 Jan 2017 17:51:48 +0800 Subject: [PATCH 103/167] cJSON:Add float format process --- components/json/include/cJSON.h | 1 + components/json/library/cJSON.c | 1 + 2 files changed, 2 insertions(+) diff --git a/components/json/include/cJSON.h b/components/json/include/cJSON.h index 466d10dbd08..92ed8c3b485 100644 --- a/components/json/include/cJSON.h +++ b/components/json/include/cJSON.h @@ -90,6 +90,7 @@ extern cJSON *cJSON_CreateTrue(void); extern cJSON *cJSON_CreateFalse(void); extern cJSON *cJSON_CreateBool(int b); extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateDouble(double num,int i_num); extern cJSON *cJSON_CreateString(const char *string); extern cJSON *cJSON_CreateArray(void); extern cJSON *cJSON_CreateObject(void); diff --git a/components/json/library/cJSON.c b/components/json/library/cJSON.c index ab3043711e5..2a5c3921619 100644 --- a/components/json/library/cJSON.c +++ b/components/json/library/cJSON.c @@ -694,6 +694,7 @@ cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->ty cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateDouble(double num,int i_num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=i_num;}return item;} cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} From d5464074ddaa3de09d8a9435f3750a5ba15ba507 Mon Sep 17 00:00:00 2001 From: qiyueixa Date: Mon, 9 Jan 2017 18:44:27 +0800 Subject: [PATCH 104/167] update wifi libs --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 6ccb241d457..231ee92755a 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 6ccb241d457f9efddb9cc6b82307335400d7f8f9 +Subproject commit 231ee92755a41c92f6243e0550557ce9e1131744 From 433ff1474ec01d31cd0a4b22157e9ff533d4e7a3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 3 Jan 2017 11:34:23 +1100 Subject: [PATCH 105/167] WiFi software coexistence: Clarify KConfig description Ref http://esp32.com/viewtopic.php?f=13&t=844&p=3576 --- components/esp32/Kconfig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 90cd734bf15..2a27c4df39f 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -39,12 +39,13 @@ config ESP32_ENABLE_STACK_BT #endchoice config SW_COEXIST_ENABLE - bool "Software do control of wifi/bt coexisit" + bool "Software controls WiFi/BT coexistence" depends on ESP32_ENABLE_STACK_BT && ESP32_ENABLE_STACK_WIFI - default "n" - help - Software do something control of wifi/bt coexist. For some heavy traffic senario, - do sotware coexist, may be better. + default "n" + help + If enabled, WiFi & BT coexistence is controlled by software rather than hardware. + Recommended for heavy traffic scenarios. Both coexistence configuration options are + automatically managed, no user intervention is required. config MEMMAP_BT bool From b877216fefa8a49901b1f0f9a083c2a1d5dd5269 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 6 Jan 2017 10:14:32 +1100 Subject: [PATCH 106/167] stdatomic.h: Include stdint.h, required for some limit types --- components/newlib/include/stdatomic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/components/newlib/include/stdatomic.h b/components/newlib/include/stdatomic.h index 09c0cf73e00..beba325b1a2 100644 --- a/components/newlib/include/stdatomic.h +++ b/components/newlib/include/stdatomic.h @@ -32,6 +32,7 @@ #include #include +#include #if __has_extension(c_atomic) || __has_extension(cxx_atomic) #define __CLANG_ATOMICS From a54791846b54d1d24f5728acaf63f1c80b24f627 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 10 Jan 2017 11:12:02 +1100 Subject: [PATCH 107/167] coap: Initialise/update the libcoap submodule --- components/coap/component.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/coap/component.mk b/components/coap/component.mk index b5f6ec43c25..86993b29e90 100644 --- a/components/coap/component.mk +++ b/components/coap/component.mk @@ -7,3 +7,5 @@ COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libc COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_time.o libcoap/src/debug.o libcoap/src/encode.o libcoap/src/hashkey.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o port/coap_io_socket.o COMPONENT_SRCDIRS := libcoap/src libcoap port + +COMPONENT_SUBMODULES += libcoap From 315b3f979ffed5e1beef1e5f515d972394faa2dc Mon Sep 17 00:00:00 2001 From: Liu Han Date: Wed, 7 Dec 2016 15:37:59 +0800 Subject: [PATCH 108/167] components/tcpip_adapter: Allow to set different hostname for each interface --- components/lwip/port/netif/ethernetif.c | 68 +++++++------- components/lwip/port/netif/wlanif.c | 88 +++++++------------ .../tcpip_adapter/include/tcpip_adapter.h | 10 +-- components/tcpip_adapter/tcpip_adapter_lwip.c | 26 +++--- 4 files changed, 82 insertions(+), 110 deletions(-) mode change 100755 => 100644 components/lwip/port/netif/ethernetif.c mode change 100755 => 100644 components/lwip/port/netif/wlanif.c diff --git a/components/lwip/port/netif/ethernetif.c b/components/lwip/port/netif/ethernetif.c old mode 100755 new mode 100644 index 6b1245e2e6d..90a5b241b03 --- a/components/lwip/port/netif/ethernetif.c +++ b/components/lwip/port/netif/ethernetif.c @@ -55,8 +55,6 @@ #define IFNAME0 'e' #define IFNAME1 'n' -static char hostname[16]; - /** * In this function, the hardware should be initialized. * Called from ethernetif_init(). @@ -78,14 +76,13 @@ ethernet_low_level_init(struct netif *netif) /* device capabilities */ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; - + #if ESP_LWIP #if LWIP_IGMP - - netif->flags |= NETIF_FLAG_IGMP; + netif->flags |= NETIF_FLAG_IGMP; #endif #endif - /* Do whatever else is needed to initialize interface. */ + /* Do whatever else is needed to initialize interface. */ } /** @@ -113,30 +110,28 @@ ethernet_low_level_output(struct netif *netif, struct pbuf *p) LWIP_DEBUGF(NETIF_DEBUG,("eth_if=%d netif=%p pbuf=%p len=%d\n", eth_if, netif, p, p->len)); return ERR_IF; - } - + } + #if ESP_LWIP - q = p; - u16_t pbuf_x_len = 0; - pbuf_x_len = q->len; - if(q->next !=NULL) - { - //char cnt = 0; - struct pbuf *tmp = q->next; - while(tmp != NULL) - { - memcpy( (u8_t *)( (u8_t *)(q->payload) + pbuf_x_len), (u8_t *)tmp->payload , tmp->len ); - pbuf_x_len += tmp->len; - //cnt++; - tmp = tmp->next; - } + q = p; + u16_t pbuf_x_len = 0; + pbuf_x_len = q->len; + if(q->next !=NULL) { + //char cnt = 0; + struct pbuf *tmp = q->next; + while(tmp != NULL) { + memcpy( (u8_t *)( (u8_t *)(q->payload) + pbuf_x_len), (u8_t *)tmp->payload , tmp->len ); + pbuf_x_len += tmp->len; + //cnt++; + tmp = tmp->next; } - - return esp_eth_tx(q->payload, pbuf_x_len); + } + + return esp_eth_tx(q->payload, pbuf_x_len); #else - for(q = p; q != NULL; q = q->next) { - return esp_emac_tx(q->payload, q->len); - } + for(q = p; q != NULL; q = q->next) { + return esp_emac_tx(q->payload, q->len); + } return ERR_OK; #endif } @@ -154,9 +149,9 @@ void ethernetif_input(struct netif *netif, void *buffer, uint16_t len) { struct pbuf *p; - + if(buffer== NULL || netif == NULL) - goto _exit; + goto _exit; #if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { @@ -178,7 +173,7 @@ if (netif->input(p, netif) != ERR_OK) { p->payload = buffer; p->user_flag = PBUF_USER_FLAG_OWNER_ETH; p->user_buf = buffer; - + /* full packet send to tcpip_thread to process */ if (netif->input(p, netif) != ERR_OK) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); @@ -187,7 +182,7 @@ if (netif->input(p, netif) != ERR_OK) { } #endif _exit: -; +; } /** @@ -211,14 +206,11 @@ ethernetif_init(struct netif *netif) /* Initialize interface hostname */ #if ESP_LWIP - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; - + netif->hostname = "espressif"; #else - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; + netif->hostname = "lwip"; #endif - + #endif /* LWIP_NETIF_HOSTNAME */ /* @@ -239,7 +231,7 @@ ethernetif_init(struct netif *netif) netif->output_ip6 = ethip6_output; #endif /* LWIP_IPV6 */ netif->linkoutput = ethernet_low_level_output; - + /* initialize the hardware */ ethernet_low_level_init(netif); diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c old mode 100755 new mode 100644 index e114105f307..f9def492182 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -56,8 +56,6 @@ #define IFNAME0 'e' #define IFNAME1 'n' -static char hostname[16]; - /** * In this function, the hardware should be initialized. * Called from ethernetif_init(). @@ -67,11 +65,7 @@ static char hostname[16]; */ static void low_level_init(struct netif *netif) -{ - - - - +{ /* set MAC hardware address length */ netif->hwaddr_len = ETHARP_HWADDR_LEN; @@ -83,18 +77,14 @@ low_level_init(struct netif *netif) /* device capabilities */ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; - + #if ESP_LWIP - #if LWIP_IGMP - - netif->flags |= NETIF_FLAG_IGMP; + netif->flags |= NETIF_FLAG_IGMP; +#endif #endif - - #endif - - /* Do whatever else is needed to initialize interface. */ + /* Do whatever else is needed to initialize interface. */ } /** @@ -115,29 +105,29 @@ low_level_init(struct netif *netif) static err_t low_level_output(struct netif *netif, struct pbuf *p) { - wifi_interface_t wifi_if = tcpip_adapter_get_esp_if(netif); - struct pbuf *q = p; - err_t ret; + wifi_interface_t wifi_if = tcpip_adapter_get_esp_if(netif); + struct pbuf *q = p; + err_t ret; - if (wifi_if >= ESP_IF_MAX) { - return ERR_IF; - } + if (wifi_if >= ESP_IF_MAX) { + return ERR_IF; + } - if(q->next == NULL) { - ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + if(q->next == NULL) { + ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + } else { + LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); + q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); + if (q != NULL) { + pbuf_copy(q, p); } else { - LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); - q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); - if (q != NULL) { - pbuf_copy(q, p); - } else { - return ERR_MEM; - } - ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); - pbuf_free(q); + return ERR_MEM; } - - return ret; + ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + pbuf_free(q); + } + + return ret; } /** @@ -153,9 +143,9 @@ void wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) { struct pbuf *p; - + if(!buffer || !netif) - goto _exit; + goto _exit; #if (ESP_L2_TO_L3_COPY == 1) p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); @@ -182,9 +172,9 @@ wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); pbuf_free(p); } - + _exit: -; +; } /** @@ -208,25 +198,11 @@ wlanif_init(struct netif *netif) /* Initialize interface hostname */ #if ESP_LWIP -//TO_DO -/* - if ((struct netif *)wifi_get_netif(STATION_IF) == netif) { - if (default_hostname == 1) { - wifi_station_set_default_hostname(netif->hwaddr); - } - netif->hostname = hostname; - } else { - netif->hostname = NULL; - } -*/ - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; - + netif->hostname = "espressif"; #else - sprintf(hostname, "ESP_%02X%02X%02X", netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]); - netif->hostname = hostname; + netif->hostname = "lwip"; #endif - + #endif /* LWIP_NETIF_HOSTNAME */ /* @@ -247,7 +223,7 @@ wlanif_init(struct netif *netif) netif->output_ip6 = ethip6_output; #endif /* LWIP_IPV6 */ netif->linkoutput = low_level_output; - + /* initialize the hardware */ low_level_init(netif); diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 861f7ccb8da..07bdc12f913 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -98,7 +98,7 @@ typedef struct { typedef enum { TCPIP_ADAPTER_IF_STA = 0, /**< ESP32 station interface */ TCPIP_ADAPTER_IF_AP, /**< ESP32 soft-AP interface */ - TCPIP_ADAPTER_IF_ETH, /**< ESP32 ethernet interface */ + TCPIP_ADAPTER_IF_ETH, /**< ESP32 ethernet interface */ TCPIP_ADAPTER_IF_MAX } tcpip_adapter_if_t; @@ -126,7 +126,7 @@ typedef enum{ } tcpip_adapter_option_id_t; /** - * @brief Initialize tcpip adpater + * @brief Initialize tcpip adapter * * This will initialize TCPIP stack inside. */ @@ -411,12 +411,12 @@ esp_interface_t tcpip_adapter_get_esp_if(void *dev); */ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list); -#define TCPIP_HOSTNAME_MAX_SIZE 31 +#define TCPIP_HOSTNAME_MAX_SIZE 32 /** * @brief Set the hostname to the interface * * @param[in] tcpip_if: the interface which we will set the hostname - * @param[in] hostname: the host name for set the interfce + * @param[in] hostname: the host name for set the interface, the max length of hostname is 32 bytes * * @return ESP_OK:success * ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY:interface status error @@ -428,7 +428,7 @@ esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *ho * @brief Get the hostname from the interface * * @param[in] tcpip_if: the interface which we will get the hostname - * @param[in] hostname: the host name from the interfce + * @param[in] hostname: the host name from the interface * * @return ESP_OK:success * ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY:interface status error diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 726434b5fa0..03f364fe673 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -706,34 +706,35 @@ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapt esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *hostname) { +#if LWIP_NETIF_HOSTNAME struct netif *p_netif; - static char hostinfo[TCPIP_HOSTNAME_MAX_SIZE + 1]; + static char hostinfo[TCPIP_HOSTNAME_MAX_SIZE + 1][TCPIP_ADAPTER_IF_MAX]; if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || hostname == NULL) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } - if (strlen(hostname) >= TCPIP_HOSTNAME_MAX_SIZE) { + if (strlen(hostname) > TCPIP_HOSTNAME_MAX_SIZE) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } p_netif = esp_netif[tcpip_if]; if (p_netif != NULL) { - if (netif_is_up(p_netif)) { - return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; - } else { - memset(hostinfo, 0, sizeof(hostinfo)); - memcpy(hostinfo, hostname, strlen(hostname)); - p_netif->hostname = hostinfo; - return ESP_OK; - } + memset(hostinfo[tcpip_if], 0, sizeof(hostinfo[tcpip_if])); + memcpy(hostinfo[tcpip_if], hostname, strlen(hostname)); + p_netif->hostname = hostinfo[tcpip_if]; + return ESP_OK; } else { - return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; } +#else + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; +#endif } esp_err_t tcpip_adapter_get_hostname(tcpip_adapter_if_t tcpip_if, const char **hostname) { +#if LWIP_NETIF_HOSTNAME struct netif *p_netif = NULL; if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || hostname == NULL) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; @@ -746,6 +747,9 @@ esp_err_t tcpip_adapter_get_hostname(tcpip_adapter_if_t tcpip_if, const char **h } else { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } +#else + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; +#endif } #endif From 2e78b397bc68ce8beaba95c543689a6d72c778ae Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 10 Jan 2017 16:04:04 +1100 Subject: [PATCH 109/167] config: Simplify WiFi & Bluetooth config options Removes redundant menu options, splits WiFi configuration out from generic ESP32 configuration. --- components/bt/Kconfig | 29 +- components/bt/component.mk | 3 + components/esp32/Kconfig | 304 ++++++++++----------- components/esp32/heap_alloc_caps.c | 2 +- examples/05_ble_adv/sdkconfig.defaults | 7 - examples/12_blufi/sdkconfig.defaults | 7 - examples/14_gatt_server/sdkconfig.defaults | 7 - examples/15_gatt_client/sdkconfig.defaults | 7 - 8 files changed, 150 insertions(+), 216 deletions(-) diff --git a/components/bt/Kconfig b/components/bt/Kconfig index 9a8014fa66a..204ccd1e5e8 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -1,41 +1,26 @@ -menu "BT config" -visible if MEMMAP_BT - +menu "Bluetooth" config BT_ENABLED - bool - depends on ESP32_ENABLE_STACK_BT - help - This compiles in the low-level BT stack. + bool "Enable Bluetooth stack" config BTC_TASK_STACK_SIZE - int "BT event (callback to application) task stack size" + int "Bluetooth event (callback to application) task stack size" + depends on BT_ENABLED default 3072 help This select btc task stack size config BLUEDROID_MEM_DEBUG bool "Bluedroid memory debug" - default no + depends on BT_ENABLED + default n help Bluedroid memory debug -#config BT_BTLE -# bool "Enable BTLE" -# depends on BT_ENABLED -# help -# This compiles BTLE support -# -#config BT_BT -# bool "Enable classic BT" -# depends on BT_ENABLED -# help -# This enables classic BT support - endmenu # Memory reserved at start of DRAM for Bluetooth stack config BT_RESERVE_DRAM hex - default 0x10000 if MEMMAP_BT + default 0x10000 if BT_ENABLED default 0 diff --git a/components/bt/component.mk b/components/bt/component.mk index a51478af933..12cc088412c 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -1,6 +1,7 @@ # # Component Makefile # +ifdef CONFIG_BT_ENABLED COMPONENT_ADD_INCLUDEDIRS := bluedroid/bta/include \ bluedroid/bta/sys/include \ @@ -71,3 +72,5 @@ COMPONENT_SRCDIRS := bluedroid/bta/dm \ . COMPONENT_SUBMODULES += lib + +endif diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 2a27c4df39f..d07d0fe846c 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -20,42 +20,6 @@ config ESP32_DEFAULT_CPU_FREQ_MHZ default 160 if ESP32_DEFAULT_CPU_FREQ_160 default 240 if ESP32_DEFAULT_CPU_FREQ_240 -#choice ESP32_WIFI_OR_BT -# prompt "Select stack to enable (WiFi or BT)" -# default ESP32_ENABLE_WIFI -# help -# Temporarily, WiFi and BT stacks can not be used at the same time. -# Select which stack to enable. - -config ESP32_ENABLE_STACK_WIFI - bool "WiFi" - select WIFI_ENABLED if ESP32_ENABLE_STACK_WIFI -config ESP32_ENABLE_STACK_BT - bool "BT" - select MEMMAP_BT if ESP32_ENABLE_STACK_BT - select BT_ENABLED if ESP32_ENABLE_STACK_BT -#config ESP32_ENABLE_STACK_NONE -# bool "None" -#endchoice - -config SW_COEXIST_ENABLE - bool "Software controls WiFi/BT coexistence" - depends on ESP32_ENABLE_STACK_BT && ESP32_ENABLE_STACK_WIFI - default "n" - help - If enabled, WiFi & BT coexistence is controlled by software rather than hardware. - Recommended for heavy traffic scenarios. Both coexistence configuration options are - automatically managed, no user intervention is required. - -config MEMMAP_BT - bool - depends on ESP32_ENABLE_STACK_BT - help - The Bluetooth stack uses memory that cannot be used as generic memory anymore. This - reserves the space for that within the memory map of the compiled binary. - This option is required to enable BT stack. - Temporarily, this option is not compatible with WiFi stack. - config MEMMAP_SMP bool "Reserve memory for two cores" default "y" @@ -96,19 +60,10 @@ config MEMMAP_SPISRAM depends on ESP32_NEEDS_NEW_SILICON_REV default "n" help - The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the + The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the main memory map. Enable this if you have this hardware and want to use it in the same way as on-chip RAM. -config WIFI_ENABLED - bool - default "y" - depends on ESP32_ENABLE_STACK_WIFI - help - This compiles in the low-level WiFi stack. - - Temporarily, this option is not compatible with BT stack. - config SYSTEM_EVENT_QUEUE_SIZE int "System event queue size" default 32 @@ -146,7 +101,7 @@ config NEWLIB_NANO_FORMAT ESP32 ROM contains parts of newlib C library, including printf/scanf family of functions. These functions have been compiled with so-called "nano" formatting option. This option doesn't support 64-bit integer formats and C99 - features, such as positional arguments. + features, such as positional arguments. For more details about "nano" formatting option, please see newlib readme file, search for '--enable-newlib-nano-formatted-io': @@ -181,42 +136,42 @@ config CONSOLE_UART_NONE endchoice choice CONSOLE_UART_NUM - prompt "UART peripheral to use for console output (0-1)" - depends on CONSOLE_UART_CUSTOM + prompt "UART peripheral to use for console output (0-1)" + depends on CONSOLE_UART_CUSTOM default CONSOLE_UART_CUSTOM_NUM_0 help - Due of a ROM bug, UART2 is not supported for console output - via ets_printf. + Due of a ROM bug, UART2 is not supported for console output + via ets_printf. config CONSOLE_UART_CUSTOM_NUM_0 - bool "UART0" + bool "UART0" config CONSOLE_UART_CUSTOM_NUM_1 - bool "UART1" + bool "UART1" endchoice config CONSOLE_UART_NUM - int - default 0 if CONSOLE_UART_DEFAULT || CONSOLE_UART_NONE - default 0 if CONSOLE_UART_CUSTOM_NUM_0 - default 1 if CONSOLE_UART_CUSTOM_NUM_1 + int + default 0 if CONSOLE_UART_DEFAULT || CONSOLE_UART_NONE + default 0 if CONSOLE_UART_CUSTOM_NUM_0 + default 1 if CONSOLE_UART_CUSTOM_NUM_1 config CONSOLE_UART_TX_GPIO - int "UART TX on GPIO#" - depends on CONSOLE_UART_CUSTOM - range 0 33 - default 19 + int "UART TX on GPIO#" + depends on CONSOLE_UART_CUSTOM + range 0 33 + default 19 config CONSOLE_UART_RX_GPIO - int "UART RX on GPIO#" - depends on CONSOLE_UART_CUSTOM - range 0 39 - default 21 + int "UART RX on GPIO#" + depends on CONSOLE_UART_CUSTOM + range 0 39 + default 21 config CONSOLE_UART_BAUDRATE - int "UART console baud rate" - depends on !CONSOLE_UART_NONE - default 115200 - range 1200 4000000 + int "UART console baud rate" + depends on !CONSOLE_UART_NONE + default 115200 + range 1200 4000000 config ULP_COPROC_ENABLED bool "Enable Ultra Low Power (ULP) Coprocessor" @@ -247,13 +202,13 @@ choice ESP32_PANIC prompt "Panic handler behaviour" default ESP32_PANIC_PRINT_REBOOT help - If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is + If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is invoked. Configure the panic handlers action here. config ESP32_PANIC_PRINT_HALT bool "Print registers and halt" help - Outputs the relevant registers over the serial port and halt the + Outputs the relevant registers over the serial port and halt the processor. Needs a manual reset to restart. config ESP32_PANIC_PRINT_REBOOT @@ -336,8 +291,8 @@ config TASK_WDT_CHECK_IDLE_TASK help With this turned on, the task WDT can detect if the idle task is not called within the task watchdog timeout period. The idle task not being called usually is a symptom of another - task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the - idle task getting some runtime every now and then. Take Care: With this disabled, this + task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the + idle task getting some runtime every now and then. Take Care: With this disabled, this watchdog will trigger if no tasks register themselves within the timeout value. config TASK_WDT_CHECK_IDLE_TASK_CPU1 @@ -354,7 +309,7 @@ config BROWNOUT_DET default y depends on NEEDS_ESP32_NEW_SILICON_REV help - The ESP32 has a built-in brownout detector which can detect if the voltage is lower than + The ESP32 has a built-in brownout detector which can detect if the voltage is lower than a specific value. If this happens, it will reset the chip in order to prevent unintended behaviour. @@ -408,25 +363,25 @@ config BROWNOUT_DET_RESETDELAY choice ESP32_TIME_SYSCALL - prompt "Timers used for gettimeofday function" - default ESP32_TIME_SYSCALL_USE_RTC_FRC1 - help - This setting defines which hardware timers are used to - implement 'gettimeofday' and 'time' functions in C library. - - - If only FRC1 timer is used, gettimeofday will provide time at - microsecond resolution. Time will not be preserved when going - into deep sleep mode. - - If both FRC1 and RTC timers are used, timekeeping will - continue in deep sleep. Time will be reported at 1 microsecond - resolution. - - If only RTC timer is used, timekeeping will continue in - deep sleep, but time will be measured at 6.(6) microsecond - resolution. Also the gettimeofday function itself may take - longer to run. - - If no timers are used, gettimeofday and time functions - return -1 and set errno to ENOSYS. - + prompt "Timers used for gettimeofday function" + default ESP32_TIME_SYSCALL_USE_RTC_FRC1 + help + This setting defines which hardware timers are used to + implement 'gettimeofday' and 'time' functions in C library. + + - If only FRC1 timer is used, gettimeofday will provide time at + microsecond resolution. Time will not be preserved when going + into deep sleep mode. + - If both FRC1 and RTC timers are used, timekeeping will + continue in deep sleep. Time will be reported at 1 microsecond + resolution. + - If only RTC timer is used, timekeeping will continue in + deep sleep, but time will be measured at 6.(6) microsecond + resolution. Also the gettimeofday function itself may take + longer to run. + - If no timers are used, gettimeofday and time functions + return -1 and set errno to ENOSYS. + config ESP32_TIME_SYSCALL_USE_RTC bool "RTC" config ESP32_TIME_SYSCALL_USE_RTC_FRC1 @@ -438,85 +393,104 @@ config ESP32_TIME_SYSCALL_USE_NONE endchoice choice ESP32_RTC_CLOCK_SOURCE - prompt "RTC clock source" - default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC - help - Choose which clock is used as RTC clock source. - The only available option for now is to use internal - 150kHz RC oscillator. + prompt "RTC clock source" + default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC + help + Choose which clock is used as RTC clock source. + The only available option for now is to use internal + 150kHz RC oscillator. config ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC - bool "Internal RC" + bool "Internal RC" config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL - bool "External 32kHz crystal" - depends on DOCUMENTATION_FOR_RTC_CNTL + bool "External 32kHz crystal" + depends on DOCUMENTATION_FOR_RTC_CNTL endchoice - config ESP32_DEEP_SLEEP_WAKEUP_DELAY - int "Extra delay in deep sleep wake stub (in us)" - default 0 - range 0 5000 - help - When ESP32 exits deep sleep, the CPU and the flash chip are powered on - at the same time. CPU will run deep sleep stub first, and then - proceed to load code from flash. Some flash chips need sufficient - time to pass between power on and first read operation. By default, - without any extra delay, this time is approximately 900us. - - If you are using a flash chip which needs more than 900us to become - ready after power on, set this parameter to add extra delay - to the default deep sleep stub. - - If you are seeing "flash read err, 1000" message printed to the - console after deep sleep reset, try increasing this value. + int "Extra delay in deep sleep wake stub (in us)" + default 0 + range 0 5000 + help + When ESP32 exits deep sleep, the CPU and the flash chip are powered on + at the same time. CPU will run deep sleep stub first, and then + proceed to load code from flash. Some flash chips need sufficient + time to pass between power on and first read operation. By default, + without any extra delay, this time is approximately 900us. + If you are using a flash chip which needs more than 900us to become + ready after power on, set this parameter to add extra delay + to the default deep sleep stub. -config ESP32_PHY_AUTO_INIT - bool "Initialize PHY in startup code" - default y - help - If enabled, PHY will be initialized in startup code, before - app_main function runs. - If this is undesired, disable this option and call esp_phy_init - from the application before enabling WiFi or BT. - - If this option is enabled, startup code will also initialize - NVS prior to initializing PHY. - - If unsure, choose 'y'. - -config ESP32_PHY_INIT_DATA_IN_PARTITION - bool "Use a partition to store PHY init data" - default n - help - If enabled, PHY init data will be loaded from a partition. - When using a custom partition table, make sure that PHY data - partition is included (type: 'data', subtype: 'phy'). - With default partition tables, this is done automatically. - If PHY init data is stored in a partition, it has to be flashed there, - otherwise runtime error will occur. - - If this option is not enabled, PHY init data will be embedded - into the application binary. - - If unsure, choose 'n'. - -config ESP32_PHY_MAX_TX_POWER - int "Max TX power (dBm)" - range 0 20 - default 20 - help - Set maximum transmit power. Actual transmit power for high - data rates may be lower than this setting. - -config ESP32_WIFI_RX_BUFFER_NUM - int "Max number of WiFi RX buffers" - range 2 25 - default 25 - help - Set the number of WiFi rx buffers. Each buffer takes approximately 1.6KB of RAM. - Larger number for higher throughput but more memory. Smaller number for lower - throughput but less memory. + If you are seeing "flash read err, 1000" message printed to the + console after deep sleep reset, try increasing this value. endmenu + +menu "WiFi" + +config WIFI_ENABLED + bool "Enable WiFi stack" + default y + +config SW_COEXIST_ENABLE + bool "Software controls WiFi/Bluetooth coexistence" + depends on WIFI_ENABLED && BT_ENABLED + default n + help + If enabled, WiFi & Bluetooth coexistence is controlled by software rather than hardware. + Recommended for heavy traffic scenarios. Both coexistence configuration options are + automatically managed, no user intervention is required. + +config ESP32_PHY_AUTO_INIT + bool "Initialize PHY in startup code" + depends on WIFI_ENABLED + default y + help + If enabled, PHY will be initialized in startup code, before + app_main function runs. + If this is undesired, disable this option and call esp_phy_init + from the application before enabling WiFi or BT. + + If this option is enabled, startup code will also initialize + NVS prior to initializing PHY. + + If unsure, choose 'y'. + +config ESP32_PHY_INIT_DATA_IN_PARTITION + bool "Use a partition to store PHY init data" + depends on WIFI_ENABLED + default n + help + If enabled, PHY init data will be loaded from a partition. + When using a custom partition table, make sure that PHY data + partition is included (type: 'data', subtype: 'phy'). + With default partition tables, this is done automatically. + If PHY init data is stored in a partition, it has to be flashed there, + otherwise runtime error will occur. + + If this option is not enabled, PHY init data will be embedded + into the application binary. + + If unsure, choose 'n'. + +config ESP32_PHY_MAX_TX_POWER + int "Max TX power (dBm)" + range 0 20 + default 20 + depends on WIFI_ENABLED + help + Set maximum transmit power. Actual transmit power for high + data rates may be lower than this setting. + +config ESP32_WIFI_RX_BUFFER_NUM + int "Max number of WiFi RX buffers" + depends on WIFI_ENABLED + range 2 25 + default 25 + help + Set the number of WiFi rx buffers. Each buffer takes approximately 1.6KB of RAM. + Larger number for higher throughput but more memory. Smaller number for lower + throughput but less memory. + +endmenu \ No newline at end of file diff --git a/components/esp32/heap_alloc_caps.c b/components/esp32/heap_alloc_caps.c index a89455835c3..a4ff870f390 100644 --- a/components/esp32/heap_alloc_caps.c +++ b/components/esp32/heap_alloc_caps.c @@ -192,7 +192,7 @@ void heap_alloc_caps_init() { // TODO: this region should be checked, since we don't need to knock out all region finally disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe8000); //knock out ROM data region -#if CONFIG_MEMMAP_BT +#if CONFIG_BT_ENABLED disable_mem_region((void*)0x3ffb0000, (void*)0x3ffc0000); //knock out BT data region #endif diff --git a/examples/05_ble_adv/sdkconfig.defaults b/examples/05_ble_adv/sdkconfig.defaults index e435f383c81..dcf4ad2c2d1 100644 --- a/examples/05_ble_adv/sdkconfig.defaults +++ b/examples/05_ble_adv/sdkconfig.defaults @@ -5,10 +5,3 @@ # BT config # CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y diff --git a/examples/12_blufi/sdkconfig.defaults b/examples/12_blufi/sdkconfig.defaults index e435f383c81..dcf4ad2c2d1 100644 --- a/examples/12_blufi/sdkconfig.defaults +++ b/examples/12_blufi/sdkconfig.defaults @@ -5,10 +5,3 @@ # BT config # CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y diff --git a/examples/14_gatt_server/sdkconfig.defaults b/examples/14_gatt_server/sdkconfig.defaults index e435f383c81..dcf4ad2c2d1 100644 --- a/examples/14_gatt_server/sdkconfig.defaults +++ b/examples/14_gatt_server/sdkconfig.defaults @@ -5,10 +5,3 @@ # BT config # CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y diff --git a/examples/15_gatt_client/sdkconfig.defaults b/examples/15_gatt_client/sdkconfig.defaults index e435f383c81..dcf4ad2c2d1 100644 --- a/examples/15_gatt_client/sdkconfig.defaults +++ b/examples/15_gatt_client/sdkconfig.defaults @@ -5,10 +5,3 @@ # BT config # CONFIG_BT_ENABLED=y - -# -# ESP32-specific config -# -CONFIG_ESP32_ENABLE_STACK_BT=y -# CONFIG_ESP32_ENABLE_STACK_NONE is not set -CONFIG_MEMMAP_BT=y From 881157e1ed5e7e789be9ba6f4cb63fd4f7232961 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 10 Jan 2017 13:05:19 +0800 Subject: [PATCH 110/167] Add documentation to panic handler functions, move watchpoint stuff from tasks.c to port.c, account for non-32-bytes-aligned stacks when setting watchpoint, add debug reason explanation to panic handler --- components/esp32/include/esp_panic.h | 33 ++++++++++++++++- components/esp32/panic.c | 37 +++++++++++++++++-- .../freertos/include/freertos/portable.h | 7 ++++ components/freertos/port.c | 16 ++++++++ components/freertos/tasks.c | 3 +- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index ce4ea3c501c..aa83c6d3811 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -16,16 +16,47 @@ #include "esp_err.h" + +/** + * @brief If an OCD is connected over JTAG. set breakpoint 0 to the given function + * address. Do nothing otherwise. + * @param data Pointer to the target breakpoint position + */ + void esp_set_breakpoint_if_jtag(void *fn); #define ESP_WATCHPOINT_LOAD 0x40000000 #define ESP_WATCHPOINT_STORE 0x80000000 #define ESP_WATCHPOINT_ACCESS 0xC0000000 +/** + * @brief Set a watchpoint to break/panic when a certain memory range is accessed. + * + * @param no Watchpoint number. On the ESP32, this can be 0 or 1. + * @param adr Base address to watch + * @param size Size of the region, starting at the base address, to watch. Must + * be one of 2^n, with n in [0..6]. + * @param flags One of ESP_WATCHPOINT_* flags + * + * @return ESP_ERR_INVALID_ARG on invalid arg, ESP_OK otherwise + * + * @warning The ESP32 watchpoint hardware watches a region of bytes by effectively + * masking away the lower n bits for a region with size 2^n. If adr does + * not have zero for these lower n bits, you may not be watching the + * region you intended. + */ esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags); + + +/** + * @brief Clear a watchpoint + * + * @param no Watchpoint to clear + * + */ void esp_clear_watchpoint(int no); #endif -#endif \ No newline at end of file +#endif diff --git a/components/esp32/panic.c b/components/esp32/panic.c index b4a84aee8ea..c5b18870a93 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -164,8 +164,37 @@ void panicHandler(XtExcFrame *frame) panicPutStr("Guru Meditation Error: Core "); panicPutDec(xPortGetCoreID()); panicPutStr(" panic'ed ("); - panicPutStr(reason); - panicPutStr(")\r\n"); + if (!abort_called) { + panicPutStr(reason); + panicPutStr(")\r\n"); + if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) { + int debugRsn; + asm("rsr.debugcause %0":"=r"(debugRsn)); + panicPutStr("Debug exception reason: "); + if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep "); + if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint "); + if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) { + //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK + //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the + //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0. + if (debugRsn&(1<<8)) { +#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + panicPutStr("Stack canary watchpoint triggered "); +#else + panicPutStr("Watchpoint 1 triggered "); +#endif + } else { + panicPutStr("Watchpoint 0 triggered "); + } + } + if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr "); + panicPutStr("\r\n"); + } + } else { + panicPutStr("abort)\r\n"); + } if (esp_cpu_in_ocd_debug_mode()) { asm("break.n 1"); @@ -363,11 +392,11 @@ esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags) if (flags&(~0xC0000000)) return ESP_ERR_INVALID_ARG; int dbreakc=0x3F; //We support watching 2^n byte values, from 1 to 64. Calculate the mask for that. - for (x=0; x<6; x++) { + for (x=0; x<7; x++) { if (size==(1<pxStack, 32, ESP_WATCHPOINT_STORE); + vPortSetStackWatchpoint(pxCurrentTCB[xPortGetCoreID()]->pxStack); #endif From 26d1a23308bbe61ed0d9f7a1da3657f0bafcf009 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 10 Jan 2017 16:39:43 +1100 Subject: [PATCH 111/167] config: Move WiFi & BT toggles to Components menu, same as Ethernet --- components/bt/Kconfig | 10 ++++------ components/esp32/Kconfig | 10 ++++------ components/ethernet/Kconfig | 4 ++-- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/components/bt/Kconfig b/components/bt/Kconfig index 204ccd1e5e8..d2227868cef 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -1,7 +1,7 @@ -menu "Bluetooth" - -config BT_ENABLED - bool "Enable Bluetooth stack" +menuconfig BT_ENABLED + bool "Bluetooth" + help + Select this option to enable Bluetooth stack and show the submenu with Bluetooth configuration choices. config BTC_TASK_STACK_SIZE int "Bluetooth event (callback to application) task stack size" @@ -17,8 +17,6 @@ config BLUEDROID_MEM_DEBUG help Bluedroid memory debug -endmenu - # Memory reserved at start of DRAM for Bluetooth stack config BT_RESERVE_DRAM hex diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index d07d0fe846c..bc35ca0e8f6 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -427,11 +427,11 @@ config ESP32_DEEP_SLEEP_WAKEUP_DELAY endmenu -menu "WiFi" - -config WIFI_ENABLED - bool "Enable WiFi stack" +menuconfig WIFI_ENABLED + bool "WiFi" default y + help + Select this option to enable WiFi stack and show the submenu with WiFi configuration choices. config SW_COEXIST_ENABLE bool "Software controls WiFi/Bluetooth coexistence" @@ -492,5 +492,3 @@ config ESP32_WIFI_RX_BUFFER_NUM Set the number of WiFi rx buffers. Each buffer takes approximately 1.6KB of RAM. Larger number for higher throughput but more memory. Smaller number for lower throughput but less memory. - -endmenu \ No newline at end of file diff --git a/components/ethernet/Kconfig b/components/ethernet/Kconfig index 9eb897cbba1..0ea94eaafd6 100644 --- a/components/ethernet/Kconfig +++ b/components/ethernet/Kconfig @@ -1,8 +1,8 @@ menuconfig ETHERNET - bool "Enable Ethernet" + bool "Ethernet" default n help - Enable this option to enable ethernet driver and show the menu with ethernet features. + Select this option to enable ethernet driver and show the submenu with ethernet features. config DMA_RX_BUF_NUM int "DMA Rx Buf Num" From 5eb8eb385599ee459655516e10ca168309e5a5e0 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 10 Jan 2017 14:41:12 +0800 Subject: [PATCH 112/167] SPI master: rename transaction flags from SPI_* to SPI_TRANS_*, like the documentation says. Also add some explanation about the SPI signals in the documentation --- components/driver/include/driver/spi_master.h | 10 +++---- components/driver/spi_master.c | 28 +++++++++---------- docs/api/spi_master.rst | 21 ++++++++++---- examples/26_spi_master/main/spi_master.c | 4 +-- 4 files changed, 37 insertions(+), 26 deletions(-) diff --git a/components/driver/include/driver/spi_master.h b/components/driver/include/driver/spi_master.h index 4dd8738ad70..ea901b9924d 100644 --- a/components/driver/include/driver/spi_master.h +++ b/components/driver/include/driver/spi_master.h @@ -86,11 +86,11 @@ typedef struct { } spi_device_interface_config_t; -#define SPI_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode -#define SPI_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode -#define SPI_MODE_DIOQIO_ADDR (1<<2) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO -#define SPI_USE_RXDATA (1<<2) ///< Receive into rx_data member of spi_transaction_t instead into memory at rx_buffer. -#define SPI_USE_TXDATA (1<<3) ///< Transmit tx_data member of spi_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this. +#define SPI_TRANS_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode +#define SPI_TRANS_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode +#define SPI_TRANS_MODE_DIOQIO_ADDR (1<<2) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO +#define SPI_TRANS_USE_RXDATA (1<<2) ///< Receive into rx_data member of spi_transaction_t instead into memory at rx_buffer. +#define SPI_TRANS_USE_TXDATA (1<<3) ///< Transmit tx_data member of spi_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this. /** * This structure describes one SPI transaction diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index fd4c5b2e299..1f1ea1ef083 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -452,10 +452,10 @@ static void IRAM_ATTR spi_intr(void *arg) if (host->cur_trans) { //Okay, transaction is done. - if ((host->cur_trans->rx_buffer || (host->cur_trans->flags & SPI_USE_RXDATA)) && host->cur_trans->rxlength<=THRESH_DMA_TRANS) { + if ((host->cur_trans->rx_buffer || (host->cur_trans->flags & SPI_TRANS_USE_RXDATA)) && host->cur_trans->rxlength<=THRESH_DMA_TRANS) { //Need to copy from SPI regs to result buffer. uint32_t *data; - if (host->cur_trans->flags & SPI_USE_RXDATA) { + if (host->cur_trans->flags & SPI_TRANS_USE_RXDATA) { data=(uint32_t*)&host->cur_trans->rx_data[0]; } else { data=(uint32_t*)host->cur_trans->rx_buffer; @@ -557,8 +557,8 @@ static void IRAM_ATTR spi_intr(void *arg) //QIO/DIO host->hw->ctrl.val &= ~(SPI_FREAD_DUAL|SPI_FREAD_QUAD|SPI_FREAD_DIO|SPI_FREAD_QIO); host->hw->user.val &= ~(SPI_FWRITE_DUAL|SPI_FWRITE_QUAD|SPI_FWRITE_DIO|SPI_FWRITE_QIO); - if (trans->flags & SPI_MODE_DIO) { - if (trans->flags & SPI_MODE_DIOQIO_ADDR) { + if (trans->flags & SPI_TRANS_MODE_DIO) { + if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { host->hw->ctrl.fread_dio=1; host->hw->user.fwrite_dio=1; } else { @@ -566,8 +566,8 @@ static void IRAM_ATTR spi_intr(void *arg) host->hw->user.fwrite_dual=1; } host->hw->ctrl.fastrd_mode=1; - } else if (trans->flags & SPI_MODE_QIO) { - if (trans->flags & SPI_MODE_DIOQIO_ADDR) { + } else if (trans->flags & SPI_TRANS_MODE_QIO) { + if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { host->hw->ctrl.fread_qio=1; host->hw->user.fwrite_qio=1; } else { @@ -579,9 +579,9 @@ static void IRAM_ATTR spi_intr(void *arg) //Fill DMA descriptors - if (trans->rx_buffer || (trans->flags & SPI_USE_RXDATA)) { + if (trans->rx_buffer || (trans->flags & SPI_TRANS_USE_RXDATA)) { uint32_t *data; - if (trans->flags & SPI_USE_RXDATA) { + if (trans->flags & SPI_TRANS_USE_RXDATA) { data=(uint32_t *)&trans->rx_data[0]; } else { data=trans->rx_buffer; @@ -604,9 +604,9 @@ static void IRAM_ATTR spi_intr(void *arg) host->hw->user.usr_miso=0; } - if (trans->tx_buffer || (trans->flags & SPI_USE_TXDATA)) { + if (trans->tx_buffer || (trans->flags & SPI_TRANS_USE_TXDATA)) { uint32_t *data; - if (trans->flags & SPI_USE_TXDATA) { + if (trans->flags & SPI_TRANS_USE_TXDATA) { data=(uint32_t *)&trans->tx_data[0]; } else { data=(uint32_t *)trans->tx_buffer; @@ -657,10 +657,10 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t * { BaseType_t r; SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); - SPI_CHECK((trans_desc->flags & SPI_USE_RXDATA)==0 ||trans_desc->length <= 32, "rxdata transfer > 32bytes", ESP_ERR_INVALID_ARG); - SPI_CHECK((trans_desc->flags & SPI_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32bytes", ESP_ERR_INVALID_ARG); - SPI_CHECK(!((trans_desc->flags & (SPI_MODE_DIO|SPI_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG); - SPI_CHECK(!((trans_desc->flags & (SPI_MODE_DIO|SPI_MODE_QIO)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 ||trans_desc->length <= 32, "rxdata transfer > 32bytes", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32bytes", ESP_ERR_INVALID_ARG); + SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG); + SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG); r=xQueueSend(handle->trans_queue, (void*)&trans_desc, ticks_to_wait); if (!r) return ESP_ERR_TIMEOUT; esp_intr_enable(handle->host->intr); diff --git a/docs/api/spi_master.rst b/docs/api/spi_master.rst index 99f2f6a7ca9..4d7693c07cb 100644 --- a/docs/api/spi_master.rst +++ b/docs/api/spi_master.rst @@ -29,6 +29,17 @@ The spi_master driver uses the following terms: * Bus: The SPI bus, common to all SPI devices connected to one host. In general the bus consists of the spid, spiq, spiclk and optionally spiwp and spihd signals. The SPI slaves are connected to these signals in parallel. + + - spiq - Also known as MISO, this is the input of the serial stream into the ESP32 + + - spid - Also known as MOSI, this is the output of the serial stream from the ESP32 + + - spiclk - Clock signal. Each data bit is clocked out or in on the positive or negative edge of this signal + + - spiwp - Write Protect signal. Only used for 4-bit (qio/qout) transactions. + + - spihd - Hold signal. Only used for 4-bit (qio/qout) transactions. + * Device: A SPI slave. Each SPI slave has its own chip select (CS) line, which is made active when a transmission to/from the SPI slave occurs. * Transaction: One instance of CS going active, data transfer from and/or to a device happening, and @@ -113,11 +124,11 @@ Macros .. doxygendefine:: SPI_DEVICE_HALFDUPLEX .. doxygendefine:: SPI_DEVICE_CLK_AS_CS -.. doxygendefine:: SPI_MODE_DIO -.. doxygendefine:: SPI_MODE_QIO -.. doxygendefine:: SPI_MODE_DIOQIO_ADDR -.. doxygendefine:: SPI_USE_RXDATA -.. doxygendefine:: SPI_USE_TXDATA +.. doxygendefine:: SPI_TRANS_MODE_DIO +.. doxygendefine:: SPI_TRANS_MODE_QIO +.. doxygendefine:: SPI_TRANS_MODE_DIOQIO_ADDR +.. doxygendefine:: SPI_TRANS_USE_RXDATA +.. doxygendefine:: SPI_TRANS_USE_TXDATA Type Definitions ^^^^^^^^^^^^^^^^ diff --git a/examples/26_spi_master/main/spi_master.c b/examples/26_spi_master/main/spi_master.c index 1cdbf72ad55..2013dfd9c95 100644 --- a/examples/26_spi_master/main/spi_master.c +++ b/examples/26_spi_master/main/spi_master.c @@ -168,7 +168,7 @@ void send_line(spi_device_handle_t spi, int ypos, uint16_t *line) trans[x].length=8*4; trans[x].user=(void*)1; } - trans[x].flags=SPI_USE_TXDATA; + trans[x].flags=SPI_TRANS_USE_TXDATA; } trans[0].tx_data[0]=0x2A; //Column Address Set trans[1].tx_data[0]=0; //Start Col High @@ -183,7 +183,7 @@ void send_line(spi_device_handle_t spi, int ypos, uint16_t *line) trans[4].tx_data[0]=0x2C; //memory write trans[5].tx_buffer=line; //finally send the line data trans[5].length=320*2*8; //Data length, in bits - trans[5].flags=0; //undo SPI_USE_TXDATA flag + trans[5].flags=0; //undo SPI_TRANS_USE_TXDATA flag //Queue all transactions. for (x=0; x<6; x++) { From 0e701e1caccff50c6ed91f8cfd2a9b359cdbf35c Mon Sep 17 00:00:00 2001 From: shangke Date: Fri, 6 Jan 2017 13:49:42 +0800 Subject: [PATCH 113/167] ethernet: support flow control --- components/esp32/include/soc/dport_reg.h | 18 ++- components/esp32/phy_init.c | 4 +- components/esp32/system_api.c | 10 +- components/ethernet/emac_common.h | 10 ++ components/ethernet/emac_dev.c | 31 ++++- components/ethernet/emac_dev.h | 21 +++- components/ethernet/emac_main.c | 137 ++++++++++++++++------ components/ethernet/include/esp_eth.h | 4 + docs/api/esp_eth.rst | 1 + examples/17_ethernet/main/ethernet_main.c | 25 +++- examples/17_ethernet/main/tlk110_phy.h | 10 +- 11 files changed, 211 insertions(+), 60 deletions(-) diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index f84346717c3..ef231e316ed 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -1035,14 +1035,20 @@ #define DPORT_WIFI_CLK_EN_V 0xFFFFFFFF #define DPORT_WIFI_CLK_EN_S 0 -#define DPORT_WIFI_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) -/* DPORT_WIFI_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ +#define DPORT_CORE_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) +/* DPORT_CORE_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: */ +#define DPROT_RW_BTLP_RST (BIT(10)) +#define DPROT_RW_BTMAC_RST (BIT(9)) +#define DPORT_MACPWR_RST (BIT(8)) +#define DPORT_EMAC_RST (BIT(7)) +#define DPORT_SDIO_HOST_RST (BIT(6)) +#define DPORT_SDIO_RST (BIT(5)) +#define DPORT_BTMAC_RST (BIT(4)) +#define DPORT_BT_RST (BIT(3)) #define DPORT_MAC_RST (BIT(2)) -#define DPORT_WIFI_RST 0xFFFFFFFF -#define DPORT_WIFI_RST_M ((DPORT_WIFI_RST_V)<<(DPORT_WIFI_RST_S)) -#define DPORT_WIFI_RST_V 0xFFFFFFFF -#define DPORT_WIFI_RST_S 0 +#define DPORT_FE_RST (BIT(1)) +#define DPORT_BB_RST (BIT(0)) #define DPORT_BT_LPCK_DIV_INT_REG (DR_REG_DPORT_BASE + 0x0D4) /* DPORT_BTEXTWAKEUP_REQ : R/W ;bitpos:[12] ;default: 1'b0 ; */ diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index 6154beae908..bfc5a15f5ed 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -39,8 +39,8 @@ esp_err_t esp_phy_init(const esp_phy_init_data_t* init_data, assert(calibration_data); // Initialize PHY pointer table phy_get_romfunc_addr(); - REG_SET_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST); - REG_CLR_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST); + REG_SET_BIT(DPORT_CORE_RST_EN_REG, DPORT_MAC_RST); + REG_CLR_BIT(DPORT_CORE_RST_EN_REG, DPORT_MAC_RST); // Enable WiFi peripheral clock SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf); ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index 63a28de5334..60fa1796cdc 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -111,9 +111,13 @@ void IRAM_ATTR esp_restart(void) uart_tx_wait_idle(1); uart_tx_wait_idle(2); - // Reset wifi/bluetooth (bb/mac) - SET_PERI_REG_MASK(DPORT_WIFI_RST_EN_REG, 0x1f); - REG_WRITE(DPORT_WIFI_RST_EN_REG, 0); + // Reset wifi/bluetooth/ethernet/sdio (bb/mac) + SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG, + DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST | + DPORT_BT_RST | DPORT_BTMAC_RST | DPORT_SDIO_RST | + DPORT_SDIO_HOST_RST | DPORT_EMAC_RST | DPORT_MACPWR_RST | + DPROT_RW_BTMAC_RST | DPROT_RW_BTLP_RST); + REG_WRITE(DPORT_CORE_RST_EN_REG, 0); // Reset timer/spi/uart SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, diff --git a/components/ethernet/emac_common.h b/components/ethernet/emac_common.h index 774c287ad5a..957337d2a78 100644 --- a/components/ethernet/emac_common.h +++ b/components/ethernet/emac_common.h @@ -72,6 +72,9 @@ struct emac_config_data { eth_phy_check_init_func emac_phy_check_init; eth_phy_get_speed_mode_func emac_phy_get_speed_mode; eth_phy_get_duplex_mode_func emac_phy_get_duplex_mode; + bool emac_flow_ctrl_enable; + bool emac_flow_ctrl_partner_support; + eth_phy_get_partner_pause_enable_func emac_phy_get_partner_pause_enable; }; enum emac_post_type { @@ -109,6 +112,13 @@ struct emac_close_cmd { #define DMA_RX_BUF_SIZE 1600 #define DMA_TX_BUF_SIZE 1600 +//rest buf num +#define FLOW_CONTROL_HIGH_WATERMARK 3 +//used buf num +#define FLOW_CONTROL_LOW_WATERMARK 6 + +#define PHY_LINK_CHECK_NUM 5 + #define EMAC_CMD_OK 0 #define EMAC_CMD_FAIL -1 diff --git a/components/ethernet/emac_dev.c b/components/ethernet/emac_dev.c index 244da084323..ba61bb750b6 100644 --- a/components/ethernet/emac_dev.c +++ b/components/ethernet/emac_dev.c @@ -34,6 +34,24 @@ static const char *TAG = "emac"; +void emac_enable_flowctrl(void) +{ + REG_SET_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_TRANSMIT_FLOW_CONTROL_ENABLE); + REG_SET_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_RECEIVE_FLOW_CONTROL_ENABLE); + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_DISABLE_ZERO_QUANTA_PAUSE); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_TIME, 0x1648); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_LOW_THRESHOLD, 0x1); +} + +void emac_disable_flowctrl(void) +{ + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_TRANSMIT_FLOW_CONTROL_ENABLE); + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_RECEIVE_FLOW_CONTROL_ENABLE); + REG_CLR_BIT(EMAC_GMACFLOWCONTROL_REG, EMAC_DISABLE_ZERO_QUANTA_PAUSE); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_TIME, 0); + REG_SET_FIELD(EMAC_GMACFLOWCONTROL_REG, EMAC_PAUSE_LOW_THRESHOLD, 0); +} + void emac_enable_dma_tx(void) { REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_START_STOP_TRANSMISSION_COMMAND); @@ -100,18 +118,21 @@ void emac_dma_init(void) REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_FORWARD_UNDERSIZED_GOOD_FRAMES); REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_OPERATE_SECOND_FRAME); REG_SET_FIELD(EMAC_DMABUSMODE_REG, EMAC_PROG_BURST_LEN, 4); - REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG,EMAC_DMAOPERATION_MODE_REG); + REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_DMAOPERATION_MODE_REG); +} + +void emac_mac_enable_txrx(void) +{ + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACRX); + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACTX); } void emac_mac_init(void) { REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACMIIGMII); - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); + REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); REG_SET_BIT(EMAC_GMACFRAMEFILTER_REG, EMAC_PROMISCUOUS_MODE); - - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACRX); - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACTX); } void emac_set_clk_rmii(void) diff --git a/components/ethernet/emac_dev.h b/components/ethernet/emac_dev.h index bc04d8d9e2e..dc1045b92fe 100644 --- a/components/ethernet/emac_dev.h +++ b/components/ethernet/emac_dev.h @@ -52,6 +52,9 @@ void emac_enable_dma_tx(void); void emac_enable_dma_rx(void); void emac_disable_dma_tx(void); void emac_disable_dma_rx(void); +void emac_enable_flowctrl(void); +void emac_disable_flowctrl(void); +void emac_mac_enable_txrx(void); uint32_t inline emac_read_tx_cur_reg(void) { @@ -77,22 +80,32 @@ void inline emac_poll_rx_cmd(void) void inline emac_disable_rx_intr(void) { - REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_INTERRUPT_ENABLE); + REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_INTERRUPT_ENABLE); } void inline emac_enable_rx_intr(void) { - REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_INTERRUPT_ENABLE); + REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_INTERRUPT_ENABLE); } void inline emac_disable_rx_unavail_intr(void) { - REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); + REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); } void inline emac_enable_rx_unavail_intr(void) { - REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); + REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG, EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); +} + +void IRAM_ATTR inline emac_send_pause_frame_enable(void) +{ + REG_SET_BIT(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_SBD_FLOWCTRL); +} + +void inline emac_send_pause_zero_frame_enable(void) +{ + REG_CLR_BIT(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_SBD_FLOWCTRL); } #ifdef __cplusplus diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 06e641453b1..9446e8ee354 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -68,6 +68,7 @@ static TimerHandle_t emac_timer = NULL; static SemaphoreHandle_t emac_rx_xMutex = NULL; static SemaphoreHandle_t emac_tx_xMutex = NULL; static const char *TAG = "emac"; +static bool pause_send = false; static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par); esp_err_t emac_post(emac_sig_t sig, emac_par_t par); @@ -96,9 +97,9 @@ static void emac_clean_tx_desc(struct dma_extended_desc *tx_desc) tx_desc->basic.desc0 = 0; } -static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc ,uint32_t buf_ptr) +static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc , uint32_t buf_ptr) { - if(buf_ptr != 0) { + if (buf_ptr != 0) { rx_desc->basic.desc2 = buf_ptr; } rx_desc->basic.desc1 = EMAC_DESC_RX_SECOND_ADDR_CHAIN | DMA_RX_BUF_SIZE; @@ -215,6 +216,8 @@ static void emac_set_user_config_data(eth_config_t *config ) emac_config.emac_phy_check_init = config->phy_check_init; emac_config.emac_phy_get_speed_mode = config->phy_get_speed_mode; emac_config.emac_phy_get_duplex_mode = config->phy_get_duplex_mode; + emac_config.emac_flow_ctrl_enable = config->flow_ctrl_enable; + emac_config.emac_phy_get_partner_pause_enable = config->phy_get_partner_pause_enable; } static void emac_enable_intr() @@ -276,6 +279,11 @@ static esp_err_t emac_verify_args(void) ret = ESP_FAIL; } + if (emac_config.emac_flow_ctrl_enable == true && emac_config.emac_phy_get_partner_pause_enable == NULL) { + ESP_LOGE(TAG, "phy get partner pause enable func is null"); + ret = ESP_FAIL; + } + return ret; } @@ -293,13 +301,13 @@ static void emac_process_tx(void) { uint32_t cur_tx_desc = emac_read_tx_cur_reg(); - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); - while (((uint32_t) &(emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) { + while (((uint32_t) & (emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) { emac_clean_tx_desc(&(emac_config.dma_etx[emac_config.dirty_tx])); emac_config.dirty_tx = (emac_config.dirty_tx + 1) % DMA_TX_BUF_NUM; emac_config.cnt_tx --; @@ -317,21 +325,43 @@ void esp_eth_free_rx_buf(void *buf) { xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.cur_rx]),(uint32_t) buf); + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.cur_rx]), (uint32_t) buf); emac_config.cur_rx = (emac_config.cur_rx + 1) % DMA_RX_BUF_NUM; emac_config.cnt_rx--; - if(emac_config.cnt_rx < 0) { - ESP_LOGE(TAG, "emac rx buf err!!\n"); + if (emac_config.cnt_rx < 0) { + ESP_LOGE(TAG, "emac rx buf err!!\n"); } emac_poll_rx_cmd(); xSemaphoreGiveRecursive( emac_rx_xMutex ); + + if (emac_config.emac_flow_ctrl_partner_support == true) { + portENTER_CRITICAL(&g_emac_mux); + if (pause_send == true && emac_config.cnt_rx < FLOW_CONTROL_LOW_WATERMARK) { + emac_send_pause_zero_frame_enable(); + pause_send = false; + } + portEXIT_CRITICAL(&g_emac_mux); + } +} + +static uint32_t IRAM_ATTR emac_get_rxbuf_count_in_intr(void) +{ + uint32_t cnt = 0; + uint32_t cur_rx_desc = emac_read_rx_cur_reg(); + struct dma_extended_desc *cur_desc = (struct dma_extended_desc *)cur_rx_desc; + + while (cur_desc->basic.desc0 == EMAC_DESC_RX_OWN) { + cnt++; + cur_desc = (struct dma_extended_desc *)cur_desc->basic.desc3; + } + return cnt; } #if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE static void emac_process_rx(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } uint32_t cur_rx_desc = emac_read_rx_cur_reg(); @@ -341,7 +371,7 @@ static void emac_process_rx(void) emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]),(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; //if open this ,one intr can do many intrs ? @@ -353,14 +383,14 @@ static void emac_process_rx(void) static void emac_process_rx_unavail(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } uint32_t dirty_cnt = 0; while (dirty_cnt < DMA_RX_BUF_NUM) { - if(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { + if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { break; } @@ -369,7 +399,7 @@ static void emac_process_rx_unavail(void) emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]),(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; } emac_enable_rx_intr(); @@ -380,7 +410,7 @@ static void emac_process_rx_unavail(void) #else static void emac_process_rx_unavail(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } @@ -388,11 +418,11 @@ static void emac_process_rx_unavail(void) while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { - //copy data to lwip + //copy data to lwip emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); emac_config.cnt_rx++; - if(emac_config.cnt_rx > DMA_RX_BUF_NUM) { + if (emac_config.cnt_rx > DMA_RX_BUF_NUM) { ESP_LOGE(TAG, "emac rx unavail buf err !!\n"); } emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; @@ -404,7 +434,7 @@ static void emac_process_rx_unavail(void) static void emac_process_rx(void) { - if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + if (emac_config.emac_status == EMAC_RUNTIME_STOP) { return; } @@ -412,16 +442,16 @@ static void emac_process_rx(void) xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); - if(((uint32_t) &(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { + if (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { - while (((uint32_t) &(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) { + while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) { //copy data to lwip emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); emac_config.cnt_rx++; - if(emac_config.cnt_rx > DMA_RX_BUF_NUM ) { + if (emac_config.cnt_rx > DMA_RX_BUF_NUM ) { ESP_LOGE(TAG, "emac rx buf err!!\n"); } emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; @@ -429,16 +459,16 @@ static void emac_process_rx(void) cur_rx_desc = emac_read_rx_cur_reg(); } } else { - if(emac_config.cnt_rx < DMA_RX_BUF_NUM) { - if((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 & EMAC_DESC_RX_OWN) == 0) { + if (emac_config.cnt_rx < DMA_RX_BUF_NUM) { + if ((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 & EMAC_DESC_RX_OWN) == 0) { while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { //copy data to lwip emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); emac_config.cnt_rx++; - if(emac_config.cnt_rx > DMA_RX_BUF_NUM) { - ESP_LOGE(TAG,"emac rx buf err!!!\n"); + if (emac_config.cnt_rx > DMA_RX_BUF_NUM) { + ESP_LOGE(TAG, "emac rx buf err!!!\n"); } emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; @@ -462,12 +492,18 @@ static void IRAM_ATTR emac_process_intr(void *arg) if (event & EMAC_RECV_INT) { emac_disable_rx_intr(); + if (emac_config.emac_flow_ctrl_partner_support == true) { + if (emac_get_rxbuf_count_in_intr() < FLOW_CONTROL_HIGH_WATERMARK && pause_send == false ) { + pause_send = true; + emac_send_pause_frame_enable(); + } + } emac_post(SIG_EMAC_RX_DONE, 0); } if (event & EMAC_RECV_BUF_UNAVAIL) { emac_disable_rx_unavail_intr(); - emac_post(SIG_EMAC_RX_UNAVAIL,0); + emac_post(SIG_EMAC_RX_UNAVAIL, 0); } if (event & EMAC_TRANS_INT) { @@ -475,25 +511,43 @@ static void IRAM_ATTR emac_process_intr(void *arg) } } +static void emac_set_macaddr_reg(void) +{ + REG_SET_FIELD(EMAC_GMACADDR0HIGH_REG, EMAC_MAC_ADDRESS0_HI, (emac_config.macaddr[0] << 8) | (emac_config.macaddr[1])); + REG_WRITE(EMAC_GMACADDR0LOW_REG, (emac_config.macaddr[2] << 24) | (emac_config.macaddr[3] << 16) | (emac_config.macaddr[4] << 8) | (emac_config.macaddr[5])); +} + static void emac_check_phy_init(void) { emac_config.emac_phy_check_init(); - if(emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) { + if (emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) { REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); } else { REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); } - if(emac_config.emac_phy_get_speed_mode() == ETH_SPEED_MODE_100M) { + if (emac_config.emac_phy_get_speed_mode() == ETH_SPEED_MODE_100M) { REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); } else { REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); } - - emac_mac_init(); + if (emac_config.emac_flow_ctrl_enable == true) { + if (emac_config.emac_phy_get_partner_pause_enable() == true && emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) { + emac_enable_flowctrl(); + emac_config.emac_flow_ctrl_partner_support = true; + } else { + emac_disable_flowctrl(); + emac_config.emac_flow_ctrl_partner_support = false; + } + } else { + emac_disable_flowctrl(); + emac_config.emac_flow_ctrl_partner_support = false; + } + emac_mac_enable_txrx(); } static void emac_process_link_updown(bool link_status) { system_event_t evt; + uint8_t i = 0; emac_config.phy_link_up = link_status; @@ -502,6 +556,10 @@ static void emac_process_link_updown(bool link_status) ESP_LOGI(TAG, "eth link_up!!!"); emac_enable_dma_tx(); emac_enable_dma_rx(); + for (i = 0; i < PHY_LINK_CHECK_NUM; i++) { + emac_check_phy_init(); + } + evt.event_id = SYSTEM_EVENT_ETH_CONNECTED; } else { ESP_LOGI(TAG, "eth link_down!!!"); @@ -534,7 +592,7 @@ esp_err_t esp_eth_tx(uint8_t *buf, uint16_t size) } xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); - if (emac_config.cnt_tx == DMA_TX_BUF_NUM -1) { + if (emac_config.cnt_tx == DMA_TX_BUF_NUM - 1) { ESP_LOGD(TAG, "tx buf full"); ret = ERR_MEM; goto _exit; @@ -557,13 +615,13 @@ _exit: static void emac_init_default_data(void) { - memset((uint8_t *)&emac_config, 0,sizeof(struct emac_config_data)); + memset((uint8_t *)&emac_config, 0, sizeof(struct emac_config_data)); } -void emac_process_link_check(void) +void emac_process_link_check(void) { if (emac_config.emac_status != EMAC_RUNTIME_START || - emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { + emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { return; } @@ -580,7 +638,7 @@ void emac_process_link_check(void) void emac_link_check_func(void *pv_parameters) { - emac_post(SIG_EMAC_CHECK_LINK,0); + emac_post(SIG_EMAC_CHECK_LINK, 0); } static bool emac_link_check_timer_init(void) @@ -634,10 +692,13 @@ static void emac_start(void *param) emac_check_mac_addr(); emac_set_mac_addr(); + emac_set_macaddr_reg(); emac_set_tx_base_reg(); emac_set_rx_base_reg(); + emac_mac_init(); + emac_config.phy_init(); //ptp TODO @@ -818,7 +879,7 @@ void emac_task(void *pv) esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) { - if(sig <= SIG_EMAC_RX_DONE) { + if (sig <= SIG_EMAC_RX_DONE) { if (emac_sig_cnt[sig]) { return ESP_OK; } else { @@ -831,11 +892,11 @@ esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) ret = xQueueSendFromISR(emac_xqueue, &evt, &tmp); - if(tmp != pdFALSE) { + if (tmp != pdFALSE) { portYIELD_FROM_ISR(); } - if(ret != pdPASS) { + if (ret != pdPASS) { return ESP_FAIL; } } diff --git a/components/ethernet/include/esp_eth.h b/components/ethernet/include/esp_eth.h index 2aae9d20249..e36c3ebfe6a 100644 --- a/components/ethernet/include/esp_eth.h +++ b/components/ethernet/include/esp_eth.h @@ -79,6 +79,7 @@ typedef eth_duplex_mode_t (*eth_phy_get_duplex_mode_func)(void); typedef void (*eth_phy_func)(void); typedef esp_err_t (*eth_tcpip_input_func)(void *buffer, uint16_t len, void *eb); typedef void (*eth_gpio_config_func)(void); +typedef bool (*eth_phy_get_partner_pause_enable_func)(void); /** @@ -95,6 +96,9 @@ typedef struct { eth_phy_get_speed_mode_func phy_get_speed_mode; /*!< phy check init func */ eth_phy_get_duplex_mode_func phy_get_duplex_mode; /*!< phy check init func */ eth_gpio_config_func gpio_config; /*!< gpio config func */ + bool flow_ctrl_enable; /*!< flag of flow ctrl enable */ + eth_phy_get_partner_pause_enable_func phy_get_partner_pause_enable; /*!< get partner pause enable */ + } eth_config_t; /** diff --git a/docs/api/esp_eth.rst b/docs/api/esp_eth.rst index 1fcb1c23513..371aa5b2333 100644 --- a/docs/api/esp_eth.rst +++ b/docs/api/esp_eth.rst @@ -28,6 +28,7 @@ Type Definitions .. doxygentypedef:: eth_phy_func .. doxygentypedef:: eth_tcpip_input_func .. doxygentypedef:: eth_gpio_config_func +.. doxygentypedef:: eth_phy_get_partner_pause_enable_func Enumerations ^^^^^^^^^^^^ diff --git a/examples/17_ethernet/main/ethernet_main.c b/examples/17_ethernet/main/ethernet_main.c index fc4347f7d89..57c6f973579 100644 --- a/examples/17_ethernet/main/ethernet_main.c +++ b/examples/17_ethernet/main/ethernet_main.c @@ -71,6 +71,22 @@ bool phy_tlk110_check_phy_link_status(void) return ((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & LINK_STATUS) == LINK_STATUS ); } +bool phy_tlk110_get_partner_pause_enable(void) +{ + if((esp_eth_smi_read(PHY_LINK_PARTNER_ABILITY_REG) & PARTNER_PAUSE) == PARTNER_PAUSE) { + return true; + } else { + return false; + } +} + +void phy_enable_flow_ctrl(void) +{ + uint32_t data = 0; + data = esp_eth_smi_read(AUTO_NEG_ADVERTISEMENT_REG); + esp_eth_smi_write(AUTO_NEG_ADVERTISEMENT_REG,data|ASM_DIR|PAUSE); +} + void phy_tlk110_init(void) { esp_eth_smi_write(PHY_RESET_CONTROL_REG, SOFTWARE_RESET); @@ -78,8 +94,12 @@ void phy_tlk110_init(void) while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) { } - esp_eth_smi_write(SOFTWARE_STAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE); + esp_eth_smi_write(SOFTWARE_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE); + ets_delay_us(300); + + //if config.flow_ctrl_enable == true ,enable this + phy_enable_flow_ctrl(); } void eth_gpio_config_rmii(void) @@ -140,6 +160,9 @@ void app_main() config.phy_check_link = phy_tlk110_check_phy_link_status; config.phy_get_speed_mode = phy_tlk110_get_speed_mode; config.phy_get_duplex_mode = phy_tlk110_get_duplex_mode; + //Only FULLDUPLEX mode support flow ctrl now! + config.flow_ctrl_enable = true; + config.phy_get_partner_pause_enable = phy_tlk110_get_partner_pause_enable; ret = esp_eth_init(&config); diff --git a/examples/17_ethernet/main/tlk110_phy.h b/examples/17_ethernet/main/tlk110_phy.h index 5f2ca644dc8..a21bc57aeab 100644 --- a/examples/17_ethernet/main/tlk110_phy.h +++ b/examples/17_ethernet/main/tlk110_phy.h @@ -5,7 +5,15 @@ #define PHY_IDENTIFIER_REG (0x2) #define OUI_MSB_21TO6_DEF 0x2000 -#define SOFTWARE_STAP_CONTROL_REG (0x9) +#define AUTO_NEG_ADVERTISEMENT_REG (0x4) +#define ASM_DIR BIT(11) +#define PAUSE BIT(10) + +#define PHY_LINK_PARTNER_ABILITY_REG (0x5) +#define PARTNER_ASM_DIR BIT(11) +#define PARTNER_PAUSE BIT(10) + +#define SOFTWARE_STRAP_CONTROL_REG (0x9) #define SW_STRAP_CONFIG_DONE BIT(15) #define AUTO_MDIX_ENABLE BIT(14) #define AUTO_NEGOTIATION_ENABLE BIT(13) From 2e06c6ba382cda6313441b46610a30be17153b56 Mon Sep 17 00:00:00 2001 From: shangke Date: Tue, 10 Jan 2017 21:54:53 +0800 Subject: [PATCH 114/167] dhcp: fix dhcp err when wifi and ethernet coexist --- components/tcpip_adapter/tcpip_adapter_lwip.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 03f364fe673..b2725e3447f 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -529,7 +529,12 @@ static void tcpip_adapter_dhcpc_cb(struct netif *netif) } if ( !ip4_addr_cmp(ip_2_ip4(&netif->ip_addr), IP4_ADDR_ANY) ) { - tcpip_adapter_ip_info_t *ip_info = &esp_ip[TCPIP_ADAPTER_IF_STA]; + tcpip_adapter_ip_info_t *ip_info = NULL; + if( netif == esp_netif[TCPIP_ADAPTER_IF_STA] ) { + ip_info = &esp_ip[TCPIP_ADAPTER_IF_STA]; + } else if(netif == esp_netif[TCPIP_ADAPTER_IF_ETH] ) { + ip_info = &esp_ip[TCPIP_ADAPTER_IF_ETH]; + } //check whether IP is changed if ( !ip4_addr_cmp(ip_2_ip4(&netif->ip_addr), &ip_info->ip) || From 89e0ecc2720b5edad45a706d7c9d67b06ab5bcde Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 00:24:50 +0800 Subject: [PATCH 115/167] build system: add IDF_VER environment variable and preprocessor define --- components/bootloader/src/main/bootloader_config.h | 1 - components/bootloader/src/main/bootloader_start.c | 2 +- docs/build_system.rst | 9 +++++++++ make/project.mk | 6 +++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index d097083d2a8..6b37899ac3b 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -23,7 +23,6 @@ extern "C" #include "esp_flash_data_types.h" -#define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index e5fda9cf530..102e70c3def 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -233,7 +233,7 @@ static bool ota_select_valid(const esp_ota_select_entry_t *s) void bootloader_main() { uart_console_configure(); - ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); + ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER); #if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED) esp_err_t err; #endif diff --git a/docs/build_system.rst b/docs/build_system.rst index 2e388bd4956..7a33a412560 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -166,6 +166,7 @@ The following variables are set at the project level, but exported for use in th - ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``. - ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain. - ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain. +- ``IDF_VER``: Git version of ESP-IDF (produced by ``git describe``) If you modify any of these variables inside ``component.mk`` then this will not prevent other components from building but it may make your component hard to build and/or debug. @@ -267,6 +268,14 @@ To create a component KConfig file, it is easiest to start with one of the KConf For an example, see `Adding conditional configuration`. +Preprocessor Definitions +------------------------ + +ESP-IDF build systems adds the following C preprocessor definitions on the command line: + +- ``ESP_PLATFORM`` — Can be used to detect that build happens within ESP-IDF. +- ``IDF_VER`` — Defined to a git version string. E.g. ``v2.0`` for a tagged release or ``v1.0-275-g0efaa4f`` for an arbitrary commit. + Build Process Internals ----------------------- diff --git a/make/project.mk b/make/project.mk index e2a93ce1190..6d9feaa7c4f 100644 --- a/make/project.mk +++ b/make/project.mk @@ -176,6 +176,10 @@ else endif @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) + +# Git version of ESP-IDF (of the form v1.0-285-g5c4f707) +IDF_VER := $(shell git describe) + # Set default LDFLAGS LDFLAGS ?= -nostdlib \ @@ -200,7 +204,7 @@ LDFLAGS ?= -nostdlib \ # CPPFLAGS used by C preprocessor # If any flags are defined in application Makefile, add them at the end. -CPPFLAGS := -DESP_PLATFORM -MMD -MP $(CPPFLAGS) $(EXTRA_CPPFLAGS) +CPPFLAGS := -DESP_PLATFORM -D IDF_VER=\"$(IDF_VER)\" -MMD -MP $(CPPFLAGS) $(EXTRA_CPPFLAGS) # Warnings-related flags relevant both for C and C++ COMMON_WARNING_FLAGS = -Wall -Werror=all \ From 833102cbf325c1a50b8a6a1a6f4ccd6af45bdf65 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 00:52:45 +0800 Subject: [PATCH 116/167] esp32: place cross-core interrupt handler into IRAM esp_intr_alloc is called with ESP_INTR_FLAG_IRAM, so the ISR should be in IRAM. --- components/esp32/crosscore_int.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index f7ea4f6a745..b58d3d62c5c 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -44,7 +44,7 @@ static volatile uint32_t reason[ portNUM_PROCESSORS ]; ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. */ -static void esp_crosscore_isr(void *arg) { +static void IRAM_ATTR esp_crosscore_isr(void *arg) { uint32_t myReasonVal; //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg; From 6c86586e975eb262d28332b148c655cd0be542e5 Mon Sep 17 00:00:00 2001 From: Liu Han Date: Wed, 7 Dec 2016 17:34:26 +0800 Subject: [PATCH 117/167] components/lwip: Set the ping target info Add API for set the ping target info and get the ping result --- components/lwip/apps/ping/esp_ping.c | 131 +++++++ components/lwip/apps/ping/esp_ping.h | 94 +++++ components/lwip/apps/ping/ping.c | 366 +++++++++++++++++++ components/lwip/apps/ping/ping.h | 56 +++ components/lwip/component.mk | 4 +- components/lwip/include/lwip/port/lwipopts.h | 25 ++ 6 files changed, 674 insertions(+), 2 deletions(-) create mode 100644 components/lwip/apps/ping/esp_ping.c create mode 100644 components/lwip/apps/ping/esp_ping.h create mode 100644 components/lwip/apps/ping/ping.c create mode 100644 components/lwip/apps/ping/ping.h mode change 100755 => 100644 components/lwip/include/lwip/port/lwipopts.h diff --git a/components/lwip/apps/ping/esp_ping.c b/components/lwip/apps/ping/esp_ping.c new file mode 100644 index 00000000000..56ab6a985a0 --- /dev/null +++ b/components/lwip/apps/ping/esp_ping.c @@ -0,0 +1,131 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp_ping.h" + +#include "lwip/ip_addr.h" + +typedef struct _ping_option { + ip4_addr_t ping_target; + uint32_t ping_count; + uint32_t ping_rcv_timeout; + uint32_t ping_delay; + uint16_t ping_id; + esp_ping_found_fn ping_res_fn; + esp_ping_found ping_res; + void *ping_reserve; +} ping_option; + +static ping_option ping_option_info[1]; + +esp_err_t esp_ping_set_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len) +{ + esp_err_t ret = ESP_OK; + + if (opt_val == NULL) { + return ESP_ERR_PING_INVALID_PARAMS; + } + + switch (opt_id) { + case PING_TARGET_IP_ADDRESS: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_target.addr = *(uint32_t *)opt_val; + break; + case PING_TARGET_IP_ADDRESS_COUNT: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_count = *(uint32_t *)opt_val; + break; + case PING_TARGET_RCV_TIMEO: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_rcv_timeout = (*(uint32_t *)opt_val) * 1000; + break; + case PING_TARGET_DELAY_TIME: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + ping_option_info->ping_delay = (*(uint32_t *)opt_val) * 1000; + break; + case PING_TARGET_ID: + ESP_PING_CHECK_OPTLEN(opt_len, uint16_t); + ping_option_info->ping_id = *(uint16_t *)opt_val; + break; + case PING_TARGET_RES_FN: + ping_option_info->ping_res_fn = opt_val; + break; + default: + ret = ESP_ERR_PING_INVALID_PARAMS; + break; + } + + return ret; +} + +esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len) +{ + esp_err_t ret = ESP_OK; + + if (opt_val == NULL) { + return ESP_ERR_PING_INVALID_PARAMS; + } + + switch (opt_id) { + case PING_TARGET_IP_ADDRESS: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_target.addr; + break; + case PING_TARGET_IP_ADDRESS_COUNT: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_count; + break; + case PING_TARGET_RCV_TIMEO: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_rcv_timeout; + break; + case PING_TARGET_DELAY_TIME: + ESP_PING_CHECK_OPTLEN(opt_len, uint32_t); + *(uint32_t *)opt_val = ping_option_info->ping_delay; + break; + case PING_TARGET_ID: + ESP_PING_CHECK_OPTLEN(opt_len, uint16_t); + *(uint16_t *)opt_val = ping_option_info->ping_id; + break; + default: + ret = ESP_ERR_PING_INVALID_PARAMS; + break; + } + + return ret; +} + +esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time) +{ + esp_err_t ret = ESP_OK; + + ping_option_info->ping_res.bytes = ping_len; + ping_option_info->ping_res.ping_err = res_val; + ping_option_info->ping_res.resp_time = ping_time; + ping_option_info->ping_res.total_time += ping_time; + ping_option_info->ping_res.total_bytes += ping_len; + + if (res_val == 0) { + ping_option_info->ping_res.timeout_count ++; + } + + if (--ping_option_info->ping_count != 0 && ping_option_info->ping_res_fn) { + ping_option_info->ping_res_fn(PING_TARGET_RES_FN, &ping_option_info->ping_res); + } else { + memset(&ping_option_info->ping_res, 0, sizeof(esp_ping_found)); + } + + return ret; +} diff --git a/components/lwip/apps/ping/esp_ping.h b/components/lwip/apps/ping/esp_ping.h new file mode 100644 index 00000000000..ba9bec2d630 --- /dev/null +++ b/components/lwip/apps/ping/esp_ping.h @@ -0,0 +1,94 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ESP_PING_H_ +#define ESP_PING_H_ + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_ERR_PING_BASE 0x5000 + +#define ESP_ERR_PING_INVALID_PARAMS ESP_ERR_PING_BASE + 0x00 +#define ESP_ERR_PING_NO_MEM ESP_ERR_PING_BASE + 0x01 + +#define ESP_PING_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return ESP_ERR_PING_INVALID_PARAMS; }}while(0) + +typedef struct _ping_found { + uint32_t resp_time; + uint32_t timeout_count; + uint32_t bytes; + uint32_t total_bytes; + uint32_t total_time; + int8_t ping_err; +} esp_ping_found; + +typedef enum { + PING_TARGET_IP_ADDRESS = 50, /**< target IP address */ + PING_TARGET_IP_ADDRESS_COUNT = 51, /**< target IP address total counter */ + PING_TARGET_RCV_TIMEO = 52, /**< receive timeout */ + PING_TARGET_DELAY_TIME = 53, /**< delay time */ + PING_TARGET_ID = 54, /**< identifier */ + PING_TARGET_RES_FN = 55 +} ping_target_id_t; + +typedef void (* esp_ping_found_fn)(ping_target_id_t found_id, esp_ping_found *found_val); + +/** + * @brief Set PING function option + * + * @param[in] opt_id: option index, 50 for IP, 51 for COUNT, 52 for RCV TIMEOUT, 53 for DELAY TIME, 54 for ID + * @param[in] opt_val: option parameter + * @param[in] opt_len: option length + * + * @return + * - ESP_OK + * - ESP_ERR_PING_INVALID_PARAMS + */ +esp_err_t esp_ping_set_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len); + +/** + * @brief Get PING function option + * + * @param[in] opt_id: option index, 50 for IP, 51 for COUNT, 52 for RCV TIMEOUT, 53 for DELAY TIME, 54 for ID + * @param[in] opt_val: option parameter + * @param[in] opt_len: option length + * + * @return + * - ESP_OK + * - ESP_ERR_PING_INVALID_PARAMS + */ +esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t opt_len); + +/** + * @brief Get PING function result action + * + * @param[in] res_val: ping function action, 1 for successful, 0 for fail. + * res_len: response bytes + * res_time: response time + * + * @return + * - ESP_OK + * - ESP_ERR_PING_INVALID_PARAMS + */ +esp_err_t esp_ping_result(uint8_t res_val, uint16_t res_len, uint32_t res_time); + +#ifdef __cplusplus +} +#endif + +#endif /* ESP_PING_H_ */ diff --git a/components/lwip/apps/ping/ping.c b/components/lwip/apps/ping/ping.c new file mode 100644 index 00000000000..014d4c65743 --- /dev/null +++ b/components/lwip/apps/ping/ping.c @@ -0,0 +1,366 @@ +/** + * @file + * Ping sender module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +/** + * This is an example of a "ping" sender (with raw API and socket API). + * It can be used as a start point to maintain opened a network connection, or + * like a network "watchdog" for your device. + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 && LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "ping.h" + +#include "lwip/mem.h" +#include "lwip/raw.h" +#include "lwip/icmp.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/inet_chksum.h" + +#if PING_USE_SOCKETS +#include "lwip/sockets.h" +#include "lwip/inet.h" +#endif /* PING_USE_SOCKETS */ + +#ifdef ESP_LWIP +#include "esp_ping.h" +#include "lwip/ip_addr.h" +#endif +/** + * PING_DEBUG: Enable debugging for PING. + */ +#ifndef PING_DEBUG +#define PING_DEBUG LWIP_DBG_ON +#endif + +/** ping target - should be an "ip4_addr_t" */ +#ifndef PING_TARGET +#define PING_TARGET (netif_default ? *netif_ip4_gw(netif_default) : (*IP4_ADDR_ANY)) +#endif + +/** ping receive timeout - in milliseconds */ +#ifndef PING_RCV_TIMEO +#define PING_RCV_TIMEO 1000 +#endif + +/** ping delay - in milliseconds */ +#ifndef PING_DELAY +#define PING_DELAY 1000 +#endif + +/** ping identifier - must fit on a u16_t */ +#ifndef PING_ID +#define PING_ID 0xAFAF +#endif + +/** ping additional data size to include in the packet */ +#ifndef PING_DATA_SIZE +#define PING_DATA_SIZE 32 +#endif + +/** ping result action - no default action */ +#ifndef PING_RESULT +#define PING_RESULT(ping_ok) +#endif + +/* ping variables */ +static u16_t ping_seq_num; +static u32_t ping_time; +#if !PING_USE_SOCKETS +static struct raw_pcb *ping_pcb; +#endif /* PING_USE_SOCKETS */ + +/** Prepare a echo ICMP request */ +static void +ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len) +{ + size_t i; + size_t data_len = len - sizeof(struct icmp_echo_hdr); + + ICMPH_TYPE_SET(iecho, ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = PING_ID; + iecho->seqno = lwip_htons(++ping_seq_num); + + /* fill the additional data buffer with some data */ + for(i = 0; i < data_len; i++) { + ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; + } + + iecho->chksum = inet_chksum(iecho, len); +} + +#if PING_USE_SOCKETS + +/* Ping using the socket ip */ +static err_t +ping_send(int s, ip_addr_t *addr) +{ + int err; + struct icmp_echo_hdr *iecho; + struct sockaddr_in to; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + LWIP_ASSERT("ping_size is too big", ping_size <= 0xffff); + LWIP_ASSERT("ping: expect IPv4 address", !IP_IS_V6(addr)); + + iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size); + if (!iecho) { + return ERR_MEM; + } + + ping_prepare_echo(iecho, (u16_t)ping_size); + + to.sin_len = sizeof(to); + to.sin_family = AF_INET; + inet_addr_from_ipaddr(&to.sin_addr, ip_2_ip4(addr)); + + err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to)); + + mem_free(iecho); + + return (err ? ERR_OK : ERR_VAL); +} + +static void +ping_recv(int s) +{ + char buf[64]; + int len; + struct sockaddr_in from; + struct ip_hdr *iphdr; + struct icmp_echo_hdr *iecho; + int fromlen = sizeof(from); + + while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) { + if (len >= (int)(sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr))) { + if (from.sin_family != AF_INET) { + /* Ping is not IPv4 */ + LWIP_DEBUGF( PING_DEBUG, ("ping: invalid sin_family\n")); + } else { + ip4_addr_t fromaddr; + inet_addr_to_ipaddr(&fromaddr, &from.sin_addr); + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip4_addr_debug_print(PING_DEBUG, &fromaddr); + LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now() - ping_time))); + + iphdr = (struct ip_hdr *)buf; + iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4)); + if ((iecho->id == PING_ID) && (iecho->seqno == lwip_htons(ping_seq_num))) { + /* do some ping result processing */ +#ifdef ESP_LWIP + esp_ping_result((ICMPH_TYPE(iecho) == ICMP_ER), len, (sys_now() - ping_time)); +#else + PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER)); +#endif + return; + } else { + LWIP_DEBUGF( PING_DEBUG, ("ping: drop\n")); + } + } + } + fromlen = sizeof(from); + } + + if (len == 0) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv - %"U32_F" ms - timeout\n", (sys_now()-ping_time))); + } + + /* do some ping result processing */ +#ifdef ESP_LWIP + esp_ping_result(0, len, (sys_now()-ping_time)); +#else + PING_RESULT(0); +#endif +} + +static void +ping_thread(void *arg) +{ + int s; + int ret; + ip_addr_t ping_target; +#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD + int timeout = PING_RCV_TIMEO; +#else + struct timeval timeout; + timeout.tv_sec = PING_RCV_TIMEO/1000; + timeout.tv_usec = (PING_RCV_TIMEO%1000)*1000; +#endif + LWIP_UNUSED_ARG(arg); + + if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) { + return; + } + + ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + LWIP_ASSERT("setting receive timeout failed", ret == 0); + LWIP_UNUSED_ARG(ret); + + while (1) { +#ifdef ESP_LWIP + ip4_addr_t ipaddr; + esp_ping_get_target(PING_TARGET_IP_ADDRESS, &ipaddr.addr, sizeof(uint32_t)); + ip_addr_copy_from_ip4(ping_target, ipaddr); +#else + ip_addr_copy_from_ip4(ping_target, PING_TARGET); +#endif + + if (ping_send(s, &ping_target) == ERR_OK) { + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, &ping_target); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + + ping_time = sys_now(); + ping_recv(s); + } else { + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, &ping_target); + LWIP_DEBUGF( PING_DEBUG, (" - error\n")); + } + sys_msleep(PING_DELAY); + } +} + +#else /* PING_USE_SOCKETS */ + +/* Ping using the raw ip */ +static u8_t +ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) +{ + struct icmp_echo_hdr *iecho; + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_ASSERT("p != NULL", p != NULL); + + if ((p->tot_len >= (PBUF_IP_HLEN + sizeof(struct icmp_echo_hdr))) && + pbuf_header(p, -PBUF_IP_HLEN) == 0) { + iecho = (struct icmp_echo_hdr *)p->payload; + + if ((iecho->id == PING_ID) && (iecho->seqno == lwip_htons(ping_seq_num))) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now()-ping_time))); + + /* do some ping result processing */ + PING_RESULT(1); + pbuf_free(p); + return 1; /* eat the packet */ + } + /* not eaten, restore original packet */ + pbuf_header(p, PBUF_IP_HLEN); + } + + return 0; /* don't eat the packet */ +} + +static void +ping_send(struct raw_pcb *raw, ip_addr_t *addr) +{ + struct pbuf *p; + struct icmp_echo_hdr *iecho; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff); + + p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); + if (!p) { + return; + } + if ((p->len == p->tot_len) && (p->next == NULL)) { + iecho = (struct icmp_echo_hdr *)p->payload; + + ping_prepare_echo(iecho, (u16_t)ping_size); + + raw_sendto(raw, p, addr); + ping_time = sys_now(); + } + pbuf_free(p); +} + +static void +ping_timeout(void *arg) +{ + struct raw_pcb *pcb = (struct raw_pcb*)arg; + ip_addr_t ping_target; + + LWIP_ASSERT("ping_timeout: no pcb given!", pcb != NULL); + + ip_addr_copy_from_ip4(ping_target, PING_TARGET); + ping_send(pcb, &ping_target); + + sys_timeout(PING_DELAY, ping_timeout, pcb); +} + +static void +ping_raw_init(void) +{ + ping_pcb = raw_new(IP_PROTO_ICMP); + LWIP_ASSERT("ping_pcb != NULL", ping_pcb != NULL); + + raw_recv(ping_pcb, ping_recv, NULL); + raw_bind(ping_pcb, IP_ADDR_ANY); + sys_timeout(PING_DELAY, ping_timeout, ping_pcb); +} + +void +ping_send_now(void) +{ + ip_addr_t ping_target; + LWIP_ASSERT("ping_pcb != NULL", ping_pcb != NULL); + ip_addr_copy_from_ip4(ping_target, PING_TARGET); + ping_send(ping_pcb, &ping_target); +} + +#endif /* PING_USE_SOCKETS */ + +void +ping_init(void) +{ +#if PING_USE_SOCKETS + sys_thread_new("ping_thread", ping_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); +#else /* PING_USE_SOCKETS */ + ping_raw_init(); +#endif /* PING_USE_SOCKETS */ +} + +#endif /* LWIP_IPV4 && LWIP_RAW */ diff --git a/components/lwip/apps/ping/ping.h b/components/lwip/apps/ping/ping.h new file mode 100644 index 00000000000..fa2f7aae16f --- /dev/null +++ b/components/lwip/apps/ping/ping.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef LWIP_PING_H +#define LWIP_PING_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * PING_USE_SOCKETS: Set to 1 to use sockets, otherwise the raw api is used + */ +#ifndef PING_USE_SOCKETS +#define PING_USE_SOCKETS LWIP_SOCKET +#endif + + +void ping_init(void); + +#if !PING_USE_SOCKETS +void ping_send_now(void); +#endif /* !PING_USE_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_PING_H */ diff --git a/components/lwip/component.mk b/components/lwip/component.mk index df116f1a069..9444d229e8a 100644 --- a/components/lwip/component.mk +++ b/components/lwip/component.mk @@ -2,9 +2,9 @@ # Component Makefile # -COMPONENT_ADD_INCLUDEDIRS := include/lwip include/lwip/port include/lwip/posix +COMPONENT_ADD_INCLUDEDIRS := include/lwip include/lwip/port include/lwip/posix apps/ping -COMPONENT_SRCDIRS := api apps/sntp apps core/ipv4 core/ipv6 core netif port/freertos port/netif port +COMPONENT_SRCDIRS := api apps/sntp apps/ping apps core/ipv4 core/ipv6 core netif port/freertos port/netif port CFLAGS += -Wno-address # lots of LWIP source files evaluate macros that check address of stack variables diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h old mode 100755 new mode 100644 index 14ec0e67a4f..6d1dfbd4978 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -189,6 +189,10 @@ ---------- RAW options ---------- --------------------------------- */ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#define LWIP_RAW 1 /* ---------------------------------- @@ -402,6 +406,27 @@ */ #define DEFAULT_ACCEPTMBOX_SIZE 6 +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define DEFAULT_THREAD_STACKSIZE TCPIP_THREAD_STACKSIZE + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define DEFAULT_THREAD_PRIO TCPIP_THREAD_PRIO + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_RAW_RECVMBOX_SIZE 6 + /* ---------------------------------------------- ---------- Sequential layer options ---------- From 59540eeae12fd2dcbcc1ad6346d026c56fa12b34 Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Wed, 11 Jan 2017 11:04:41 +0800 Subject: [PATCH 118/167] component/bt: bug fix of lack of checking bluetooth stack status inside API functions --- components/bt/bluedroid/api/esp_blufi_api.c | 16 ++++ components/bt/bluedroid/api/esp_bt_device.c | 4 + components/bt/bluedroid/api/esp_bt_main.c | 15 +++- components/bt/bluedroid/api/esp_gap_ble_api.c | 58 +++++++++++++-- components/bt/bluedroid/api/esp_gattc_api.c | 73 +++++++++++++++++++ components/bt/bluedroid/api/esp_gatts_api.c | 56 ++++++++++++++ .../bt/bluedroid/api/include/esp_bt_device.h | 2 +- .../bt/bluedroid/api/include/esp_bt_main.h | 17 +++++ components/bt/bluedroid/btc/core/btc_main.c | 4 +- docs/api/bt.rst | 1 - docs/api/esp_blufi.rst | 6 +- docs/api/esp_bt_main.rst | 3 + 12 files changed, 242 insertions(+), 13 deletions(-) diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index 094fbfae5fd..ac84ee3fc24 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -24,6 +24,10 @@ esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) { + if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + if (callbacks == NULL) { return ESP_FAIL; } @@ -37,6 +41,10 @@ esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn btc_msg_t msg; btc_blufi_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_SEND_CFG_REPORT; @@ -53,6 +61,10 @@ esp_err_t esp_blufi_profile_init(void) { btc_msg_t msg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_INIT; @@ -64,6 +76,10 @@ esp_err_t esp_blufi_profile_deinit(void) { btc_msg_t msg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_DEINIT; diff --git a/components/bt/bluedroid/api/esp_bt_device.c b/components/bt/bluedroid/api/esp_bt_device.c index 745e446c232..1eb48c98acb 100644 --- a/components/bt/bluedroid/api/esp_bt_device.c +++ b/components/bt/bluedroid/api/esp_bt_device.c @@ -14,9 +14,13 @@ #include "esp_bt_device.h" +#include "esp_bt_main.h" #include "controller.h" const uint8_t *esp_bt_dev_get_address(void) { + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return NULL; + } return controller_get_interface()->get_address()->address; } diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index 2dd9166d229..3093b56403d 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -21,6 +21,19 @@ static bool esp_already_enable = false; static bool esp_already_init = false; +esp_bluedroid_status_t esp_bluedroid_get_status(void) +{ + if (esp_already_init) { + if (esp_already_enable) { + return ESP_BLUEDROID_STATUS_ENABLED; + } else { + return ESP_BLUEDROID_STATUS_INITIALIZED; + } + } else { + return ESP_BLUEDROID_STATUS_UNINITIALIZED; + } +} + esp_err_t esp_bluedroid_enable(void) { btc_msg_t msg; @@ -114,7 +127,7 @@ esp_err_t esp_bluedroid_init(void) return ESP_FAIL; } - esp_already_init = true;; + esp_already_init = true; return ESP_OK; } diff --git a/components/bt/bluedroid/api/esp_gap_ble_api.c b/components/bt/bluedroid/api/esp_gap_ble_api.c index 6e18f65822b..34914f8b4dc 100644 --- a/components/bt/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/bluedroid/api/esp_gap_ble_api.c @@ -14,6 +14,7 @@ #include +#include "esp_bt_main.h" #include "esp_gap_ble_api.h" #include "bta_api.h" #include "bt_trace.h" @@ -23,6 +24,9 @@ esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback) { + if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } return (btc_profile_cb_set(BTC_PID_GAP_BLE, callback) == 0 ? ESP_OK : ESP_FAIL); } @@ -31,7 +35,11 @@ esp_err_t esp_ble_gap_config_adv_data(esp_ble_adv_data_t *adv_data) { btc_msg_t msg; btc_ble_gap_args_t arg; - + + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + if (adv_data == NULL) { return ESP_ERR_INVALID_ARG; } @@ -54,7 +62,11 @@ esp_err_t esp_ble_gap_set_scan_params(esp_ble_scan_params_t *scan_params) { btc_msg_t msg; btc_ble_gap_args_t arg; - + + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + if (scan_params == NULL) { return ESP_ERR_INVALID_ARG; } @@ -72,6 +84,10 @@ esp_err_t esp_ble_gap_start_scanning(uint32_t duration) btc_msg_t msg; btc_ble_gap_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_START_SCAN; @@ -84,7 +100,11 @@ esp_err_t esp_ble_gap_start_scanning(uint32_t duration) esp_err_t esp_ble_gap_stop_scanning(void) { btc_msg_t msg; - + + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_STOP_SCAN; @@ -96,6 +116,10 @@ esp_err_t esp_ble_gap_start_advertising(esp_ble_adv_params_t *adv_params) btc_msg_t msg; btc_ble_gap_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_START_ADV; @@ -108,6 +132,10 @@ esp_err_t esp_ble_gap_stop_advertising(void) { btc_msg_t msg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_STOP_ADV; @@ -121,6 +149,10 @@ esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params) btc_msg_t msg; btc_ble_gap_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_UPDATE_CONN_PARAM; @@ -133,7 +165,11 @@ esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_ { btc_msg_t msg; btc_ble_gap_args_t arg; - + + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_SET_PKT_DATA_LEN; @@ -148,7 +184,11 @@ esp_err_t esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) { btc_msg_t msg; btc_ble_gap_args_t arg; - + + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_SET_RAND_ADDRESS; @@ -163,6 +203,10 @@ esp_err_t esp_ble_gap_config_local_privacy (bool privacy_enable) btc_msg_t msg; btc_ble_gap_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_CONFIG_LOCAL_PRIVACY; @@ -176,6 +220,10 @@ esp_err_t esp_ble_gap_set_device_name(const char *name) btc_msg_t msg; btc_ble_gap_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + if (strlen(name) > ESP_GAP_DEVICE_NAME_MAX) { return ESP_ERR_INVALID_ARG; } diff --git a/components/bt/bluedroid/api/esp_gattc_api.c b/components/bt/bluedroid/api/esp_gattc_api.c index 28fc4295758..4baa0a77fc3 100644 --- a/components/bt/bluedroid/api/esp_gattc_api.c +++ b/components/bt/bluedroid/api/esp_gattc_api.c @@ -15,12 +15,17 @@ #include #include "esp_gattc_api.h" +#include "esp_bt_main.h" #include "btc_manage.h" #include "btc_gattc.h" #include "btc_gatt_util.h" esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback) { + if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + if (callback == NULL) { return ESP_FAIL; } @@ -34,6 +39,10 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id) btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + if (app_id > ESP_APP_ID_MAX) { return ESP_ERR_INVALID_ARG; } @@ -51,6 +60,10 @@ esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if) btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_APP_UNREGISTER; @@ -64,6 +77,10 @@ esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, b btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_OPEN; @@ -79,6 +96,10 @@ esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id) btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CLOSE; @@ -92,6 +113,10 @@ esp_err_t esp_ble_gattc_config_mtu (esp_gatt_if_t gattc_if, uint16_t conn_id, ui btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + if ((mtu < ESP_GATT_DEF_BLE_MTU_SIZE) || (mtu > ESP_GATT_MAX_MTU_SIZE)) { return ESP_GATT_ILLEGAL_PARAMETER; } @@ -110,6 +135,10 @@ esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_SEARCH_SERVICE; @@ -133,6 +162,10 @@ esp_err_t esp_ble_gattc_get_characteristic(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; if (start_char_id) { @@ -158,6 +191,10 @@ esp_err_t esp_ble_gattc_get_descriptor(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; @@ -185,6 +222,10 @@ esp_err_t esp_ble_gattc_get_included_service(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; @@ -211,6 +252,10 @@ esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR; @@ -232,6 +277,10 @@ esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR_DESCR; @@ -256,6 +305,10 @@ esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR; @@ -283,6 +336,10 @@ esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR_DESCR; @@ -311,6 +368,10 @@ esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_PREPARE_WRITE; @@ -330,6 +391,10 @@ esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_EXECUTE_WRITE; @@ -347,6 +412,10 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_REG_FOR_NOTIFY; @@ -366,6 +435,10 @@ esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_UNREG_FOR_NOTIFY; diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index 2504e58f8f3..1f0d668e5a9 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -15,6 +15,7 @@ #include "string.h" #include "esp_gatt_defs.h" #include "esp_gatts_api.h" +#include "esp_bt_main.h" #include "btc_manage.h" #include "btc_gatts.h" #include "btc_gatt_util.h" @@ -23,6 +24,9 @@ esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback) { + if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } return (btc_profile_cb_set(BTC_PID_GATTS, callback) == 0 ? ESP_OK : ESP_FAIL); } @@ -31,6 +35,10 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + //if (app_id < ESP_APP_ID_MIN || app_id > ESP_APP_ID_MAX) { if (app_id > ESP_APP_ID_MAX) { return ESP_ERR_INVALID_ARG; @@ -50,6 +58,10 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_APP_UNREGISTER; @@ -64,6 +76,10 @@ esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CREATE_SERVICE; @@ -80,6 +96,10 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_INCLUDE_SERVICE; @@ -96,6 +116,10 @@ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_ btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_CHAR; @@ -115,6 +139,10 @@ esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_CHAR_DESCR; @@ -130,6 +158,10 @@ esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_DELETE_SERVICE; @@ -143,6 +175,10 @@ esp_err_t esp_ble_gatts_start_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_START_SERVICE; @@ -156,6 +192,10 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_STOP_SERVICE; @@ -171,6 +211,10 @@ esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_SEND_INDICATE; @@ -189,6 +233,10 @@ esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_SEND_RESPONSE; @@ -205,6 +253,10 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, b btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_OPEN; @@ -220,6 +272,10 @@ esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id) btc_msg_t msg; btc_ble_gatts_args_t arg; + if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + return ESP_ERR_INVALID_STATE; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CLOSE; diff --git a/components/bt/bluedroid/api/include/esp_bt_device.h b/components/bt/bluedroid/api/include/esp_bt_device.h index 51c24f28812..c84d042d664 100644 --- a/components/bt/bluedroid/api/include/esp_bt_device.h +++ b/components/bt/bluedroid/api/include/esp_bt_device.h @@ -26,7 +26,7 @@ extern "C" { * * @brief Get bluetooth device address. Must use after "esp_bluedroid_enable". * - * @return bluetooth device address (six bytes) + * @return bluetooth device address (six bytes), or NULL if bluetooth stack is not enabled */ const uint8_t *esp_bt_dev_get_address(void); diff --git a/components/bt/bluedroid/api/include/esp_bt_main.h b/components/bt/bluedroid/api/include/esp_bt_main.h index b24daf33cd4..fad010d2c2b 100644 --- a/components/bt/bluedroid/api/include/esp_bt_main.h +++ b/components/bt/bluedroid/api/include/esp_bt_main.h @@ -21,6 +21,23 @@ extern "C" { #endif +/** + * @brief Bluetooth stack status type, to indicate whether the bluetooth stack is ready + */ +typedef enum { + ESP_BLUEDROID_STATUS_UNINITIALIZED = 0, /*!< Bluetooth not initialized */ + ESP_BLUEDROID_STATUS_INITIALIZED, /*!< Bluetooth initialized but not enabled */ + ESP_BLUEDROID_STATUS_ENABLED /*!< Bluetooth initialized and enabled */ +} esp_bluedroid_status_t; + +/** + * @brief Get bluetooth stack status + * + * @return Bluetooth stack status + * + */ +esp_bluedroid_status_t esp_bluedroid_get_status(void); + /** * @brief Enable bluetooth, must after esp_bluedroid_init() * diff --git a/components/bt/bluedroid/btc/core/btc_main.c b/components/bt/bluedroid/btc/core/btc_main.c index 6fae3af4357..323a81f75ac 100644 --- a/components/bt/bluedroid/btc/core/btc_main.c +++ b/components/bt/bluedroid/btc/core/btc_main.c @@ -42,14 +42,14 @@ static void btc_sec_callback(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *p_data) static void btc_enable_bluetooth(void) { if (BTA_EnableBluetooth(btc_sec_callback) != BTA_SUCCESS) { - future_ready(*btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE), FUTURE_SUCCESS); + future_ready(*btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE), FUTURE_FAIL); } } static void btc_disable_bluetooth(void) { if (BTA_DisableBluetooth() != BTA_SUCCESS) { - future_ready(*btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE), FUTURE_SUCCESS); + future_ready(*btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE), FUTURE_FAIL); } } diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 6a14d40684a..2eae5dd4cf8 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -6,5 +6,4 @@ Bluetooth Bluetooth Controller && VHCI Bluetooth Common - Bluetooth Classic Bluetooth LE diff --git a/docs/api/esp_blufi.rst b/docs/api/esp_blufi.rst index f620edeb83e..9dd80851665 100644 --- a/docs/api/esp_blufi.rst +++ b/docs/api/esp_blufi.rst @@ -5,9 +5,9 @@ Overview -------- BLUFI is a profile based GATT to config ESP32 WIFI to connect/disconnect AP or setup a softap and etc. Use should concern these things: - 1. The event sent from profile. Then you need to do something as the event indicate. - 2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. - Even you can define the "Key Exchange/Negotiation" procedure. +1. The event sent from profile. Then you need to do something as the event indicate. +2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. +Even you can define the "Key Exchange/Negotiation" procedure. Application Example ------------------- diff --git a/docs/api/esp_bt_main.rst b/docs/api/esp_bt_main.rst index cc6e80b2e51..48bb0c9cc0e 100644 --- a/docs/api/esp_bt_main.rst +++ b/docs/api/esp_bt_main.rst @@ -34,6 +34,8 @@ Type Definitions Enumerations ^^^^^^^^^^^^ +.. doxygenenum:: esp_bluedroid_status_t + Structures ^^^^^^^^^^ @@ -42,6 +44,7 @@ Structures Functions ^^^^^^^^^ +.. doxygenfunction:: esp_bluedroid_get_status .. doxygenfunction:: esp_bluedroid_enable .. doxygenfunction:: esp_bluedroid_disable .. doxygenfunction:: esp_bluedroid_init From ee59fa75f4edaf5124369a7751382ece9c6e32ef Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 11 Jan 2017 11:25:56 +0800 Subject: [PATCH 119/167] Rename SPI Master IO pins to more common terminology, add better explanation to queue_length initialization value --- components/driver/include/driver/spi_master.h | 12 +-- components/driver/spi_master.c | 78 +++++++++---------- components/driver/test/test_spi_master.c | 12 ++- examples/26_spi_master/main/spi_master.c | 10 +-- 4 files changed, 55 insertions(+), 57 deletions(-) diff --git a/components/driver/include/driver/spi_master.h b/components/driver/include/driver/spi_master.h index ea901b9924d..83110d9d05d 100644 --- a/components/driver/include/driver/spi_master.h +++ b/components/driver/include/driver/spi_master.h @@ -46,11 +46,11 @@ typedef enum { * the IO_MUX or are -1. In that case, the IO_MUX is used, allowing for >40MHz speeds. */ typedef struct { - int spid_io_num; ///< GPIO pin for spi_d (=MOSI)signal, or -1 if not used. - int spiq_io_num; ///< GPIO pin for spi_q (=MISO) signal, or -1 if not used. - int spiclk_io_num; ///< GPIO pin for spi_clk signal, or -1 if not used. - int spiwp_io_num; ///< GPIO pin for spi_wp signal, or -1 if not used. - int spihd_io_num; ///< GPIO pin for spi_hd signal, or -1 if not used. + int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used. + int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used. + int sclk_io_num; ///< GPIO pin for Spi CLocK signal, or -1 if not used. + int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used. + int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used. } spi_bus_config_t; @@ -80,7 +80,7 @@ typedef struct { int clock_speed_hz; ///< Clock speed, in Hz int spics_io_num; ///< CS GPIO pin for this device, or -1 if not used uint32_t flags; ///< Bitwise OR of SPI_DEVICE_* flags - int queue_size; ///< Transaction queue size + int queue_size; ///< Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_device_queue_trans but not yet finished using spi_device_get_trans_result) at the same time transaction_cb_t pre_cb; ///< Callback to be called before a transmission is started. This callback is called within interrupt context. transaction_cb_t post_cb; ///< Callback to be called after a transmission has completed. This callback is called within interrupt context. } spi_device_interface_config_t; diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 1f1ea1ef083..e9b8fb3cb04 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -200,11 +200,11 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, spi_bus_config_t *bus_confi SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); SPI_CHECK(spihost[host]==NULL, "host already in use", ESP_ERR_INVALID_STATE); - SPI_CHECK(bus_config->spid_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spid_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG); - SPI_CHECK(bus_config->spiclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spiclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG); - SPI_CHECK(bus_config->spiq_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->spiq_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG); - SPI_CHECK(bus_config->spiwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spiwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG); - SPI_CHECK(bus_config->spihd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->spihd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->mosi_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->sclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->sclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->miso_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->miso_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->quadwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->quadhd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadhd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG); //The host struct contains two dma descriptors, so we need DMA'able memory for this. spihost[host]=pvPortMallocCaps(sizeof(spi_host_t), MALLOC_CAP_DMA); @@ -212,51 +212,51 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, spi_bus_config_t *bus_confi memset(spihost[host], 0, sizeof(spi_host_t)); //Check if the selected pins correspond to the native pins of the peripheral - if (bus_config->spid_io_num >= 0 && bus_config->spid_io_num!=io_signal[host].spid_native) native=false; - if (bus_config->spiq_io_num >= 0 && bus_config->spiq_io_num!=io_signal[host].spiq_native) native=false; - if (bus_config->spiclk_io_num >= 0 && bus_config->spiclk_io_num!=io_signal[host].spiclk_native) native=false; - if (bus_config->spiwp_io_num >= 0 && bus_config->spiwp_io_num!=io_signal[host].spiwp_native) native=false; - if (bus_config->spihd_io_num >= 0 && bus_config->spihd_io_num!=io_signal[host].spihd_native) native=false; + if (bus_config->mosi_io_num >= 0 && bus_config->mosi_io_num!=io_signal[host].spid_native) native=false; + if (bus_config->miso_io_num >= 0 && bus_config->miso_io_num!=io_signal[host].spiq_native) native=false; + if (bus_config->sclk_io_num >= 0 && bus_config->sclk_io_num!=io_signal[host].spiclk_native) native=false; + if (bus_config->quadwp_io_num >= 0 && bus_config->quadwp_io_num!=io_signal[host].spiwp_native) native=false; + if (bus_config->quadhd_io_num >= 0 && bus_config->quadhd_io_num!=io_signal[host].spihd_native) native=false; spihost[host]->no_gpio_matrix=native; if (native) { //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway. - if (bus_config->spid_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spid_io_num], 1); - if (bus_config->spiq_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiq_io_num], 1); - if (bus_config->spiwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiwp_io_num], 1); - if (bus_config->spihd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spihd_io_num], 1); - if (bus_config->spiclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiclk_io_num], 1); + if (bus_config->mosi_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], 1); + if (bus_config->miso_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], 1); + if (bus_config->quadwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], 1); + if (bus_config->quadhd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], 1); + if (bus_config->sclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], 1); } else { //Use GPIO - if (bus_config->spid_io_num>0) { - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spid_io_num], PIN_FUNC_GPIO); - gpio_set_direction(bus_config->spid_io_num, GPIO_MODE_OUTPUT); - gpio_matrix_out(bus_config->spid_io_num, io_signal[host].spid_out, false, false); - gpio_matrix_in(bus_config->spid_io_num, io_signal[host].spid_in, false); + if (bus_config->mosi_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->mosi_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->mosi_io_num, io_signal[host].spid_out, false, false); + gpio_matrix_in(bus_config->mosi_io_num, io_signal[host].spid_in, false); } - if (bus_config->spiq_io_num>0) { - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiq_io_num], PIN_FUNC_GPIO); - gpio_set_direction(bus_config->spiq_io_num, GPIO_MODE_INPUT); - gpio_matrix_out(bus_config->spiq_io_num, io_signal[host].spiq_out, false, false); - gpio_matrix_in(bus_config->spiq_io_num, io_signal[host].spiq_in, false); + if (bus_config->miso_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT); + gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false); + gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false); } - if (bus_config->spiwp_io_num>0) { - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiwp_io_num], PIN_FUNC_GPIO); - gpio_set_direction(bus_config->spiwp_io_num, GPIO_MODE_OUTPUT); - gpio_matrix_out(bus_config->spiwp_io_num, io_signal[host].spiwp_out, false, false); - gpio_matrix_in(bus_config->spiwp_io_num, io_signal[host].spiwp_in, false); + if (bus_config->quadwp_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->quadwp_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->quadwp_io_num, io_signal[host].spiwp_out, false, false); + gpio_matrix_in(bus_config->quadwp_io_num, io_signal[host].spiwp_in, false); } - if (bus_config->spihd_io_num>0) { - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spihd_io_num], PIN_FUNC_GPIO); - gpio_set_direction(bus_config->spihd_io_num, GPIO_MODE_OUTPUT); - gpio_matrix_out(bus_config->spihd_io_num, io_signal[host].spihd_out, false, false); - gpio_matrix_in(bus_config->spihd_io_num, io_signal[host].spihd_in, false); + if (bus_config->quadhd_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->quadhd_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->quadhd_io_num, io_signal[host].spihd_out, false, false); + gpio_matrix_in(bus_config->quadhd_io_num, io_signal[host].spihd_in, false); } - if (bus_config->spiclk_io_num>0) { - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spiclk_io_num], PIN_FUNC_GPIO); - gpio_set_direction(bus_config->spiclk_io_num, GPIO_MODE_OUTPUT); - gpio_matrix_out(bus_config->spiclk_io_num, io_signal[host].spiclk_out, false, false); + if (bus_config->sclk_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->sclk_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->sclk_io_num, io_signal[host].spiclk_out, false, false); } } periph_module_enable(io_signal[host].module); diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c index 0fd47cbdbae..119c94bc570 100644 --- a/components/driver/test/test_spi_master.c +++ b/components/driver/test/test_spi_master.c @@ -21,11 +21,11 @@ TEST_CASE("SPI Master test", "[spi]") { spi_bus_config_t buscfg={ - .spid_io_num=4, - .spiq_io_num=16, - .spiclk_io_num=25, - .spiwp_io_num=-1, - .spihd_io_num=-1 + .mosi_io_num=4, + .miso_io_num=16, + .sclk_io_num=25, + .quadwp_io_num=-1, + .quadhd_io_num=-1 }; spi_device_interface_config_t devcfg={ .command_bits=8, @@ -33,8 +33,6 @@ TEST_CASE("SPI Master test", "[spi]") .dummy_bits=0, .clock_speed_hz=8000, .duty_cycle_pos=128, - .cs_ena_pretrans=7, - .cs_ena_posttrans=7, .mode=0, .spics_io_num=21, .queue_size=3 diff --git a/examples/26_spi_master/main/spi_master.c b/examples/26_spi_master/main/spi_master.c index 2013dfd9c95..577cada4c2a 100644 --- a/examples/26_spi_master/main/spi_master.c +++ b/examples/26_spi_master/main/spi_master.c @@ -249,11 +249,11 @@ void app_main() esp_err_t ret; spi_device_handle_t spi; spi_bus_config_t buscfg={ - .spiq_io_num=PIN_NUM_MISO, - .spid_io_num=PIN_NUM_MOSI, - .spiclk_io_num=PIN_NUM_CLK, - .spiwp_io_num=-1, - .spihd_io_num=-1 + .miso_io_num=PIN_NUM_MISO, + .mosi_io_num=PIN_NUM_MOSI, + .sclk_io_num=PIN_NUM_CLK, + .quadwp_io_num=-1, + .quadhd_io_num=-1 }; spi_device_interface_config_t devcfg={ .clock_speed_hz=10000000, //Clock out at 10 MHz From 0c31bdf643e1cdb67c2120407cd94453aa1d01c9 Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Wed, 11 Jan 2017 11:31:35 +0800 Subject: [PATCH 120/167] component/bt: check for registration status of callback function before using it --- .../bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c | 7 ++++++- .../bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c | 8 ++++++-- .../bt/bluedroid/btc/profile/std/gatt/btc_gattc.c | 8 +++++++- .../bt/bluedroid/btc/profile/std/gatt/btc_gatts.c | 13 +++++++++---- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index c126273a723..85be60b7b22 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -35,7 +35,12 @@ #include "esp_blufi_api.h" -#define BTC_BLUFI_CB_TO_APP(event, param) ((esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI))((event), (param)) +#define BTC_BLUFI_CB_TO_APP(event, param) do { \ + esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI); \ + if (btc_blufi_cb) { \ + btc_blufi_cb(event, param); \ + } \ + } while (0) #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index ede530e7725..e2f4815087c 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -26,8 +26,12 @@ static tBTA_BLE_ADV_DATA gl_bta_adv_data; static tBTA_BLE_ADV_DATA gl_bta_scan_rsp_data; -#define BTC_GAP_BLE_CB_TO_APP(event, param) ((esp_gap_ble_cb_t)btc_profile_cb_get(BTC_PID_GAP_BLE))((event), (param)) - +#define BTC_GAP_BLE_CB_TO_APP(event, param) do { \ + esp_gap_ble_cb_t btc_gap_ble_cb = (esp_gap_ble_cb_t)btc_profile_cb_get(BTC_PID_GAP_BLE); \ + if (btc_gap_ble_cb) { \ + btc_gap_ble_cb(event, param); \ + } \ + } while (0) static void btc_gap_adv_point_cleanup(void **buf) { 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 9bc7b0d2bd2..4b6947940f7 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -22,7 +22,13 @@ #include "bt_trace.h" #include "esp_gattc_api.h" -#define BTC_GATTC_CB_TO_APP(event, gattc_if, param) ((esp_gattc_cb_t )btc_profile_cb_get(BTC_PID_GATTC))((event), (gattc_if), (param)) +#define BTC_GATTC_CB_TO_APP(event, gattc_if, param) do { \ + esp_gattc_cb_t btc_gattc_cb = (esp_gattc_cb_t )btc_profile_cb_get(BTC_PID_GATTC); \ + if (btc_gattc_cb) { \ + btc_gattc_cb(event, gattc_if, param); \ + } \ + } while (0) + void btc_gattc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { 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 c7afabfa2a6..3a988fc3b11 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -23,7 +23,12 @@ #include "esp_gatts_api.h" -#define BTC_GATTS_CB_TO_APP(event, gatts_if, param) ((esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS))((event), (gatts_if), (param)) +#define BTC_GATTS_CB_TO_APP(event, gatts_if, param) do { \ + esp_gatts_cb_t btc_gatts_cb = (esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS); \ + if (btc_gatts_cb) { \ + btc_gatts_cb(event, gatts_if, param); \ + } \ + } while (0) #define A2C_GATTS_EVT(_bta_event) (_bta_event) //BTA TO BTC EVT #define C2A_GATTS_EVT(_btc_event) (_btc_event) //BTC TO BTA EVT @@ -299,9 +304,9 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.read.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.read.trans_id = p_data->req_data.trans_id; memcpy(param.read.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); - param.read.handle = p_data->req_data.p_data->read_req.handle, - param.read.offset = p_data->req_data.p_data->read_req.offset, - param.read.is_long = p_data->req_data.p_data->read_req.is_long, + param.read.handle = p_data->req_data.p_data->read_req.handle; + param.read.offset = p_data->req_data.p_data->read_req.offset; + param.read.is_long = p_data->req_data.p_data->read_req.is_long; BTC_GATTS_CB_TO_APP(ESP_GATTS_READ_EVT, gatts_if, ¶m); break; From a2e0c2432e4874a0346aa587885657217e965fcb Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 01:14:18 +0800 Subject: [PATCH 121/167] esp32: sanity check ISR handler address passed into esp_intr_alloc Return ESP_ERR_INVALID_ARG if the handler is not in IRAM (or RTC fast memory) --- components/esp32/crosscore_int.c | 6 +++-- components/esp32/include/esp_intr_alloc.h | 5 ++++- components/esp32/intr_alloc.c | 6 +++++ components/esp32/test/test_intr_alloc.c | 27 +++++++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index b58d3d62c5c..1e131eeef71 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -73,11 +73,13 @@ void esp_crosscore_int_init() { portENTER_CRITICAL(&reasonSpinlock); reason[xPortGetCoreID()]=0; portEXIT_CRITICAL(&reasonSpinlock); + esp_err_t err; if (xPortGetCoreID()==0) { - esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); + err = esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } else { - esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); + err = esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } + assert(err == ESP_OK); } void esp_crosscore_int_send_yield(int coreId) { diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h index c1f91dd2e36..7195d07d870 100644 --- a/components/esp32/include/esp_intr_alloc.h +++ b/components/esp32/include/esp_intr_alloc.h @@ -124,6 +124,9 @@ esp_err_t esp_intr_reserve(int intno, int cpu); * * The interrupt will always be allocated on the core that runs this function. * + * If ESP_INTR_FLAG_IRAM flag is used, and handler address is not in IRAM or + * RTC_FAST_MEM, then ESP_ERR_INVALID_ARG is returned. + * * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux * sources, as defined in soc/soc.h, or one of the internal * ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header. @@ -264,4 +267,4 @@ void esp_intr_noniram_enable(); } #endif -#endif \ No newline at end of file +#endif diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index 4fdda2f396f..1cb2fba5d9d 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -446,6 +446,12 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre if ((flags&ESP_INTR_FLAG_SHARED) && (!handler || source<0)) return ESP_ERR_INVALID_ARG; //Statusreg should have a mask if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; + //If the ISR is marked to be IRAM-resident, the handler must not be in the cached region + if ((flags&ESP_INTR_FLAG_IRAM) && + (ptrdiff_t) handler >= 0x400C0000 && + (ptrdiff_t) handler < 0x50000000 ) { + return ESP_ERR_INVALID_ARG; + } //Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts. if ((flags&ESP_INTR_FLAG_LEVELMASK)==0) { diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp32/test/test_intr_alloc.c index 329fc9a9e4d..a5e6f068348 100644 --- a/components/esp32/test/test_intr_alloc.c +++ b/components/esp32/test/test_intr_alloc.c @@ -201,3 +201,30 @@ TEST_CASE("Intr_alloc test, shared ints", "[esp32]") { timer_test(ESP_INTR_FLAG_SHARED); } + +TEST_CASE("Can allocate IRAM int only with an IRAM handler", "[esp32]") +{ + void dummy(void* arg) + { + } + IRAM_ATTR void dummy_iram(void* arg) + { + } + RTC_IRAM_ATTR void dummy_rtc(void* arg) + { + } + intr_handle_t ih; + esp_err_t err = esp_intr_alloc(ETS_INTERNAL_PROFILING_INTR_SOURCE, + ESP_INTR_FLAG_IRAM, &dummy, NULL, &ih); + TEST_ASSERT_EQUAL_INT(ESP_ERR_INVALID_ARG, err); + err = esp_intr_alloc(ETS_INTERNAL_PROFILING_INTR_SOURCE, + ESP_INTR_FLAG_IRAM, &dummy_iram, NULL, &ih); + TEST_ESP_OK(err); + err = esp_intr_free(ih); + TEST_ESP_OK(err); + err = esp_intr_alloc(ETS_INTERNAL_PROFILING_INTR_SOURCE, + ESP_INTR_FLAG_IRAM, &dummy_rtc, NULL, &ih); + TEST_ESP_OK(err); + err = esp_intr_free(ih); + TEST_ESP_OK(err); +} From 69a7cfa9766e6b712fd7aa2f2a9e356423b19fe2 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 11 Jan 2017 11:55:23 +0800 Subject: [PATCH 122/167] Also update documentation to new conventions --- docs/api/spi_master.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api/spi_master.rst b/docs/api/spi_master.rst index 4d7693c07cb..5b57d85f426 100644 --- a/docs/api/spi_master.rst +++ b/docs/api/spi_master.rst @@ -27,18 +27,18 @@ The spi_master driver uses the following terms: now, only HSPI or VSPI are actually supported in the driver; it will support all 3 peripherals somewhere in the future.) * Bus: The SPI bus, common to all SPI devices connected to one host. In general the bus consists of the - spid, spiq, spiclk and optionally spiwp and spihd signals. The SPI slaves are connected to these + miso, mosi, sclk and optionally quadwp and quadhd signals. The SPI slaves are connected to these signals in parallel. - - spiq - Also known as MISO, this is the input of the serial stream into the ESP32 + - miso - Also known as q, this is the input of the serial stream into the ESP32 - - spid - Also known as MOSI, this is the output of the serial stream from the ESP32 + - mosi - Also known as d, this is the output of the serial stream from the ESP32 - - spiclk - Clock signal. Each data bit is clocked out or in on the positive or negative edge of this signal + - sclk - Clock signal. Each data bit is clocked out or in on the positive or negative edge of this signal - - spiwp - Write Protect signal. Only used for 4-bit (qio/qout) transactions. + - quadwp - Write Protect signal. Only used for 4-bit (qio/qout) transactions. - - spihd - Hold signal. Only used for 4-bit (qio/qout) transactions. + - quadhd - Hold signal. Only used for 4-bit (qio/qout) transactions. * Device: A SPI slave. Each SPI slave has its own chip select (CS) line, which is made active when a transmission to/from the SPI slave occurs. From b455b0246c09cc27589228b7222da8ada0a83f12 Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Tue, 10 Jan 2017 17:50:32 +0800 Subject: [PATCH 123/167] add wpa2 set id api and modify null data tx description 1. wpa2: add wpa2 set id api 2. low rate: modify null data tx description --- components/esp32/include/esp_wpa2.h | 101 ++++++++++++++++++---------- components/esp32/lib | 2 +- 2 files changed, 66 insertions(+), 37 deletions(-) diff --git a/components/esp32/include/esp_wpa2.h b/components/esp32/include/esp_wpa2.h index 1857d19a16e..e33e0e6dcfa 100644 --- a/components/esp32/include/esp_wpa2.h +++ b/components/esp32/include/esp_wpa2.h @@ -24,35 +24,58 @@ extern "C" { /** * @brief Enable wpa2 enterprise authentication. * - * @attention wpa2 enterprise authentication can only be used when ESP32 station is enabled. - * wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. + * @attention 1. wpa2 enterprise authentication can only be used when ESP32 station is enabled. + * @attention 2. wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. * - * @return ESP_ERR_WIFI_OK: succeed. - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed. + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_enable(void); /** * @brief Disable wpa2 enterprise authentication. * - * @attention wpa2 enterprise authentication can only be used when ESP32 station is enabled. - * wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. + * @attention 1. wpa2 enterprise authentication can only be used when ESP32 station is enabled. + * @attention 2. wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. * - * @return ESP_ERR_WIFI_OK: succeed. + * @return + * - ESP_ERR_WIFI_OK: succeed. */ esp_err_t esp_wifi_sta_wpa2_ent_disable(void); +/** + * @brief Set identity for PEAP/TTLS method. + * + * @attention The API only passes the parameter identity to the global pointer variable in wpa2 enterprise module. + * + * @param identity: point to address where stores the identity; + * @param len: length of identity, limited to 1~127 + * + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0 or len >= 128) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + */ +esp_err_t esp_wifi_sta_wpa2_ent_set_identity(unsigned char *identity, int len); + +/** + * @brief Clear identity for PEAP/TTLS method. + */ +void esp_wifi_sta_wpa2_ent_clear_identity(void); + /** * @brief Set username for PEAP/TTLS method. * * @attention The API only passes the parameter username to the global pointer variable in wpa2 enterprise module. * * @param username: point to address where stores the username; - * len: length of username, limited to 1~127 + * @param len: length of username, limited to 1~127 * - * @return ESP_ERR_WIFI_OK: succeed - * ESP_ERR_WIFI_ARG: fail(len <= 0 or len >= 128) - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0 or len >= 128) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_set_username(unsigned char *username, int len); @@ -67,11 +90,12 @@ void esp_wifi_sta_wpa2_ent_clear_username(void); * @attention The API only passes the parameter password to the global pointer variable in wpa2 enterprise module. * * @param password: point to address where stores the password; - * len: length of password(len > 0) + * @param len: length of password(len > 0) * - * @return ESP_ERR_WIFI_OK: succeed - * ESP_ERR_WIFI_ARG: fail(len <= 0) - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_set_password(unsigned char *password, int len); @@ -83,15 +107,16 @@ void esp_wifi_sta_wpa2_ent_clear_password(void); /** * @brief Set new password for MSCHAPv2 method.. * - * @attention The API only passes the parameter password to the global pointer variable in wpa2 enterprise module. - * The new password is used to substitute the old password when eap-mschapv2 failure request message with error code ERROR_PASSWD_EXPIRED is received. + * @attention 1. The API only passes the parameter password to the global pointer variable in wpa2 enterprise module. + * @attention 2. The new password is used to substitute the old password when eap-mschapv2 failure request message with error code ERROR_PASSWD_EXPIRED is received. * * @param password: point to address where stores the password; - * len: length of password + * @param len: length of password * - * @return ESP_ERR_WIFI_OK: succeed - * ESP_ERR_WIFI_ARG: fail(len <= 0) - * ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) + * @return + * - ESP_ERR_WIFI_OK: succeed + * - ESP_ERR_WIFI_ARG: fail(len <= 0) + * - ESP_ERR_WIFI_NO_MEM: fail(internal memory malloc fail) */ esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(unsigned char *password, int len); @@ -104,13 +129,14 @@ void esp_wifi_sta_wpa2_ent_clear_new_password(void); /** * @brief Set CA certificate for PEAP/TTLS method. * - * @attention The API only passes the parameter ca_cert to the global pointer variable in wpa2 enterprise module. - * The ca_cert should be zero terminated. + * @attention 1. The API only passes the parameter ca_cert to the global pointer variable in wpa2 enterprise module. + * @attention 2. The ca_cert should be zero terminated. * * @param ca_cert: point to address where stores the CA certificate; - * len: length of ca_cert + * @param len: length of ca_cert * - * @return ESP_ERR_WIFI_OK: succeed + * @return + * - ESP_ERR_WIFI_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(unsigned char *ca_cert, int len); @@ -122,17 +148,18 @@ void esp_wifi_sta_wpa2_ent_clear_ca_cert(void); /** * @brief Set client certificate and key. * - * @attention The API only passes the parameter client_cert, private_key and private_key_passwd to the global pointer variable in wpa2 enterprise module. - * The client_cert, private_key and private_key_passwd should be zero terminated. + * @attention 1. The API only passes the parameter client_cert, private_key and private_key_passwd to the global pointer variable in wpa2 enterprise module. + * @attention 2. The client_cert, private_key and private_key_passwd should be zero terminated. * * @param client_cert: point to address where stores the client certificate; - * client_cert_len: length of client certificate; - * private_key: point to address where stores the private key; - * private_key_len: length of private key, limited to 1~2048; - * private_key_password: point to address where stores the private key password; - * private_key_password_len: length of private key password; + * @param client_cert_len: length of client certificate; + * @param private_key: point to address where stores the private key; + * @param private_key_len: length of private key, limited to 1~2048; + * @param private_key_password: point to address where stores the private key password; + * @param private_key_password_len: length of private key password; * - * @return ESP_ERR_WIFI_OK: succeed + * @return + * - ESP_ERR_WIFI_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_set_cert_key(unsigned char *client_cert, int client_cert_len, unsigned char *private_key, int private_key_len, unsigned char *private_key_passwd, int private_key_passwd_len); @@ -145,9 +172,10 @@ void esp_wifi_sta_wpa2_ent_clear_cert_key(void); * @brief Set wpa2 enterprise certs time check(disable or not). * * @param true: disable wpa2 enterprise certs time check - * false: enable wpa2 enterprise certs time check + * @param false: enable wpa2 enterprise certs time check * - * @return ESP_OK: succeed + * @return + * - ESP_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable); @@ -156,7 +184,8 @@ esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable); * * @param disable: store disable value * - * @return ESP_OK: succeed + * @return + * - ESP_OK: succeed */ esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable); diff --git a/components/esp32/lib b/components/esp32/lib index 231ee92755a..f688a5e1b2f 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 231ee92755a41c92f6243e0550557ce9e1131744 +Subproject commit f688a5e1b2f5e4cb8dd2cdbd8dedf63a74b1d063 From a98d07d65027ea8e8e3bfc81203c6e8893aab869 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 11 Jan 2017 13:01:48 +0800 Subject: [PATCH 124/167] Fix clock divider calculation --- components/driver/spi_master.c | 60 ++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index e9b8fb3cb04..204e577edac 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -389,39 +389,51 @@ esp_err_t spi_bus_remove_device(spi_device_handle_t handle) return ESP_OK; } +static int spi_freq_for_pre_n(int fapb, int pre, int n) { + return (fapb / (pre * n)); +} + static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) { int pre, n, h, l; - //In hw, n, h and l are 1-32, pre is 0-8K. Value written to register is one lower than used value. - if (hz>(fapb/2)) { - //Can only solve this using fapb directly. + + //In hw, n, h and l are 1-32, pre is 1-8K. Value written to register is one lower than used value. + if (hz>((fapb/4)*3)) { + //Using Fapb directly will give us the best result here. hw->clock.clkcnt_l=0; hw->clock.clkcnt_h=0; hw->clock.clkcnt_n=0; hw->clock.clkdiv_pre=0; hw->clock.clk_equ_sysclk=1; } else { - //For best duty cycle resolution, we want n to be as close to 32 as possible. - //ToDo: - //This algo could use some tweaking; at the moment it either fixes n to 32 and - //uses the prescaler to get a suitable division factor, or sets the prescaler to 0 - //and uses n to set a value. In practice, sometimes a better result can be - //obtained by setting both n and pre to well-chosen valued... ToDo: fix up some algo to - //do this automatically (worst-case: bruteforce n/pre combo's) - JD - //Also ToDo: - //The ESP32 has a SPI_CK_OUT_HIGH_MODE and SPI_CK_OUT_LOW_MODE register; it looks like we can - //use those to specify the duty cycle in a more precise way. Figure out how to use these. - JD - n=(fapb/(hz*32)); - if (n>32) { - //Need to use prescaler - n=32; + //For best duty cycle resolution, we want n to be as close to 32 as possible, but + //we also need a pre/n combo that gets us as close as possible to the intended freq. + //To do this, we bruteforce n and calculate the best pre to go along with that. + //If there's a choice between pre/n combos that give the same result, use the one + //with the higher n. + int bestn=-1; + int bestpre=-1; + int besterr=hz; + int errval; + for (n=1; n<33; n++) { + //Effectively, this does pre=round((fapb/n)/hz). + pre=((fapb/n)+(hz/2))/hz; + if (pre<0) pre=0; + if (pre>8192) pre=8192; + errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz); + if (errval<=besterr) { + besterr=errval; + bestn=n; + bestpre=pre; + } } - if (n<32) { - //No need for prescaler. - n=(fapb/hz); - } - pre=(fapb/n)/hz; - h=n; - l=(((256-duty_cycle)*n+127)/256); + + n=bestn; + pre=bestpre; + l=n; + //This effectively does round((duty_cycle*n)/256) + h=(duty_cycle*n+127)/256; + if (h<=0) h=1; + hw->clock.clk_equ_sysclk=0; hw->clock.clkcnt_n=n-1; hw->clock.clkdiv_pre=pre-1; From c5927146791cd166edd86d8a4b30c78c14dc215c Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Wed, 11 Jan 2017 13:36:48 +0800 Subject: [PATCH 125/167] component/bt: fix typos in "if" condition espressions --- components/bt/bluedroid/api/esp_blufi_api.c | 8 ++--- components/bt/bluedroid/api/esp_bt_device.c | 2 +- components/bt/bluedroid/api/esp_gap_ble_api.c | 30 ++++++++-------- components/bt/bluedroid/api/esp_gattc_api.c | 36 +++++++++---------- components/bt/bluedroid/api/esp_gatts_api.c | 28 +++++++-------- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index ac84ee3fc24..70f5c9ce8f3 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -24,7 +24,7 @@ esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) { - if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { return ESP_ERR_INVALID_STATE; } @@ -41,7 +41,7 @@ esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn btc_msg_t msg; btc_blufi_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -61,7 +61,7 @@ esp_err_t esp_blufi_profile_init(void) { btc_msg_t msg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -76,7 +76,7 @@ esp_err_t esp_blufi_profile_deinit(void) { btc_msg_t msg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } diff --git a/components/bt/bluedroid/api/esp_bt_device.c b/components/bt/bluedroid/api/esp_bt_device.c index 1eb48c98acb..dffcbe80b20 100644 --- a/components/bt/bluedroid/api/esp_bt_device.c +++ b/components/bt/bluedroid/api/esp_bt_device.c @@ -19,7 +19,7 @@ const uint8_t *esp_bt_dev_get_address(void) { - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return NULL; } return controller_get_interface()->get_address()->address; diff --git a/components/bt/bluedroid/api/esp_gap_ble_api.c b/components/bt/bluedroid/api/esp_gap_ble_api.c index 34914f8b4dc..f9401d8c42c 100644 --- a/components/bt/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/bluedroid/api/esp_gap_ble_api.c @@ -24,7 +24,7 @@ esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback) { - if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { return ESP_ERR_INVALID_STATE; } return (btc_profile_cb_set(BTC_PID_GAP_BLE, callback) == 0 ? ESP_OK : ESP_FAIL); @@ -36,7 +36,7 @@ esp_err_t esp_ble_gap_config_adv_data(esp_ble_adv_data_t *adv_data) btc_msg_t msg; btc_ble_gap_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -63,7 +63,7 @@ esp_err_t esp_ble_gap_set_scan_params(esp_ble_scan_params_t *scan_params) btc_msg_t msg; btc_ble_gap_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -84,7 +84,7 @@ esp_err_t esp_ble_gap_start_scanning(uint32_t duration) btc_msg_t msg; btc_ble_gap_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -100,8 +100,8 @@ esp_err_t esp_ble_gap_start_scanning(uint32_t duration) esp_err_t esp_ble_gap_stop_scanning(void) { btc_msg_t msg; - - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -116,7 +116,7 @@ esp_err_t esp_ble_gap_start_advertising(esp_ble_adv_params_t *adv_params) btc_msg_t msg; btc_ble_gap_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -132,7 +132,7 @@ esp_err_t esp_ble_gap_stop_advertising(void) { btc_msg_t msg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -149,7 +149,7 @@ esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params) btc_msg_t msg; btc_ble_gap_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -165,8 +165,8 @@ esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_ { btc_msg_t msg; btc_ble_gap_args_t arg; - - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -184,8 +184,8 @@ esp_err_t esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) { btc_msg_t msg; btc_ble_gap_args_t arg; - - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -203,7 +203,7 @@ esp_err_t esp_ble_gap_config_local_privacy (bool privacy_enable) btc_msg_t msg; btc_ble_gap_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -220,7 +220,7 @@ esp_err_t esp_ble_gap_set_device_name(const char *name) btc_msg_t msg; btc_ble_gap_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } diff --git a/components/bt/bluedroid/api/esp_gattc_api.c b/components/bt/bluedroid/api/esp_gattc_api.c index 4baa0a77fc3..8b9cc99e87e 100644 --- a/components/bt/bluedroid/api/esp_gattc_api.c +++ b/components/bt/bluedroid/api/esp_gattc_api.c @@ -22,7 +22,7 @@ esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback) { - if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { return ESP_ERR_INVALID_STATE; } @@ -39,7 +39,7 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id) btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -60,7 +60,7 @@ esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if) btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -77,7 +77,7 @@ esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, b btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -96,7 +96,7 @@ esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id) btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -113,7 +113,7 @@ esp_err_t esp_ble_gattc_config_mtu (esp_gatt_if_t gattc_if, uint16_t conn_id, ui btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -135,7 +135,7 @@ esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -162,7 +162,7 @@ esp_err_t esp_ble_gattc_get_characteristic(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -191,7 +191,7 @@ esp_err_t esp_ble_gattc_get_descriptor(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -222,7 +222,7 @@ esp_err_t esp_ble_gattc_get_included_service(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -252,7 +252,7 @@ esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -277,7 +277,7 @@ esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -305,7 +305,7 @@ esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -336,7 +336,7 @@ esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -368,7 +368,7 @@ esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -391,7 +391,7 @@ esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -412,7 +412,7 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -435,7 +435,7 @@ esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, btc_msg_t msg; btc_ble_gattc_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index 1f0d668e5a9..71b5a4338c0 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -24,7 +24,7 @@ esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback) { - if (ESP_BLUEDROID_STATUS_UNINITIALIZED == esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { return ESP_ERR_INVALID_STATE; } return (btc_profile_cb_set(BTC_PID_GATTS, callback) == 0 ? ESP_OK : ESP_FAIL); @@ -35,7 +35,7 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id) btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -58,7 +58,7 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if) btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -76,7 +76,7 @@ esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -96,7 +96,7 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -116,7 +116,7 @@ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_ btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -139,7 +139,7 @@ esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -158,7 +158,7 @@ esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -175,7 +175,7 @@ esp_err_t esp_ble_gatts_start_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -192,7 +192,7 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle) btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -211,7 +211,7 @@ esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -233,7 +233,7 @@ esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -253,7 +253,7 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, b btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } @@ -272,7 +272,7 @@ esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id) btc_msg_t msg; btc_ble_gatts_args_t arg; - if (ESP_BLUEDROID_STATUS_ENABLED != esp_bluedroid_get_status()) { + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } From babeed417050584c83eea21306723bd53770914e Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Wed, 11 Jan 2017 14:08:16 +0800 Subject: [PATCH 126/167] component/bt: change MACRO to inline function when invoking callbacks --- .../btc/profile/esp/blufi/blufi_prf.c | 61 ++++++++++--------- .../btc/profile/std/gap/btc_gap_ble.c | 21 ++++--- .../btc/profile/std/gatt/btc_gattc.c | 60 +++++++++--------- .../btc/profile/std/gatt/btc_gatts.c | 51 ++++++++-------- 4 files changed, 98 insertions(+), 95 deletions(-) diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index 85be60b7b22..e88c775f5ff 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -35,13 +35,6 @@ #include "esp_blufi_api.h" -#define BTC_BLUFI_CB_TO_APP(event, param) do { \ - esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI); \ - if (btc_blufi_cb) { \ - btc_blufi_cb(event, param); \ - } \ - } while (0) - #define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] @@ -71,6 +64,14 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data); static void btc_blufi_recv_handler(uint8_t *data, int len); static void btc_blufi_send_ack(uint8_t seq); +static inline void btc_blufi_cb_to_app(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) +{ + esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI); + if (btc_blufi_cb) { + btc_blufi_cb(event, param); + } +} + static void blufi_create_service(void) { if (!blufi_env.enabled) { @@ -682,75 +683,75 @@ void btc_blufi_cb_handler(btc_msg_t *msg) switch (msg->act) { case ESP_BLUFI_EVENT_INIT_FINISH: { - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_INIT_FINISH, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, param); break; } case ESP_BLUFI_EVENT_DEINIT_FINISH: { - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_DEINIT_FINISH, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEINIT_FINISH, param); break; } case ESP_BLUFI_EVENT_BLE_CONNECT: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_BLE_CONNECT, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_CONNECT, param); break; case ESP_BLUFI_EVENT_BLE_DISCONNECT: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_BLE_DISCONNECT, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_DISCONNECT, param); break; case ESP_BLUFI_EVENT_SET_WIFI_OPMODE: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_SET_WIFI_OPMODE, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_SET_WIFI_OPMODE, param); break; case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP, NULL); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP, NULL); break; case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP, NULL); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP, NULL); break; case ESP_BLUFI_EVENT_GET_WIFI_STATUS: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_GET_WIFI_STATUS, NULL); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_GET_WIFI_STATUS, NULL); break; case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_DEAUTHENTICATE_STA, NULL); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEAUTHENTICATE_STA, NULL); break; case ESP_BLUFI_EVENT_RECV_STA_BSSID: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_STA_BSSID, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_BSSID, param); break; case ESP_BLUFI_EVENT_RECV_STA_SSID: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_STA_SSID, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_SSID, param); break; case ESP_BLUFI_EVENT_RECV_STA_PASSWD: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_STA_PASSWD, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_PASSWD, param); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_SSID, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_SSID, param); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD, param); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM, param); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE, param); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL, param); break; case ESP_BLUFI_EVENT_RECV_USERNAME: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_USERNAME, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_USERNAME, param); break; case ESP_BLUFI_EVENT_RECV_CA_CERT: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_CA_CERT, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CA_CERT, param); break; case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_CLIENT_CERT, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_CERT, param); break; case ESP_BLUFI_EVENT_RECV_SERVER_CERT: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SERVER_CERT, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_CERT, param); break; case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY, param); break; case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: - BTC_BLUFI_CB_TO_APP(ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY, param); + btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY, param); break; default: LOG_ERROR("%s UNKNOWN %d\n", __func__, msg->act); diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index e2f4815087c..41615675386 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -26,12 +26,13 @@ static tBTA_BLE_ADV_DATA gl_bta_adv_data; static tBTA_BLE_ADV_DATA gl_bta_scan_rsp_data; -#define BTC_GAP_BLE_CB_TO_APP(event, param) do { \ - esp_gap_ble_cb_t btc_gap_ble_cb = (esp_gap_ble_cb_t)btc_profile_cb_get(BTC_PID_GAP_BLE); \ - if (btc_gap_ble_cb) { \ - btc_gap_ble_cb(event, param); \ - } \ - } while (0) +static inline void btc_gap_ble_cb_to_app(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + esp_gap_ble_cb_t btc_gap_ble_cb = (esp_gap_ble_cb_t)btc_profile_cb_get(BTC_PID_GAP_BLE); + if (btc_gap_ble_cb) { + btc_gap_ble_cb(event, param); + } +} static void btc_gap_adv_point_cleanup(void **buf) { @@ -508,16 +509,16 @@ void btc_gap_ble_cb_handler(btc_msg_t *msg) switch (msg->act) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT, param); break; case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT : - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT, param); break; case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, param); break; case ESP_GAP_BLE_SCAN_RESULT_EVT: - BTC_GAP_BLE_CB_TO_APP(ESP_GAP_BLE_SCAN_RESULT_EVT, param); + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_RESULT_EVT, param); break; default: break; 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 4b6947940f7..537be9092ce 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -22,13 +22,13 @@ #include "bt_trace.h" #include "esp_gattc_api.h" -#define BTC_GATTC_CB_TO_APP(event, gattc_if, param) do { \ - esp_gattc_cb_t btc_gattc_cb = (esp_gattc_cb_t )btc_profile_cb_get(BTC_PID_GATTC); \ - if (btc_gattc_cb) { \ - btc_gattc_cb(event, gattc_if, param); \ - } \ - } while (0) - +static inline void btc_gattc_cb_to_app(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + esp_gattc_cb_t btc_gattc_cb = (esp_gattc_cb_t )btc_profile_cb_get(BTC_PID_GATTC); + if (btc_gattc_cb) { + btc_gattc_cb(event, gattc_if, param); + } +} void btc_gattc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { @@ -203,7 +203,7 @@ static void btc_gattc_get_first_char(btc_ble_gattc_args_t *arg) memcpy(¶m.get_char.srvc_id, &arg->get_first_char.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_char.char_id, &char_id, sizeof(esp_gatt_id_t)); param.get_char.char_prop = out_char_prop; - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) @@ -233,7 +233,7 @@ static void btc_gattc_get_next_char(btc_ble_gattc_args_t *arg) memcpy(¶m.get_char.srvc_id, &arg->get_next_char.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_char.char_id, &char_id, sizeof(esp_gatt_id_t)); param.get_char.char_prop = out_char_prop; - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_CHAR_EVT, gattc_if, ¶m); } static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) @@ -262,7 +262,7 @@ static void btc_gattc_get_first_descr(btc_ble_gattc_args_t *arg) memcpy(¶m.get_descr.srvc_id, &arg->get_first_descr.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_descr.char_id, &arg->get_first_descr.char_id, sizeof(esp_gatt_id_t)); memcpy(¶m.get_descr.descr_id, &descr_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) @@ -291,7 +291,7 @@ static void btc_gattc_get_next_descr(btc_ble_gattc_args_t *arg) memcpy(¶m.get_descr.srvc_id, &arg->get_next_descr.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_descr.char_id, &arg->get_next_descr.char_id, sizeof(esp_gatt_id_t)); memcpy(¶m.get_descr.descr_id, &descr_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_DESCR_EVT, gattc_if, ¶m); } static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) @@ -316,7 +316,7 @@ static void btc_gattc_get_first_incl_service(btc_ble_gattc_args_t *arg) param.get_incl_srvc.status = status; memcpy(¶m.get_incl_srvc.srvc_id, &arg->get_first_incl_srvc.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_incl_srvc.incl_srvc_id, &incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); } static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) @@ -342,7 +342,7 @@ static void btc_gattc_get_next_incl_service(btc_ble_gattc_args_t *arg) param.get_incl_srvc.status = status; memcpy(¶m.get_incl_srvc.srvc_id, &arg->get_next_incl_srvc.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.get_incl_srvc.incl_srvc_id, &incl_srvc_id, sizeof(esp_gatt_srvc_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_GET_INCL_SRVC_EVT, gattc_if, ¶m); } static void btc_gattc_read_char(btc_ble_gattc_args_t *arg) @@ -429,7 +429,7 @@ static void btc_gattc_reg_for_notify(btc_ble_gattc_args_t *arg) param.reg_for_notify.status = status; memcpy(¶m.reg_for_notify.srvc_id, &arg->reg_for_notify.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.reg_for_notify.char_id, &arg->reg_for_notify.char_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_FOR_NOTIFY_EVT, arg->reg_for_notify.gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_REG_FOR_NOTIFY_EVT, arg->reg_for_notify.gattc_if, ¶m); } static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) @@ -449,7 +449,7 @@ static void btc_gattc_unreg_for_notify(btc_ble_gattc_args_t *arg) param.unreg_for_notify.status = status; memcpy(¶m.unreg_for_notify.srvc_id, &arg->unreg_for_notify.service_id, sizeof(esp_gatt_srvc_id_t)); memcpy(¶m.unreg_for_notify.char_id, &arg->unreg_for_notify.service_id, sizeof(esp_gatt_id_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_FOR_NOTIFY_EVT, arg->unreg_for_notify.gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_UNREG_FOR_NOTIFY_EVT, arg->unreg_for_notify.gattc_if, ¶m); } void btc_gattc_call_handler(btc_msg_t *msg) @@ -539,19 +539,19 @@ void btc_gattc_cb_handler(btc_msg_t *msg) gattc_if = reg_oper->client_if; param.reg.status = reg_oper->status; param.reg.app_id = reg_oper->app_uuid.uu.uuid16; - BTC_GATTC_CB_TO_APP(ESP_GATTC_REG_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_REG_EVT, gattc_if, ¶m); break; } case BTA_GATTC_DEREG_EVT: { tBTA_GATTC_REG *reg_oper = &arg->reg_oper; gattc_if = reg_oper->client_if; - BTC_GATTC_CB_TO_APP(ESP_GATTC_UNREG_EVT, gattc_if, NULL); + btc_gattc_cb_to_app(ESP_GATTC_UNREG_EVT, gattc_if, NULL); break; } case BTA_GATTC_READ_CHAR_EVT: { set_read_value(&gattc_if, ¶m, &arg->read); - BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_CHAR_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_READ_CHAR_EVT, gattc_if, ¶m); break; } case BTA_GATTC_WRITE_CHAR_EVT: @@ -565,7 +565,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) param.write.status = write->status; bta_to_btc_srvc_id(¶m.write.srvc_id, &write->srvc_id); bta_to_btc_gatt_id(¶m.write.char_id, &write->char_id); - BTC_GATTC_CB_TO_APP(ret_evt, gattc_if, ¶m); + btc_gattc_cb_to_app(ret_evt, gattc_if, ¶m); break; } @@ -575,7 +575,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) gattc_if = BTC_GATT_GET_GATT_IF(exec_cmpl->conn_id); param.exec_cmpl.conn_id = BTC_GATT_GET_CONN_ID(exec_cmpl->conn_id); param.exec_cmpl.status = exec_cmpl->status; - BTC_GATTC_CB_TO_APP(ESP_GATTC_EXEC_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_EXEC_EVT, gattc_if, ¶m); break; } @@ -585,7 +585,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) gattc_if = BTC_GATT_GET_GATT_IF(search_cmpl->conn_id); param.search_cmpl.conn_id = BTC_GATT_GET_CONN_ID(search_cmpl->conn_id); param.search_cmpl.status = search_cmpl->status; - BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_CMPL_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_SEARCH_CMPL_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SEARCH_RES_EVT: { @@ -594,12 +594,12 @@ void btc_gattc_cb_handler(btc_msg_t *msg) gattc_if = BTC_GATT_GET_GATT_IF(srvc_res->conn_id); param.search_res.conn_id = BTC_GATT_GET_CONN_ID(srvc_res->conn_id); bta_to_btc_srvc_id(¶m.search_res.srvc_id, &srvc_res->service_uuid); - BTC_GATTC_CB_TO_APP(ESP_GATTC_SEARCH_RES_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_SEARCH_RES_EVT, gattc_if, ¶m); break; } case BTA_GATTC_READ_DESCR_EVT: { set_read_value(&gattc_if, ¶m, &arg->read); - BTC_GATTC_CB_TO_APP(ESP_GATTC_READ_DESCR_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_READ_DESCR_EVT, gattc_if, ¶m); break; } case BTA_GATTC_WRITE_DESCR_EVT: { @@ -611,7 +611,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) bta_to_btc_srvc_id(¶m.write.srvc_id, &write->srvc_id); bta_to_btc_gatt_id(¶m.write.char_id, &write->char_id); bta_to_btc_gatt_id(¶m.write.descr_id, &write->descr_type); - BTC_GATTC_CB_TO_APP(ESP_GATTC_WRITE_DESCR_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_WRITE_DESCR_EVT, gattc_if, ¶m); break; } case BTA_GATTC_NOTIF_EVT: { @@ -632,7 +632,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) BTA_GATTC_SendIndConfirm(notify->conn_id, ¬ify->char_id); } - BTC_GATTC_CB_TO_APP(ESP_GATTC_NOTIFY_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_NOTIFY_EVT, gattc_if, ¶m); break; } case BTA_GATTC_OPEN_EVT: { @@ -643,7 +643,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) param.open.conn_id = BTC_GATT_GET_CONN_ID(open->conn_id); memcpy(param.open.remote_bda, open->remote_bda, sizeof(esp_bd_addr_t)); param.open.mtu = open->mtu; - BTC_GATTC_CB_TO_APP(ESP_GATTC_OPEN_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_OPEN_EVT, gattc_if, ¶m); break; } case BTA_GATTC_CLOSE_EVT: { @@ -654,7 +654,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) param.close.conn_id = BTC_GATT_GET_CONN_ID(close->conn_id); memcpy(param.close.remote_bda, close->remote_bda, sizeof(esp_bd_addr_t)); param.close.reason = close->reason; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CLOSE_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_CLOSE_EVT, gattc_if, ¶m); break; } @@ -665,7 +665,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg) param.cfg_mtu.conn_id = BTC_GATT_GET_CONN_ID(cfg_mtu->conn_id); param.cfg_mtu.status = cfg_mtu->status; param.cfg_mtu.mtu = cfg_mtu->mtu; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CFG_MTU_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_CFG_MTU_EVT, gattc_if, ¶m); break; } @@ -683,12 +683,12 @@ void btc_gattc_cb_handler(btc_msg_t *msg) gattc_if = BTC_GATT_GET_GATT_IF(congest->conn_id); param.congest.conn_id = BTC_GATT_GET_CONN_ID(congest->conn_id); param.congest.congested = (congest->congested == TRUE) ? true : false; - BTC_GATTC_CB_TO_APP(ESP_GATTC_CONGEST_EVT, gattc_if, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_CONGEST_EVT, gattc_if, ¶m); break; } case BTA_GATTC_SRVC_CHG_EVT: { memcpy(param.srvc_chg.remote_bda, arg->remote_bda, sizeof(esp_bd_addr_t)); - BTC_GATTC_CB_TO_APP(ESP_GATTC_SRVC_CHG_EVT, ESP_GATT_IF_NONE, ¶m); + btc_gattc_cb_to_app(ESP_GATTC_SRVC_CHG_EVT, ESP_GATT_IF_NONE, ¶m); break; } default: 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 3a988fc3b11..9bc41d2ab1d 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -23,16 +23,17 @@ #include "esp_gatts_api.h" -#define BTC_GATTS_CB_TO_APP(event, gatts_if, param) do { \ - esp_gatts_cb_t btc_gatts_cb = (esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS); \ - if (btc_gatts_cb) { \ - btc_gatts_cb(event, gatts_if, param); \ - } \ - } while (0) - #define A2C_GATTS_EVT(_bta_event) (_bta_event) //BTA TO BTC EVT #define C2A_GATTS_EVT(_btc_event) (_btc_event) //BTC TO BTA EVT +static inline void btc_gatts_cb_to_app(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + esp_gatts_cb_t btc_gatts_cb = (esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS); + if (btc_gatts_cb) { + btc_gatts_cb(event, gatts_if, param); + } +} + void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { btc_ble_gatts_args_t *dst = (btc_ble_gatts_args_t *) p_dest; @@ -232,7 +233,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) } param.rsp.status = 0; - BTC_GATTS_CB_TO_APP(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m); + btc_gatts_cb_to_app(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m); break; } case BTC_GATTS_ACT_OPEN: { @@ -291,12 +292,12 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.reg.status = p_data->reg_oper.status; param.reg.app_id = p_data->reg_oper.uuid.uu.uuid16; - BTC_GATTS_CB_TO_APP(ESP_GATTS_REG_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_REG_EVT, gatts_if, ¶m); break; } case BTA_GATTS_DEREG_EVT: { gatts_if = p_data->reg_oper.server_if; - BTC_GATTS_CB_TO_APP(ESP_GATTS_UNREG_EVT, gatts_if, NULL); + btc_gatts_cb_to_app(ESP_GATTS_UNREG_EVT, gatts_if, NULL); break; } case BTA_GATTS_READ_EVT: { @@ -308,7 +309,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.read.offset = p_data->req_data.p_data->read_req.offset; param.read.is_long = p_data->req_data.p_data->read_req.is_long; - BTC_GATTS_CB_TO_APP(ESP_GATTS_READ_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_READ_EVT, gatts_if, ¶m); break; } case BTA_GATTS_WRITE_EVT: { @@ -323,7 +324,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.write.len = p_data->req_data.p_data->write_req.len; param.write.value = p_data->req_data.p_data->write_req.value; - BTC_GATTS_CB_TO_APP(ESP_GATTS_WRITE_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_WRITE_EVT, gatts_if, ¶m); break; } @@ -334,7 +335,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) memcpy(param.exec_write.bda, p_data->req_data.remote_bda, ESP_BD_ADDR_LEN); param.exec_write.exec_write_flag = p_data->req_data.p_data->exec_write; - BTC_GATTS_CB_TO_APP(ESP_GATTS_EXEC_WRITE_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_EXEC_WRITE_EVT, gatts_if, ¶m); break; } case BTA_GATTS_MTU_EVT: @@ -342,14 +343,14 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.mtu.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.mtu.mtu = p_data->req_data.p_data->mtu; - BTC_GATTS_CB_TO_APP(ESP_GATTS_MTU_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_MTU_EVT, gatts_if, ¶m); break; case BTA_GATTS_CONF_EVT: gatts_if = BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id); param.conf.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id); param.conf.status = p_data->req_data.status; - BTC_GATTS_CB_TO_APP(ESP_GATTS_CONF_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_CONF_EVT, gatts_if, ¶m); break; case BTA_GATTS_CREATE_EVT: gatts_if = p_data->create.server_if; @@ -358,7 +359,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.create.service_id.is_primary = p_data->create.is_primary; param.create.service_id.id.inst_id = p_data->create.svc_instance; bta_to_btc_uuid(¶m.create.service_id.id.uuid, &p_data->create.uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_CREATE_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_CREATE_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_INCL_SRVC_EVT: gatts_if = p_data->add_result.server_if; @@ -366,7 +367,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.add_incl_srvc.attr_handle = p_data->add_result.attr_id; param.add_incl_srvc.service_handle = p_data->add_result.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_INCL_SRVC_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_ADD_INCL_SRVC_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_CHAR_EVT: gatts_if = p_data->add_result.server_if; @@ -375,7 +376,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.add_char.service_handle = p_data->add_result.service_id; bta_to_btc_uuid(¶m.add_char.char_uuid, &p_data->add_result.char_uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_ADD_CHAR_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_CHAR_DESCR_EVT: gatts_if = p_data->add_result.server_if; @@ -384,27 +385,27 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.add_char_descr.service_handle = p_data->add_result.service_id; bta_to_btc_uuid(¶m.add_char_descr.char_uuid, &p_data->add_result.char_uuid); - BTC_GATTS_CB_TO_APP(ESP_GATTS_ADD_CHAR_DESCR_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_ADD_CHAR_DESCR_EVT, gatts_if, ¶m); break; case BTA_GATTS_DELELTE_EVT: gatts_if = p_data->srvc_oper.server_if; param.del.status = p_data->srvc_oper.status; param.del.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_DELETE_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_DELETE_EVT, gatts_if, ¶m); break; case BTA_GATTS_START_EVT: gatts_if = p_data->srvc_oper.server_if; param.start.status = p_data->srvc_oper.status; param.start.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_START_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_START_EVT, gatts_if, ¶m); break; case BTA_GATTS_STOP_EVT: gatts_if = p_data->srvc_oper.server_if; param.stop.status = p_data->srvc_oper.status; param.stop.service_handle = p_data->srvc_oper.service_id; - BTC_GATTS_CB_TO_APP(ESP_GATTS_STOP_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_STOP_EVT, gatts_if, ¶m); break; case BTA_GATTS_CONNECT_EVT: gatts_if = p_data->conn.server_if; @@ -412,7 +413,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) 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); + btc_gatts_cb_to_app(ESP_GATTS_CONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_DISCONNECT_EVT: gatts_if = p_data->conn.server_if; @@ -420,7 +421,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.disconnect.is_connected = false; 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); + btc_gatts_cb_to_app(ESP_GATTS_DISCONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_OPEN_EVT: // do nothing @@ -435,7 +436,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) gatts_if = BTC_GATT_GET_GATT_IF(p_data->congest.conn_id); param.congest.conn_id = BTC_GATT_GET_CONN_ID(p_data->congest.conn_id); param.congest.congested = p_data->congest.congested; - BTC_GATTS_CB_TO_APP(ESP_GATTS_CONGEST_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_CONGEST_EVT, gatts_if, ¶m); break; default: // do nothing From daa2b7cbc968feaa0f259e6b52020e954dde6ffb Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 11 Jan 2017 14:13:37 +0800 Subject: [PATCH 127/167] n, h and l actually are 6-bit; they go from 1 to 64. --- components/driver/spi_master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 204e577edac..53be8b29de6 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -396,7 +396,7 @@ static int spi_freq_for_pre_n(int fapb, int pre, int n) { static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) { int pre, n, h, l; - //In hw, n, h and l are 1-32, pre is 1-8K. Value written to register is one lower than used value. + //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value. if (hz>((fapb/4)*3)) { //Using Fapb directly will give us the best result here. hw->clock.clkcnt_l=0; @@ -414,7 +414,7 @@ static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) { int bestpre=-1; int besterr=hz; int errval; - for (n=1; n<33; n++) { + for (n=1; n<=64; n++) { //Effectively, this does pre=round((fapb/n)/hz). pre=((fapb/n)+(hz/2))/hz; if (pre<0) pre=0; From 23448e7121f8bb2ba8553706ef9c209d187c4b6f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 10 Jan 2017 23:43:18 +0800 Subject: [PATCH 128/167] newlib: build with wcsftime function --- components/newlib/lib/libc.a | Bin 4957212 -> 5022558 bytes components/newlib/lib/libc_nano.a | Bin 3867014 -> 3569308 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/components/newlib/lib/libc.a b/components/newlib/lib/libc.a index 4377fa252f5a9b3870fddc8773a74cb6d59c4d38..36a1cc60784a9e8f16051db824fd87eaad84a16d 100644 GIT binary patch delta 815349 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� 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 z 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%0A7`z_1?51{I)N!R&elhWqRc47FU|>*( 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

V_^6RWnbiDVE7MZU*}_B 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

WfOG!Y-;DIDS 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 delta 773183 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

gI-AE=oRU#qzUe1h8a8|EBR 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�o1sFwo({C!3=ApfZ7VM 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�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

%Eqh$ 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

&RT}6hRCtvJI_(KorMZHP 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�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

hgHjXd&Q=hclYxOj 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%0A4_ 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%0A7(zz_vxKj&azNPx1LIT;wzplo?g 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

-l!x1U`u?YS;+~)tlCaS> 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%0D>6Agk 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%0A4=z;GSP2B~=t zWq;#jV5k@3f>`j5n}I1a0QqbIlnqj}49W&MZZDJ# zQgaB(28myTvO()_Z$sH2@t06G$eiy;Y3O$6fx%e0uc=#ZO)(A2%$Rn}cpzIJn28Jvs8>FTU%AU-} zz%U=m28r+HV_>KU`To2B1H&b##0yX@#1FB6pPzw29?JF*WMI&QvV#N}7~G)j9|8;v z`AF;rC>wMWq-GhEy;*>P;UJU^GV>yo4RYu&C>tcsB?$6lJp%)XqXp(LFjxpe z?#Bj&3P?O2DxNRMz%U2O23@AQ6UqkNo%{lc{Ts^e5Mp3Z5&~Jkz+fQ6zz_swgRU*k zgR*Oc7#QY*76gK-OHg652qXb5WR61FJq!#Cr=jc)1_lPuy{90@f$ltg0u`Uaz`*bd z%AUZ$z`zWuikTP~K=*FOL)oB2+5lyP*7>zV+4UgK3@8W0mK1@w8l=Gu$_6d&%Y(98 zMHm=%L)oCUdgnoGVFrdJ3=9msq7c2H+u`J)Y|!O%Jy162{?zSI_C-+!hU=h9+d#en zEqnV6l>o72#UKWOuGj`$z70|XVh2LSK?YSq*`VAp70TWt#=x)-%C;3}U^s`w{s3kF z7h_-$6o;5^3+j-oK{>|a3=Hm2c9s+aLn@TrBhJ9k2W9h0FfhzRVjqCA&xHINiZ;UL)o(>7#P+<*&sEC zpzQM!3=H?7Y>=UUp==dN1_m`rh-FJ985qov*bz{6x+DWbC6o@-a^^Gr5G3lr6Cr8#C4%;YiR}scPJYqo(g5R$TBe0K-qI;85mYW z*}J6~7*0dkpiP}up=>TW28J)vp!OdV1A~kV1B09l#4?bB^r7s3G7Jpr#^|A~ME>I3gb0n0V zF3Z4B24#aZw?WzKWEmJXK-q?J3=D^m*pHy>51=J7auCZvYQ&*zbvXtGXDAzVM{gKt zK?o>o)yXk1G(aW($}uoZKw@u%viHj|FkFPPL28~s*`MVY7$oH(9hf-`3=EneHWLE_ z$O2m^`>{L&LkbeR6v}=m&rr`W70OvG&%m%1%6=%%z;Fu6zAn$eAgciJ4e0vBNGKaL z^py%_7b!3>v_sjT@?bZVy+MnC;S!X+NsEEuBa}T?g@Hjz5o9K4L4XPagASO(z%W~d zfguFSPF7@KsD!dX;!~mQ#fl6J>!ECr_*p3XjWPqn6DXTig@Hj>31k5SgR&9>gE5qy zp~Apm3uPCnFfhb`24q1|(5}S5&IOi zlr09Di&ueIHdBRxK?{lP1!YI5Ffe36*&sFbPI@7!pzNFK z3=Geq?C+}e3=HgQ5JPP=7#IYhY&Q)C1_LNNT8)7r7Rm-WE)UAKQfFZ3gR?O?3ta5m5OLGSmjdVPas|pw7S$3uR}kGcYtj z*&xl+pzOu!3=Er~Y>?hVQ1&Zz28K6KcCH2k!#^ap9B3+)iGe{!gMq;g$_A;a4~BA* zH5eFlH6bAZn!n zb}58i&j8A=OCTID`!kdcnyCcc@(qf6Z*2w!(DWwAV$g_VnKr~wkeW+SHfYu|SqCBx znpEtEvJdGnFo0$sL3%+WlMGvAG z#5Mu3g&7z?O*eBW8`MAagt9?Hpt&G669dB}JqCtTQ1&}L28JI{_K$iq1_m{Kh@oEk z3=E(NL6{?gq2fPG7#LDOY+(ilkfD7bHWLHGCVd8mcThIy&TlCLh&iD9xj}b*gF+5; zw>M}`4`wFlPH&JnNX=;jX!{@Jn>SF+pc|>R3?T-A=I0`z?Ei)g49!sXT0;f~(EJY ziwQt+3>pUnjbDSK*CWUg^`Jp;5QiHyDa*jX;07Hy1Stlwxj_SV z3=H5~sX-b*bBCZShqVkQ&fL6o?JeI|*tgXif>l=7ugVp9NYF z2GRf;4xPooz`zaKn8U!puo|iXGz|)3bAyIA85kH2fy9{@7(k<#=Rs^?&;cb33?Mc) zbbR+NNSuj*0W>oD8N?QbEDi;+xj_pt85kJ;g2b5^7`Q;=ze1qVNMQzs00ssI5F6$* z&=5PwXP{wp5C*Y9r^uLs)PssrMg|6ZC>wM%OemBM8vV|LvRfG$7(ipgAWwp3(Lii& zP`ofOFsuNn0hRxtvtd?)IFKPxhD}g5XqId@lnpvF<~WEg%)k)Az`y`v!+d-eBo693 zFfuT_0pN z88F8o9EPt<)BW6Z#p*#CZ$icHGchpSfwDp3kDzRjfxn?_(D5(A&=FHf&^aeiHs}x* z5Fg}V@R=@9anN}%%22i@GXsMflnpwhL7N%WC}Cn?Fl1(6&_j|ihO$9p!e&r5=oAP$ zC>vy%Ba{s~SjGp+4q|3t@Q1QN7KB3Cpp#(Yp=^-eBq+N9G$@$?<$xrzp={9UGlft# zXwbO?$_5$Q3}tsRGcdG4*`Tv4KzxwzK}XYo2Bkr4(7^O0BsH_4Y>-_GL2OW>1dUpQ z1V9>AF*7hM2T6d=31Vkp0P#WMpi%9$P;tZ$6NSuj*0d!~!2!qTA z9oq7n71}5P1q%ao1RHcZiy)K@I*~;J$_CApNJH76b_oH!U5?7tGK;ldc44?t>_ekuoNNjfKQ~^khJSVhZROMt~P=ZQ;7Mi#~*`U)` zJfUoVP6h@aC>wNqOdymU&dIvhpeoFvOzvL0%e0vNVx)KgHBWd zVUR;XC#tYP=OsX=rGPL<9CTVr08|`wKuQOcox#n(P~QjTOyOo=mkuV z7-mA*py1sEWrGemIR|Bf4ln^7_Xe_zjfa7O89HA9I<|uo%I4=`VBmqWLE=IXc0B{= z5E2mxhXHg@i6fK^^06P34LXn{5y}RgFH#I;gAODCVUWe314&jw#X)C{fG|iLbk+!H zq6O4o0xi-2vAIEQR0alyGqBkj(7_}3p_)NwhdhL`L5Gb9LMKW<4w8eiLFb4VLD`^l zL0q70&^aO?46+z>jz}$39CSno2!q5yM}(Y&ik}C~)|`QIKu2s`g0ey9e_VmGL1%<8 zLnl)}ju3*f!DoR$*`T969H4B_Q6L};vJ7+-NF!7nbj$|`gTz6{d@P5})~w}YU|0oJ z1Uh141C$Lq!ebMZ4LauIE|mS4kAdLwP2 zMi`V0I!Pk~$_Aah0m2}2Kqqf3go=Ys)c|3TIOtRjkbNLF=wOUv^-x8i(=$Fp*&xgQ zL)oB{F@&H~K%j#zRG@6o$rvCEG6-}sMif*Wbm#>LgTz6HUNk_(+XNXHnxX8C8Vn4J zq3n9li5APC9MGv3x1ns1Bi=#Tpu;VgLDNA@3=H7IDxhr8;T9kavJ7;%MJ7}nbZ`X- zgTz4xS4;zm3q#gLf!N%jJ|qJJ!yF-K`42j{Vl`9|Xu%YS%?({NwGkxF#J~VL_2LAS z4VtUE4q^*K7FB`RFhlQx#F-cvK!;vDhq6KH1))W{=md;RC>wO@MGlk=Ix(XN$_5>J@e0ZYEvWekWrOCqm_YMcprt4x3=FJD zY(5YhG+Pf^{srQ|EEWezFflNIPRVeDvO$IhLfN1bFe0IB(8(C_NbDpi8+1-Y8I)Zk z!oW}oWrNP4sE4va6L0lRP!8y@iaAjB3K0f|%}_RIM(!At4LS|u3zQ8yNkaxY!3H`# zLl4RZosy9ZWrI%1$b_;>L>U;SLfIh4&4aM(89=9AEQD~tCu6LIvO%+UAURMX0-c6& z5=s0Klnpxc;vJL?QvUr149#(4LUQT1Ih*+Xwd~_gATpe4rPOK(_ttZbhyPeC|jgn zoPpsrlmj~50)#=X1D$T62%Yi+9b5szAaT&a75-51P;mx^ASfGj>_Y^U4LYDA3d#l@ zT+t3?gB&p#$_AZJu}mD=Fa{ksu^TD@I-mlCL6(6IsQ3sK2OT~E!XRwO7!#^k+bV7qL==cVZX3&`qAPi!wNHH*YLgy?&$2EX3NE~!rLlIQGLW+T*6v_r2 zc2EsvgHB?og|b1%HLQiQL5|o7WrI#-I0t3dgN|8v4&{K3WdLE2WuRjjG@uikpd%MR z7$j~X&A<=>6$c$~5Cvs}PEd$}vO%XTbU@jC(hLmUP&Q~s{bVQ`bZEj-(D4l*LqW$Z zfG~&+I%eSkR1xR^f@e@R=yZe^P&ViU1u^JED9C5ZP&VkK1al}Gbnt;Elnpv50fa&3 zgAPh)g^Kse)H5)EPC@`lfKEYJ4VBm^!@#f($_AYw0OEtxfDSm=3>62Rg76f|207vr zlnpxUfD<}#3Oc+%8p;M8b^yX4b3lh3gn-0B?SIfo1|STQ03BoivJk`u9Y#C6o=aY&(<_y;)# z1_S5>ENEN55rkdO0NVQ>58*I?EX#tjL3{jbp={8W{eCDLw8bBUL5701_#cIegSPL3 zFi0GuSyUM5(jO;7lzJPODZ!kh(Xz)4eTI3NDXLry);xD zv;*H4$_6!BRbR(lWz83fvDzZWVF+C>k-AaT$xdXR-6HfSgO zW2hR?Zh1lI{5Hrkc_uJ-Y|sYw9Z)uCllyZh8{~-3P&Q~YJ12B<9JDuG3Cad-X4g@Lw*Nu9 z*;AkrppEJv3~~f$qxuA>IA~w^G$wY$gVVBq$rSzq|~}2JIK`fU-gR z%Rv}q0cd~uL8v%r?>Gp9#6f$;K2p=?GC1_n0h>^^8qwgi+7+QJROAag)lxc#8wks1sPAPf=*?anTN zii37%S3=nJ3?R-H2!{c*Rr>;z4ce-G6Uqkdh6eFL27$I`KZ1(C&|qMA3T1{W`}`Rh7(i@p(0)$PZVhJW`UKEE zaw8}kv@hHd$_BaA8;M;FWjAOsFw{cXpkdsnP&Q}{!v`oEw8nuKx}E{FwOa?o7G_{z zVPs$cvFl;u#MU4QCI$uvZ3YHgC>u0Y90p~B_KW91*`WR3?I1R29||J_1BeYXX97r^ ziGg9NHUq;XC>vzXN+=t&sNxiqeN~&Gp5ZZwBh0`6+M@(w!wh-@5@%vy_^8dm@D9ob z83Zx_6vv>w<4VvK8K6Do#vryZ0|RJ(6Nn8n#|9+M#K7RF!@yt%WrNJ|2ea!L7(#Uz z7=pkY1_sbFj|3`t^(q3?Melf=wWCCI*J>It&b3plpx@C!lQ5 zX7&$IHfWzZ6KM5^FarZCn1Ih+% zT2BM9g&7z?yR|@Um^t|%aV7?a5?uy{LMR*LpjIebN0)(N5tLmI+S$Gv#1Up-0B!XG zv0(-s1c@^-FdWlmU^onAgABR?WrH@ke}}R`yW;skYfyw47(jE|AU4b#5s)|&1B0X< z1A`cp4Khbh4_5x`F)$cHB|v+C9YAbh1_sa`GY}g#=j;X&2jyvf28L898?>Xo8pIZ6 zU;yny1F>NiH-f~O7#Q007#NzNY>>q>pzI}}?fdiK90rDkP&Q~e#~~>DtR4fy4G>$H zfdRC)4aA07a1SKT#K7=akAdLvyc{Z=RkwD*4(5_>-q`w)~33YC{oHt0-%KOnX+0|RI`ABYXJj0w8( zhuwgIfd$G2Stbr;gO(4OK-l%*vjW^f9AO3q&{jYY8)lF%NF16A5}|DHp#xAh=nR2L zAhs|A18D0Yhz-*_10)XBdl*xCR7n9&U&D1(7^fm8vlVgE@!|+K9&pI=wP0K?#%% zI>?|K#1>{?0PW-iv0)ZZ1c^iSu7|OW7#MaSu}?wSpo0%CfZ6p744`FS-@zOP2GGd| z{Gg>?!VCyj)EC$L3osLipVhb}cfVPu@*f717AaSVP*-$oU z8QFn)Cl?jsv9wQ-sBtAaSV0GoWnHy0+yYHfW6kBLf47 z4fEw%kT_KR5fB@+MuCxm0mOzcEdq%%F))BmX!r~|z5$dnK?~i!K_x&7<5)lo;Di|% zKs#$eY?uXHAaSS#l2A5i;hYVW9c#wG;08)WEe5L=jm z0kp*!#Dk-nq2j_gz2mw>i4v%j0y^AAmlpm@C_ z0|Nsi=u|f7%0(C(wAK#Bjz&@gx)}~8o{1z5+FlJ4FGmswA2EsR5l!0TUMkZ7zp8NC~uh5helJ zw+&-kAt`c4VuPw9n3_l=@l+%>c&j|r0#KfTsRyMv82b=RJ;?v(U>pVphFeJNXGm<& z>O+|3e@Nn7pbPAvmWd&;m5|taNNj5)wg(dfLp{vU5G08NBz7(myBmo;9f=KEtq3z` zJ(BotB=&10_ICuko`IPekpRVz*r3&gFhdQH#BGt-UP$b4Bz6iC8?@dJW=4HHj767NT1&qiWzMq-23?7{S&MiRdcWrO_>+Pw}_^d2e!3X9)JY;Djb zY0!jbj>PsyVn-vf(~;Q4NbGtf_9oCRY%q(xKzD;dEeJzmCnK>7kl3|I?CD7CB}nXz zNNmuqc9>nK;o|iS3@_mv1_sbtJD4J7(A8j22MHjtK}+XgY7CLY?U2~sNbCqCb}ACP z2*d{MUIhhpJ%|IftOtob6N$YHi4EFL4|Bu;Byl;=yHsGG z7)ks+5}O5d%^5VIaU!uns{~M}5_>xm`!Evw zJQDjh5*u`!0?ho+NaFv&Y*6@vwoQQUKZE*49Eq)r#MVb*TO+YS=R3eG2u2bI-^T_u zGaE^~9Esfmy3P!yc`A|w=zIp4IB3}+jC~79%`+tSUnDkY2_j6rDCqh#s3R1S*t$?Q z*#DML4k#1I$67<%x`-^V1j@ z81{j#GlNN-1Zjp!oCC3;uD%IkL&fib*iiA8AU0I|9f%DT|BJ*H<7HrgStbo)L)Cb| z+4T$z!5|LQpa>8fs;C&mhWe(Q7vdYxCZGn8I8;qHhz->XI(!0V*(xOQ^&mFXoP$X0 zqaZUam>3v9TU<_oB%p>~1+k%qg4RO939U^u;`OF{5O;w(1TLWK)S%jY zk=U_FY|yevm=R@2;@wE>DM;+ONbF@u?DY^fIQ1~>fN((0VmOGzK83`-fy91@#D0au zeh*?p1BwrH0U4C71!6Ug70pIsuSR0;Mq(cWv7rW?2C<=fAA#6V@#i2m zRQwZ&4Hf?mVnf918JIwqg+V3QL2QTwgE$gf9f@rOVnfxNgV<2@?nvx1Bz7Z+4OI_X z(+La3X&`YSQ22w+1OjoOia={SVT!ha#Gw`(1hJuNj)B-v@oPxzhafgo%`*@is^%kz z4Hf?eVnf9lL9I7fqEHh;v^@+#Y^Vk^5F4t&5yXayyMfqH@jwt8Djo)6L&Xz8Y^Zn| zhz%77Ej9(&3-&)~g(=L(3qgvY8bAl2z{D>giC+b=p$0tyv7rYMf)*QdbRQg)Er7%p z1+k&(KR)Q8){|}61yFVy;2l(_8?RdXc;ZYGSKem{UAk9gN}gM zP&F@+*r4UIFh>}G?t_D>F%g3}2sBFW1`>yw;|*d%)fB+kp!_!p#$jM!04;xo8F~{* z{5=wzTO1KWpw+D~^=?SwF-UCC>Q{`bDPcs=MS8kn>e=>92a2&N;k zmmskpBC!QPw>m-9`yjD9k=S#Q*td|_;-HI@K-&;Oi50Z)5ax(xhy+NO0kk|5CjJ*m z+zNDc5Y*yKBz7MXdovRI1`-=|unWvgC3&Ru=?=Q)2Wn^z5_=*Ndm9q_HWHfwbQum* zuPPGT3yGbN#GV3UgVN_t7>9v@;T{s38Fb$a)KCp1wl5O92#Gx%i49ux2J_`ZByl#- zwK7mMRg@SQrWZUF=cxBY5`76`L(2gH(6uU1wmA~p1BvZ}#0H(l0xRIck;LPX*vUxj zEC?GE2n-B)5DqxvOOe=>NbCkAb_)`_8;RYI#0H)G0&~!8B=JQ^>}AR+h<3qRB#Dhk z>>Wt#JxJ`sNbKWC>~l!$OJMf&eRDbLroUOhDlu8+hQwr^RT9(REN7IMZotAMF}-KK zqQvw&SA``e&silg`OXcA=`nJ=JkvkO$*D}Xsppw)uu+U>`kNo(Jku3|BzUIxh;d0w zpVP^2Fn!K?VT0*4Ym6nP`}pu1Ot%q{=9&KCwzkdmJMz3d(|Z<4^GyGfpvyD;K^H&I z^qwRE2}$r+HuTiu%b;;;DEsmBi{f13)Bk`>sK}Gy5dv+UXM{?E2eetI@A@S!H~qmX zHG}C2JB1{s2YePVnC>INCo#Qerx4HdhUKz6(?7@>Nlag2W@0e?M81r~^fw?D&qNl9 z=`t&14H)^Rf1IHr&L}xOaUzR2qssKVUj)RbTO8GpnEpacT4MSckir^&eu?P;kJKfm z-`T1uF}>%JI?r^StztaWXMn{*ge9iiY}1sO{s6>{Nzvt*exp!^XS&Z8VTtK;)b$Le z8#uB`Og{rsvPa5*XZitYE*@SD7D%N5N?khB6*csfr{4i-h|$oKn0{xOv;P0bjB$x;?s4USa}#XOn0ml z5ua`|g@tGOn}6awj2EW+2Jwqe|M5?pXL`vT6$8c})7OGjoN!__nBMSIU1Iu~9|Ann zg86NxEBqAjm~N4-D=~e~R2B)_epZBoCnK?EBeCZrv6myUS5KFn#v;zRfBM;dLe|qm z=BXG=SJ*Ek!TT3U4de9Q5Poq+p6Ryo`qtB9PH6B<4=__#nXbSnAu)YHhHl059!3cs z#`fuRr?ZGpUlYpDGkr#+FwgWgI(j_3%h(W(TQz;+Drx2EHRkFPj5ntnhVhF}*O|d0 zG5v_Sy2NxFH6w}XE5i7BrfVqb^Gsi%tH;BrF+Fp&wDokJg(?!$J5FjyOuwOK#KRar zed0kO@#zYBdOXu*6b*P7OQ&CasV+V}CY+y#x1Swh-{k3qGg*{*K|`&e)&wX*S55a_ zBP~9?LEVUF`kQcmiRp8qg(arP)QRv+pYuvxWjc?N0nhXuD+GC_+stB-nBEZq@(f5; zM_HeTQDu6dvc5Q@)%3d&{NmFyl=XS0&xjG`nZ973CJ%242g2rz>4h3b%F|`)MR=ya z0jaQ1HZYj(prY?FJtkL|XZjBo2_D8<(*xH@i%;(X3Ft)e^YDH`GU40w#&y!l)Bo(( zl$gF}owUUC8deD&Ni|M{9lA(t!|8=*G{mRd#0m3Ex6w2*n11Gn5D#x6l8TJ!XO9Sp zPnU`2H<;dIqn&-58bg?Og_@nYqfe&L{|%5;%=EE3agHb_fM z&oS1Mn0_N)*I>HCdv$~9TR>uG4rxkEU-Mp_hp}$@+~q3b(=Vv&^GsjRBqG7MaQfZ% z>f+ORH1rLopRrSym>#o2g=cz#hCUDDqv^gJ65`V>HcInMpP-@7GyRUb0nc=vL}8xk z9UKw{({FrGmzX}GS;Sy^#}Q4Q=@S;Pv`o(_(&b^CHeJ$8&w6@?gF4UjH=6o9j0dI% zCJBpA_t_-vG5vw2fx+|uE{O_8=jn`_rNyUrw2DYff0HCE!JCFOY?U?rqLzX9^fi|> zc&42al9231lIurePn+(`Edk1->ISO2k;L~Su|d0VVF~Kg^x9-$@#%Ngs0eVv#2!t* zxmntJdcYTTiRm{kYw++2fkua*RjKrJ#uJ+2(*>N>C8lSb7Lu5LV66(vbPY>Ak?B1; z`UcZ2+C>Z)tEV3XDe76mA~8MUtGdDTD@#}mru*m`@Jv^*(&L$)%H=S{tw78@-bj%5qH|&wv z9@7oCNsCXvb5%oPy2LUTp6M6Lb$O=qT+@)4{-s=3VtU4Rbpyt^(=VRV6rX-(o3zAq zn@$m)=>|4>D%0P5SLb28INkQNra0rB>4HDh#Tj2t&poXvK3!os3(s_)AL=}ejMHPc zON&omJR?UBX>mV)~Wq8V1vQZ1oJLFE}qG z!8-#w0t9mJoar08M8u~V>hnyW^HZH?dd&?Dp6PG8K<)sE$yDh|Ob;>CmzW-5r^mzi zWO}5Xo-*$bBm@3TpKGYE%nQ289u|&b(-+$5DNkR}Eg~^JWF?CPqto<@XEntceWx?p z>xobQV`#vm8Yh6rgsDjEOeA*E^n)u|#HU*r8AwbQxuqd7U1SxD1n(jw70adv-qKKJ z+%M?_-!j8!Z=l8=#8zd>SunBKcfT70^Uv4IM&pdi96 zqSIY_MUZx z8WwTMsYo(2k=P5S8?Ipy=Usy&v|+mE8Wv^VLr6l$ri+^DD>L4ho_j|_ocA%3%=77r zrUv5EZ7yo^Nd7_+2kp>>g+J@`#EY8Zi~`fo-q8@BF0z(IV)~yu8a#$tLI{6?24rCB zO_9WHkk}5>H=62;Pmfv4!owIe{jP|FIBzVHbmH{byBf-j#nX93CB!A`kYt*W*qzho z_KS#1PC*iyfyACa{q0&7@#zs}`V!MM*0D(Nf_C`A9Cl{$n=Z*KsBX>fyDHN4J-+a zeA5m0ON&omATA-n3mVUWxl4Qc+({zVyuQ%U2#~J=r(b=bq09>!tbnP?onB~ZAU?h4 zswNLFXb&$;ym`9oWD(`*d#-9qOfPt-A;Ak8rGQE9ogR2VTAA_E^s^EY;?wUu)ZpQL zh-A&H>3=08#Cg9W3H_X2cuiAzx`m{K#PkoFSR@QJp<@ytXXzrbO_122u?m=#_S1VM zCB%7skfZ~q$8Kg(=1oHq%9^fgZJ^9pH$CyXrg&&4l1wiWdm0jZ77}|g5_>rkdjk@C z%k;h02FkoAkc7@mZ+xsF&UkY==M7C|#`n`@Z4AWu{vat}5SxDOv4+z00viJfUS;Um z2gu{<(;1~D#Ha7Mp~*8{Ufg|vi$WH*v{KN5S| zbX!{k@#%M-Xz(yDonClTQ@I{AgaQlL-AGCgA+e7kvCks0K|76MdT$_!-$7zOMq)ol zV!wm1L07>sFnoe=K;gjf6N&v7iOnL8C|)?Gw?5TS=9NMclAEq*XCTfC+Q$rYlEL)7 zG7{pv)<~-Crw4*mc_9e}PLH)S5SIiE0l_pTAW5fBmz9+e=Pg1KDx0qROvAb!wAC4= zV>*&F=(J^+_#!0nl}PNhNbD_0>>Wt#{YdP?NbFM(Hsnm7a}W+VAslc}q)eYHCm|l1k0ex##I8bO*CDZ6k=UI` z>0aM-p0w#9oEO-iXBBio`yG#6E$?9<1CK5Xrv@sH z@tH{M1xV~A(=RGWh}W-26558u-i5?Ih{QgM#6E+>zJSEOj>Nu=#D0Xt1|4NC3>#W~ zizM+8iTwkK{RfH7EQLr2>_}`rB(@L|TM~&4IxYYfPAXFM2!k|`6zL`>gES(FS&-PEBTj`uC%l1jH9wMs*mPNC3GsSG zBq3EKwhj{80Eumm#0DLW5A&=OlDHcZ+ZTx)h{TS7ut5offdOcB(@R~ zTN8<`i^MiTVp|}w?UC5dGSj(KCAjLnki`6u*r7=5NF;Uw5<3NnosGoKM`D*Dv8#~S zjSw~@5I|>jz(TnPA^{GN2}o?vg$Xbmngu*mm!I-MPh@F_JFC`fh4{MbT$Y~ z;xLlLaU}LRB=#jF_Dv-AT_pAsB=!p=_Io7uXBZo_i{TfH!@$4*x;g^pAXZsKngJa) z0uvWN5*L|1RZT*#b#4bl-gKnyTS=@yrJ_(6E4T%l9s{^Kfp{zS1pjIF$T7$&ijKtoK#NLO* zJ~aKHx`cTBStOw=NbDO(?E6UU$4Kl~NbGk=?5{}dpGa)b-6Al5u*iYE0Xip}fdO>r zG)#gYA_2}4Vn}Q$B(@?FTNR0|gTywN-m4*DUGIw|6o|x*Kw`%rv6GS5=}7E6Bz6%J z8+5D;%;~jA;w=z1#D5(S4%koqNbJc->{&?cc}VP~NbHqJ>r8W0Zt^g01{gS zi7k!9mPcZ%A+fa-rfca)aPb-|1@nR%)ITE`LiQR<6?nGkuBC$bBHDKn?fQUl|QszTA;2>O##9oEOUN>D; zS3)`QB$CisB=%J#Ht4J*m^}}X#GfLu-y*R=_x!-r|C~NoS3=yDRS_xjkl2DqY*8dO z==dd=5sK5F>Pjfr+agIjBC$P@*r4?%Fm0ho;*m(~L?m`95<3@(U5La6T|xw}nro3H zT9MeDNbHG7Y|yPmFbB;=5?_eKUWvqBi^SfF#NMgM0t&$CT>27n^`I+{V1`~qQgjoE zeHV!hx)uqh9(24Ej14+k3da6|q#ks#6ioa-k~q5($PLhid)yEXIE_govE`B2>PT#D zB(@0>8+3dX%rZwLaaSZZ=;$h#njj^8uq(h8M}lPzl$XF5Qz=CBMRp5Z%E>xJELIY z%*qJ=@F1~4E1Y0z#F4~7$7aFARUqPi;K0y;aKLOsB(^CM+YO2Bg~Sd*VuwwCXegmv z4_W~Qvj?;Q3dRO4fP%3>3!q@^1|%au3!q@)paoDcHfRA9j6DNM{Tw9rQV1JTF|34e zz<%0<#NLL)-jBovEqa1k06GE;#=d~026QYKO#CU5_;)1sZ{>JItgwNu6N65tb0M)s zk=T+*Y!xIn=%hB7WyVP2=F=;UC6uSHFqYr}A1ek^lYpcq1&N)H#4bi+*CDZ+kl4LQ z?1@P1IY{gU5cYIq6A3}y^$-p?q<2jZG?5U`y@(`q6^VTpiTx0X{St}&7K!~8iTx9a z&8Uh9W7g>pO(c{hC6R<=k=Ux!1x+Qy>kW~FOp(~ONNh(WwkHxBbYmGTltYokBazsN zNbFQ3Ht38scpXrPBvFaPu0>+^BC#hTv1cN&=OVE|2eZK(w*pCg0}^`+=m0jDqTNUm z`;piukl1IC*q4#m*OA!wk=T!s*sqY-?_g|Dp8N{qFfcIuL}D|jAtI9ni48gp4CZKl zByo8pwlWf18;Pxt#5Pl7V3^KhF2PZ6g(T{P#CAhs`y#Oek=T()Y|yQ8FqfquiDw|O z^O4xa5H@6jrV7FVXP3I^jph={^^=f6Mu&!{uPP+6N$~Jj)+=TBsLEcTR?rfpM?Zhy*QGX zG!k10iLHjj)j;I18b`27{ z0g2s%#GZh}o`b{&9R>(9e+`oO26dz<$2~|A2d0NwN+|Q*K@xf}eWIm=IOB)uTP-D& zLj^PtK`esAmO)}GAh9)&*q}=VVXiSo5;vbNY9*mu?};Sji^L8=Vn-mc(~;QONbF)H zb~zHe0g2s$#O{HxAu%!m!U4y^bR_m{B=%|~_If1t4kY#-BsS;(M3}#iBZ-3!MTCi8 z(Lib~f=)k#NxVeT@DYjqb$X$-gt#Q5CL%sKk=VS`r&>!`*MqKVgqdWBByEAjwn1V$ zBeC6)*nUXtAS8Aq5<3=&od#h;!ZZuQ0f%WJ61xHC?=?Dv`%Shtar=PWvP-gr({jZILcs-*QBD7hN*t|$= z&>@d7lO&PEWs%sbNNi0cwjmN5bPFcT99xJuBsf7QNWvsMArj!A^+jTbBC#Wp*a=z; z46x-^sYv3PNbEu+Ht70Im|eA6)A!j*aMd>=$#){Ldy&{vk=Qel*b9-^OOe=Xk=PrN z*gKKfdm(Jl@jeU;XQ#{BNr>m(hKPave;sZ{pPpzhAzt5uBm_FY6Q*q@lK3Jd_A(^)dL;H{B=#O8_5mdJNhJ1J2pf`SuR%Cq zH{L>GKSyG}Mq+-r)jp)w?PEfTvCiQR?7?n7dOP9ufK4U+gGBsS=TQka^}5b^2D z9VG;L4?sBJ@I5m9sH23k_S4ix4 zNbIjj?4L+%&~dCVbJ+9{@x+V77DQr8z}O)F%fL7c3=FDBY)vG#0TLT@Z!yd=TO@Hu zB(?_<+XsmqjKmJtV_?vQ8I*t|k%Gj|Mq=krXLOYiuWvvS>O^AqBC)3)d;eeCO6(sf@BsS=XSeXBwBZ?26* z%Si0&NbDC#>^BH@J;P@Nhv7RC`yUb;bSN&&ahwK7ITDF2hQtQly$w^Zi6pLz#I^^s zr?YuT2zi3e%7v*91dD@{B11G1J06LhiNwxDVwX+N^^g#+Z$uJmMPm0Ou_q(3=OVEe zBC%H>vDYB6cO$X)Be73H*pO?L&q6q0k6c4y-`<$8WYM92yw zv6Yb6YDjD=BsS;}UswqGB8dkgv2&5wg-C4BLBGOG43JUl8YBtO4d*aJ+mXaUCji65 zXCa9%LSiqQUg#wu9=Z`pXa^E|4-)$<68j<&8+3Ls%$g@i;;*OQ^^y>;|A!>RWP}Jh zP9!!j5*u`sFw8j6UF-3>6Fv44@0BK@O>A zU|@I*6$f1^4!U?6Bo4aC9CQ{Whz+{DoRJyg@D-pFxu9&&<>jJK_C^K<26-qObQ~z? z;%SiHeGCi?CZJ2FK^)Kx=hjd~M;RCxKo?4b#6f3+xwO zq3jQg3=FDJHt29uZ7BN-BLjmWlnvS=ZVqKLFflOLLfNcL3=Hm2Hpsp}2)mvEbnQCm zR%?);EE5AmDnx=oo{51WAIb(@*Iou?gRX3^fwDnIXf;9E=AcV`p=>KA28KQ;+m4BW z0kl&aWWECv1H&w+xC`j|c9wcbuz)zrpc0__-q%3cpo^n7LD`@?;CDdTpi8IsLD|_% z3=Bu0>^vq0hEq^>Ark`w=&Egy#l=hv4A-FIpnK!*K-m*P@eex66QpPc69dC5s01h< ze}J;LF)=WFgR(*Q#{Yq`L0OWC72<)fObiShQ1(A21_nMT8+1~u2$T)FFJ21D=4EDJ zP+(wO+ zc>t6RI(an=%I0HXV2FXT{a6_489*0{gB;Po!oZLLl>h~K9+drzg@K_2$_610Y>?PSD0?+41H%p|dn+pg!#*f`J1YYN=tgsp#V=SH7*0XOU$ZhWT!6AcmzZCJ zvcIu1Fx-K%f3PwzJgSFsK)0K}gt9r<7#Q9|*`V9dL7G4ofNnki0~H6|bk4*EiF*e& z1_qEGkQx^@1_nN;IOrmBkbaPO0viK^6jZ#6je$V{%B}}phYq?j9i-?c8v_H#U=SO0 zqqz}O(Ni`C1`8$ zJv#$KKa}mt&cHAY$_5?n3cAi6WWFCe1H&SycrbfC1H(!v2XyA^1}GbJMf!FqJBOWt z0dx>8NOL|r1H)0MIOr_cGf3>~Pr=gxvc62v|Xx^&(Is^}Oy1A{e`eUhDl!3oMf!_L6q z31wemXJGJ$vahl;FoZ(cH`o~%Vxa8X>!mej{ z&CbA31>rD&E|PDBvY9v-7`mZs(B<)yplr|`@iU-o&_THKplr}(^Gl%YSPljTP$+?X z2|5%P6iOg=ItK&8HmI5`4hDujp!f$#E+83=G~-_Dc>1hA=4m4F>}QC|E&01|49L3>E*u z!N8CSW&h(~U;u?ANDUV!14AiPoS&0{p&H5-;bdTFgtApR8R{88!2!|$I_F|4RKkE0 z;!}{gF((7VN~pLgCj-M~DBFUQfdORu$-p25WrHpQP=T`NaxyUJLD`Eq85ls01(^dn%GU`hzLS%I!KWU|*~7`e z5C&x*;ACJ(fU*yBGB9L8*|#_u7)qe*r<@E7bx`(OP6mb!DEk8^1H&XJ`wJ%n1IQsD zi@$L)Ff4|OGjTC6tc0@bxwse@)3wAn2UkoAyhn+i-F-alpW2*!0;8yj^kor_z#MIkcLDq1_o9x zNbu%xF)(mL*+pCo41!R06&C{o$Wb8mOE(Qh-D7&4Dfx!sM?&4x#uz|9t za4|5rLD@UG7#ISe>_c3j_=j?ib1^WaK-t&17#Q-P?7LhH3>8rJ11<&zkmVppJmF$s z=z)rZ4lJGqW&h)1U|0ZUb8<5R%6|!N28NR$4if`| z6gLCIWhh&Xn}GpjIY_f6Hv_|CsJJ#a1H(%w+mf4s;XRaX&&|N_70L!3aQqv}1|M+D z4GG;yZUzP(C_90hfuSCBax%z*G;Rh4MW{puHvp%UM@85p)f*}u3M81_QhY&;AMC!lQ5DU+9=Y!My? zhC5KUI1d8@=!j=f2r2O}FnoZD`|vO@fXo7kgHC8>;ekX)Og#?+10R%=!o$EI0cDr* zFff1&0BNY7 zx9~796!AdIf6)2PAWJ}&?crfy02u~igATyyg=#p%!@w{Z%D%$Gz%UQWzR$zJumZ|{ z$iu*}3Ce!L!@vMC3uMj*9tH-GSs=CyF9X8`s2W9HhI)ouP>wDy0|Q7CNRcTo0|V%M zXb{_-mx192RE;k$0|OH;B`-0?2GF_DAoZZ*pcSCv1-uLlT2MCV2xt>1 zyOo!L!4Ax>XJF{yWncguE)CM$!^^-B1eRc6=;vi%h=sC2=R~JN*>iar7z&~61-uLl z)ll{#UIqq`!60*%@Iv&1*vohs7-mA%tl(u}SPW&a=4D`5TMy-I2|`*;}`UPIXjco`TzLfMCT85sUR*~fSp7})qAA$N(F zfk6Put_PhiBn9PM=Vf3}fwDn|SnEOAAjK9?wkRJ1gA-28Ksa@vVII3=D6e zoPB%@4Bw#atDrG9en@aV=3`*sg0f%pF))Zg*?;*M804UA7Jdc>4JezPpMk*$%I4u` zV6cI*`S}?b+@Nd~eg=jB5F1qf>+mx$M1eR=3=9_h3=AnywiQ1ELmrfE%g?}20cCsg zGcYtk*+u*e3_VbGIX?r#G$^};pMhZklns&x9jOiS{XBjKhAmL>CHxEw_4}Zl<@^i` zC!p*#{0t12pzQVh3=DUmY>@mjDEkIK1H%U>`z}8N!!IcN0Y3u+ivT3#p71j;@Il$n z`571_pzN>w3=B#Tc0B{=cv>9@hk;pufx!&QW)omwaDcK!1Q-~+plpy>2$bzAz`y`H zrU~S;Kmi7Z45)ad00To2lnpwIy9UZG6kuRzgR)Bm7#Jo9K+FG10S1OyPzjLYB~bPh z0S1P3P&P!bh!10R%qMSy`p63YH7z`&paWrNP{)`zlr1sNEuplne=1_oCs z8+3Me0FKgK?a7+P zawZBgFnoiurwB4I{DHEk3odQB=NQc0B_Fn-Bv-IGDr0ATPwg06M@M6lWjn7#QY4*`R~JmqXblLJSNWq3lW_28JD=)5t-V)d(># z?1M^75Mp3B0%gw^VqiE0Wp5E;V7LHf9}r?-xCUi|4h_EpWrNP90v!+zGV`<$1H%ib z_(dTGhIdf*eIW*hFHm;<6CnnMUr-L{G;z>r;ULYggcumugdx6sE5yJ6IxHL{{tcuS zD*i)=fk6Vw2ALrTW&agoU{HawLFQ>e*&y=_pllIg1_m?GY2+Y7#X%QzLM5bx85o?P zY$ebgm{2z8#Bo0;8)Qfblx-}`zz_vxTL?2SBtY4qGquv7Y-eExh8!r{O_+h92+H;p zu4iDVfO0@bl-EJo{-DeGpzIW328J#u8)V1?D7##kfngey4LX?%bm}RhWSRbR-u@!#rUIh65l8(CD-<1H&;Wdzml; z!x<=hr7#1-B`AB1FayI4C>wO#*F7kEuP_6{6Da$zFarbVv~!U8Ah8cn@#n$}4E5ij zoVTExq@nE3!VCeHt3{t z9Vk0hgn_{b$}SXPV6cF&>ltc97#Qp!9EMgA1_n1MyF-M50d&|H$PwKl3=AN{K-$fW0Rzlfqq6`e{ zp==dV28OLrwxK8k!)_?spJeZfdS-ekfLv*3=CON@!z5h3wNW8t9}pA<+1bnivDa0;q(h7z4vH zC|ggAfng1l4LWUp6O?Tv#=x)x%C;0^VAuy`TZ=I;9EGx7#26SrJ_UIIbPD}tsCb|l z1H&ySJ4B3u;UOsgL5jk~7#Khy0AhnqWP1-)6fMTU015$+c$^pm!(XU)q8I}Mix?y> zlf@VqxS{MEF$M-u2!Pb*i7_yUL&ZzP7#L)s>>4o!1`Q~?zC(4LZjSWFbgB=otM9s5t11wk9b1vls(I z50w2)jDcaA7> zFl2}`FvvjJpo9I@pzLYl3=AL(K^80&XJD{`imw!BU~qx5_lYww_(9o6#TghPpzIUk z3=ByiHmLkRBhJ8(1L80-Fq{`>U?_vKpNKOsG(g#3#2Fa6pzNRG3=C7C>_6fR44}i} zK#pLLU|?7Q6=#-UU|0`j^GYx!GSa@OE558fl7dmU%Llo zgN|H#0cFQXFfe?AvJ)j37(kYT^yWw~FtABLLas!Dfk6Pu1|5GU1!Yf^U|>*zvZqKe zFz7+p^$as47#Kijxq%FtEy2Lx1d(9aAi==k17#nOU| zkYr#0-Nyi8Yf3UO?1QS&lVo53Sq>7nlw@GI1QiFJS9S-=4wGbHcm`$1N-{8jECi`f zkYr%^1r<-JmtgVR8U zf-ZY-fQrwSWMFWIvX@FSFa$x_pi3ZPpzIBj3=CKVu~&;=483qfo~DF%i`P&Hyw3=C_aY|zPHpd-#eY80dx7(nKN z*eX&C45y%K)TJ2e8LmJ%Hc|`>_n>SaDF%iYPZJ{)0~Pa)3(oN-;2aLD`^# zwm=qwEZZZ+zz_!&2OX}J0cBs5VqhqOvR_FtFn~@71F8QY#lX-875^;7z%T*I{vpM{ zFbm55EycjFq#nu3~GcbT00#Y9(&A{*lDh@i^>K~LHFU`QfAq@$+`V0^Qgqau^8l)K*WT5OmX$A&0 zD0`YT0|V&vEs&urq!}2jpyC^(85mrk?9I{)41Q4d4rvAk(8*dL^}D4R7?Pmk7o-^& za-i%R(hLk`($Mn%nKZ<5kcJo13=CaRMW9ovra;->r5PAN2V#NLD9SJ}fE)s1tI04h zY=WvWl3`%j17%yuFfbg0vO!mofGh{850PPDxCIpt2c4hv1j>n#VPJR%Wyi}fF#LeB zD`XfLm}DTq)gi;c06OssWKgdR1A`b;9CXl=0+c;PhJir~$_5?!1Uk(Mq<*Ok1A`q@ z9CVH-b)nyqN)LD>ni3=A)!>=aoB2GEI6Aal}XA%{SL*p;#j44{LbKwO_kTH}!MUH{N7RsI@$H3qYWv`QCU;v$q1Ttrf90Nl%RD8P}14Am5y+@9LAzuz! z{(}w~0v&n;(r`tNfdOP8i2X#4fuR?w;iVh{!*nS7jT{5RLMZ!#90S7|DEqS<1H(2b z8+3}$0VtbQo`C^$U=henE_nt9kR2d4uRKFN!#$`X&@nJOcyhG$4?pXUH=! z^h3pG%QG;{gt8aNGcYWMvO)CPdMF2U9ME$ z1H%I-`?@>>!)qw}u{;CAHz@m^JOjgDDEp^80|T=HB;;5W7#Kh&`G6c+54u&1A1VPl z#zz#&R#0GI03GE6QlziIz@P{fH&S3=P=~Tj6&M(Fp==8U1_om&+ggEv!4k>_o!es% zWk)J7FnB`QsR|4X!3xmwA9P|*6jY)}fq?;Znh(h0QUwNvbf`G!#GX7TyIz5Tp#;iq zRA6AJhO%1~7#Ki&kluC$1_lrYv3nF47(gfZfY_517#Jo))h|$}XJD8G<*ZO(U|0xc zgDzkK@j)8)DKIdsgNmP1U|`q^WnWNWU^oC}UsGUUI0a=tQea@X0%bo@fL!1NGUvGh z1H%ib_!k8RhEE_isQd>Vy7LFbVParVQeA_D_R9mui-MFs{ZsCcp>1A`BgU8Bgr5C&yWR%BqP2Pp(;n5oFXkOh^P zqsYKe0%b2yWMHU+vKK2dFmyoKTND`>CPCSU6d4#mdO+qJQDk6P1{FW9$iT1x%D$k; zz_1I-1|5b2QYQpDlJ=V-1H(C}#6Lv_h8s|}fD!}4BPd%!iGkq_lnpxc2BZgMnSv4n z1A`JIw3L+?7`UKp(BU>BP_}~-1A`ot4LZ<91Ii9iVqh>*f|mcmN(>A(P>BR31_n1M zyGMzE0i+OQ*+eAD9pzI$?3=Air>_18j440s6 z(8)G;pln`c28L%)Ht2j~kXay$K_}S!f{M#2Gcd3yL(6|1Wd;U5Wk@haC^IlfK-sa% z3=B$8c8)Rw14tdnpb}*U1~aI5n=%7~1C-sP%)kIT-3FuvbmUA3RD6mu14A5?4Z43T z1Ipf@%uvrz1mzr1W?-m+vX3e=Fn|mO8FWIKfng$49CWk{=+Z8b_<3aphGkH3(9trW zySYH(kCho1K=*Nh*w2(17(jP$f!H6F85ltKZh_dcDhv#F!FuZ%7?e~P7+!!m3=Fy| z3=E&4Y*Q5mhJR4Dy9xsXrwSy5d{r12M4;>d6$S=*C_7Yzfk6w(j!0O~AUz44}hjK$=-p85mwe#d%a27(fTkfW%c*85sUU#dTB} z7&uiSA*T;Iw*<;IQDtC|RfU%S=Bf+~>QIRQRR#voc{3n`(p4E4tfAtdvtV4I>;hE= z27f5KM3sR7be0TAeYq+FLo!snLzRI6qz5EEO_hNGBnM*8P-S3fgsPbhI@+Zh$^o4P zGZo6-qsqWAAId(Z%D}J^%D$w^z_1z0exl02uoudHqsqW=9Lj#L%D`|D%KoCtz;GMN z{;tZv@D$4CQDa~LsS^T~{~~ITBV9nQQ&MAKU{-?!mx>w#122@VuExM14rQCDF)%1X z*$HY444|W1KzdWv7#K{U;)QAq4E9iVr5Xc+CzRc-#=sB^WlvONV5kS3#sbnjMU8x zC>wNI%62IGp&A3jK?u8^;jJ121L#;4kmmPl3=CHx5)7Zz7#QwD*$nCo3@@Q<9d!nV z&rmk#qr>HY9ghSag)fpHPp={6zC7{zzKo)>bC@F)AFIHz@XoRx2 zsxvTvPB;On*{;sOFdZtst6rUfVG)#bRGoogEtLIHoq=Hol>JSef#EQe&8We^a1P4m z)L>w^31v%bFfcrUvK2KL7~VtKDjEz7zo2YQ4F(2Q4M+%q4le;6egYobi4^jO}hp|Jp)J~h|{IPzyLbm1jO#uU|^_)Y5*N!(hOzK&|qNbgtC`t zFfjB(*~>H-7^Xtm8#NdhK*yVa%-OENz_1W14m!33bkqq*{Eh|#!&b0(Jp;oB4F-n& zU=9PrXAK61lTh|g4F-nGQ1*Wf28O#(HmfEB!(%9$Lz964bfgK$P(DothWAi$RZRwl zuTZvuCIiE7DBE0J2}r%GCIbU|y(T0+JTw^?c%ke-(19ILc8DeegCvw4uF1e4 z4`oMbGBAKF0T~pd$-tlu6_3|sU;v$r0uoQrWMBYU2x6yeGBDUe)qoB#afY(%D>NAx zJfWP~nhXs7Q1(1c28K{5d!Z%+Lo}4VOp}2j5z5}E$-n?I7-Yc~O$LTssQ5Nb28LoN zd#5G?LnV}bPLqKF=HV-v3=FNB(DMJfCIdq^RM9O>28M}H_FYW|hUrlD15E~oxlr~S zO$LU=Q1(Af28NYTHt0Z;%}_S876ZdxD4SD@fdO)^7#QrJ>>e!!1{Wy1 zUyFgk3(B6P#lR2%WrI#E35T+0XfZIvLfNym7#LEZ?D<*@4E0%14(KQokPAT}u|$i3 zp&Tl{MvH-=7Rp|y#lX-4WpC1AU;sG^q<)JQ1H%NUIOtfDX;Ah7Ee3`;Q1)>x28KmY z_9-m}h7}NYJ;PZo1_qG9AcM|pF))A(1F z1H&088+2X@NDoNOBP|AoyHIh^*(r~q>~C5O3~!-q0c{3`&!G4RX^_xnVE7A_aL{I8 zVAqDkG3ZniK`1*=n}IK3Z3YHCC>wM-i3yawK%0TV9?D*$ z&A{LaWnZn=W?=Avaz1J^Fa$x_f3z7GVxjE++6)ZIP`0WL149;+?Wn`RPyl87=rAyV zPD=rKCRB%kp%E&cp~Jw?4P}Fl>X-;+gO2BztqpDeZ`Wa9SPGRmq{F}fIwu8W&e?c`=RX5It&cQplktM28N4Jwu~+V!*wWIN0))&A(ZW{%fJ9S8U3i7rDu!*8gfdR+zv(0M2ziB??(25ucl6m;k^Fo;6gOLZ9-q@nB$x(p0TQ1&rh z1_li%`=TxbgE5qSU6+9YbPfv0Owef)4p4DPJq8BQIVj-q9}PVQ22k*U6oF2O2!|^2 z)MH?Xg|Z9u7#LEZ>^40HhI}Y{k{$yC=#&(YUeJjRpp#KR>?L{(4DC=gXZ08uKqsSs z#2@Q1FwBICztdx2m|qX&{Ly1zSP5mz=`%2_hq5*G85nj#+4lMj42Pj?FMS4vQ&4t* zJ_ExAC_6%*f#Eikov6>i@DR!dok8#d%3iL|zyOLukb~+s=`%3=g-UGKXJBB_g#`N^ zeFg@8DEp{B1A{1({ZXHR0Tj<5&FlsY4C+vEegg&uT_{`DfPujr$~HG(U;v$<0#a{d zz`)=R6%R0AUp1PHyJQ6fX+?KPbTLph-B_Zy*XT|)+j z-B7lpAp^rvDBH)7fdOq2kqs3=FrS><&W)hNn>47(j=efGl=1W?(oB6%RCK zU;t$=ka)N;1H)ygc$6^%!(Ax5+?as@lgt9}-7#Osn>{v4f22&_I*^Ge!ROo=r$uMJJaDs|=n=vqe&LRPq|7*<{7=ob^ zH_R9qqM++i@x%E&9Z*v9)Q0WDdh%jehSPK=;F=t@d4rP~{GcX*4vTMy5 z7*0dk4dx6Cm!Rwg<_ruspzIyy3=H?6?7ij;44_gFWc~qj28LHq@%m%t3=ChPoD=2@ z41b{P3+4y9SBffy4%_tbrN507-le z5*u{IB23K@B=HMK>^l%P=-?p+2G9x`m82TA+{5*xIp1*)c=;Q^Aw8zeU9 zzCEZS1{NuV?*)+9GDvLDm3c7rCP?C-wJR`j(83uQ8?-=%5wyS&bZAWql7<2#HfX^L zOhX5f_!K1e0wgx*Ryvq^&>9sO`v{Vn3rOrcpam{4MK6#fKnqh~;-FjOU~C>~L`X;= zu~m@RptUG4^)^W29!TsEC>xai7!sfyP)L9lpujX&Ac=!+YJ-VSKoXyW#0IT7fvMSo zBz^#ieFlkr1GK;erUA6h1jhb=r05S48+5fAOpS;PA|ya-OJL$UNaCQ|(O}{(Na6u- zc0FW02~1H2T!Mk21c}{%#0K4922&4OLjq$jK~l2;iM<^G6 z-XO7mAhAJW{p@GCUna=n~T)f@~Nhkt|4O*50Gok=VyatKgfyADI z#9n~J23^AiGiL{qIA}2lBWMZ}`!kwZu0#2b* zFg9p`28{g%N%IdRHfS*hObzHtB^X;q0TB`!NNf`%Ht4P-cnR-=BoTqcPC;T9AhBzZ z*d0i0(CtPrM=U@RUxUQnfy6!nT8shH09q3PW8Xni1X}k169=t*fUy}A5x(R>VoM;g zK}#KA>Oo5)U~C(x8c_OU@PKkaSuq5O4O-p+(~yHCUV+4JL1IrpVuO}6!1S&_5(h1P zfQcVaWXOWIM9&~80xe^JDFR&t1Y?6P|ADdpAgSk2LWBfp=>kj*Xo&-itusCMskpeR z3zAaM;sls<43c=p^odW!#p@f8gh1;LU`l5oi7!E7Z$M)2L1KfJ9KiHmK@tZoS%8VZ zK@tZoQ($CbZ~{dKc%1=Ek$^J7n=(jj&?P!BH6}>n4%0t^`~g~(0FzEZk}g1EgBB0K z)N~+;gH|8F#1|lmuR&t(Kw=+3VuS9lffw<2kR(7024IGMK@w+BLHLgci7kP|2HjHw z(`$evZiB@3Kw^ieAQkbTi)vt+L5l=nY|tVB7#p-O0LBI_27s|am(al2E08SMg2X<6 z#6AOKgR9v@;RzD^0}}fW5}QL65fUOuYy~7XXmJ3{K^92jE=X+9dH{G4AA=;3 zfy6FBVmBbMdyv?miGP@7pt*h+8+6wTj18Lihp|CdxsNy0!jRe22vsP z07>Ev68i@dn*}u04QB)$WQeFTYp0W@3lf_FbTb9i_n?Vkm|h7aaTO%C z0TSB=iS2=4*E575I1J#cFQ69WAc=!!mSL8G=4xSV(EKcn4VsgMu|e~&Fg9rF6~+cl zx5C(J)W39zN5Ffb$=O3X?Zl3s2j~ zDZuP-gmJpOf~qR(%V`V@9U$T91^*-jI8%-=GAJ;DG)+9hB+phjoq@r_aQeIz3QBA( zM;RGbI8E=;lT~8dd4hqVk8AoX1yv`R8`|$z`)3006LD8fq_9{GNZjT>jh>;2Ak=Lb96dXuD8K?h0swO)9L$*Ha^tYm#^QQ;wW#hK( zV3Fw#Sg}CImyMHwVTQ7Fhxf^o%Ez@?P6)R#GECQIys3PGd4}?=Ig?KQnZY@e%X?M( zq8kB93p!aem}Eap?-A2nXU(pZcw>>`4W$JPMu|6r60ayFUQ9~7T9kOLk%8d>2ZIKe z5)*?`;&UZoHbVx6h9G4HWdQ|+FL#)l6mL#Hn5HgVe_JW>iBjSNrNl={iI0^M@6|FX zCEij>ysMOWy$!Td`=PdplL&)R;zOsz`)5Sjwy-)ZQGl2|XVoUfn~M~0sJ{8H%caJ3 z86q0i^{;IWD@f;kozQR*CbmF^MTvJ}xpcW~*^?9>PFj$>XhGhl1tm=jGI;!X`sxoY z$ZBI?NK$<0r1((V#VPTjlNf_j;{EAUnAmz4+;=j$DKq%kUZ29`*3RH=Yp1g;eF_ts z2ZOtrgpRZM6sBWg3_79VQ<#pKGt_+Q5n=0PmHlADmcE6lt&7!Wn!6cOn>T}x7)Vbv z!?9M@7?4TFA{pFmrF_EDw=l6a*0cJQfs_R^_#6W%vtw|pVU5|*K849Gm?3FFV)_)O zHXnwVHjtbrgIg&p1Hb>IWjOX9G}AEro(W2 z3zM4{rjmsaOA}aWT1hvoSlYN57%qu1y3b|zPD@IB=*G^#Bhtpkz~Cgt*gk~|EcT#2RN>fy z{~tmX+~)sh;8Af1RbZR@UqNYsgVF@0Ll@X){nueuH|FLM;Wlzgyu-tEZ0i3e#Tzn7 znjCK0tPG9}3_NX4iI3U%85q$DQO z=3QKrBv!7*VWcE_40J>~LoJiJ8jFszQIeREx`>kEH363eTFOd?{;XhHl(HTaI}Aqk zN@6lC-pNTxV(BUfjk!$bDo~ATAdOI6MvAwLycmvc0PQVMyr}*Zl+K(K@2m+?U(5w@ zz8fn80~Qag`|r!nCeI-4uD+ZTqOc9Lqw=fK#d>CSRqG^WG3g-hwxGm|P03<1?Xk@5 z>g++vqHG{fq^pA*k}4LYDq^H~O+aaZm8sF;KP#9QC9OZk#lXN2qzv+TX;HG6E_1mW ztC6y38xun~%-c#xt`AbY6%@tLrp3Vil&MXNAxZJhnlSZx{4RgG=)W)fF&Si+yGb%I z$b42;Hj_321-#-lnO5)Qq{M6Ks;ohZSKF8v%vC`yPP`VRcm>_XpraO*6tBrNdj|z2 zUJF-dGpbj-dW?yIL0y?e$4M#invvoaCBJ5D3_*%_ z)|jf}ODs>P|Bq#FlVOk!SBE7QHc1AVPwq-)(ke=c7a`UsfgG&NYNS{Xaxqrh1ymL+ zN?MO_v6EBcHFsq;BRmdHQoNO*Z3fsnoi!GC9jn?~Pm*h?=~yMjTbf+8 z^{j1~{}@08$ug(J$4QCz+L#zXWeH1D;$x@8dpsuTPc@G*G9)S9l1@uXyag!&pSJ$b z#rPcq_xGuN-{I8^Vi-$Q%iAU7Iv^hxeu2SMH ztI(jtyKFlDKn|!!Bws=fxXmNX!@6j(jf3fAo*o`lNsQ1a$@#y)NDb-> zXGGbSGdC&TQc}Dd)xpEXBkHiUd6iNVxLI-R-QSwe3d+qg!UnDa=2HUdjT9dQDLx8t zKH%V}$iv*mz`!7F3^8(P`u|u^EW7D5m``GATf&^A_+-I`FK1a6DL(4i262fP*I&1J z%uug~S^Z&~&CLFk$xVg9Nb#CXi+3<6m#eTkLCOVik(zkT30y8TDL$|=F*;oTCxh8Y z@evQJfd|M1PKkGH+1QvE7@UF`q(hAkGeMkUlz9DV+<#v#H7?!oNla{Q%uQgY98$ak za?1&*TW0-ft7m3_IcC>ywi@OH;{yhUieZlhk7Y5-usFC1yg34L8LY(O2&z}S3QnbH zr4~HZ-CnfV1`^v4OQ6A(e1J!khuI-`azdhFKtM@S;+>$x`)*P&%a@xeH!0qqbK}xt z8%X?1-3>VK1!4lIt;XaQ$$ZR?`OspU`bCR9q|O9v_#(9mCY{E`#Ad;~d>d0+F!LQ1 zjJWq^W>8Q&0FNZ!|FK+ZTn0&p{+|(Yb7xjU3K+3V(6A8e`qt)v2$@x1*=(8597$5V z8>Co&TRP3jl_4qdZj<6ItB$6`TW#ro8IY5RnUUfxEoE?9;Fh$D6RZqSb^C7%N*%|d z{{|`EQlHA?ro#;Jftc4{H%(>+_S6zkP~%MmNs9HiK&jx51+$hKC?1*=Z-HVSzlwWN>zjZy+OZAuI}S3$G_1DifW zOl$ZorejJB%S}0gPVL{8n#88X5VRmMd>7L(b%vk?Y0I|;1TCltm&#&OVF*&ZcV=qP zA_kt002hU(#Ct)j>X$!Z3R+Vi;3Ckbz`)R?crTEFAs}Kx&{T$?#Cykn{D+Cv!v%R- z-53~nmNqHgYhUWz_TfK6d5TTYdXNbqtu6}3KqqfbPtgcuVBqlxm?FTVIw4|mlj8NL z!0;KuL2Lg9xCqFwf(&N?#hV(J0<2}8D)+yQg`r9DI=7N`N6?9XO^MeRg))RGy>CyE zF{)R*uB3R^+>q&550la1e}@!rZW3kyb*iNQ%YH~wysotVe$cUhn?xC6+7z?}(kF34 ziszuj>#CCfWq1x85eU<$tiMT$4}G9HRxc)-M<%N3rI8l(tn-u8&L?FTnr znb=M+$b5EJ)`YZK6|c#()q58gC0;96V^31N%66WCfdSHDO}v(*c!ljW0|T@m1vju% z*qjuvvR!6ifHY3QC8--&&l4s#aDxuimJV|#(4=DnwWdK0B=_Z<0`=flH>6GHu55;I z7}Q;?NszJ|+@J)z3e=`^Szu)bX@#bMTS$xACv~=O45^2XFj?+H;?&s@4zP&Hu)*Wf z7aEsg3^L!0E;75TsYs_JC0M3X<|ug;9e4P z`ULlq5Rv1{+^){*1nwn);-}u(C`HUk1*8j_L^ZhrN)?h6?kvzXd+7;Lyfv=W2P?(I@cZIKMe)ERuDx6fia=E>lb4WgYGe5AKaF&ztHU^8Z5 z*t8%Gq~4ZcxwTHr?CrCd-0T>ZTl2(tZ=c1)X2sy<$pBK52vVcYuxUXmNQF5=TfH;G zrUiN1r9zflb8lLZ2@+LeaI<6Bw4el}QHNpEf=ZB}HUpa_14Db7;G*^Q3(^FnL&ata zanF=iYV%<5+7YJBw`pBH8xuqMGbT4PhE411H>3%$*)uTQ@wlV9X*t7&V*=af2n4OD z4@eW>k!Vw8NYh|)Gi2c2!XsPH!?WRR^MMT|f4mPJ`Ey`zv)F1*H!g-W2~ZccO_|}2 z3&I30rxo=MX#zYVZ5j-lj{H5e;P3J7Txk~vhY~jx1_lN-l{z!vCd!~zQgck0 zVbfBE4ci0`Enzq=&2pU0Vb9g+&-gYeKHU&{ASm%!kmB{A`o#MncZCTgEd)g%59={O zhC_fbm9*Z^>1|AJJ zW(FP;P}`NK1k{Y?Y1yQBeNi;SrqfKD60aXpyv3~~edbW&t*}S;dR&#({Z~j6;O_WV zuc64s%Allp{gC2Kr9=NV34tmfrNry1TmBg-UN<*2wj#Y#p`UW z3{5BgZIWPs)I?2*uTnRH9Kx-npiq*g^vLPRUk8VhCdKO?R0I_|4k#(!YD&EBpmabf z@fN7}WCXU#=PP`6to4}C-EyO-f~L3&J7*`V`pH1Ix%%Q$b3+T zNP+FQ4qJQ09XtXXp<}4N^^lU3J&iL-@y;GMa0gWU8Iv2hdRpV5 zz8E@Q!^VnbTt{^>q2iTb=~}O}z>56h^_T&uzquw1GRR=0yxxr!G5WNY88U$OwBw&G zS1!AoEQ55qI(SUPNO}D+N$?dMp8R_V%3 zfAExzXS%>zZ4Ook1_lQ0=@+kXi8Izu-^k4-T>plFf#I7t0|N_az=ZDtyQ%~O1GpK> zz{2>0fq{YNK4@GK)UIVri+}jx$7+5_)wlHvm zZiHm@16}pV!oY0L!oa}l|C@n59ebqEb^?85r0MK*n-#gKoWpuz9$Rn5G95tLxO?Vr5`ZVKdcWVqoB2 z!NkD820Cnlfr0xWBLf2)XweA+3-?+O+Y(|%C^G{Cn-z%7!wtGykPUPI1%m*$E65%j zkhlnUH%PrLh%Lblx~Y%N4#bw>js>~c9>i8)5C{ORYG>4C^T+^MeSnRDfz1=dWZ(wf zjmG8$VmAo>2Z{TbFfuSOFbWz>H~6e0Yq*PrfuV;jcq>TXZIF{gKuiYi10Z%N$aog+ zGLRF(b~7UiVo?zaDh28wXqQUy!y}AYYV#m<-&Y+tt`g!P*%3S<)C77=*8| zF)(~!D`NCyVPFus!oa}5R>lZYz#sw|QfI4XtOKPO5zw`kP!5Mk96JL8l*1zevY4$F ztVciubh{y2eLdrJRt5$U5zyV2Y>i+k36Zs+P-_NrWJDA}oEF9ttPBhaqVc%}3~X(T zr`Z`8R76Zc0oTEJhn;~zL*y*T?Ok9!IwHLwJ-uL#fk+Z4u=>FEn23OGB4wMv_=p{J zm~#P1XkxD5)U$ND&tRf1_l=qP|&i?WCVq$hsaWpt+N;_ zm>3v*M5cqxp3CUW#=vkx! z2=d=vP&~+RSA%RlXvE0Cpup`8Vjl+GL8!tF>NKz&z0AtMpur8grH<{ycUA@l9qu(C zpPdwBV_-1gi!V)LU^{bTy3Tbq*|0QFR9zGYUC+l2x&@Byk~$j$gF9b*P7(v#Rgfb* zxQ~M@z5#MX0Cx~5yl&26U|s@*|OwF9#mM3_MK*@HQ3BCQ~;4&dUAN5q$X`s*8NGWA|83=9HnVT_<# z0!8M7!YGmvR2(pf`~h*Ipf;(4LM9q4!y)nm#EAiG;bGus0Vx-bWo2LpU`t>G-Hj(Q zA7oQ9I8GTvoI$En7(t$95lIEbR4SOmBeI5#A&P-54a^e}nF|WPbg%(3A|Q{mWq>VL z5%C4NBonMmLnISqTo#yPAo36tX4znlg@^#ivpHangNPCH^anT9V(NRC7#P?!#5foj zc>P(ySHQV*Ffed~uKeU+0Hq8D?krHA2eVnYPqQ#Ea4>)(k%NI5bm1iDD$vEf%oVH* z3|uxKKQMy^k=c{DIl$F|2s^0yVA;>ez#s*>ft6905ma6?uHs-|kgjE6nC@Anu2BDh znSp`vA}iQB1_lODMaDP>bfKGE9!MNy9jI=SPb>g&K@yC?3R4-unglo*7#Kqox>>+H zF%U0Q@d}7{nH8jwF@k}g1$2=dKj`!z27VThEwZ4CSs61KL7hkkhI$4DZBTJIr;?L_ zL9U>rh=DN^Tv91AFfhoc6@a*)@`*86sXUp1F_+Pffq@~Kfq_9eg@J(~TbzMGg)xe$ zh(U!hnyHw9fkCy5gMlH3fdOPN1LK^>oD2+V?HmjY(-{~T)F*Q=FfazI&jV2*8k;~v zGoXb=XJCq6fw~$XSl-+FPRt^v=@PNH-f|OIx7Q%QUL>F6Iks`HU}w$mKRVNu_BFj8ho} z#X<5LDzp z7cel+XEYLLU`PUSGYc3P7chb{K?8`JmQ%pMxQNjLbV@gfpOaF+z_^&vPMm>Zqc8)5 z4v4#i(FL?sFiIPAzbE5TMmtdYI|5c+z`(ei(Mz0x0d%c{QVz(lm5c%63=AKI85k5N zfqc4_F-@F-ffdxEWY7hrXvSl#diz0P_LuPq69a>86NvqtQST_oANyEAhkol`2CgQ)OFdDIR@i8#yEe9zxVf`b*z)-KB29{v`Ai}_40Ltm0>yNaXK{40Q0&4#8i!d-~ zn}c*r0P{h2HfVnWr9lWEbTxwZQ4oJ33n;}t0jbXbrIbl5peCcdCFNJ z{$HGdp$K$;6=M|3#0m9mpv+&wq{0}>XvJU%I)J_xlz?Cg)_@f-TB|U|K!T3JaDo^E z!whi-h8?{1-~t3@3iHGb6)KD|j0Oyb7eGhTb3iWM0u?dEpdtp8J5?BCK#5(2F&31@ z84OFs85lrCDiV3{+N6lz{;xTaHyW3MxAltL#ju?B;r`(ubhZ7qQCzgv!3hCaVm( zg#hF%6?o)89n7Hdl#hYoGBX21I4Ey|BigT?1=Qv*2HjHdkB@-?)L?buXJF7ND_~#@ zVF6buQ4AV-f(#6xk@L;`3=G=KK?%Bq1ylsj0i_7gRY<$U85ls#8^=Lf zE<(f*TEIF$-C9sAzekaU#P$Gv=Y#zrxFYdl`vVTVI?rbSSBt2 zWq$B2t{~%Xa6tloAw&c7#5eVIDvZ&L2GDQ^UFQl8chJ?Wpn$vyQHbybI5=Y9&IQ#0 zbqt0U+zbq|5)2IgA<7}4h2$wn>_l)gFo42VNdy`+bL!dZRX|DZM;$0|LAfrA8xpt< zFg1|s4VrLzxEUBg;gSK9g?bnsdQcC~f~qro1U0T7rUAvcpWF=f;Cv6d(g>0R7z}-R zAT9u1lLU?;6fKE7ki>o!Vjw8C3_(|af|4`n9wHUSD3E871NAHqG%>&7frOHjD2o4M zKpGef*?1wc`Vd)A7%~`Y@aG2rk=Nsf-Z5ch)a3IZ!ZcCS$gFT}lupj$aX zDQP;ycvy_qLqoQm50W{yqDaGX#crr9=xQH?9WdFuP+8DTMwqe@5)hL`#h|{1xR0p} zl-XJ8AvHbdG80g7YKf)@Tp~c72s*j}qz2RiLRb^cR01{#Vh-qD6OfulJm!EdTLGz= z4pLJOEu0t(L8m)_gm;64p`xHd3CVb6&~+yuH3;jWVZgx80=kPw0@OR>X91Dmu{>6B z1_p7^c!xNsuOklTf;xr_{NSb{Kg)Aka|#?+vE!a14Aq$0|%&`2x?|?f&>^C z_*rtN&$rf;uIFb71P!!-Mp-~DVX(9SgG87(1B0muXg~_oOig1n1N8(!n0t@eV+IBW z5aya_rpm&=0K#17&AzfRFn};ur5R`t2!wggn8va(Fn};mI-}Wn&;>nA3=CXT%s|~g z5atSfV7*ccc< zm|xuN4hsVV2y-=?MKLijL@{l5Xi=ZR$ivFO%xA@I&FU%4Br)CATS#U4_GUg2eLY4) zVL?VAHV296PLhnm3=C?l42Hr?;tUMxV1@)}pokHq0wk$9UC@(LWcqpy7Is!GkcrcM zg+-*MS9?oyF)>L@?{8DrnEu{QQ;UgFc)Fy$rV$5d_W~nBkkxcX9&XX;32tUAV!oiZ zKSL0RiUJK5Gkj(YnjbaYCQ3!Z71TihVUVOhXh@o&f&oN>qJ^2En1Kz%2!=@1foPBk ztRM*^8IV2@&Y515$}Uk~!N9-(8vJEs2m(=y;8uZH+aPWLQLo_=Ak82Q>bQes7#V^< z)KjbqK%$HcAt3c24AKJ;0=Wt@J^<>pgNEW68G=9*52#(q5Cx*57#SFt875As2Qff{ z^DN*%02u_rAdS*s)xjVNq==be;uI9s!Sg{XK^UYOq=bt9Z}AzyOkB zWC)36WMD`F8yy0oQo#->uA2{HtN@D#gQzoL@rhgNL5%ZY@emND%nWw;mU<9F4b*33 z2m?{(FmVvW79?KJ5C)?6z$8En(C`r>Ll}s<1QFj*4`N&ebIR)GgBW)~x*5Vi)Elt) z#0~Wz#!qGj26l#t8|vAVndXByjG#UqLkNgkBLsEU86gG+Z&2@=p?>0)dQk6HL;@-W z>a4oLI3R{E=)@R?U=RfgGiHW~TR^>KP>?cYFfuSyK)el_Czt|e27##QU}gx2Isyr# zHT9Pm85ltQV@8G$5G4e1c0I$yHT58t2-rU%AW8xx0TKr>G$7(2N(UwmV(3G}L6i$j z9K-;vbz@`*0Z~3MaS#JEB+bYW0-`|UgUlfFGeH9nB@lx^R1Y%)0~3Ssd=T9a5e8A9 zPCr;Zh%p1K#$-N-o&`|@qL#qy0x_0D#6i?Ym^g^R!U8hia6X7;gG5U`h~@*$3xOO2 zVt`irGBFs;2hrjXMIZ_^unl$uh@k;C$ap@8wt}buQEy=8fEYg@;vfn%&;Zs8Vq60i z&I}LzFeA1rZ`m4V?lL;^%buz^#%f#H-e zh#3WHHJ%XxGeOivFf;mw2m=FX$c2$1>IUdC#2=y{715yk48_F2Ohl>=0;PJ;6>LaF zSRyk611JT7;wY7qfgyvFfq{*oh@p%@W#K=YPdyP4)+5oTbxCJc&) zBF6b32FRO?3_)N&iGs!J=QD~jFmQrB7StiZz_1jYhr_^mmVto*R?smq7}U=P7j>YK zKZc1t^+_BI3|SnYke%2AEOl-gF0l7NlqXocm}x$U5d;o}Kf{I%saOoKeDq%r0g^UcLDr^i4+TgM?6jY2R zLzq153=GEL5;D|`oq?eqJbS?q3Mx4O?!ExbtPc?uU|^6J z01F=wU|=`{VQL66Fa!&NMHxWXQbP(?SRPh1j4AgA}r8h=~Fi@JB3r;0r zOT-u$)5O_11LRsp zh9D3%1LWv>hKV!kK`fBV85tr#)DCX2Z)Vhk7@!ztWQYJ!_7IQFs0T4X1tcRw1c-vV ze@6XDAqECe6frUcgD5sgVKAc}#DLTWF(3+5@GyhywE)$Bpdtp$TO!K90E$L12NaPv zVERD}&`r7QAWdwBGV?*458#3#2tP=Zo1s2$AAFmXjahymL4!^B`QA4G#1#Y_xV;Knhi4GLBSVt^WnX3py*4 z5!_pf0#Tr)J&X*|?92=dppui3A?hkS1H%n)#tZ|kGx`Hzf?5R);3_f-G{g@oO&J-Y zK#c-O>pK{p)F~7)X7Z!_C03 z0>T8oRZ!LVj3BSVNkypb9Lnvo9%x8frZbzK;u-4P6FcYsU?t(0J> zX8^U07#Tu9R5zq$G8Nup4+S+!R)Ix>5zVF$Sn#1o4JZnj879s_j^jDViDeEric>%Z zFGyJm$bOJ}7#Si!6zC*cuznB&dVH!5CpDR;O_Yg zuVA9S3NkQ&%6G6)&};_EG(Utug`&ZH5WPzT#GdE^qCqnNj0~Y53Y3SK7$&-ay~x18 z0BVGRl!FRy5X}N^kcIMyFfjB%n4q@V3b0`Y^FcJIf@fq11y%1hpqiwfA=E>Rfgu6J zW`H!w(9G?C8ph60$}rJ|O=dpGPnYtBE2E3?QZ8u7{Ht z0|Q7cGeiBvo_dhwpydMKkqDUAKzczPUy#>8G$*)E9|`LHLOQ&Wpw1MiIm5^hiL40} z4j@e+8lowJU4(%F6eggG57a$_=!yX80*61yE|7AVHdnCkLOetm7<|FLGn@~iBOt;c zDp~~8L@TME4`Mulh`$hFV0Z;#z7v6Uze7OHt9VFL2}C7<+f+r2^FfS4Q2UFKAqeCU z5LOgpV3;Dtz`(@-TKxnPf$%}m&c+ZjnI&{GWbHBo0|Q7cC>TL*2GOA4WMqg1QK0Yy z34>_R3LrVLl+0R4_O>^w>T3+)bxkz^|IJ>P4$fQ45n{v;?U$Q$t}=ND$UGE znSRio#Tkjq_?g!piR)<3u7|{(_=Z_)dTxZ~ImwIMjLeLj%nXdoOq@&%jLh|%>jUkZ0U?()qT7hlDOn1Z1lqR2&rEtYBTNV7u?sfel&-(Q&;F?8EgC zUKJ?F>vTEX)j`U}9imVPa~jc1&(1*l0oqeE7)>oxMGlR zAP&Oi8!oU8RL_Wj#Xz3n0JBg%0}6izCKg=2;RkC0`GyV5BH$aCXP7{t&PhZ)4 z!Z(nRK#dxRgHU|~QHSaoi2qTeh8^yidS);aYuvzm!wia4!oGo|BwSGgkp)E!#F0?X zfcy`N8|wIm2+y#BGA1m)urkzx(k*HdgSeDLe?apK$=$jUa&k}4kJEIN1aTmBSRFJIfkfCj*{1J} z)0CZ_60a#V-Q$+7gaE`-;MB{=%)rSu`QT?^M%L*H@tPvjAIRy7a6^KVow=TqdHTd< zGQ#ZKVB1)x2QE-Io&M^UE)TjHoKQ11#B0j1gQ}N0PWI^+7pTilx8JF6GutJN?f>bs4J%xnz$?sNYoUo2UQr5 zvK|s{OrVs?42f1qG(c(&CQ$Ls%m^x*nOVVc#>fc?PLPwqg$^?~s+d_hMZj$bW_S&l z1=Y(32{%wgvVvkzl2dH@>qJduM$YN{Nt)uqARX10gU>QVLJn1K}3pehDZ>|*46XpVu%Vk^c#^$J85 zSK)<{P1u-u!Ksv~9#k)aQZ&eS5Cc#%IK*kVGC0MhZaoXg<@k$T6qhqFu^`omh*B5Q zrhpVFBvkM9$n`s~yL{eHC zC{-FXVMCmV%>x9&ww{>*6j%&QEXckBdkT9>VFeWh_!2xM5fD!BkkGFORn3s(3yKJc zAYp%qf+GTyRmpBNf!e}MoQ#~)t@i3WGBQuE*{d(h3u%sndLzu7yQcf5XgV^oOz%(8 zwC98ra!j1e(=VoI%Cdv93nwSr^r(IM%8dNe>-OoJGO|zKvQIzBS(Jg1nG0N0LJCi2 zP>35=>O^%*&*8|I0NPP?wlHr*jloDIy_ zuwS18Tw8E+a)G5eVQGtJ`rZBd;`PGd;ulo=FoFwHPH^vt32ZyWE@r6PAZZ4i*C9y? zwcvsjO@!kSq8Aj6pxjXpY05*)CLE8Dm4vpOL4TBVeqXZeo^ZXl9V6 zo03^nQaOF$9wryo4WI#)>5jsh!qYeSF>%=PgIDu0Fo0;#&SlWq%K{7x3?Qt;z`&pY zQV&{{31zc_j?x6}!~>l>H@)89Ok5SD1$2BhXz#fYR2nuw36cY`1*gm16A=f^iZOtc z7J(P_F))B^D1*8Hq!hGj5OfeDNF2017o-*>4q7w_a}j8*AIMQ4aS&S?svopn7bXs3 z%R!w2T44wl2VGGPTCJA{)er^pFVt%wHcSI(<(U*z^CU*lfGKEaCIbU#{h2OQ95js& zHk1J}rSAh32hHij#6fJ3<3OGP?OA7Fn69Iz%LO`tAM9~Iknf=OgV>-YlpwXBU7;W; zkOy;^7(mwxfz@|`jtzjS?_pwK5QW;g5TqC?4q}6jRsop<+O-993y2N%-}KMrs)F^( zP|ct@5tt$n8#E6A(hQm>fr*3Iuy_||22C+Q0tB><4s^yBNIhs7B1}Dq4O6d;q#m@s z4mA9y1lqr83*P4jF$lzFgE|PbX%`eRAm4))-hpV41)#-tFbhCz9;o^VBy&J)s0HA) zR510Rbyc9FS3%|!gB?^4u^2QN3Gy*01VNLLFbyC!NF1c03p^PGQ4gB9gEN0))gVzc|ES`kqpgAlI3}R3-H?c4nfr|l= zku;w|(hS;kEH-@skGeSM4mxmV03GiF3qTH51_qdWK?f7S+zVpE+^Y|sw1K$G0JN_b zYGn{BB7=vpLgYc=5yOgbZxKRGJwpjABIk92f*zXlKx~kIK$<5aX$CD4fH?@n2JH+4 zso94xl;Hr92S7_EVd_C_mmJ^&rL0n1dN z^+Uo?-KQ->T)^D>93jE*0?EA}*ccdKMK%{ZB1U=G85m#%f-LBK zBWO5**f0-(mP^4r0Aj;D-~v_;Ehs=7m=6-c5|F?Gv0*;QWJd&eJ$M!nqNahJfdS@$ z$w=Z;*dg+u0H4FozyM1bn~~IPkzr?GfVl{?_yOip5LmXaNoe20dt8+aZZNa6sfiR(o(DYWoQA8cj$M5)YmTho}QhjPpXx0j<}BdASs! zrXIXo6;#TCvL$F19p+RJ8`Qi2S=JBI35^608x(0E^`NPCn0gQ!R=4d#a`XWtN1s4) zG-v^zJm^FKkOiR0dYC~VHmt%1O&`O=L2PxXZ@fXPrJxS-<3wcbWKKjX0xib{U3LI+ zXbUGIrGZugz|5TI13KXVYUm#DTs0)RO!V=VXB&UEDIKYA$G$#-BO+9#m z-VEx-Xz*k`#NZfi1_qe;ByNP;r*I>J1+=0WY7lsJGpq%;ft!H=Ry*%TG843z4VG{& zf)p^sLo5SvV6Og;qyZEGuu4e`)S!Td3WyC0d^sLOqA&(8euS6_TBczHc!OO59 z>N)tp%NW2JLkheM3nC6$y9Kk@08}JGJ!SyfB^h7J$}zff|z_57h7>ie1nS0a&zx*r2i;6uO{A;GkwtJxBv+tv5^&hz*heiG$X9 z!-A0$R0={}4PwKRm>`lkhz+Y~^!O3^!hj#*NRXKp{D_dV<7cRcx1}8T5j9OPc>N$m zQwWmg7$nU}NEU$B9D))o$Y*s(;tl+W#??$Dhl1FkSWp6$yvva+TfvWLn;b>b0Aj;@ ze3lxlG^jyrSY_{q zBo1Oje89jEf+P-N!=j)NNgTum*#k1aQUKwB`hM{8OGr>p0IeBYw&Vfh$S{ckdh5#SsZu;7DPNj2ttFz zGlZZ%U|?XVLsHWqgs5_+B3TAv!+bwih=BpxA*=^4mVqS}(4r%d9#B38?e2l)2+%?# z*eC_)xEq*y5F0j15f5Gr1#w^k=-3))LT(a9#C?k}!f`V}`4E)) z5E~|b7|8V+8?U=$Zf!Lt#EXcCENR9xpVd9UF#6fI`cs&Edb0o_^>(w9<3=D6P#6gSK zV0j(1#tzg(09giFH3v#RAhw_=#K9moXgdMSaUeEy_=>?Cyy^|2&O(%dK>(EhK^j0y z;$Rv;Y-?!hZAQ`n+TsLDbfAq*u((_#ib&z04GS=F(7HyLnIJaI5vP&NtOu=n1UUeN z-ysr{k$HWoActRZE0njlyppp~h>$^yLpNb=jbI>6lu#o1EU|@g` zk-*j^f;5BJAP0dAvy(tr2HKqh%N?NoDX_!^VuSR7)Pojy!^A;skdHy)(cmT0kWdA! z{Dzj2^$h7q7JwF9gEWAGy&Op#wBHFP-i~AtXf-s{Aco0E>Om`{Vd@tniG!Ao!^Ag( z#bY2Y0xcwmNr2W>!+Zc@!z?%lUhxf44_bE(Q-2@HK@Y%&LW28|#B|1U0>aZB1k_ok z*KSvLoo)~$!!kXgUh~rQ54#v0rf>Le$e{>2)sTsS8QgyYu|Y=;g7~05dk$zb#B|2{ zdg6@Q(*^J7zn{L~B)7u!hW|1G(-lnk1*RuV))1I};lB)vENE^3bf&Km12aPd$n{VR zY9;keUpQGqe|o?K4%X>28Tq)UD>O*6Oi!7>Av1kSB8$NEIZRvv)1UOo3QSdy6=3`_ z{b-Y_{B)Ki7QyM2yVSYi6F&Q!G!>bR3=O6~Zql492AO*T&lNF1*x+fSh4=NBdFAXPNOh_nkLW*8b(kN#lQf%y_$g$6aowk44^3$ z7#q|Xfw4ga5+kT&VSscd89~P~GB7ZJ?gwQAH|!Z07;HcmgHjXh{7BGH1Oo#DXtg*a zXs&^QfdMr217m|4889~J;9M9Rw7mdk8EB0(%(56H^AkX9@DU;m3@IRIGeRxP07*bC z14Sv!&;pRS{Pa2|S#e&dM`}Pqiqqxhii`6?jp_giDNkSbOII8``@+D$Fa;z8Dx4V@ z7-oRjpyGysfdO=KDkCVo7#J8pGf<40knmmtQlkZ7uK=+@iHw1P0d#T*BdCo9*}uUc z!UPFQP~HczK~~IVU;vAQPN=9~59RbTFfg2gvY&(Q%VcC=5D|t92!w;!Opt}p{ZKZj zfY=9RgN{B>Wr7$ADo{WNxPmMLvD1;n+o5dG^u#;}yPg50=n#YhW8+7~u=Pk0|V$>VUQZo#*p1manNaMpo17d;-F0-eV9OWYA8ideDBeH&8aHp8tu&7UF`Ksldg+U<+k~ zPE9C>vO)EKD;GmOxFiB`CPF1Z<-uwwdk+@_!xtzUbhLyLH^fkoLqX@8fgA*CF!V#k zL5+mDNNmu~Vvrh;qt8LbK|9?3K-o({8{>sRL!C?v44_Tf22eI=zi|qb4Qfb$_E^IV z>W7Li;9+3c2W5i}wDQ#+C&O6=pQcwgFKQrXy+(M+=!2X!2%@C#J~Vj;|gL! zJEqV@ zI{~$zY|wF+&QLaJFI*rJI}ggP5ny2GfwDnr7JxcyAYZN*U|`q-l>ixf4$21k_!^WA z^6_UVI|6jXkRZenAm0l^+5JKc49ZY8$oHV5L_v8SRCj<*4h7i-Ivg|^s-~u1kb$8N z$^q?|>p^0#g0goAGBBKgvO#L@K-sSa85n*;*`RGrfx`@YS0FIkV`?S)De{6Kqo^8Gcd$J*`QGh&?pB;4XBSl4Jr;|uSQ}Y zKw^WAZ~>_Yb+sQt#or1u)H8g5a=<&*K!X8H3=E*{e40=;XjsDt$_5POqPO zpd5Q~1_oa!8?>J}4$20dMqUYJgEoz}Be9o2*_*`~7!E_(AT>9j>=&S0NhBa1039X? zI${FkVNlxg0&UC%aX@>2K_?f2*r2`7psl+gHfW|3bXXyX4Vp3p9ZCpdcW^NiH!Nb4++SChTgC?XvCkukuiJ*=7ppFZO18T2> zFo+FmsDoy$Kx|Nd3v?76hz(jh2D&o=6a}EhH;By*GJpZpL4f!Y)cMGVwxmG|$Uv=q zkY3Q@upX#5XaU(W5L-tWbhZxz1Bk;7?KXkh@E{GKRcW9j=Rj=G3bGy3W4H2)*Rz9K z-NKNia3D4}tcL{>XJTLgZCM1JSO(IT%gDe0Is_iX2Bo(GkQ&gjp$rTRAT~F&M+Q2@ z7o-NXrmPO6h6!>wKj^qKkT@tX+d$&N4AP)}5Cez<(+oP#7o@0yar!V9q#m?j?KM>V zBO?RDJ185p77WA(sR1o;`^LyHeX)zKpciQE73g3akOT{8;U%?&ORYeB zkT_^Hohek@8WclNHpos}DBBqn;!rloOjjry)R*#LV(gL(!~HprkvC>zufOM$X`rq7+PAr3z3p$ICpkcokz6w0n^4DXptO< z4`PE>`87h-fckPRP&TNg3z7q=nZ(4v&<_;{ITyqSiO*nSV3-OO2ep7_GcmA({UpIW zosY#xtRB=iTMktKS{}F-%HGVxz_0ylK z?F5v4j){TcG?WcmH+K=rzRtwJaD@rl5Cu8n7E}V%5xom#gSu(2plr~P4(PaQkRv`b zF))09ii6sH-=J*J(nZio4DyEDQ{WP;t<)0ig5pK;odaayC$L(0aN^ zC>vCWfew}t0X0NH3+GCp5}i!vR5>K?~Ym zK_nPJ3)KEV*{o~~3?K|r16t6g2klUT7OR0UNF20S4Rneih@AjE;}ygPE&c+Xjs;?a zR;Hyw^@3KfHG!_i07-y?0(43bhz(k%HVdistmnVVPpdK>^v{wsS2?de^iGyyu5r&F` z7MFndAaNNE1_nu}_!-dA=xR_7XuX#vlnq*dW(sA47O8Xw7L-5~L54hDt_s5r>SZBRBSR6xhMfYgA@0NE!3Iw1zMaBM475omE42!j-X7MHyS zi3>w6%>c2vK`j*q$VqXaU<5T*K^Vsk@B zQl&xteUKtW(7G{@B2YCV!N8ycVhcltT|sP^2GGGLAoW(93=HNVHJ~1}1Oo$z4-yA0 zUUP+tgGxXUA0!TH(nW#9g&{2r5Sv>Wv}%BXfguYd!NkA-TGdtzWrKnXbj}S(GsuxO zP;roNL41%nNPQzz92C@`gEv9qpv7xPq2eHOKqupX#4m6%Fr0;ogW8oBIHBeLHBJVG z%TNi>h{i)G8?-tObh{5Is6hkGAT~F2{1s!0;bx{uHiy1_mDJNQWpF0|N+y6oJ;-flinNu|Yln9pVFGgId6#!zMxO1TF@K zSg2mmNJi3>y8_#ieY z#e#~@bdWfx<;K9k06Iwtq#o1>0Udw}VwZC>FqA;mfXr-$vO&uV+dyn#1_sbvLK}BI z=$2NHkqityAPFYO!Hyt>paccVLLfFQYCvc1gDe0AA;>}y8x%F5V`f2Y&{D$tP;)@( z26UhkNct(7~o4Hpmg6^P@m)P>BfgyqiIuHRGY6A&? zd|U#$dW(mF0cJ7iE&z~b&_c&rsAkYA#ZD+2w2~2oL25uN8MlGNg&7z?T`&+EmH>}K z#X<8jAT~4s)-&7#Nq~Z#hk@Y%68jm5EzAI(g9EW)2?}(w6UZ{qxZg*R8YTt?(0CsM zX!uT;fuRKyo1hhppw1)%0|PfmoQZ)!ke7jh56T7=Rv2NH)SK+u_7AP0e#R=PpOy?GfJJfLjQ45uH6EzH0G>WG5a zu+WVJoe&Aq3>vMB1}TC%0%RCSJdKxuAsH$TN;oj}t-K5j)1cy@I0l_+3{nqTmpKnA z4q7U?7|I4Mzgz}ogO*mVhO+B7@-i^2gK|JiB{xIapkdN&P&P<>AC!HBmx19Rlnq)! z3F3n+2Ce%%1r-Oa%e)L_-{fUrxCUi|MqKYe*&sEKc%kJ#$e`Cy3DDxu-%vJaai}SD zbQH7}6XXz(Wgs?44~(6Pqy}UONE|eR3Oe8!#0F*KSx_~g6|EpSkoaD{dIpANPzlfh zzAK?@(CqULC>x~t9Fz@O#|b*Z7o-^^{s<}#I@=R;`VmMRw1g6L5-NxdTHnbH9YF=H z=@f>tK}$4ML2OX_AGD|wgh3iWi#okP5=;yXp!J#{3=#*e*8~|1VrTI)Fr-7(fL1$# z&RPYDgVtgeLB&DqH77#ZpbP;zKoX<|v@CNKR2;O-vVISg16r2}!XQPUb(!y=;-EE` zAPf=*uepQ{#Y%#fK0?``6^o!lUO{R=%PAG1;-Dp$c2G9R5uo!bL25utE5jh-^$eh; zlAwFtKoX#}l^_gagVt8gf+%7DEt>>kkT_`B)=t~eAv?TH# zR1Ii_;|C}kv?B5clnq)DDF9lv2+{yr?kE8r(*-Se1Q`qx2Mr`^L&ZUh0&Spd&_nFtz92Z@7L zFRDVtL3V-8{sd1%f>tRyLM1?}4nv`A&?-d`25APZQtW|>gH|AdFi0G<0`W3b9JJ7o z9XhTIDwKqwY)N4T1~Dl6i!cKNh!4`602+vvgGzu_ASy%InxHE%plr}WKy4`7P?&*1 z56T8D1T==SErl5v%%E)0fjgq@T**?My44z{?8WgM`4WQ^a4;2SZ zD%^&$L5mAPQ36u)N|=G+IaC}pF932BNF1~j^BYtg#Qq10e~<)dT_#8X#s*zS2x5a) zBg#SN0zeZIYEU-FP*7-q)PN=_^r7OQRf-_rfy6;;G|i#nAhtacI~dA7B2v%50E#z| z2GCMQPzZq7pw){YUxV176^pe{4WRXx4Nx}75zRe2^N@ zgvWW1IH>#wRV1K8ltB`p^|>HP5F4}%QW!cR0b1e+I>sF&4pMIj6$dSew1TohvmCZi zHfU92IFt=i4>}MKq!+Xz5_GCNhz%02p956{S`rB|03-ogrMMg_4qDX+I^!KA4$^QG zDh^r(c^AqCt#JGXWrNg+KxaHaY#AsUbYhxnj_TD4->gAOVoXDlrCzbNP;9l%OgPtnS)aD1R zY65Y%p(~p}m!g3bftEG?18Ej!U;wRl07Wpa-H2f8G0a797}g@O_ad=x!r0SWuS=9q?+G>&5CT^)+>ixmps@i)?&)<7 z!cs~g%Rml8M(DHdu2Ntx<3(B@fVuNl+f~f&7YJ;i)-CGP3PeM`;vI`~->fOQEcO#CIWypF?8bL}GtOVzYqmB7k~8fDv?T zE|epSB%y)CHbr7PA+bTXoWmRuj3f@44TFh;X1!qSCL}c*U~JH!@E#b4fq~%!68j1g z8#H?bGw2PH_zxsDcoGWg83E9Q6qGH4#MVS&n}KF$VTL*(Nq}aPVB*n8;+073RwVXB zB=%e+_DUr7O(gbH1iPN$BZ9*KT8#&@SP*oo0MrL^NNn)=3Q%ziBym?Hb`TO9bWst^ z%p4@~N-!G~{tOHZlffKNQee}B=$=rwj^jb zKGfB!NNigqHfSOR=Fm_i@kAtcE)shI=-MQh2GHaNjO_y2^$xWx5Q!a!#Lh-ygI4On z^iDz&pO3@_&27Nc?1YQgGca6-a~K#v%lBZ4J|c<#Lt=A-cB?}jCy&I|L1J4Xu|abl zFf)UZ#1lYl@FsQ!28LV^2Wn^)61yFV4Vnyr8MF{d9JII+Ce8!epAI!g9Eq)r#MVb* z*MpY+!8C*B9ANBVBt>yZ>}(`9Xn7z^eL0eN2NHWV5_=nn4O-XFz`(GJgMmSqiDCNs z<(jOy=RgvmrSS|53=cqTs1M#DvHu{kWkK7@p=vZhY^b52=>t$@2&yohK;lp{d=Wu(n~;WsBxe{evomXbroAd;!xxEA+e7mu|Wg=j8MH#K;lp* zf_K3~#Xm#rtp``c|Bw`c@1%iRAOYHF4rPPJ=V5`Yfh6vZ#0Fg}3mQNIWnj>lJxqNn zlA2;9b|Yvq0H&c0Bms3PXo)0D9DFkl)DfUDc$lFZKuRQwzg`!a|P zRdWx-2B`s+JC8vesKjR^HZy3$I#iqw#0F^qHD5q%sJI-64HY*Av7zF2AU0Gy7{rE( z$AQ>T@nmiWhI*K9@{lBoL2RgoIuIMGp%;k_TKowMp+z8ZsQR@aHdOs45F09f2E>Mn z-$tBykqdVF6I}oFF!6 zRU#;>h=4dy2}uweYLE(u4HXBivt@+3+7?ON3B-n~_XV+`>VrURsPBuB*b_l)(1L6R z28QXp3}Othf&w)93}Zi<9vH1FUM~VV8~|#D91j0e+B=#Z*8^LNL({$N+65`W+=1K6>=YaN8 zLi2V361xJ44H~t8CGln?anQr(jXc;U_ zd^eK#ek3+%vnNc=DJ1cWNbIXf>^n&82cYe(FwM`ABwi!2LF;c}YCyv&F!o<0HK6@^ zFmcfE3XBaJLV>Y`LE9jqAuSGLgSIP!7V*LqDS;)X+bw5IoBW{GV0z3pHi_wXUWiCc z|I@>2F#QalqQUezfm{;Pd(QDmOnyY} z195$c={k#~C8igExOdDrB&Pp(Yr-=<=AN<2bQ^OHiRn4ZWF@A*xh^a*eT|{I#PmDj zToTjIq^U_ve=|)*V!Drvtikj*;(P|v|7_NjnEqjfiNy4bbTyvoZ^R9Frn@YcHJBb# zD#9~ulQ7S8pCnzLX){z*rgzNbl9+x%kym2+iF_G}>0c!H45sh#4GHBPB#n{ zR-P{NSe<8jj)IZO^oV8BJkwYF73bk)1szKV%@~~14=NamGm1^GT_!C)J;#xiXZo2? zVTtKFf&3EFWA+H~Ot(-p;+b9{qc1UCVyiIE^qONDJkx!?3-Cn?)XeyeEOMSexB)b_6hM!|DdJE zGrcEWmuLEoDiNOPIx0pyjC-eVOxG2k?yyo?h4Jz9#Ca;>yq}TG_&&W+#YlYmf@v%& zydrD}=Soc98^SNns674RN@?rqGNJqu(_hFLNKB7ml;D}}plZZ3{l_j%9>${Sf(L}e zc|nJS!eXy&`o!rh;?q4cbtR_vxU%vvuAJVepfAq2ZTer3_ytv?km)}Z^d+YMc%g2< zC^ntZLS1~i%^ppO=^atRJkxttOY=;BGlNB9`kpLZ9>$>QXVr|v8RMtVtrbz8ZlR~g zGyTIJO_k|qEYu~YOT1E-;9Ukfstp?JtET6=vszEDP|`P;K8G3P(g=Q@=|A>r@=V`g zsm?RKV+$M4bemZ$Jkx6;_$8*lu~hfqwTE=$KxvD?d3s}vu=RA6NdAiHF|%1DraP$U z8%)<(Cv7lYfK@_b`i^=LiRm(DGK0QKRpNBDIx?+JYD3BW2CBzxCrk_2eDK1%x zq!_ed2^K#M)8{T%5ubiRU7u%qj-9#z?+PS^Yo`0oXAz(NXM;4)bcGcvEYl~L=;=)7 zX%^v`K1JO?Wx5TBbzwe>%Jdoz2_8n4>4k;5;?sE+u<-DL`ZF*)l%`KSE+jsEO`$Fi zqs{a}Q$6wNa}q)2(Gg9F=@Lb{64PHC(d6N+;zszbar(yvEaKC3T10rJ=M?FxOiyu8 zmzb`xNt$PRz(oxn#skv>lR)|IlRD4z8JY$>)6X2$l$idYMMQ%42a+*=rZYOKH&4H# zr7tmkPqD7VbRKPeiRo`ZtR1bO$Scv6;O*c+*xNJRu|(IJas711^WA*fdAgP`uEC$nD)FpVABWYVbT~^mXnQ`xQTNib4 z#uL+hbq&O)r)-s$;Jt^W>e2MKWxC>wZ>OJ45f_mdvPeu%I4e{!o#&c{#Pknmg(RlmvDTBA{zp$=V!D8vy211_#eGK$@rvKP3%`@HSybup>6tqzd${lgj1G_}Tr~4Qf@J#=)oP|fS0!gwFiQPK= zq^+JZ?;<3jWz#R77ZRU-2c)-S1q&!a7#iv;Pv3DvgNO0f^o;@%;*8IyJD%0F<`n{+ z)dVdO#HO#U(iN9fMiSCNVjE1qSf#5x{mxlUgXw2(YDi4CvDcH}Z9-DfHr?}_rn2oE zBq3019Txr@k;JzmvG*de4^H3OBcg106-fxR<^g8JLnQI1NbI-MKicbwPoJ?%nrFI< zF(}AZvG9Znf=)_;dJeP*0%nQ~k~nC09ZXyuNgUL%fr%R-iCa$pDw+ZYF}?7% zhPWZ<-d~sw&@u=ZI}S-{DiS+$dZLNGxMT^EP&E>}e)_{+5pmBhB%z5&?5RlXIY{gU zNbKcE?A6mv`$Vj#AK5MKA^8$X{4Emu%k;T-G{mRx=o2yE6%|6HbIIv<`$U36J&}Zb zk=USB6)?|6B8evI>2FQ-l^Hvy&+Qix=beHiGh@1=nZ7vV(&>$tG?k~< znCbIO*I38Gqk05M%?Tv-O(gc+=?l&D#U)=L3B5;Rf1W;bue9>?9qU*mrgz-akeJS6 zuFoSWFO2Z98WLM;`rEzI;=HCvLN3$inj45u&oS5MQ4K~CPeNjYR!_h}C3kwSn1uLr zo-3LXjNQ}Et_M{c>sfdh7fs)}Pg768jGln_;@^ z0}W+EX%U3u<&oIxNNjB+w$b$72O8qjUx-UcOfT5T;xWC((tu}r&s9wxRnV~2o9`c&57?kT#fZa!u1i^)He_Mp1;jxRBU<(`_GV zC`*E7{$b|pBT1W0KWk+muIh*+1ls-xQyPLK9x+|=x~B5gcgWRue&9o zH$6aHIiVi36${n{2kpNC?KK0{A)q~1AU5c1tUBl(G|=HZpnX;#anLSe(B3K#8#L%O z6RHNZ7-2D#4cZp~+ByYN13Ii^J5(IB2z4KXUC#ho2zCU*0iW7=3d%NTU|_faWm|z( z6F}KE3=9mQtxzCCL8JGNq2kUA3=A)!Y|xVT_fR%yO8P654VnV~4`qXpOT-UA*`F8~7>+^N zUqM?f&OkXo7#J8pN0ERm`^Uh*a1$yHx^edblnvTq`W(vUXJlY_i^To`WrOaBWnh79 znp9(CU|@%`L0eIIp={6v(4b@Bg_!CY7(n||WuX$FJ*uE>Z6HU0?m7TXu7cR0EvBYW zHK2W|)=>6pMg|5)D0@321A{x1y_1oF!57Nj&B(wI3}u7%^ntd!fy@M*RFeRj)dg`t zdsNe*ia@jTxllG}duTC~4cabR31x#e`@!`7WMW`wg^Gjb#k-+w(8j-sP&Vkqn(0tB zXrJm_D7zk%x)(z^pwzt*$_DKY1Rd4_avW&i z9<%gn&=8p;OE$bW>gL074N zhq6IA;4hR7Iw}WrU<}A&&}9aktdO_|9h<`sWrOx)ibC1t%nS_DPjPzjHhl&|*`STFQBd}B zW(Ee(T(=NtsoN@M28MK~1ZWunXyO~>AW&8+hKhr({;q_w_ki*(lnvUS*$QQYmKtMEVfnhV04Z0eACzSo4 znSo(Hlnpv4=qQv8+8liv$_8z$25n;mS-{7_z;GQZ4mwQeE|d-0I{O&Hu4e$9Ec6n> zVUS{BV0aH@%djvoe1)<>7sCIBvcYQ~*dTEX%Bt*8HfYzW5R?ttc`5^CgVsc-LD}{! z3=9TPHfZOm6=|ylO*kt9gAbGq z+7=rIWrNOW0HsRwPN&4P+IgI0BvKslg&vvp8*4=V#h2b2v;#h|MJK$@qpGBC`9 zii1)y=$IprIB1{kDyTSUuPta3I7l3{w{{;?d@Cyh!wD!GwAc0$m|f4n0NR9m2h3q$ zxW~%C@C?cZ?YsQ|Wxr)*VE6@P|7T@jVBvs-7HI!1=u!cYWgKh_3=&Xr(0*JcC|iJy zfk6k#2JPQ9gR%|T7#JKtmyLilgVwZoK_#r&7#KpJY|sW?&}{=CHK5a{Qla9YQ>j1; zEI{I*4Zfg_o**`8lP_rFCx{K&)Y}DBpTWk!Fa^p6ZSn=(b`4Tf4@#jcpc4IT3=Er~ zY|w_^Jy7;sHU@@cQ1*H@28IhzHfZDTEhu{j8w0}=C>yj97_=Y*WC7^ds~=Et(8ggV zPDsc-1l`UK+Uf{W1WK)9P>I)U3=9fTHfZOtHk1uIC(97Z2A!W}17(A@GP^<9pq<45 zP__s=0|RJ7C&+xzPU94)IA{lQ9+Yj(UeCZ#0p)=1u>h_00BP`KXJF`oibu0EFieB8 zLA#F^K-nqm3=FHFY|w7xEl@V-OfJy650E*a-ODGS;=Sw)440s6&;p%1AU3G{pU%#} z@C?LZVqgGmU;YSXgHrboC>wO>7ibL#$WTz~X5oSa<92oi1}-RjCp!a!0F(_n6iy7v z1|4xA17#m&XJAl*vX6uEKWL!{$WYK}ar#gR(8*w?P&Q~UwKbFt+6L_gWrGe0^M$fO zd!~b-Y|u98NGKb$tvMdb1}!TBEiwVw1=`)54H2(r0BvtBgK!wMIT#okplk~c28J#u z8?@CKbR7dovoi++!#t=sXc-b{!3jtlbRrq(tS%5cii3dxv;h>v25oEJ4ppDT!N9N= z$_8zfJ`7sj0#XFpGkq2+0m@3Cl_(%_P*%DL6$c${b|1=~#KFMu6v_r=tJhHWG!6!a zk5D!!i-GQe0GYFpgMr~MR2;O&nu8k>eXGIwAIjOm!N368Nea>c+Hb7}6+g_uz+eDn zgZ5vWL)oV}AiGCF>OuRpK^sOv>^mF`44zOm_c<6C{Gsee91IMhQ1%NB1_scb6X5ng zXcIT+NHdV;j~omPpq->3HUlRELoQSUXcISR)eA_Rmy>~^5-Kju$-n^GuL%+dZR&1? zii7rKcSG5reb^JBY+FtShUrkY2Ppr8mdSuLgEnO^hDw0)@Jc8fv`KqCl%2uJz_1I- z2JO#22xWr~jyn!zgZ5#cg|b0=u`ff}?VJn@p!3#1c6D(wFo1TXg4_R~eEt-wXeuWI z!)qve1}6iJ#5Uk!U;u4h1+k5| z7#J2q4Fa8?w-U+*ZP;E9WrNPs+X`ia7MAUXvOycdL6>%b%t_;7U;u4%1+hUJ!_V?S z+y9{5;FqBqK)cC7Yj{A4KpVa9L&ZV){}q%y8*~;Qlnt6z`3Gf#@;nDGBs#W$vKy2Q z+5|2MWrNP~lZUcTg7UvAlyifNfk7L}e#phZUAH~4xe8?>Q&E0hgd#kL#DUdj#G@(R+sf}4TiI8=NUHvCS$_8yDuZ6NfJI}*gz?1XZ1c^DY_q3nDf28O9nb`cK)!)z!Uw7ng) zHy30XNbd%ycn=Q)!!9TrwDbH3lnq*LcMi%1W#_9xYje?r;Uco-P|L)oBg%gPVQexM!fpcP{vhd$xZAO`&Yi*=eAI9zp62co`TRq2k88 z3=E(ZW*~9UQJ6tcanLUGNGKb$6CHFIB)I$s<-=5{1ZZb@Hk6&o%fL_wW#{lRFqA{t zdAtk^wNQ2;F9Sm}lnpx2r4!0-;$>jyhq6K0b1IY#%ATNI#~{b`@iH(htcOZW;ALO{ z-4p|o0Bu`e3l)FC%fPT1%6`ksz_1g_{=mz?upi3)#LK_{+NBKA`<0i0;WShnG=F&! z$_8y7zYb-C&fvTYW!HoDy@Rfq0ci&9jeiN1(B@-ccn@WRwxxfCvOy~ge?!^!pw)o_ zkoX22>d6jegAVlsO%8(01no!%ZHor6K{*MuB^t!e;A3D=6@Zohd<+cQP|cu&KMkR5 z(6)7JC>ylB9kh=cq#1NtDCkU05WAO;fguK}29$e}p={9B_Dm=nw6#4S%3i?7z)%Wh zuL0%%YA6SEHBlp!eSnXFp&iOT%*Vjc3uT|=V_=vJWnbiDV3-MIU*}_Bm=9%xw!$xk zvO!zpS3}vLrIMg&Opv2r@G&rK2Z@8)|DbFIn#Tl5eB)za0Bz(3u|a$1L5F{W*q}9( zH$WOdXV>vFFg${?LD}jJlr6^3zyR9J4N?zUhY8xu4Pt|~(Q^qxqRAMvgB{8SZL6=B zgK|Kd??D^9K^lDd85ltGmLN80uRUmkH;5h1&%odeRS(*G?+Imt_TYy=*`R&+pjk_h zdeFXm(DgPTb}2stLlIOBXdiwJgk8@7+MwSC;V{hOXJ7zr@&;)DZOWeo6$j-8&}MIt zI4ISxgNlPvJ?KPIkoXCH28KgWanN4{if*aF80%=6=wIa1i?=KLf)n&{lU4 z2eg_03seIrb%XA`0f{pRFfecmL4po+*egGj4LSrCG|LH613GO$0V)nk>7e6ILE?%6 z3=AevaTNgu20JJlbO3+{lW@fKiUh=H;}rvZTGJwXOV3NSDfK*h5K7#OOc zY|x2|LfN2GX>UW>pyO&ELfN2W4&Ff7#{?J{zCqcblLr`tAt48vh3A5@AA(LA zfUc8T3=E)D zF9K!X6l7okZ4?Ko0i}8ksQ6<+1_mQ2yB>5df-RH-I>gr*%Kj?Iz~BdEgARF!fU-g7 zCL}@G+(HZtIZ!s}9ECC{8???Dv>_a1G3caX(CjaW4LVX`3RI1Y5Cg+J&;>mp3D6P7 zpc4c@Y|z;Xo1lu4g%}w2K-r+P7LGyLpc8{HK-r+P89>M6g7kupJO&+&3u1%LVgT(@ z2eCo9;RjSb=xhdNQAomEAXLx50NPm&QUp4Am>((uI>J~K$_5={0$PL&QUh9+tq2td z-6o|DWnUCxV9xeS*`V$7)lfDl z8#Y4OpoCc84&@vdW?<-rvd;=LFieKBLB|?^_RWJVz9h`R06H-k#J(cTz_1jm26X(v zYAE}jFayIzC>wP2!FDJcbo{|yC>wOl@?i+Oo&j_Y!bu2+;fF8-!+9v1QG|ivDwNGG z!oYAF$_5>Y@DR!d9g*-H$_5>M@D|E86=7ib3}su2FfjatvYkX282&@q9wH13te~y# zAm4xva^@C;Bnr^E2!c>H=zImx#($6+&;ie~P;t=l2+B}4=y(K8C>wM}OUP2_4Jy(Q*As)&GokpDsWrI$T zE`YK@xd*fa0AwiWY=v5=_!bcchGr;xmk0wxCzK63b76uQwEPDh+As|&0Xkg*v{(RS z&|MJ*2GFHSAU5bAh!s#Zpo1XRLD`@zwgt)tokqP2$_5>SaRAExFT%iZ9LfeQ>Np2w zvxzd)GhBgkK-usXl+7*5zyLZe8e}o(zz5Lj0U$Q$V23wQHJ~G|KS9}`gCIbs3V_sr z4toHdE)8OX4t8J>heRtV_i#bkpmQAr!0dVk22ko219KP{K!-iZK-r+f9+aSL&{7W# zDEp8o1A`uv4LS${bW#DxGEl;`f{I@fWngfCvTukoFt|b44@4Ole4y;dq6`c{prZ~z znxBa>FhoElKqp6lj=KhlgHmn^RQ!)9149;+4NAqJ4F@1KOkxZSWl(WeF$M+U7&bxK_F@bSJD_aP$rbycY!5L8h9gimD4~Nk8GtMXt!cR+4sHKui!m@08#`x?&1+t9CSnl=m2k!_$)C7hIdeL&AXJ7zbDFxCD zI{IQAR2+01`W7e~bWHkwC>wN3*hwfGbTr0gC>xZP?n2q1qcWaD*`T!X5z1~9XJDxR z4dsB&&0v*;1YN&40|P&ly-=KiK@!RaZ8TAavR8{UFz7Qb z1|1SV9m)p9?|dj*Q-XnE8I%pWC}<6o4Z7K6lVm-_$Dmt9c0eUSXKn6-vOz~!9)Yq! zmwTLovO(u3UVySeClp?TvO$Li-hr|~=kGm+vO&k)J%_SEhk$@qSTTYwaGZX(UMypK z?-$Jw{_URAC1Tj8U%VqOtO_b?V5_n~$pgj)l_@Z`&-9OX#Kr4Dr2|X|R3gCGpqPfS zK{+4B24!{_8xdCH?PRx7)<1jEVd_ZFVL1J@AGQc--iXgESkk~p%Yzri| z3y3{Euu)rnx`HU93gd?9iUrKdtQkEF3_qqj&X-VTd&0oL@JM2M9UHqcM;`-2i7Eqw z+~kLi@=V7hC$F%`|oi9{bZo0!&77o^9k_-%7(-#VADzk#pu+sF4>H5m7UnChA z3MLo&2v2WFR2N{qvYCP5&veBMePy;3DFy~jiRt?i)s@&TY++zX5uARHRZEGr*NB1P z-1LPPScRu6B&iFqMw>D)Y?-dhrlrhga*Tl?L2G(klDZNTzt42OOno(`DIccm32Umc z>iaM-7);)1Ej;}Jo0b6Ujt>kB3DX;eHI>=oeHa+tNKThy*HU7W_{hMZqdMI$SzU?M zJB@)MXFB6&A>ru_>{+jAKh9H%RaXezV*$Yo%#02_7zBx8`rz`z0GPD@c2U~&_7@x zKy%p)Y%D#XyC|6$IY27{SwO4pm`|rLFfg!NGENWV($eJv9jwbJJsEV)O$hgj?LAyt zj7&^_7^e5zFq%#eSk1)7S(00zpH!NelQP}Wo?UmkG_TeyE`5aH#W*GtkV00roYcJZ zl8osOuPP`_zssxjbh>~(qo#ndZn}wCnxUCNnr=#FQAy?W#M|-#(-*vB<(Xbks>-u{ zH@}uAtL{>e1E3`U=#U;zg$MHYPSEO0CI$x3TGPW&HqUg!nVix_520Mp)qk&{Y|urQ zAE9i}-4oxTY|zouzZs_2iE3?{e&D4k%XAYltvS;b>eM)<-w@OCm|jq)#=_db$iN^m zJ<)?ve!7RaR>1TE4IP&0bHufLm}0o6&$D3@nV) z(<>yj1g0l^P+*zfBcY|jn#sbz5Ib2hMtJ%O2`vHEW2_7ej*}nS3Qzwap(Vh`KAllg zOP$evx?_!i_;d$JEdfT8>4B13>eB^YDzPx$oW5`myE0?K^o1ZH1$k|j>07)QMW#QH z)Dp&@=nM^wEhb-x)nF9aZY!mg%*efpk%55+l!~Jnr>~aQQm!`y1tc3Y0|S!>qZjBx zL?-sNOrX^(tQ$Z)4iC$jI9S|4NuL|E^pV9Av@V!|8?@$%#Se5?6AJ_LFOWX}42k1U=@ zVF`y$y%ZAzg9@vuEE59*_i_dX23B*BS_W>=qDfW@5SxYj8c5s{#OC0RWo2MswF0qu zxRaO}7+9@AYyoaP5ZeaC7UBK|veOpCmf(H_61M}fWw;eUdhJ1M1qK1oZHF9;9IPI( zAgkw254g%9&bJ4o(?=6@eJZ1%)bxwDnPiQ!nHd;*Sc6xAlz~=evW9?|4BW;bwV@!> zSh$yhd=>_}mV$#@o@u(m9VR_q&@w*`*5XefeHW(Z?~s&pieh14_`q7kXa~CV0JL0` zwTuyD0E37U$bf3bVm1Z_77@@@9;^@!hY0AF6DWsAL~r_eIW0++b_NCwk>4Q0yTB@RL<&I4d%+w75i5{PA0ue3mx)Lh zNM-`#b#?{@3lVKlSWRREEithXc@2`81XkuCA^?(^$_QF;;v$kgU9rSK()KqfhUPL_ zurV;)5CJVsW}OH2%M%gMp--&y89@uTUWkN(lq~?8^M-++@!a zJd>4yL4{i#6f{Rqure@caD!G0vYvRu%D|w*Z8bgq9+N&F=t@i0i#%)$3@+SKtkXB_ zk`yxnMK|jWkQD*kpd-UrZ-R>Z5N%bt)n~%+udSm`E^sO_x*F zl4Npanr^5d6(bSM%)r2=0lI{N7qqUHhn>NOgMop2!t}MuTH=h3(-SKU#2Ndi-&NMK zU|ca>Ohv1Jan|&96)g?M71KAUXmtskVrF0fVa^3=GRzDNGSffm@u_VuP}O?J#4Zae z-Waw_PxR-=V$(I%Gtx7dzOjiz6RFx{{LE{Q#C5c1*F)k?e8a4@{kNu;3KK_sab|j6 zVov;Y{h4CY(;rB(NlrI7Z*U5&wl$r;eljoX^d~IxAE%e=Y5kr4BF&U<`pP{D`qN)G z8C=+2Z=l7(h*|AkoWY$kJ)cQRVmqgi)?{Arr8e-UEF+}f1FCUBOV2^IFKDL+=t?9I z8%Yp=nw`qh$03{1_lPuRYf3i&_!2PP;pNN1_l=>8?>VXbnqNVO&9|MLkLtH zbdX^blpW8&z>olCr!g=vq=8oYgEWA4bbu~-0z`!s8Y5|Bn4ax=`P&fz5?qy(LSOjHH2JQ3!RlXq2(-{~T zc0eUSbtdSZAdood%*iuQ@s$h=4A-FSt)SDcplndP?iG~1pMinl3zQAo-SG>`2AysQ zs&PSPUSMEgU}I)rFa>e0gKAG^NZj87HI<-j(C!Y%7iFSuxFF#ehRaJ~9;1y?N=M#brhq1?)>3aShYCes=1IFwmIR}Yp>H;iUgX1yT> zX*4M_N>2aS$gR#8IDKI_mpWs}bU`;Qb=F96P~ReP1uv+jl<20V&d596F@j5-71ZAf zn9kVAsLbjh#lT=S{b8h`q+D@%adKi_nqI!Xp@{`X%YC}WUiOsf0{lFz+iTsm#2L9k zTQ_)iv4Hl1OrP(eB~fn&Y9(D_0ymsM=UXx}v4bv+V&-54HDQ=JScI7w7?>H^_JA5D zj2vAc9uw<45XHd`N_(vK@P-pBXeRkUW4l@gT38?;KU}9j9VATcPtj3)?eP6u1 z7^B4WjU{s8)17A+3QqqUFE3lK4rwHTQUf=r(GF`Qfo}VSHIhJ^hFGDEq%crv4Q(WW z8%EGZ5-4r5LK{h-)#|L!M$!sU6$Wi2fsPJjg*B2uJBz@Lq##f$X4UkC3G(864In@G z$Uz!O64Te+;FC23ZA0l{4W18D2HH2n8UkW6aQ_6E3~eNB0#)u|OPLrLIJkADZwTho z=L2o8;9xC&4$^jxVR~SqysWbVw2=gAUx|R5kFZ9P0?2@BM$kHH77>t(SRot^5zxV! zP!5mC7v|}{A$$^y_S5%?3P>{jW1ij^!Y9EG+6lqh!FUYZB;sJ2CMF=^1UeOzwU4op zm4U%TqzzO>PhdO)ZvUu&f^Z@uXob3s2x!wB>m;x;2NBTb7S^eZkJ%X*Ttv*L``zM` zwD}2Yjm%}#1-F0hfjINP&U_*Q+6=@xpYc6A1H%iE)gT)dfXxB5e?Yf}@t&S8f6_#U z7gPbWZU>#q%ESF+`h-wEanCZ4IlDmZ5((~Zkj}lJAd%q)^|e?Jg4!ht+~Oed!+qeU z4`@FU>(Ra7rVpq!!g}H%xang&{X;09J};C7qsQW1Zuv3HY&14GJ-M;g9xbK0B`vOg5oS1EW;rJ+LOc@!wAyC z!@$o1QZ5YIG6HG_Ed`ka+S0|E42}#25zvk{))YpNH(5kLRRe1(n8PFD%*tTTz?uf; ziHQ6MIWL_NWPpswA`l1G4g&3;V$B3A(*T{R#K4*b<`{^CPoEvZC&}nHJyF#()<7QI zFk;|fVBk#vwHDbK3_%T}K2QpOar%TjdEe;;t7HtPJG^G%fH#t)w}(b+i85htA8o%8tEIqG ze*kngE_5*BG_>ggY8qXHvO!Iw>rgf*+1!P)L8r8VZioaIj-dUgP;q_+1_n^k3laxy zi2Vu`2aifX*`OPnK+Q2B&=@Lc6D+iy!~i;y8+3gnNV6{JFhgjiZot66APHrgGB7ZJ zZh!=-0VP+^Es!9#69WT-HdGC$!w(t&0EzoEFff=y#X%#Mpqm~+;#mv~49>8o5s2dn zRRr4X>JMduHoJyG*+rn6aG>l`1_lPujgBCLKpS5{TZuvJS_TG&T&NmQo-T&6LHE3X z4h9CP0qu1K&3%B_lR!-)&?&(n4(NgiP=NztgYp6>2ZPw4?!k1ZL7*lR==5NaIOrsE z*nVTs=2*~4!XR-_Rka?f9@Mwr3T1}!6&48w2CwOc%UMOHzb()b62xp5iJUXFn108C zNnpCeJuUI+`Gs1pT%cAp&mGVX;pu#Twe;*AK@A&5Hc)_(vG z5DV*NFvXq-5@ccL2DL$0Kyl2>$PqqWyGYBN(PnybkyaS1FCznk@AiE~T6~P`piVUt z!^Prlfr7FnDEq$vwUD93!aFD%R1|z- zn4V{$wrsisi!#e}88ID>={B`$3e!*2XsJvu5NBeU-cqZkGQELAyJGqQITe=aDs^fa z(|6QrsW48NURbB5KAojbD}a$>dSVl^`t+JQEe%GC>59);)LEA>F))0eo|w+0%osDB zaj~2_>uhEQhOFra+ZmNvS)s#<%F}pnT8F#0naLX69fw21h(A6C~n6vCGWD4l4AR z89CafpKR39X7rf;wNXo%6_hBrr^_~JU1J9wqR+(8hCAYQr#Clit)G6SmKR4%UvJT> z#f)FW>k3(n+Z&j*IT*KJXxFk}QUecqL*o;4U_2<>gM0;QVuRSAxLwXL-R_I=az?i4 z^X@44F>*{l*vK@WQDJ&w6O;Dz58YZSj2Y86HZiGBx9QOem~OzT%rbpKkCxAL0eKaU z=}&sJR2bKQBXD{GNMOfSCIwbdsS*Z>L(o2R$H@aYzz!i z;I7W}TYXx*1Y+65!~_z{Dbo%6wUW6&M<4Kj<{`SK&+FGx@OcG_Hh2W{F@n1`e?fwb z9H34(GZU*6NRWeF4ZJGg`^{pi&hi%*aj6m-;oNQjO|(psEr5$!#PWuq!x4g0ellQUM*N!pz8)0}^E9hyxWdOsp*+9tS&U%$J#k71RJ^W?^3d%Kt3P zEbN@qOZICise@)2IG6=NQpYTwa4Rnkh(Tz;oLt0y>KWAZfkcv-FEh)*& zP0deBNvw=7VnAx0UW{XcHaZs_*1C^9rGUx>4kl1jlVkgzV_ILB)Se?vfrBbcP$>W^ z5nyxJpjzrT!}Po=g=LIWrW@9(X+z2baH9j<#N4n(s{+*WV40pWK~!bB!Br-f={HVm zsZ3X>mtvWAMyrJJ3Z#)a=M1RCU{_|DzNLv-g%#96XP7SdlvSBgWBS2OlIpDYm>C#W zLfRXiQ%q$;2{!35dJl1xpB{DY#mi zzV{r~4#kCqq5;zl&T9p5fo79=K$qoAp5Ap{OTir++wkNe1d2>XHqa0VGb4u|Bzb^F z`ItG_K?gxGv#^3{1VljqS_{C;$Pu&s<9RJJMl$j~+a;}8_}d!+(?8ftf{O-s>@m7+ z5;HrZ1HmypL0wyC`+{p)8AKNVdN;L}LE08Eo0&PL*Yt}jOuMC}!Ze3{x}3K5yy*q~ zqAb%>nwX$b`{9)(V)u;f^TJbeXM8GSj!**3w{HFg&_#H$8A0llt@vAk(L`FsU$#ObD>z4sZb=a9V55G*unsk|5%zmI|ZG^xB_V<%|oa3vOjnpDy!D zOJkacGRyRotxPJ6x!~+Sy&#`MaC+r7W@!EgEn0yj23F8&6G&S43rP#6khJh%9VcX1 zfzcM67N#56XtPW&(a-^#2}&FRkmN97`bJ3|$?0}~v;^hw4>C+=oU5hH_yklGs7>Gh zN6VE9G$YHS$jHDjWBR^AErldd8yHa@gn|nKHqb$C&~7C&6Dz10#mvDDnm1+v9rXwA za)72TnOWFDr?@e*u!C0$F>-)v8y0Zcu>-XDjn#kpguhzGjEU2a{MEAK0*!5eFzeLm z7Yo@erteo)6rY}u#40}hMV2AY^tyjqQ`k8{T^NSvBorzi|7)$A`d`}*dwThl#V9;I zfl+%EMqL6Lh{|T#-oT>0hiUo=9wQz}(D8SmA_LSI09`@{VuOkgj_HZ)+RLYZ_-@LB zG~yJnj2U^v$)Hw^WqQF2NtWpvb!sZpRXDX(Kmo zlx6xEPHmUz4WNh?;nLP%Iv5elbE+eY$|8 z4$JhAZA>cDOB_@*7-xc04dWI_333urf~9sLq$_3 zV7kCGIsWPS1q!Cq5AbO7aD%4act8vO>LDFf&uCBrVPXfJZOF{Q3hKf#bFgSIfrl+j zz!V2)teKgK6|@e4nFE|SSU_uJ;i(2Rc+N6w+B_{$9nhKwCRk?`bbB5%3p=QL$;`;{ z9W*b+0y<}r4K$w2a%6fXueJi?>FKjTl-u;(yxNM4Y}4=ZYU@ja`ot^>puQ>>2Wa@7 zMG-V)$ipEqU6oJUoiS*7E}ynIqsa7DKJ6GrhwYE}wCfq!K@A=zh9|_Qvh5Cn+Les# z#U=4YDXC@C59Tl#PrtWVOBZ{hYY^7ngpuCB114yN)b6JAmm1 zq=T9vtF6JvF@2(}wl*UdB&8iV1{%v;r=`LQI)Kj?(nAFu3~y0>7;&TXJCMKQWN%>f;y?-ac@;{H4oyzS_Fp3%*~<0Br%X; zN`9K0>2wDLZ5|=eGF~2DMh1pXP)IN^Fo;gqYh{)Q2gNR;%(4L`Nk%qMKru6NfUe$T zW@7aK3BubjphX$XEbQPu3=2DGM4g$DBM3Bt!2;^bvQ~l?YS>OctDr5xczyaa1#JyM z(A*ISGqExAfD(Q5ba_SX575eul%DQ8CGGU71=`%xWhvX=bx+)7DlNKM$Gq8*JrDQ%m?3~drYIyVXG+6zc(0W&TE*La|o@3b&c zmgx%G+H)A+fJR)^ApkZ575dkQT2u zq{{nHWg<9z<5nhhRtIJVhEzzYwG`Af2A5jTAhn$wq_$I#(1F%=YT$Bf`Ua5k;My(> zQgm5>i>~PbN10d{Aq`&r;>zOk#LN;Li%XD4-;B*I%^)MMC6Hw0udnS0?w#_07A{?w zK1*L)!5z}ifsL<&2Gy7u*$hExhmiwxa4a(uD`=@VGY30ps*jn46ja9@jmI(N1q=kz}Y z+R5yoQ&^c8ZV+D}O}8`BzBawYSlbtS5wyu((q#HqV{P1hC{{>&diw@5?KMnlPmwys zZ;^({K>bBn5%is5I;iKwxB;GSrU$?h&h!gnprKk&pUJ^mTZ7R7GUGDAT03BRgSZaM z^c7m7D$^70GO|qPvC&qU?vTjHGW~~^s0wR16KKZ+c=Ck z1;l0Ofka!uJ`r|K#CrXSp!J6AF4`X$QDR72*m8RKK@r92dd13E7VR(G!;~_;Pk@PM zyQI6eH%C2a{uNd=z~)Opt^&=c!r}!qg$iPWk}_-^KPLkN1852rBn~_7$v2GBS&hz%}WQ?JjK@CewC>zwE1+Dl3X#fp`Id7jBpzX{!eZc_}mgxqmOd8XIv{j}X z%rRnN+yYrT=M$t|FS{ur_G^3^Z)RdO?7J!D9Nui>%75pd%k^AjKT$7)wYhm7Q+bpezoqG()EwuGdm$ z;t-l1*TJa98q2}Ja0WcI%L>}Fe+DvHtT}z*WL9-n9xhOo3R+FaXfwT$iA8++l~8SA z{B@^^v6+d&^u!e$8jL*C?-{a5PWKMet~COkFwOIyfq?NbnHbqX#TgSL z2WXm|iHQ|7D9FUb?l}G4Ph)9A(1a5c2TKAdMYFJi0-K42y#_oe$ixDkVPs_i9j(p8 z$N^f(z-BXjpS`xYFX;YcCS_2nW8ugJ?T-PoIT%=3KywzHN+2N>5R(gZJT@aJnK6Kp z6yqGwgeEs=VvA7@#AFQOj+wqHTswxbbvk>5wj!T4XmNjcTBD)4 zJuX69mysQma+w%1L}VY8mfp-->^bK zfOP^714GVa!B}P1n`{gW7a@CS)=cM3*H&k|GkszUv#eZ6T4r8~UcSD90=7xA=^Q_e zEf^c7uT0m@Hv(-o;Q?*M0_AB?0Be8OjF>-*`kT5Z^f;KHMF|jj%)2fa; zNC^jvJS44xW*3=Q*g@%qg@uWQ9kfUXo>sR`pO~So>H=z2^Drrb0+=g1wUU8J3A8wq zfg4N;@5ir?qo2mVW zk$nfqK@4@c)8dhA?c(XL^mJ^|)8h0?Ioe+_8hG0;=4x+d4F#YC z%o@`ZNIiA2TZ@Ybe;K?Y!cxL*- zGHrEM(7M0k=@+LnDziQS^--rAz7bYtR0JogX6b$A#ULZ%naU{q#Y zJ3Y~hQGL1srwPmSl%r}Yj3v__mJ{kK7#SHDK-zRCrhl|!Q=VQ?p>3cIDmj=L!TT_o z;jK34st8bpz|6u5%6QBy>>k@MS7@(cM5>?_Qv_|NKd91n#t8oDiMK&(+C*elPfsY6 zJTN_>fmd+**Bb55jErZdH@dQwGj5okSg#$-7&zT<5}PtBXpN`ibiqJIWyUwt8|9eQ z8C9kqbYoLz+%x@PmY_PL!E`})Hg(1`(*?5yK}&}|)+vimf6$;UjHM^8v6tO~>4wU5 zyGHGQ^@~9}%b1x!bH$)|XJ7y=iwCioAe$DMS=d1<@|l^~SAhE89IT*aT+AFSpt}&5 z8QDM!vzQq<`a$cXnOH$*oHKKKgFIGEi*yCk_mOQM;f zTgpHyQknfg%@!60mh&K2_=9$(vD}`X|4K!Y4Ya zFq?<_`sBtA-Fnb8xC*N&=v01gP^N$_hRy@6#)R$|11*k#ErzxNtqO%MhPDAMW`!<> z76-ARi=pp;^g7$Nn*q|`1DY~nU=$RdESD(D z477uSbpqo7cF-0uK~TU= zWCSgkwh;j>pkbZFSjx)4;2;9tU^f-KQ_Mxgm3i|1Bng`@pw-HA85O}xpg}RqIuGpX zCnAaZ@@Sy`zq^2 z&>8kF+zitnTo)8)RGO3`E(yA&g!KU{BWQ0Ic#Gg8P=bx&{xR7g#fUL;^8B9ZyeB|L z%QAu%+6q6L-u+%hl5yemv!D5m8O^5Oe6OO$_HgLzOH~mJ(95; z)N2sY1#w{evP?n284Z@<5Xohp9{fQ?hwbP)DxixYK$vYcqsn4t1_lsjD`r#y?NMRa{!x$5j&b^f4P1uPc{a%LZ&&Zt zc3@30g#Kid0x)?yXA3u?q{_?_rss%>`aWTr~a6MD3+0wO$+(bZ1qIy=?aM zHoHmHE;UDJqpU(OQ&c*!QM=BnI7;|P+|Ikctf7;m#1mJ zRgagUY%K-`hFefJXh{TU zfD%-lg1R@)pyHrazi%MyBnBS_28K@%4g;u&1#M3NX#nl}{s$Eg0gXCA*RduuFff2N zF@n^9*3*FY^MTl)T1yP71~jn(+AIqaub6JQS#kSxgLRxX(;w_$S78Mmkq|O{qZ*^~ zWQBVy(;qC;R+)a`g9r=bpXrS1jLOpu8rU7CKiDcFFui8Eb^xQm^s~#g{TZiBmtCP< zK3(8~0t+i>fLD6D;C2!D>3ddaYfRsu#?3N)L8FoiE9jt3`RRfywUt>Nlo=SFOgGeI z6rNtNS4@BvbkLy7^hA)VlIJ=YA<4Xx*pcSg^c?*E=&+*le^YC)^D zS0JU(YHc@299pfJfQlkmwf2-@`r9a8<>?$(1$h~zKvkJ!BxK9;WKb0~31mhHh{?ds zGd(1l*M0i$c3vK)3k=g`H)zW;+D_kpRZx--w5SML8?lJ^On(>6D>*&AgO_)D#|CZQ z>1RLi^D>4^@9*GMi`51h2&;pbKqVxs4oU-YU|W+x>ylXez!imwNCQX)Rxg1rd|`#v zOQ0=$@Op`db$Z-Keu?R)Vt9EqK+SwsScL?d0ftvdpu2QfVHMJv>G7Ss4t$_42J3c6 z^>d$P`oS(HF;DPzv0ac2IH0xhta~AK5ojC?Ru>6^Tn4R+L_k3dt&2bfFe|h!@`Kbx zpkWtSU8D!Diwr>*IKDY?NrI-u3lGmxp!>ghYEdYT?}LvY6QsBT`+ zMVMi??d`GaX=5-a*Jtt@OJIlOutyBf7q@a%xE>;WrwzXJm?-55N6xKsGR}YY{63 z9TNis2($NSPhw(V0AY?)ZSYnL-5 z7D^jWpLb7Abo%|n+Ov?V&=u1k#3^Y^Pl#ZX-=1((I|)Z+x?SM7wt-mvUZg4%RwaU} zP*B){Y9LS*dIegOfkrW4)uRk(mn>8qbQTD#dIT*GfL4zTppG=Gb_7+eu-Xx{xdvK0 zhB1H^uChR@KG5nTSoH{6s0OPZLD$g1sz=ayEU?YYZVU_zN>II^Di<^@337DE^o@6v z#p|OP7#QrK(y%q6O?raP zngFpuBW9rUB|vP@-i~ch^;bbFdZ27js~fhB8MHX-1XLWfJMkQp4O;ekg^58Qd=foq zXb`m54`e9lbb$v@MWAN(GbkHWFur2iUUpx*j*-%%F`$0@Uu);c&M$y zDWS-~0E#Nv$qyUlr{_G>UI1x52t3mEVGNlb`$)Td`h@4kEKC*5)A_cGC{6$KNLz!o z#E^kOWx8OqnDF$1?IHrxeI9FTu+A_9jeRD91Sho12(kzJVmo2Tz~I0% z{oi&GCDuw&28Kn`KR(u0W|fwPwLKDchzPL8NW;z}dT^aZfN{$7i|m}@(>Lr85n$pl znXV@wsKz=+8gxccppEdfogxB^BGd0a(NvErJ6IhICX>4jXT|h}ub9!O`pThK)PqlmMXE8!Xu|atUlw3hHqQA}y>IO5h+k+?$ zR#3;5nS%w~duL<=-89F{$N{>ek(r4Vl%|+D*gt^wMEkQaFhCn5^`KENNPitP5DV|G z7lPDvFfnj4K>O?4rYCB12~QVzuFcJO7u-{yZuwkW%>s0fJTs&t4%*qp91Ln^^02Uk zgDPoQ69u#|kr#A%m;|dXsAI$3#5|qUp9tz~LeF*qon8Rjw!8&Ye?qq{gJy1FXS;xgl3-`MfOZ7Kwk_*|)I*yzt|057 zO&W090oJ5ZVrF3AVANyv0A+1%SCAG@5R-v>6-bL0h}|Fv+WW=|>%(7To-CgzW0c7V zKI{e5+~EdYK*tJg`SgMGK|A%Jl?ANOPQ4JMQ$Jn$r8bWTXlV~?38-Vwz%34{ElWYw z5eox93uy13FsRoI@8q8ag$JyYe;;HztdqZhX?p%kZ3$D*W>wZ&@F^PtB0M1D>cOoc z5s}lN76GiAe-NY>*3Ewg?&fQJ04abqvA|U{tcg_!vKH3F3Yz}+rM8;1FDQIqU3<_G zKCG~=edct#SK5-cQXu1CEvYJyYFO7EbiE z<+TvxVra7pwBZugtcn6z1MT8p2iXE`R)I!jS)t9Uc#vLbvuXm<^uRaldVDiM`k;OM zO2+9AuM3JXHcW4Ltu4+6I*J0;%~xTX?ij3M%aq4Fc|p%Ksbip!fcEY|JE1_wwuFIt z@J-VVLR5^IY+0tCt(23H2AyERYR|ZYje&tp1au`Rs{;-uq)&K(y^|8VlV4&^Wtl8k!n1#q+mg!I5YG?A4u`n>O ziGz}*lFA|8J!5nWZ$TG;R9l zZ`wDe|4lbtKK=X;Z9B}-*^e*zz$dKmOwX%f;@IB*Q+pz}C1}_UHdek8+8P0syz8NC zQ0oP>B?eUQfyT-~*MoxC9Mdgt3M(6eCZ=G0f6)BrO{g}|jKqB?8#FBg+jeX*xv-&q z`h+~G1jdl*3vGAoB~{?jMOsIg3cVkxDWIybKIRknO3Uldd6CkK)r6IoZ`2{ik2Fl2V`S(9AOZ%M^YU)^K44 zh6>2M;}&@k6TDdG{B*??OzNx~L>L$nAg6c;fDQtIRC`mobO>!=G`FyT494e7|H!42 zEd=Vq@PLj>0W~8)!LBe}@0yCd52z2u%mF?>kC~ATymp6?19a0bGZQOl5f(ECJLocD zW)@aZBZvhw>B|H@ii3d}zGMe9|Af+i8r%!}T1MNysQkVWJrjZ7NTm#h(0VZ1T@U?Y?I z^bc!9E2bNqHszT9BTQ6f`V%o7mFW$qOJEVYHdUbU3uu=BXjdX=0u+>YLCc*$G!rA68>q3#$N}oP zFfp;FgLq8rpv3_gr)Gi%6PY;JL47hN4i?aAJ0=$J>R~3xiYf5atUhS80Je+(bZicr z9^>@=_S#|!phgO89RXH5~Id6Z5?r@_YBh|wROU{KvgIRGp9~p zc%0p6x~!vi>vjVj9UVq?(0XJhJPUKCKiAb+ihV-1DMiq4da1t74$O)Nyg)$VFQ>$G z-=n5H+xHvjyksS6g$~H8pvneR@qkK65SyKWfdO==6o}2sz`$@2I*}{Ez`y`H``8;v0gx+bx~N~_DDDPna-?&;tSkRPTCrZB2A3QsTm#j4HWm4~Cw^vBXPv+a-+*mpqeEZ=wuvcdLquaGkH++gHaZ7HKv$82(luzr z4@5IDvQ3ztYO7<;0ooDA#KKy=eTl73HY4Ml?E>~Xb&TvBp!AMsT7SEwqs|qSw#`M@ z@-CK=^2D6!g0qFSF_Tf^ZF!IFZ=H1}v(|$qGhwY3VrKY3wInDGL5XKOG{J!C99ZH3 ztw4b#9?)qhu*3s8G!B+{K+_!1#1q8;N@1|X14?1A!~>f1fF&M71_lP$5-`yG2W$x# zXt^zH2^eTe255{N!GGrzb3uW0_v!tuu#FX1bt{j{kH4E^U_SDLy(S z(-U?|u}r_?qocvd13uj~;2Mhn(*vIATE04Jj9SwRL90?Wl z`05BS@=jmqtE0~9%Fe*h0-jrV;Hx7r{lO1bmg!%7byQeSvNJI3hb&L&nC|GOqs|Jt zS5gApz%ei~H?jatAV4vp`30ltiXRoU8Lv#g>!*_`1v-9(kp?)@tE_b zC;00~tAMIWMi$USJ!1$1D+_2OiH(s16e;YGNqbh7(&-Z)2up0=?yr-`sGeF>R8UgH zkXlrfSDMQZUs{}66kl9Ylv-4jkG|Q_1bqJQ^vQubdRS9cz;x*#ou|{|3nlZX-}`Qu zF#X(gNulizf_3IFG1^S84b$;wY?*#GOsAaDV!CU%jz43@^r;}KXZq7{9qs8X2TTMe zf3Rhl?jocjznV?3CsumlWj?GqGb1IzeH2;%iBb?K2~F zj&iVqR@DYfPfpTNXWY5HJ4weGCG!O?U^JS}pUEpf{cW-i-}H~{ir9n6b^7@fowJ}| znm=7`Bfr4(xJeoc)BWm%1h!YD>1^X=G?;E!qEpUjFnuM6+A>|SR415mANXkDj#3=~ z#v{`Of3pgMgK7ha&pQ2Jsg63UEjI%L4=7BgmFWmD9+)mzrlZd4$<4s9ZTiPMrYh4D z%XHKk1*Tt|t1K&5nwOqhQl5;X#-3jAUDkK{-22?h(*^i>*tc7k>u54^e+12EU1VTj zn87$bzd}d39&~Ub3llqN{Evl$bv0<_iUYiegN2a|G&9V?$N`$@W?^Fe208$bgB>){ z$ea(FgaWVqV7>qvh2jB?|1d8EsSp6|8)so*bpR;=_1Rg}K_aZIkR$RyH45lxVNj{U z{-249fq}&bw8n(Jb-Hw=j%=(y^qf3a&^dYCpcD3>3qe3#S{6Uh8GbAb%%Bk@7JtyT z5N1u#m{l;i^T5LV5!CM$1MO91VPOZI<;V*04 zXutqE{A4!W!9z*69&|2}3acq-hK3vDGS~##R*=~ipgu4QH+Yf=I{dUAG{yp5DAEe* zRznwxfX=pq4L@0f)If)y7(nJgho4eG;;`W-(B4S!sd<5*3A88EH%yQb=Lg*y&*}r3 z^kZNY3}>8f*Cr`r*vAYWZUPPdavuR17Xo53a0i0e(BUS~usLfOXiS)cJ8gP*o1{J; z=u80))?&~QE%#}L=?Biq$uRM*n|@D+TY9?XH4Z`E0MPitY|wx<54XYe`f45VXiy=< zIuGP)32soMj&;Et76t|x2H^_md3zwP2>8re*sxSQ$dqcvi{P~u~XIjY9Sz@WiB8x$lbyg;|pbJtH-^yJZ;e!*Lb zjTdyt73;+;@L7HF(=XQQh-;^TBI5=q{sOo`!@jIHK?|`$xOqShe*o%gM{t9uRv)oW z&zol;HeIt$M~QL6be}q%>5PKY@5FIS$bgfWGUI8`q6f%I9N4*hADE|aFyNM$Zcwko zTVD@yl^vrbs6G?f2jbX+Icy@}B`OY#p!M54BA~-BS)Ca{^_>6%2S`kloq>T9v>A|z zK^Qch30f7C4LWP@J17<+89|wsK?JnOA3iL$3KWRZU>OdPV<5l8Ob-y|(1@`I*_*-$ ziX;{h(9{WQDwx9~G6$q34a^Y{@dk0y8Tml>c8h=xA%u^N?Eo2;309^d0tzeEEHKAF zqPW#&)M>C)K(T8YCs{m)|d{OKFa z`IM%g?$()vG@*EG`huSZrPC+$>b$^gfE@h9oHD)OsUXkxPklOC0w_yzKua+|O+!$V z3APLeG$IEYkp+o^+OVMEaS$8SZUt=)1hGM#A=vO6Xx{KCbesh=B?vkt9i#>{#{#Mv zL2OX>6SR8}ymbN8&IgUUf+Rp$7POEO#0Kpe;Dk&H8w4gxJ>?T4~K>&K@;*`WRJpam@;y`WY-D1;e7-GJ?~ zoILuB(+ds=t1z9Ap8js3jvu4RblXKb^OX<|RizRZkl zpw*$wj2xiWI5QI~XblE42V@Hu3oB@OG&2iySc4t3@sF92qh`8c4_noAg_|mx(;qw# zm6|?bnKIk-kIQw+k@F7jHN@K$7by2LZueNFvxkvUV*0@@=5od-(+w|31vA<}RtMdf|NSr-sy}z%<7Cj(-kjDsk8F1fKCwth0gSU8+F(;h-on!8Wj z1RatJTIL7Zro+IX!NASHfp&6=<@7^2Oh(iHdr2xz&tIp*IsNkvY`2IgE>O;$e)kp2 zf$63jbe=QLpRTATt;~3Nx}u91qy>E74GU;G_2L^Akjp1-)B%ksD!ye=XSA69aH9@^ z9iWErt$HTYKYB_k3xhV9^MLwTpo9#L03 zs!zYLQ|Ash=pZhhDrN?TTE^+ycj+kC2ZP$c@C!orLK^X)1_KKt+i4Jwk>fh3ZO+8{ z8boohgVQi*D-ScI5f7RUVum#0LGykrutvNps1ZMb1$53exDo#xRFQx$2w{LX;vX_0 z8u1B`Mm%ISH;X%{7Ur%3X@@rAn?P(>BOY`)0<;kiS_i-aYs9|bNk+R6=E#|>`%L7V5{ zpo$mTJm&+kZ9okp5pGUUB@Atze+7x#fy8CFjX~nD<~ai+Xm*&@1Jo$v-VbS=p9eV= z);hOggtX2RkXq-Uvl>{Tt#db!Vrc99IH*|$ZJj$pTIc^kv(M1h`CW$T1$%X5oj}vs zpt)Zo(9It+K>A^gY!{FL)r{%jMmFg9R#pgyLj*j}4B_yII8HymS4UDh1LP@KBRe0| zs)9ALy{AXFOG?&9gUs$=JPKan4a)eeuts(xNHwgH9RsqpkFf#V$Of;op1^n-yz1Ky z6jT$zH;34WfTjUiCxMkYh$w?(rZPSPAAAi88+aodv|o%B+{kud@DTx(vaE9%b-;~m za0hE1*n9Am;GnBRUWkA$$zfdpHXGE)1~sU7C0M5Odn;+zgH9u3-43d!dAR?9e1xT$ z4Q|ry0>!Tc_X3dL_JRUeh8wgq7uN390f`^(1-HAwbJa)pfLDrlfy_Js+M}Yw?F;hO z$zQAt3Q0|OSw`H*8x&s9);DP1C#>}iS~SKAZGD4A zI9MNnvPum17pCbtK1zm+^QXu8C`~tI1KB6bXaPDYTNrfeGpizFB51N1oUD|=YzC2K zAlJfL7mbW9lerVAa`Imx(o`kli%>WqBTS&rzqXuM@$U;tr` zBaCXGTNgl>bG|xgV+sg!u`#Ou0c}R!eo;}%j&XW{Kac8kg9lu4)9alCIHnuSXJeWE z_o&Wi_6Sfx%5VU*zK?s;dq1vSlK!_=??xk1`t2Xcd& z^01k4&>RQoY!Fa`6m(EG>_BeNO8c+S<~!*6Fox-Yrt-q|pwVd1a1BT~XfPVIN)^Ng zMJ=c-1F=C1?LmV*AU3F>2O2g4u|bRe^r7uw(4>(mlnq+#XANb8hP)i1Y*4q=9m)oU zh%Xa^7uP zT*M&fOi$l)Uq@rQ!6!Ku(0=n>Y|7Jl9_R!xeNdbp_dv&wu>o?-bOtX2L(KGxUJBr2 zra8cEX~rYd3k}TFS$*I~O!q>Lm_9mP@jnx&p`rMMO`Y-V^o6%2#iw83322HnWF zP)AK=I{1)j(4Dyr(+hbF!G}x-gGaZgFMXsVq-==q+C0z=W(FqH&q*1o3xV$7U}j)| zwG$N?ExM=ZlLKzZQ~$e2C-0vrlYsRB>s!B+VZfzWv%&nmo-vwEpw9; z!=VNLwT*eY+ilo4GB9-Q-o(vtLh!(!4F?$*ex2EM<`56VNkI_%&#wQQ_!+o&Z`e?> zt((d14rrq6!0rV*xEF9U@XV@ryUMU>LDqH&gLZ3PpVQkVls7HN*giw6?GnSL1tr^O zFu7e|*tDQ(`wS+wa}1jn6mFMbXJcmI>2PCU(BWvftI&3WVUrYN(}JA#*_=Ae26q*_ zt&SaLkl}Fq@qf{R6a@vBrmLxge6`*d&H+rA%%U z85$WF8d?{0a4g_ph)|K~@SZwTMS4+!(uV08>>VPBNr}96KadGVK&%Q()*|Pva0dw4i~9doBCTz?X@Q3=9uA7+xga2<%|wo;RT*_!t*h zjRaK9Oh;5zGaR|!74$9O;i_NB(ZTqjBe;{XO`O4&t3ySm%X`O86{u5KSAc^X4K6#cTl8CxE4!`RiUOJ(9g3Em2^t$@Kk%5UaWc6X{%6bm&y%Xg$<(Izzb)WDkEj|a(=qM; zZifFEcw9kjjsIi-`L`Ol*Y(y08O%?z}<+N_aHDRGBW;x;A49Zm-Y zj1sp5rL0LxSzDB{uIG}H5?fdk!y?5UkQi5FU@%hLqCT0uNpVMqgc}EgA_K!D#VzVH zxs()lXecQqZee2qi6InOG$n4Cq_{;%Q1f_UHtZ+(k2ukq^TF?-b;uMtP8nl4HDa9oy#SP-s8%!^RekdKPSKPKpamQX} zRR#uKu5c}WFbATd=JNvO$w4A*KFsE<+>;cy1WB?Pm?{`WvMsv8xabtrN3tJ`7I|2i z@Gu#rEUNixl(^OCpn%b3Mx~S`N-2wjQi_}wobYm1N?fIsxLPT3w^HIhrNsS8iMxyv z_c|pWaARPo*X2)A+~&r>pq*wU#vm5VV08F)nDGB5#cd5{2Min)#Y|WyDQ+!NTwy6` zWp0$X!boxXqJsj`#+{r-iOYj-F+FQ$@18j+aqBS-hD`?r4lU|9b$XHFj!lYNAb#kP zYLn(PTI3OQP{1lQC~<3(;x4a_ro>%FVhm@5+1NQJ)gKfvy2-@DTD0&44@XdNK-J8e zuR)31wcTu)ni6-$ax^7wo|L%f7$ZYd;x-;uC&g{rElr8roJ1IOUh&MX`DZiVMqJ0Y zje(&lap$B3$thren4Y?&bn%N0^SeGw7I?Pho2F zWMJFCuCu*-N<9->4};EikfII-o#o{sOl+&z-JBU1-0cO*O_=?$P1sUa2nB1;19Gk#iv>>N^3R8XC4Thoxc_7h1rlJK|AP01@x7}l4C|Xbgvf}W6 zcXR2Y1%)63t}>KwVPf-TDOykra&R+?TRl5N(SiyP=QnH7f+`SW+y9~kr68RjS=!3k z84!8ZLu!%Y4ks1{r^Nk`$XTJaD6xK@*p^pfU9X(D7{pXwIW1vm+sUx#pg_?rrY7-# zFpigPZVWskY&#exDQ@GLQ6$OWbo_$*4yLw^9NL~dTyr@VtXiP$Qm|@5(UE^e;tY#U zU1&Ofp?&8I9glW#CblX6b=a3LXJVVfQKYz zMu|HoDeiPIQMhJ#W5R@lNr^joT-ZuLt}438Vo@rrnXFuNs8Nixx8miO5Dc7wP4i(hgB1t6t^v^KXpOd#VK){ z(~*BpA`Fv`T$sN7k&b)02veKOe;t7YQvsts-I;1F?IG{ejD z5-jvQ8Q*f=x_Rp4UbfVb|0}o_C2nt1VqgHpU%;?NP?l(@Z(|3AZo znG2jG7!J&wP_Nz5l(_BCkqgInzkVg;sXmFEY;NfWKSm4kB3aO?O z=mE9+(RC3fHs=32Ozn%9*jPBYHF!ji{rCqe_qP>EFt|_W0-No$h~XG3gAWTE2PhF2 zNiw)kXV(TrgBvr03`_ka#mx>g6(%Kac6;?thownzvqOhMQ{v`h&;IGKu>JU7v>>@$ zgz1-qQm3U!V{1f@nrn<1D2fE zGNoGoU%~a4*-ajlW~4t)P}<?-9V4W@A!9huS;VP^~N}y~2 zi3^xU1?2{?woi#iAUXe&;*m5iCbnbDi|PYA+6+Odb@iU9?u)oVSxsq!k`O~1Cn)iN zeB}lA)vsS~xo+J$b;^@bVUa?@Hif6U|COW|{xY{IfKr9@=M5l_m4H0vB&`B+THtOz3H&W6CT`I|74)a(&*k?fp1;9SjUApvj`3@Z}gcL()M3rE4gjg9i&ZrNMj$ z)~O`KpcL5AbWnhY$>;`?Qg8r|BoDJvVCSU7?YhiOHyGLM*+5Yk{)DLw6ql>_9CcsB z7j#gdK0s+h22+I4G0+TN(?J1{j}h^>mkkkbDxK~A0Rhu zaM_T_5-GxVl0oU9K+q*7C4?bPP9Q_vmD!9`!3hJ^6i^6&O>o-dqTqrrFeuF#2M-Di$4AwA*<(CYm0F@1yObH1kZai4>3x|=C zt{XFG&;zwRfOv?Bfgw$~o=uNoPpSJNE>PtH$_i|ppacO*C7z!ap73P+_46$!q)^+- zcI)1$a|(+Tp1%8+!S$E9O&(mqNPk`c%J<*`&0bn%6Qn%54bHf@^F26Pw3L{f4FfoI2{yFx&qdVw?qT`AcH9(-jGLVnkqxv4VHw21RmxDqsajQ2~7tD3_=g+ zTrs-A@ zP_J|Ytdp>BBbX*B?wX))IFnUVv`zdkLzCiW_ZLiU29W53q(YF77#1yBvFV_|Do30i zx(xQvF?j|C>Cg4l_Yz4ys^2w}we86tkZ+t6SEs*VI;PJ6u3jM?(g0Pjiy*}^wu)#i z(_XgJWB)X`{xY-4gQ5~z3+00fWIG&L3SNz0Wc;-7gwsKRON;73 z&EK_5zrMU>xpn2#k-cn7cfx!VbWlM0a{wrB!F&U%5ffJvRVg+>6PpH8L-__xb~kQN zu2#B94W4jN2Gio1-Q7~1OoK%&Fxpn!wY25@u)En3lZP#_ACzSc59DicWa zed$`52PT0$0B-HTJb+TGfRlW^UbqT8K2SmxRx6AE@4E`v^{8#h_-+VkS0d zr7x`ls_xP2YrK^{sPF<;*G2~g>J2~zU!+hQFR17Xx`M6J$J%m$8)D1Ob{dp@WY`NT z1jM#;gW9L5@TOb_IMLzuU_F~B<65Srlm2CJ)iSfmA$+Kujgf^EH(_m{Iw>V?f;3QJ zIVc0vjLT$k<6K$TPLza%M!Sa!EI25IN^#oVASr_zQb zA%?a_2ANOoN{-SRkO(un#H6ISsopy%C|OUYIb4}lNpWKv69cGa1#+Acq!(jkbofsO zGpNwl09UkMKn({a#Z5s61&ppTAv7DIXods_*kd4fL)#5S;C6$XA;X^5a#&Nl9^P;W zS~Mdn3Q=Z5+uD2CB0;U~r4^utLoAD1AcM4XIowxF3~fFPGM~zo9HrGkxdWrY4J)ru zT5L$sq+Sp9RY{u_15*0|Z#4$-6%)fTU4}iS<%@V=4F@++3wjcys0KA1V2uVy69UvZ zUCWf3^Dl$T7gSV(+RDxgls43tE9ZkeU?;7PwdDXW8&G@zb_1l{puQm!RF#6NVIt~a zs74-ETToX|Y4!hlg${wH#LYZR$4)ST%Ln4K4mjte#zS%rr1Alm4ImGJ`*v7k1kFSB z(2f97oPc@(Bvw8dOq0OlXf_~UIR$$;EvRq`_U2J?WMEL}P~c&0O5AMA?v%LuSU(d( zk>ct-spYViczp-BfM_}>07`!mi1Y{TzeY0dWn0Un8u+gRQpLi`bVRhFl6jKnlHz6=?hV@n z!nnEAI8_@~C`>r1(3E%>-rH?bJglU+y}|3i0;lFribsxtW_Z_fKvdMjJH$ zU9JTo6F>tY3`rae(j7@b4BATSQ$O)AIVEma3T5c!;$b?rgh`FlWG%;8P9AQExlsE- zx*>+2kvcY;3FO>W2No<;T%hEvl(?OTx$VP$n2ULsA#Pa&?%9GJ7=+B)VoF0 za*(bTxRwK*pn|<~TMv!rwM?qUkXr5&11OG*LG?DGmIGy1QoFX076`ba+W@WP958ye z_1&R^5fBsS0abFP?u&R}egqdj*nNobpeih@!ab;*Uk|P2u%=>A zQar}QV6Mt)v_J*kPK2dmXouI0=wTReA_i4-w(M*(p*3B-hk)1?a8)<89ablRDl5Ew zNk~nnDnUX`rwkfgN=i!BOIKx80uT48tAc9b`eZ$PH60|wArDP~!UWW2#4L{q)O6S@ zy7~>UQ6*?i_mqo9H68Yft{yVrhR0Xr)pSe${Y9(k5G5vZMOTltNrmW?K+_DU0YY|7 z$0Kx1gpmO>kv>Urb5r8(@Ry**FsL4wT3)}12huwMl@o6NLG3?KMGy_I2qGDOLHomN znG&`ssDAtlY6~Cx!oVQiTn_6FgB!!;N*+lE1%j?INvnfeSct(Ze2wAyO_Pwyi3uP- zf*Zq&4hn!q(ugwz+=K)dG`1XV=Rn;r8IFcd0Wl9AkS9Tn6qp~u<-{gPWe4^nWc~r> zMQCmJ_-_VRFEg70iWg zkb2^85*I@(ds`rbba6Y>R~taOK`mo&dmn3rL34aPd?Xq?B83!RkoJDbF)Q#Wml9H( z;f=B+CI;}3TKgg%NY^8S37qIKT3Dd+?$?ic$TY!PCe=NEL0vHDBmt=UZ3j&fv@6+3 zYk<;B;%YP};BR7OFl_*N0A-E_tIdPC?#E3z!tZ^CM1*o0V4oS8x$kYsP0^Age7BKQKJD5*4FenLYW@u8}-e5jitXM>?UdT61iRqX#!z9JMix!+%)tR86 zVqnUcknGrW;-AtXgM?&-fM7?Z2!;SRg)otSMu#r&2pOm}D=3tRB?&1hZf1YPZOJt!JFIdM^8dDU;W7o(bidnQqB=mi0K7y!mVyBgOrb6!$fq`(qTru!)nAhj}eC zkIaTOlUL20l(-LK-&(f4tl$PFUuGT_9%ddf zr=Wl&aKrK{BcupU5)GK7xSfZUM?4{X@&u>gB*o29E|U~D^QZ=d2_!9ONyrq4atRCL zous(aq4K~paV9q-hDnP$WO!%Ia+6?S*rd31CASXOO6f%h1vcGcTBNvQ(J6&R*(!^) zm8DlL%2ux5q_}m{X^v%GhLD9Ekf!^j#6y=*qhXTbp-bQx2$OPMw8#TAz`1)<;?|<$ z9~Lq&l$=n54K|dgi3W<tg|?9x&gA#0*m2X#z9~Y zM1ee@_6!syNs2oi!VWwWWpdMJkYP_+)KN7tE0ax>p&qnAVCzgS9nP6jlN47>I;k)z z5y?kf;9vu-@8D#Bc;_q|HVbtoEdot{&04i6acj`g4-**}!qt~0fftT^2o;PJiwcMM zWG0(*Xi(zvXRTa8id(xwgA(hvwsA8YTGWwrPyk~7fn(T$i%Sj3M?s3)1C$Ot69W5% zEof0k*2JjDHerTIid!o=b=WHXHDK3+9REr2NSKvK&>|0|g91@O zNs61*pNTgq?wq8!%fTbqYo?Ooib;vP#GJU?^cWaGsX*<(GNF5rB+!_ZQO_p8pu^EA z+N8Lm>4ZX4;)*84<idH)Gp^<^1lT}@P z8!VhqLrLinBRH6tj@|s<1kQaB6Azqd_>>4M|2`=m5u5deO`d^=#VK()sM1;F;XYYY z%hD)uYuk%&44^dP1WO~Qjv%{5Y}Fq&LC`Xnz05%e1wJJn(F%q1kUuFN2}<0`cJrH( z;)*kqc$kwws~?uPae~Zhf|`}GNCHNef!_Dc#+}`gUt#MA<#lI=sGp+tMj1h4H$kZFn~@70PorY9rn%>3OYg%WC8;NgD&V2RnSE-EUcM% zB@E1sphFdIfVKg%u&{$JV`c?yzOQHUVDthRz{LKQlYxPSgS8CgAP$yepxr`@Y&SuI zj2z4$iitIn9i)UEv{9d#hn<0e*$8AF^L&s2klkUkL035OfNnZuR^?(~U=;ve62rp6 z+6Yn&I;xjd9VEhfhJ%5DDV5P2v>u<0h1CmmJ_Gwx(Ebk}&`}cXSJ^QP2{8Hs~o2pgjU?&{G~j*MzV^w~_4t#Rzm88E9J* zn+?eMBHW;zS8UL2WOG1fLbs8DHtMs%wvmBOu;5_SWAn%WS$&a{fq~5v#AM*U4|1{> zh}|H_&&9yN=3@f74uVmzf@`|rXB`P6h@Jw&K4aeJ!jE3~VJJCIhzw$P)Nrqo6I#ymGA5 z@2%C<<_iQFGaGap7Z3L{*6H$1`eOBkygLu4MvZU~1*WEsc` zK?xDi1!!!roon;ir|(^-D^V{Dij59NH}LroF(3=Oz(+#ph&Y1+6t;EkH;B^*Hr7Ps z6X?Ru35=kFA1p*vK{68=gTUuQY+_0-B2vF#=&13`xxrfMG zkfpO2DHUJGJKv4@$z{+4YgUC^kX|OG9 zucxous4Hn!49?ZIj0T`xWDOvv*@4*%BCA1>Y!BwJi3EVc-T_?R@rbl>P5--5SEgQr zje$XcEsU`Vq!)A}0$U^_sCZxy0hQ`(u$^o_K^}?*%W#M+1eqEG)&kne22w7}&%wYD zz?Q%WN+KelGacBH!O_PcVhTDDHH8u6X%>-_pyZSa=J1GY=46OsU`qq@L_~r?K1&B1 zAS0p*aw}{n+euJH$pkCY5CLtBWy=C{3`7otoRtmcScn`1IV%UuaS%~qnf~sFPE0*$ zKPS6J1E}zO!_L6K!@;l-w6U#_oq>Ua0hAILxIrhAK-etY`e1QT0l~q*yoiH=fin?Q zKQKFRGB9waf{S}O5Hk~0s`9hk;9y{oV(tKy(yKssoH2v;I>Mzgcbkhx+4CsI%b{R=f0S7umo`F*)m5YIaOQwdIfq`2FbV4={1FIm&B7a6#PMG_- z0~tSpECQWc%pkK3bRU4sUC{B^GN8kMm}Nl6MX<<#?uKKP0c~|-li}cEU|^R4okzmK zz;ARGGGL*aoYO!%&0I{V&^)(9v?`<{)1|A-n zV2~mkkc2FV$-wQ%#lXNL2Z|UL?)^**3_S864IB*oEPp|jGY5+tXm2$O3;3*Mdo~6J zMrFogE{1vrewIMcIfzN1Fj8Rz#W*WyTPC9_BdFYD1#Q4)RAZdW#lXPN0y<)a6*T(D zsLr?^ECQ~rr?N9JFlsO!1dD*2z{(1WM@`1dU=dLHBWJ?Fz`((1#JHK8fk7U$x0BHX zRJPVL$baNuU|=+5+{3}ZAb$lEfM$#!Q{_!Sjx=XH4iX1lgv)5bn83xrAnyjU&5|(^ z#MTDg(O|_0+HNO*1H`sw+zJw34AN`EcpS`TVqjpjWn2tmGl1-}V{8SnyFlve?HNG@ zn!FJg0|TQ2V>(FUF31s%jFlkv8;~L=MoL=s8ChPV_-bW1_~6VwA>O98x)jEi3K3$cAk0$21U>&L&l?Q zpx{$H2+;{P;r1L!Vd^&{L23~awZB}fkC6Eo`-=UmW6>qV+Icc1Ly#3jU_w`44_^7TA;OdMhXlJ zOL!R=v>BtA${Dm7qZtjf8Dp3@8W?nPm>C#A(wI^Vx|cygyAc%Wj52%-40>-sp|uf| z9Q4x)7#KH#S~2?IX6nCtdqbvXR{mwgE2To)`HtrKAfnF;2<2C0_@W!hPc zMr;fWs!|LL=4U|itHJswg7|-#80r}qH-bx)%Tll^g>e(3umA&ttiN*z1B0xKt1|da?>%9P!Vpen{e zmVv<%oDTOgJ_gHQsJ6_nU|`(G_!-Q7QfCf2UyN}-*nDRN1_p~_kcW>m3d=JvurhMwIV6e$Ztzcl}W?iGez+mM9Dv66(&!{mlfVSZ?WP#Gq zM34bNOrY#{Re^y)2Xy(AtpYexv(!&q0IGuRKt+@o8v_HVNy?bSq$k9{p#Fgul4#VP z@-i^IQea?EyUokM@E^puz{|iOqR7Bt57VRZiq51I0EAJgmBYrs z;Gw|4a9a>;Cv)))1_rHKHU9VqCeEm5W6yP_v%F7IZ$9hY$mU`Fl|CTwnqf#WtXg_O_sdQj_2^ zEX*L87?4c8qYSjPJuJk)U{;>Yz}U?Us?}N*85p$uco-NC^Dr>%gxJqi1S$fV%0Mn; zDv{7;jAbfe(PoTeE_qR<%^1&IvY}3!sgQxGgn_~74i5uE7U4I+LUO*{b9{yYo{BoVMRD8^|s#xR$5FfwS}lIW~2Sv1r6B1#CV9{tuRIyLoQqQKmfmxd|7Q&pvtj!q50TSh^XJC}k zW{hW>xv>SvDAZ8i8=8;OGJc`~|3S*$MqUnM>xdYBR<$l`wz{krT`ypMh#~1}!a7h;A2g(!GZ1mS0fQCLv6#XCxw% zK=KGAERn+fmKelw4-m$YY+5}c8kdMejMI~#hF>crAg0yeH?1C23MeBL2w41D%D|vy zCkZj`6n^7~Fb$Mq`J^DGNl4aH!>^y9#-&M;Y+5~9ekx^PaB7fYV7RHkz@T+W2I9=U zq??OoEXdQFWFe+;OChrq#0%NPQr`et;V1ER7U&B>NTSxJ!xH$YXkB2*$PWCDeSm=?seRoK-)8gDsJMdxuSss}gm?m|_4 z#;ywNcmr*SbrNz|Ljq!522_zP4n-v$tPEN$+6)X&6c`vHav|%k@YCpn*zI&k{VE z!45gl8Pv4|^OGTbW_hRqj1WGkYYCR;XOIBNfeB}0Q2(b8-0y`=iZafbAkV;H0vh)y zWCSr~lo%LXlIuZT_(I01@(c`&!QfGkLhz^u)5HLaYLF?A`6tFGCm9_E22eSqqp1V# zxq-Vvppqj7Uc(7kTPxs1_pN?(AY5>0|T$5`&vc@1`w`iTjn0g!oUE+EV3Tb zpb>Nq1_r(oMt9KNa3IVg?=Hd4zyQMh_U`|f85lsASJWLeN(;igiXMV23=AMFz{2Pr z$j-n3!u-wdpq1(%%%bS-#KOP;!aOSO>)9C?K$u0<|9pXaGCKnU2n*V{ zPv>G_0Ac=gkAsX13?MAP#^?b$n7y2F`~Io=CXCbnduy=PvoLaVdkR}|TeC6>Gcd?# zFfjAUa5FH-YA`T!fEaS(jLdx8+%N_5ybR2IAdaUn1A~GdBZQ+a=_#zh4KhHH6-i!+ zm4TT9rdZhoiKAiyRaOr&M^%r}P*{*rh|NJ_x|1Zx7&TS~Lt!Ry1_pI7LxO=p!vrd# z31ToXXmvI*G72&p3WJnucQr9GFiA{wl3-xafk`kh=tAAcjc}hH)P39_d-YivRKX4e z^9-O41al0b4g_!FjgK(|gDB7?#304N^Fb`oOc*0WFo*)(b->6F1)@Ns+KddrAPRJI z1~Wqy)BKB|K`@Znj10kGGoge4*yvCY1#$}$!$cnt4RQ&{nowjn`9Sr6DbPKsj11u* z3Z^(5u9%SlbgLa$9f$y#$iz@TkpZL<iA`Dfam1PW|r7lbi z#dRS14pbaOgJ$DF;-IB2paFRh8??*?bhsgi4O$8VDoa6Z(4^^l@QIbhb)Y3NuR-j3 zhT=NV+8Axn8We`&I?x&#(1Dvw3{?yu+7&7eT2}))_7x-!qO+mmpmjH(JLo{-pmjN* z31JW$L?3{v0nsNQYzENvrB9%2kjKHRY(TDq_?v+N6m1|&85!b0c7p6-WQYSDw%-7% zFBsxLHXepBL01O6gD^p6fdY|{0d{#Al1Z@$lVU;IA$np#dcgKGGJvjv2D<=c{RGgc zC`0HBMg|7ZG9pHXP*4a#G=+jRodqXUiK22jf147M={M1f+Gks%61fey@MWC#LLpg3h@hyqcnpsJA} z2t5fQJ{EcWQYP$pyfb}3_&0Y6yuBxQ6TC#!~_ua4RjAKBSRF( zgP=2V85x2=6etxiGQ@zWiQw=J0#Ts&Wn_o}QJ~{)85x2=6lgUSGedp-d=O&^s6Jtc z2T?0n7#P6fYgiZ<4uHjjKolqdnHeVfuz?t?^`KE>h9FQ-fG{XGf$vRX03F>45&=ge zDD0USCc1#NfM%!}CW4Ot1j~b>4xcj0}-rH!(0UfU>t7 z*w>*T3UsXo6T?IY(E77Q;N&pT0TeLxAgzoHp)hTr)Bq~PK&2gs2Hisp(g&hJ7BVtK zfGE(!8b}zV85aR`CZ5qR8#*q0S)+aWwku&N+43IgX zVlxDm<{7rIF);jsD5;-O4`Ow)GcYhRgn=l~6}XHHAs}iFXlWYAhK1}549mbuLO|3G zFf#%~X>dYq0x=9ha~=#KAj%djUQ#z7#OQ&DgD49wsCp119yAHh$Pf|F#lVmVnuTVF z1W`pWH6TVQSUdzooq&mh7=fS(Q-%l-mB0-(6U4}bh=ZtXuy_U2d=MiKA`YTTz~U2U z)R%BG)HBpVBtXcjROgQ z;)j!=0_GY}45dK=7DQ!o*Mq{N#$Y}uzM3HtpvA`%AOi*}Vf-tXwuHuC- zzi=}!e1kBVco-O1K+6yqLfCm27=$5A&}`LZUQpr=IVi}$a9j|~+#$-qa6}ZutPhzf z#=x*#48#se5NBXW6$dkKNHQ?omIO05OEEC)kpj7sqhdaYu~ix@en6Um;TnW#A;Z8h zSq3b+Qh|Ztg9@0rR*ivSuNs&Mi*A(g0Yy4EbAuv&9ym`$fudLq9GO8NN{174=ZwL8 z5Umdujsit@B7~X3$-vMEVS?^s6a>dtkP8lbh_Fzkh}L6HcGFGhwSP&_4r!!;5VGoTE_$Pfw2JmnD44sHg9ZZI=q3O54-=yqgK zN>*oJs8a{|IDC!<1A~wzh#8uu$-r<8%nX{R#lY}K3nUr@$_`vQ^(fGAM7a50pD)0B*%8od1a!p6V=k^^TeP-KH*nT28Eh3ffb(hLl%!I2UHil4I( zrjQH+gE2UMqCipQ17=1P$S^R}*F)G+stgRSs-Q)~6KB-U2eATGK~Y{&Hy^}kRRy^- z0z~z|Re@M@A-rj|^Fgdl5Z;X1`5@L7h$axV7tD+UQ5V3>h%2fL4D4#4NpOY;b~OeD z0X2|ybv5%r3`=mTiEvS4VDN=7K~$(3%zYqM3Rpu$-Fy(E2BHc?^}tnuSTi8JX|?k= zs4*~rCWj&61PW~s2GUkL$njlP2 zI(Z3Uf@ZiyK$Fmn3}N7*3qDW`hgI+~Fsy?xw}4ic@PkE7`573ZAk0{P28Lz`vxA?3 zVHJdVho6CgTL7$9K!AZk3&PYDU|!V5Yaq;r0`&|G?;z}t0t^gd z;NmGvMv#HQ4#Lb4WMG&DVS>sp9U-tC5kd?MZ4l-ZAqIxO5T>Ru0|V%maYlx)1Yrh- z9128MScphZXwVLwC|7%qZK!>}8o3=DrDOh(YG6}Vgs zixp#Fm;_;hO3N(}<_<9ihC2}ELoo&hW^ln7W+2YM5Cvg^iqjeh6Lh`$atL#cI0M5j z2y?GELp{S+2%ANMfx%b;>^2Jt28JLAGgN|sp&G($kYHfwgD@vbFfgouFmFgOFx-YP zxg;4F#KA>&n2aO?g9C)=D#^f*4q;A_WMEhbVS*Cn3lOuOA?%GL1A~AR*k)1C(i8|Y zMT&tT9l`{qRB3QQ66PVzzz_;yPLN?>SP3pX!Zye-FkFE!Z^|$*Fw24sGnHjv$bv8n zWEmK`A3f4E47mYz{dF1~WOZKF|OrXnc~9AuL9YfuR{9+9AilFb%?- zDaXLD0m6JB$H4Fw!W5BbV9*1Ny^Oz5|kJiY9P!8B?g8S z5atFY28MkQ=0PO}h8qwjhcW|$s4`fal`;cE2!t7-%)n3&VYVnUFie6lrz$frtbs7^ zC^Im;gfRJ37#O5gz$Vlys4y_NK-iut3=G*2<_r}EhNTcDs1`p6E?&c6(0CkV7Y53~@Eks&A)w8{=53M!L+L6{7@3=Ha^f~cM$NQak! z!34x+2m+N)&ml}u`6LD|h=M@HlL~|hDw9q_m>2jM7~Vjb4*U!Z;oyQO2vj^xfG|M? zkvh0+2|^S^L7;*t0V4WPfPta@4TOy-eS$!xj|#ZR2?7;4Hz3Trf(#5y;BqF2U5J4} z2*MN;3J7zp5Cg*{2=j&z1H)qo^O+C>!wU!#R6Lb~%cT0C8es;81z>j2 zQeg&$wGbw#sB!`qRY4vi3=BCCCTML(C4>now3xvqRS<_L1A`8PX(-CTUf#RBBa%%cLMgsTBk&wYEb<_lh$x?1wNx zC6zz8^a(^02NQ7pdyDM2vIx*)q~0>Ux+ZMPC*`=W`YodRln7!|WMJTcFhRx0N(d8Cj0Ay-ktYz* z=ZXvrFCk1&DN+e8B!WPt$Z7}^REq3|FhQlrVF(jcbOb7c+h0M5vLgsocJzZwi6Btf zF&)AL6&RA>;vfi7UIc;4i$I7dsF2tOE+nAsT5zy}O9%!AhVyI;44|Gks96PSYk`_p zj0`~_svgvEXJiNlQBvS$cnF9BHN_bjfR7Y)H63RGw?GQ@+Zmk?191u8Tc8S3LfbT7EX2nJE0QiG8p9z?~0i-uqj z1u7~S8R9_{Xl*JZLokQ}6%ULI@gRy5oUwyJ6sRCzWQYe*w;-Y*3Y6s;8R9|IDsUDL z22nco;M^P!%I;-gW-y2XWo|}>co1a<&dtFf3Y4Q68R9_{8#u29gD6mrW@HElQO_Wv zAPSU=85zPs)H-lp4F*x5{L07>0-~D0xiYvOM1!&>BSQ#?@&f0-U=Vc-%nSihQlKox z5DcO~d61DI97O$th=M3cmm?n0<%mc0GvdMh3~*MA2W8NEU}i9gDuCp_a8M522hO3v zAPSTn85zPsR2MkY1%oJ1#$#j%0a0<_JQWP0Kv|5DAp}IJf%8!?hypF(V`K;cQNJLf zAPSV17#Si!)NOE%3BFyg$iM)~HjE4rAZimha|DAZPzGUS2nSJZ;9L+4qCh!;ks%yJ z`GJ#uFo-%0W`=_((A*{?LokQ}^??`};z3;(P&b5;As&=X_JgxYFo+UV2j{K{P01F)>ehn9K@J4j4-&_gsX>{0q87Mg zvZ5Zum;vtNMuMn7$dJ#9dJrQW?8`_H1u~DBp?=~D@DKpVRFEm4;(?iA;u4ImSQmIe zZQ>GefpH7QdBDlQ0PQO+LGCLpLGCLpf!hEIC{U@x%)t47KFFgn^^l(2ocbzm28IT3 zLk2#C%FW8a02*5a`5HE=2x5cW2r*^io_f$adC(9QGsDC^aC?Z*xuYJmWr?vKGJF9_ zDxh@2%rJ3BJt(n&QVN&@>as$8xC7aTI|y0^GLs9E*o4&>7*y225k4P8gRFwgIYCUB zE6BjGLXd%hjUj9@OXy^lZGsF8pi&LS2bBS!+8M^*038y7@j(gSPZgm*N|k|O5)vOa zHULu(8WONk1F5fvSO{`Z7FYnnFIHn586XFM`pqCVhz1Q{ zFfs&z#w$9(;|)O|8v?+g5d<0~=!Y=DlUpEzK`8^2XF&#oXpl~3hKY0PK@3nxGBQMh zD3CE=aS)>uY)B-CS_vMFm^h~%6mg(90~rLOL4!Yx4DebJBn+|-gdc%C*$K`mA)uUM z3CmRI>~N z1E}a_WQf`c9R>mEJ_8zmfhMjw$cbwXBv~*p;B*8iuYw|gnPK9ZdJqFtA+RwpgDxuo z4-#%t02c>q>Os)~$?Fr>z>Nisz=Mon0gb=b%?ITuMaW?LlzI@u49uzIm=9vufQJSn zL6kpC9K=Xq2VK24aY{X#GRJ%nM^hba=ahO7!(1In7R0dy56ne_sC>9wy)wsq5U&U# z38E&zOaU?G!Br}A%m;CHgB3=Cs0+w)AkHV4P7vcivMh+hssSp?qd^p(2FRh+9P|0= zH5eF7G>}{h;sk)DCr+scG2)PAL7Zx^Iguc00XL~fv9^h z@p~Ge^sk9zJA)qh)581q#jw@5lse$;}A&@^%SgW;*@$2<14a45a$m>A&An_Lh=HL z;|i&ZrqqKNLCCToPAE9bg@UN6dSuC?S_}-w!CFfN=7Sh#ARLbQcG?UKp!$o2Vd9Ls z`Gwk$S_i@dWuRG*N^VL$h%r|iGEP}HAH)KU;4w2yoPt~C7#V^=6v!nY6(AZk zE5pbT{I*^j(y{=FgG(J~p67#1${}X$KpPoAxfVR;3{#0Dh?u5B5=3YRZPx&4XGF}k zg2(qE^?@FOVdB;@3=IA<3=DxX z3?dAmS^*|8O&wHwWK4g!UJutDq6h6+oHoTWM*Jw&IFUZoGc8C%v_vIU=a{o9MoK5=HTRFU}UZbi!*cbFfcN6bMi7UGBbfi z7{Pk$!6YM81SHGIDagRc%*@Hoz{tFfkyDa^k@;*LCo4leBQqPP5m<_qfst92lLKrN zCj%ohBPU3N1x#{)$vUvHOkk3Ob1GOD8v`S=BG^g%oDf|gFEE46VTM|23RY5*A;b^NoFHo;4uUuyBF+f*&+R&}{i`8j%$$Z`lR(a3 z0y~=#th*K*8XTN_aI+89aq`qNFftc_wcla{OUZJIFfcM#f&;%0>e9OowKix1vNW_7e6KrZZn1rMhNSHE%t*!-&vw%rPaAZR4g+w?L zC-d~Mt$JGZpd<(J5JWS?zZ_s~6<~w@vVu7+Rp5Y!Si}Yv`U;WhssaZK#NZteTT;LZ zAOZORqUR?>^*;u%W2RSv4WHiiO^pxi2u4sy!ThViz{t!1O4n@6jGWxyL<~yGoZu*I zWdu_{AvQsxsTE?!uR5@))nEs)f!PodZm@fb!6KaywMk(AfE-oN$m|1_2?48u7~cqq za~80~Z-^<=8Nn$jA8h)`Nmi z1RQR;P=_*slNU1^GXpr97?@aCn89%aVzDu2a4LWmurN!2G%zr+aDY-GCs>dZu9BM> zw9g!rQ2Ce{I3>Wcyvz(BZ!s{j2r`3{J_8er5L}%wT#N@2#|-sMEMm+IoWfwuqHxWU zaD`HE&C+l&IW}e%Xi*6&sUU?4GdN}-$sOWO z>Y1T(;HY9o4mVzKxRro{ftd#yBcN~t34j8NnE{mlK_SGP zBv^+8GXtj-m?h540BTWy(?6^jk^zMQ#1dI}Xn>+Y0IWrcnE_f2sX*P##G(dQrw)&1 zRb~cIEziKjqQlGpO2`aMEP8Nf8Ny3K1Gw`|pr+I_v6#XYnn4AbSoE10K_6{3=+5RnJlfR|rcB%q$I{<_0JwF|#y-n2_=$6>1?fODjka zRAn%;w1b!sr5zw9NGUT*8;A*t1ZI|A5ECNW4`PB+8#Bu!5L1GIv7UjMWg>_T%FoO! zQ$b9KY12SVh~nuWCPZ`shzTl~m|13ln4n6MnPn!32`ZYHS>}S65Vi9_Oo$2dK}?9~ zA`lZ)@iDV3V`TGS1~-KmKpf_BaJB?9*+5wwA_^{(Axv=90AWIM{4ua3Gb1MtdWi<| zF%t_YD`|m~7zaFNKxG@G*#OGxAonq`fbuJ(qypu7kWYwdEP%oRrLABIjUgr${B@xm zq>5%^hSfTpxSbD*MJATP>3m29iZ}G>SSf-EEl?U`V!>4@a)Da9OrX{gs2z_U=8)O} zlw|1WXia$b$LnZjPA+ukB2qXVooh$5bHTX|+*^c%E4bF3eDJd{Qk4sFI4B#^uF8dE zW<*0BXO$}mb}^{Rg*XORl?(9>sLF*nA5`T+;sRHd3u*5VUFCvG5nixkEI?Hxm}LVG zcUyQthKLJCNO=OSt|2}KRo4)YpjFq9Di>5=Lo5N+*O0c458P-_%?siRF!y)ZC)H)mz3V5qTvrjR5LffLbCrmOx|)0#eIub!aqF|eu7A!|4w_xeo4dzM)(ib6I$$&}&CKggE8AwrtR>?pFvGwafl?ZoGN3U@!X3roQpxyJUdb#1k4l5)rr1CQW}djnblw6Dj_IpS z@Dv(Yds6z*-UZ^*W1IaGZi0VZKZZYaG4zX=Yh({3F zT^nv5sC5bP2xjZDo{0rCq66x>GqJ#OKA`~}N;5vh@8FCNX-T3rFTu@K)Yhc|Sd99u z%X;vf3`I2ByvE*LXc)MymeX6f~9#$$JQkz{Lz}1pymnYun@=( zRBv6v(myi`m4<~t-6&8q5;QOZ%J{I+9nka@sKvm@3>n8R2Q@gEdB77+%xs`p1!l-( zCI?u84LafqnplD4K+wbrw6Ver9z6y%8z6eXEQAbh^ zW@b)F&;)1&cx0W4Qw==p3>uzhkoiE! zEIT7;stP*u0Uo^uyDA*y8!$@`+ByaKLkOIkKrGN?sVCU_lMI|3(--d76Bh=Fv4F;I znIX-nI!?CfiAMxP>%lX2%wWTz7R`XT=^11S2C_V$9&8*W5b8j~)y&M`_8NHP4LVs< z0~Ucyq11vJ#?0VmFQnbaz{J7|nlOP>`ydu5QkhulLBoEU;CWQYfE+7m9vSQvc6i?w zG|B~0qXHVKVTLv*A!dP_nMks*!~u<0kX4}ha|Y1-IVb@^VhvJUv9f{ED#RR+LQupp zaLR(M2RRI+7RCovClEJ-oCwhmbs{rN4alJoJqU+l`W#{($b4ncS_lSCIk1HwVN`|120}r7wgC=8{ zYe5ra(DVWB0zuP3EjSHvfLsUDB>*y$2{eNaO@U0DJk$BM>5EU#+oH#A!~DJy+_KjN zPXpen0S(zh5(P68$TQ4LpcX$T=+Fhmuy7YocLsgLs+o&%Or~6!`3&x^Apzk|4AbWw z)$^WyPeoVLq9`pVF`Xg4Jh>!4Hz~fjB(Wrw!9T>qHHaZUCnY|!IKCjiI39F>E=XN5 zLwu2m1w%nmYV!1ndw6A);tNWO;!7aLGQ<~WrspN*#DmUiE=$abPs_p%QN#z;!D7R06K#_AUwc7m?6Hjurx7+0pz&+GO(*l<3S?vB@FTLZk|4_4DtDC zX~n4}4CTogiA7*@OBiw!E8wB%>lhIq9O4+_I{jj}mZ);Lr%Q+jLt1W$0Vp`~N|WQu z6613+i%S@yT!Z`>T>Sl9rwi6<81VS|ySN20_=Wm9xdu)DkgqBs4ss;O@nC0^bg9vE}rh5A=4F3 z>iIc`IR*uQyjD;I@_G?NPGWIMJjm~1jzQrJZa$9g44HYwnN_I_MXAN*nI*{?44^1Z z&W;uDLe=hkURT7lGoTm?yP@oAYksbGIY10U)`P{J!N zNKK9}sVqok$W6^HPA!>USg0yC{lPJP_UZST)mcSBCY8p60}iYiY~pk|OW$2>R z#GK-)>9HsDC0T+4T%D&M)Y1*$hm-)2Py%JiGYuM&_23lC5D?_*%m58LP~J^REJ=+| zEh=It$;dA)PRvUwjxSCvU?`0Txj8;QIU_kIvACEaJ~1VQAsC!~;^T`!afC>%4DsnX zrKt=_If;4M#SHQB5dS~}zc{fhH3gIcg+T!bRu~`e>=Ezm;}{$~{UfWo`ts}bPPPJ>2fB6z#1@~vmr+$*QW|t7IRgX37LXhR=wciQn|1oRCJk{zkZO?O2a%*f zY|yD-An_|m;vhEb^n=H>#U=H@M`(d=MS;kJjNxEpVBnqH|3f@g2x=$jUL?@nT_9Nv zMg|5ssJI&=XvrqTE#T{%pyGZ=;sJ~d44^aNL5|1(83J`Xhz$;r>FZwV%CmBUv`#my zR1pT9h6oND(CJek8$mijM^J%W2;zg-f*|D#kYlM};-Dj{M4|rK#|TkbwqQb73_4~BrWeG9=}iWmLk2p44dlyIB+WTUnn4FM!8C)|FwN7!njsd? zMAEweq<1?1JYjLr0UHdU6n2RT5jaVv9p# z7jzpPD2_nkG#AMcOPCoz$C`sv59k^ska-|Apo6+NKsyXT0-!_KV1|O&u;g$J$<-h> z=$;dhZ$O8%!4mKvW(Ed%s0Y9oYe0PoI^zm-10qO0==3j``5-nZseE#0KXn1_p)+NP0nR zsNQ;psVoc(x==rWPR)X82C+fca|L+? zY8i+P3wY2`U0}To3=AN)Ce)LldlbMoIxsLWtN?{SFH~YHDI4FA=8v_F@6@j)KfE)+9fDU}@7wGOB zkfET>M<5AM{DIgY8suZpVPG(E5F2I?2Rj3U3Dg46MkJUT5E~R)ATu-A85j(qYCwAz zU}|Q9?s@ZPdm}Q`w*a+Do;u|;+ zwbD5x^Dl59LISiok|65^E6m3rHmvvo?GXW8W&%V>DL1iw~AP^f?L>MB8gV;h)_0Hh^Esz)m z?a-BlIvR9v6)f4#M^XdY2m{Mt$H2RJ>miy!yLds41-benl4T$^EF``liG$cM-~8c1 z#HbXgcz}8U#D;~0F?gFS#4ZzV2o18!2DF(LWC4g1kE956nilA)6OcG)k1Z_bKx{Us z??I=Y!NfsqSRMf#v}FWUvx^(i@BwX2h801ev(Z3$!MPm7fh8r-NogPnknccjSXl-- zqz)zyV#9Jd=xisLIEW1s2c7K%69=(D=7SsvI(rKy&d$S7&!7r*Dd+$cm;{IoE3-hS zp}@pJY(1#@X7F(akf3bgfzY7P>H*z&22}$(-3g`_#D+x&=wdjCcs&CHhy(I1Nb^x1 z2Jq>gAofKbL=o{6loFxw3Sz^e;5CvXK-;Nc!3#R91!4iD2@NanKnJ(L#6fI5(9SK8 zWl_9{c#7dg_$GyyfdN*2flj4@X$G-jzL^0&%>klkCX(KTyoma34KD)&ES;T4QeO|+ z77HuC-XS^S1Lz(%XfR5HkAQ$!06JL)R-}Z0A{!biAT}%%WB3>tK+Q0a$3UlJ!5j@@ z!xHrjh?(^aAWbv*Acli9EaXFSH6Nngwi%)UtR8eO1*{f0$A<_x&;d8FD0qV8I1n3F z&4_{uQ>ddw`JwqAWU(wiqWup#y$j}Q5F1uD*zqGQ1D%2aDsVu0L1*Z|)PvZt#M_3X z9(48>Eb(rGiG$*Q2S3912lx^3eH(nl21E{YS{N+8zasen#0G^R$Ps@*CyGI%j6(pl zRUFc`;S)eO8gxhp%+a7Den9qvgA~NkhH3^K_yh9|hz)af8|ZjtXwZS!FpK*H5RL{N zRR+@wV#6GLNC43sKPmw6loF^9b5;Nm-!}vp7+^sSIyeMo5Qq)38{|vSu|qI%5L*lC zXld}FLlA>O=Mlji?FBl82V0Na{gsn1dXIK)!)^26WULOfQHH^GqRP~CYT36M>oOLgV-<+aDuu_P!DqoGeGh`$j5@h2p@osrGse( zv0;vILedOkgM0we>m|&<0P0?X*zw?_Vjvy>9UTLU0??sRFf&1HNEWSUU;rH<1(N`= zVF~9ll0jF5AqIjhxP#%n5!RxTnf!*Z;;sEkt_opyaUUkpwqQr7J%3=M}v;qf{BCJdZ7Fda$P+5#2$!i z6GS02NIXLn;pzrZ1Ve)q#D+O~CXzUa4RiEDQAAL$5k++8w}~Q3!yTgah_>4`Bni;* zUocmLPBnzN8pMXxewLvAIMhKPHq2s2F+}YLItCG@7sQ6uezh>Yp!f%IV47RS5U!pe zhA0g|XBEK=0d@!$R?eIHE#g2A|plaV+S>NLad&1r3#e zdU+sCAP$HIS*9w%zyPz@OakF!&=F9usuOf{8_Z%58&-3I&T)f@gV->0K!>`)#6fJB z2ez1iT>)|376}Lqa>O19gb%JG`2ciyrR4OMDdNiYFtb5tP{GUwv0=GTN)q9A(D8Aw zqyaiU4yGQ&h9!++@ZnVuCzMD+XpmECBoQeCbRH&DZ#@Hu0}IWyAOqwS?j>&-~G0;u0umLmBiFPnYgO0R=TFd}q>w#hpp+OEZkVZHfbZ#h2FNh6ubRwu~V1VXa5C`V!OlgFx zOQabXKsU{U4BaP#gL-28;FA|1mV=IN0EvOrgAU1nnJEQ6CyX4j=MTa{4s<9g%=aKR z%zQym4g}j<&%glUfGh$TBq@t1bwS6d!Zd@}uu?Y^u9<-W#D?jOl|_VHhAg7|$_JmX z2Jt=Uyfv7k`(zmym_Q{r$NVl4Ggo%ULutFDfW+O}- z#D;=o*VL=I7A-3K2+2MM|datsVG-~5DV26s6?Y?xj~ zc|@TrB#%h2prh+Rfd}$2=xs5F6wNkS9x!^n%#nNfMAmjUr-{26RdxOf!fLN^2lB z>l6__1Dyy83wF?vpfL3yHmvXR9LX{e8j*}^;JSl1;vBUQiQ}a=xjw$S_5eV*#)Z=LC2IDfXaW61n8hrm}MX~Eaihv zErp4L*f1YF0cnCd0>p+n;*Am_<+Ce;4&8z{0(5XP%ma!bL!f#ULC2uNG;1m&@-FD? zN0`MRHZ1RkBN+-hI~7*=fDTWEsRyxP9sr%B3KIvhL3V@Oz5~PnJEER}0mOkh;(#*3 z2e*+d1D)Xv^TAiRW(Ecj8>aU!k_SM?RKhFgj{TL3b2@8aW`gi7FzRK!^Q-Zl3{(gO2NiIReCnl`EhV2w~zN zHmIKkQV%-Q50w7EWjKffYEXbA7}XGCjG!YEVc86H?jOt`5F6$j(3J$RDH6~DgD^E9 zHmsomI@l4^D+QUq27GoPBqZ0W)gvZ2LFadYE*AnR5&$1I2T>%b&H%oY2qd1P&cKie z4WYB(L+2oaC7?s-Kx-R7YCz}A!5jf%gVr2?#6btlfsVKYv3tQM!7(r>f#QFH2BQ18 zP6LrZHfSKC26TKJ%uo;;W)SE&IG8wy4YLe%5F9KDDnLO9O$Q*hC)5K6H5nM{t)QIW z;8WcoJ_a4_21|gTQ{7y@30Gt3p99Xsioze!=0Aj<+bI{3cupk9pwh1d_KxeeV)PvYC z^}oSq!9hX-bfhdy9CRilSiJ@V0}rnb0|Tg@0J%t4XZpcuQsUDClttMUSu_|JKwblR z1H`srfMj@3=M!`=n!?*!g~o1 zCRWzz?=A?dNrKENVSt#)$iNQH^AK|$?obz<{)i)~uEeS; z#Aq`8Vn2uc^fT%l0@MG@loyz`iC=-SZ2HAIA^GVWCTj=?t^ma;XrfphQ~@zFY@6O_ zr7J&OW)6?Qw0rsj(>WgM2~4l~t}DPOIo%O_1qk*vs61l?r9lQr>C6b4 z9b$ll1&j?U!(ePs;$Q?V)nQ;@02TBwHmERXl%2kBB8Qw7sBng<2Ni8FHYgc0f=YDI zePs*`42)9Kqvr7_8$xR~P`Y3QnaRKaz9b69jsYnJ&9pHvfG3t2)u#Vz780)qS;@e_ zkOPv2mNf-PY*4aegl7K=kT|H^U|?XV0kJ_^Ak~qxn&7R9_lVo5@m$4*C2^+Kw^WI znZeZTKoZ}B#6Ez;J_2Hc8c+-j3@1QrsQLAvdnOs75*I)c&~OATLSqEYr!qhWnMIfw z7(lnzfucnORM9dpFa$C%Fo4*g1sI^)?Lcf$vM7RzgQgEb6$VJ}4Q2+0X&`YX1_scQ zosAH7Jp*VJ+%^b@VK)N>D!65AWf2B}GfvO#JZ80#Shffm1jZr}wO3Sv)%Dgs$N55yLNB<$TFHWLHGDMki{ z+fX)0&37o9nTdfxf(c?KNL&}nwqjyn@P@KM;t5c89_U&_5C&Ncl9&ROSj5D@uo=n* ziJynE?=dkje1@_?;{2d>T}%uNa?A`2x={8;W(I~pC>wMaCFn{%nE5lI;>(yB7&bFA zFx0~&K=)vR*r59_FGCf9ERX=LWnyAr0Ns+P3StXEdLDKlHWLGb2MYs3IFt>#FE9hj zE@EL|XoRvs;=NGzbQT7NQ&4sj6GJ`2RVW9vO78)b4O%Mr2FeCqZ}=U`2Cc?p0v&|O z#J~Vr5(rv$1WI(E#OwqWPiJLd053ZdVqgGeR?uaEAdi6#%>*qw0;vb3_YRPHp?b(_ zoiiW~6XX={_n;+0ObiU5gH^vn*?-s=7%bT#@d`RXcp8)qy6_PiGcyMG({5128pYI*g}w+%^Wlm4RRf5fj}Baf{B3vq^JeT1}!3317(B6Pb0Da zLD?_385npuA&%4Lf?R?JG81&)8|VTw5W9(!fdO=_Acze*-F+HVeLW8s1H(=z=Qt+= z!yPCar05%z&BDdNpu`2S0A#2hlnq*z{ptrO_tI}jV>vnxNGWObiU5J83{y{er|n z7iBd-#rt^}7#2d=An{#L_6Z&ahDT5~=v>$rQ1&KX28O>-Hs~4~OYmKqxz&kAa~S$_9x~ zg|e6MF)(a^vO(gYdvQP>1Kns-{}3v{B*?%Z#1Ao4fuDiF2+E!%$iQF?WfuuTZubId z788VA?*(IThN?Nt&%kg4$_DBE1Z9KTNT7?qKW01sc0S1O< z0f-|e3NSD%gR(*5`=RU$0t^iIplpyqpj*K}dO;475QOLjiMvDD;erednNT)Jya~#l zAjrV5PY_!EgL;p*pc0_VdsslF924a3FwhM?APZWA7#K{U;?6=0453gq=zg(0D7!|8 zfnfra4O%U@3d#mu@^b~s2HiXcy6FdGW@x=I0|T2d#ML6g3=C>eHb{{@lNvO$*tErPO@L>L$@K-u?&85ll7*&uN)5s3K`A`A=~P&UXM7tpn4AXk7c4a$Q` zfbKe*17)ukVPMz`WrM`8LD^447#O}l*&u^hMIjb|?iSL6ve$_+FvLLF*`f>#wNN%l zd>WL!M3kYPVKbBiGUzgt4LZI59}?S43}S(c7z0BTls!d^fuRb@?hs>Om)Ji2IOwi59&v~{(&7vZx?pxa z7(v+2VGp(iX;KLy$)o-0tE(!olx=PatsW2pzL`H3=H3(Y!-P227Y;n zWgv0T#daXQpj+)Cq2eIRKv&y=#6f2ELd9pxGceR|fO0^ZPeIu?wN#-ZdyYN{N91 zbORR1%m^h026iQg2gH>a7&JlSSs)3JA}6RsfD!{k43rJB40PQdNDb(gthG>akY%6? z_dwz&RTvl^LdD-JF)*+wLo7J1!oVO0W$P$2FqlKxAaT%Ld?3B`pquy_p%NfNLD%tt zBtV94g^C|mW?;AhWrH+-g0dM@7#O%!5SD>%>I3No-O}fVBwhk#H>)r(Oog&R=B!hJ zw*U92FfcT!LJR^8J)DE>gxlG{PzLJSO`aURgHCrn&j4bmqBiF<+A zObiSlGb@qUpgT}N4h3H;!mAFkxJaFW0fa#YfyM!tG$7)jVaU}`HnS!J!&N97wE7Ih z2dM{jO7%1$>OmukrBF8LatIKHsrdmF2MrQ}E_e_D&1Y6=F)&Qgf@lT}(5-^9-$5^V z0J-$F76Su_4>AbkI5TaCdeC^A50nk6!1ADMka!oA4H5_4_5d;mRB&yEicivJVE6&L zRskddT71r~12Gh|{2DX|0ul%HY(OIzAU0?K1jGliK|5U>p*>>IUG$)_43Ide>zxl3 z2X)_Cq3oT|u?dixnG6gJy-;yJ&_KduDCZ!<^tc@|V)Y=6OQB+`LAx@bY>@amC>vzp zaVQ%!R&XE6e#*eW@CeE-VPs$c@j;e?_NBdoii38ry@#^DGB7ZFg0ewdQ@(@7I6)fz zGB7awLX!9oWrO-`rAhszJ0|RJu0we&^P{qi=P!5t{VqgI6>H+aV;th-p44_d%5c?O) z^gbzdv3gL5fb@dIKpXKu7{mq*V$6n`0gA?LP&R1u9SDQefHvQKf{KIo)qyZb9JH@a z13Kma+A{~jAaT&1xoD_3Xggdgs5uXk0PTnChf09Vl;-HOf zT+lHG(55vI28n|u1QavaJA?aMq5WrGG+EG7yJ}fdRy>0kMS`7(fLe zh|LX}Z((3ym;w?9jajiWFsy^Jx3V%YY=*Kyo5Oa4*g^~p0SpWbAT~F2EcqZvoQZ(} zq~;`)4cc&4e+|k3?KrywVhb@afCipHY)~x>8WjHw5@%vy_{qw^@D0ia?JWC)#AbpH zDS>vSu_Cd#L2Mz&05*sXGcye$Ue5p;%?5F}LG=Lx149)^0yL@)nwA8ynHU%*u`w{T zgV;il5pNKi8?^TaH0%r#XJTLgZF^e-Vhb@aq%bfrfY{ui8A%2PhW{XOCI$x3E;d%^ zSQcn^n;<*1qYU1<29*G9Y4ZZHg%}tz7#J8pY;I5m#lXN201{_nUo`NgJ!w*LD@&x85j;i*`P5o5FeDDY(N8L$DtBu*%=s4LD`^)C-C)Yoy7--0k5jrXd8m$942&50Rzm6L!F37>azz1c6_Ad!T z*`OgpF(@0f-$@S2R^eb^P=vBUcc*DW+4>v|3_2VP>EI638U+Rh&>nM;W#$|V3?@)T zAV=6k*{&Q63{Frs=xR_9AEX|%XU`id9>~GK;0I-c_Q-(vAT^mB)AuQ;i-GseBtYds z!vO(_chO#GeFfjB% z*`PtYiJ;XlAjg0<6HbRp%;jKUm<45nM)^Q|kebCD3=9jP;-Hn5%b;w~Sn5hB8x)$G zq3oR;3=G?#Y*06450rg~gMnc`lsylW364TJpn=#EP&R03-(?V62r{VzVsk?$mO$4& zf?AEB2__I5CjK6zo{510>o&MF6j6xNR0xB4Jrgdw_z!RI7|!-py^u&C>ylv z(G|+}=44>-fU-gRNPVGf&_z)JP&Ozxf-Z~#g%)UMBxp}0hz%;oW<%A0wl!{svO(J* z>p=ua5ojOe2dD&Sk0S_!#6f!;#i1j-AXi&M*`RHT{!ljPimo6i8#K>c4P}G2FE&8g zZCnft%}_RIhinIgUC#iz%BmZ}VF396bfp!@V$dGP-B59m4=zL5pzVmSp={9hMGyw5 z2W?-}f{q}AwkCowNZf{-fuRs84%&^l2+9WSE?mkDEd*C{Gcc@xN`Qu?LHC-2G;ii+ zVAuc^2RZH#lnvTxcpJ(F?IPrYjtC3zFfi~!*`R%N!cev(4+Dc3lnoM>fwDvEc^DYv zpd8R{L2oD<3> zfM!8KY;NdG=zSh&ZxOT;@I6Qo69WTiH{VAn8?^gR5jsQ-auDdMc95Zz@QElU&F`1paW%tHs^Um*`UpQAPiCu+RWDv6$kCp17UD)5wuZn zJ5*vX9|OZKC>yjd?f{ex+IV*e$_8!D1KmCkGV~K41H*f$IB5UW7bqLF*Nz7|P7d0r z2f`rrpuKs{P;pOw1_rl!Cl#Fe;o*e#6kP(#GvEt^)dns43ba|XnPun4^jl$FDC~T2koiz zg|b18NPx0I+uTy2Y|tLLS|}T|rw)Wc=79Fp?SzVh_RE1VNF20X?x6s*5iBFf!0;HV z2(%YY5IS%VvP=ca1_iGslnvV2<_Kkj_QHWM$WYK;xEiQ9Xqy`dgTz7G+~z~YmkKg4 zEP}E*RO=ZSjzc+T1sNDlLD`@kV<0|AGiW>8d8jyO&)Z)p8{|09rQaYmpsj7<(Afac zRyBPn8??0zgh6URTiY_A;-GD7APgQY1Z`WJ4waZI#K15M$_DNF0`WnbL0i)1L&ZVc z*3LrNAV+}i7ze2VZBKgv6$kA$1D)yt5(jNn17Q#wv^&ibI+Fm}c~%c1KoX#>XK7H0 z9AO5AOeh<)i3`LBsR8XO%Y%x8ww}#~vO$hm3uS|LgKdJcK|9G#LD`_4XCMqR2ek8y z1v1-E&j8w12Erf-(6%x|hy;VV2m^x&lnvUH1>%F$fOd&lLd8Kl$0kRvLgY|!Se zIw%{oF>D%?4cbWt!XR^2faX_3pyfYkmlz0xBtW~wKo)}7pxs@+pqfEDz|^5r7$D0m zp={8mEITM0w4o~u$_8x!17VOkpbcOXpyHs-T_6k+Usx~7z_1%CaZr?jVIPzY+8cEQ z$_8!LItFEfHgtW5vO$jEgwBQVi!m_pK-r-ER)SDAXtS09lnvV41;QYUL7Tfu#GvIr zXyXyi~3B(6!2JL}z zgNlRpNtHs`AV)Mo*`WPUlc8+T_NLWPHfTE(2!qT4ZHIaR75^x~!0-_^Tl7k#M)R1mvInt`Dcss^;PX9^N~I+T4tx}Jey8HgjqzyMkp24ZtV7l)mI zih~x3f!N%jwI>V=4EI3dOpv>H--Fmfki}ylHaBzu*>8|I69WTiKN4s<3*;csz9W!B zKy1*KB6rY~6=>fyXtf!L!wp?=77mhNVql1oVPJ@avO)7;RUozyWJMZ?%?(|ZHW?(& z#K169hJj%klnvVEGY85B?K_$WWrOx6orkhPpUE$9PO9JJ*JghAqyl7Wf7DOvUn4e4cfzU7|I6iNx2Q$5d$(5w21|TL2S?_776H#AZW7+ z2!q5ml^GZupyF=I3=GatHfVbXh!0W&+I!*w6$kB4sf4mYj%b6jLHkgqL)rD9eI@Im z9MC=#5C$0p+K2K6Dh}FX0>U71&>jLD`_K zA$d?XXe&vD3bgzO?Il?Ol>qG%0b!5@pnW3epyHsdA(xTZpg~iR8qn4d4bapj69WTi zYltC~4cdJH;)B$Hwu6{M#cfp?7_6XdU(oK01Skizmn09$2F-7lK-pEQ3=HK^Hb@*~ z7|2l2UXo6zIEZ~3#1?`q5(crkp$ml{fy9{@7(jEXKcH;T7Lz|9Hfa0@!~tQDp&&&p z(1}$L+XTc0EkR;nU;weXK@$=T3=HldaV7=^(7u&2C>yj7r3K0cIkXdr4H||6nGYJ7 z{SFleZ5gR&fKJx3f%cL>*&qpXC>ylj#2>^KVqloUz`y`v!-j+tLE=md44}EuQYag= zS7jcQeO8@;VH=1o#K16v0m6pqJp>X5O)r5o--K`&Kr_R?plr~_6)Dinun+^o90mpk z5F4gh4I~b=7^DZ}GteHEIH)*ib4wYBEyTdEfPsMl#D?i@0*OQQPSAjs|I;)W7$!p{ zK-0~0pzK8&3=H$3Y>@aVC>yi^<{5}B#J~Vr=nZ1SEcgTxhg!e}ot_45q)~>lL7QaU zKx`q<>39qb3?L3na}Y=ZsyPkH&e3FG$b_;%^V~C_Y|!qRRUozy0|RJPIfxB2XB$Wy zYR)Mr8?^K0EtCz~U?ZRfYX1u{Fo2e!gE%nFGSK;QP}KNB*`VDvX&|-`T)V zBo4K#7RmjrZ z$OcI;F))B!T?b+dF))A@+k@ELpmoNevmQa>ps7b~28Pujwh&|yK8Ou7XCp|Qi2*#Q z2?|M2A_a{h?gNPnF))DE>F)!r7JwOg3M9eAzyR9Xa{-Bc4a62=U;r)V2eD!ENRL3` zpouMQ28O3lHfS#E6O{c!n}Ojglsy4-v>kLc0%%nN3zV${Ix$WR#1Ud(0L}V?*xb-v z0%{;}h3)5BRG1j2_iWH8;RBrzY01FAAPkyOVx4}ENnei-w8aIqZvZ65qcfdvqej?v zn}wQFm>3PG%WlySuMcHrU|^(^AlSpjP znWBt>;3gIW!($|gzesE@&_S9|$B84cRgl<*NNmt5d6;Lsk;EgA*r`bDBG5c5OhY}A zL=O^sCK7uY5_=aC`#BQ(GZLE{bdDy>14wMpye-&XP!n4pNx~Y5?T*9_Mq)Q3vHOwO z%aPcixmU0S3=9nWk;G3Uv9Gf-Fx0~oaj_xN!6GE~IwbaPB=!j;_EjV{XgNR3OwclZ z7@LzFVZIoGUC*G5;4nBMvHg(P(MW92lqk&5QY7(CBsOTj08GtdB=HSM?7d)iI;c6s z0G{52X}AuSfQUaqVuNNPVQN4#kT5oArV+-L;6V7Y42j)1ec^TuYtOw%LZI0!n6_I; z;!ly-f05YCph5zglsKm|a_NiLgSI-rj8H_9R!3s%BC(B;*j7kv&^#5)oJb_`cqDcz z5<45h2BlpFhC&Dj9A4!}>{=vtGZMQKiQSLHo{Gcf+?=C@7v<-<3sy<*EZXt<3M`C|QVuLn`z|?bd zBSKdkiLH!ahcW0QI1JWEY|zdVm_flv;^4zaK|#mBzyR980#j3tq^5a#?kY|3`q@ZA z%aPbykl06&*q@Nt|B%?A$v>Diph-R$TNiYwCe$gGNNiUK8x*$;41o|1IBkO_{9u|h zk;F@p*o{c+nMmxVNNmvF70?ndkfCRIAhk7!eH)|(y7To34=8OzIbV<@KwD!Oq2j!t z6E>l2B_y^s5*sur2vcv1B<_a9_D5nzz}O)FC&D-k3=H{5>{UqY9Z2kB2zEWgRRo9O7KjZs^eKo9HS`sT4Hf?iVnfB5L5FHW*&HA?RE;o* z4HcIFv7zD`NNfu*8x;Pa%MidEQ1W14h(KZ|gV<1mGC*voL7*8+SbVo2iT8upQ1w$l zY^eJAAU0Hd35X39-vTxW%GnQ+fGRoyVnfBxgV<2L3R6{<917#NrLb9S58v{cRNF1tRDiV7h5_>U-4b{6G zN$&=bI8^WP>4`HX#Op!#6u|r^13K&zstq(%%LsKZXsQ-g0E0G!!6L{Lq#mj!2*if^ zDHg+? zV)r4j*C4TvAhAKSxiCL5f=<$enj?T{6TyO7x5#XwaaR1s*}5oYmZ&^eA!@rOukeb9l0Q1MzM_Gu)x zBYaghIcKxgbhHAEq?L0he08bDjBU~CuA8GBImlaSaCkl1>l zBk-VVYLM8okl2T1kgB{lND`u;)8U|+Es@wsNbEKw_G%>d1tj(tB(@~z*fgk_b_jMo zLllC;0NN!5^W}UbaXHYDWza;g31UMH(v^qgD$sy~6G$AIc-=v4sQP3ib|Huj)msH- zgTkMIfdRBl3YN{+fFz)rcObE^AhCZVv6U1MwTdwk+Z>5)hs1V5VtdvjaeR^3AxLb{ zqzlY(2}t58NbGDRc0Llj42fNZ#BPML!TxWBazOd72Z=oai9G{}4Vo%~IcPDGIOv=; znD{y*anQ^VOnfJj_+ABudiXfd5hRHdNbK`S?8`{(TS)ACNbILb?3YOFk4WsVaQ5`D zTM|ao3y#ScOy@~emYD8yRaj#BA4NTh={YluB&Po{GBKFmQz|1dJ?5IE#Pk?b4vFbD zFGVD#pE;l=F@4T)8J_7IEO}I>^Q;GH3zU$U9>K*ZFD{JZX+ViGri%uFwbLd zk40iSj{+~x^qgg~JkxD{iSta~ut|((y3ackiRn3}CKA(Q`dK8V+f*t^OplO}<(d8` zO^s)|#(iUh=|0m{B&Hv*W0jcxVTFmu^fR(XJd7pN3nloJr~i>P;+ei?vl!3xH=hM~ z80SuBTq3PJeM6~;#PlCKg(RlGF*7lkUhz;}V)~1jToThg)~HEL_bHI!k>mqi3k1zQ zpzRx=;XP22k(_?GViQs_Ee(O6x1dc`a*iRnDMg?J=E`!!%X zbCDDlPH#M_AwE6Aft6?a5h*T-=|@6@C8kG6>q|_(BX7hr{mvN~p6L<+{5*`Cr%x=D z5ua|OuE#T-L(0HldPa(_#B`gpGCb2AmPzwW-!V&thv_%-^uBKbO4D^JM0lp3_$I(J z{YwZ*ATj-nj6Tow1G80Bc&D&H$`g>UW=vmuOhb8k zM6ry-beSN2kLewiA`;VUg7_t-=ll@hVfw`~eNhm9)^wS9OYnlm%V1ufJbi1Gi1PG5T6z-G{{-_(FrJ!jI8Q}taVA1of{}Il!f7nx)1O2LOH8kkGvJwiXBrF7^q&1fJkx7_3Ghsx@93yV+pQP=00e#ThOV0s6bbx4zkw}uPhmxk$cm#c_RzW|bqIIkfw?Ss0+ z^d1vEgXuiYB0STls2iwEw*j#(%x6)Vu4At*Fs*A(jVOz#1S-2t&~ zfW!_oix^Cg(bSiieq#X(k7Ow~!mc_bcGL8~3s}Ubb7&gyOpiGs#4~+^rasSfkE5EP zun^oNtvtP_MTCcO-}Hk;y5f>&kaS){V&9tX`$=7S`iYAgJdA&*Gdij(PnT&G;b9b; zZU|zp(b6}VzNA=p#dM!Fpx{2P$uqr%TY`si!*t){n&Q(ZXzTOv9zq(zI5vGDw}klg zgv%N#(<|CUBpBaKpLkhAeEOPXVTtJ?Tci!9hm`6XOpnNa$^b_CJB^ZxSKe&uVeEJ{$g7N%xUPA-%=>pv%2Gf1cYD!4{ zM$+^jiOn{hai_F6qu}(~ZV_=_(Ar&?9~Gw0H8fCWG@8D)TSR>Niz;0nM#t&Cf)duM zaRLZOCLysik=VJ@6^#tU87roLyr~f^xd2IK2@-qt^uSvh;?rjs8Awbw5Rx#M9`(`!T|cvL|vfngq$gZ9oq?p8u#YfkSq zH4s-dK@tM>K4IGIk;I*+&)p*}F6oCP1Ujh%rZjT;-;0{!)6eXY=HbmoQjPZ!xMZNRu=`rEr2;=G5Elpde%xQ<0!5_F~s%+#Am(s!pXG}9NC z1a&}R((jR^KTn^zS6X@cj&&>&(>v~INKEH3*XNOx7e*vA&>1K&om$i1?v)m2G@Y(1 zCSg5YW}*nsbccP?JdCl^bJw$oPwx?v;4uU(IEJY&Mlz=yiCu@pZkk@ZPg-2E7fEOe z5_`tIwZ-hNbH?R>;p(_PG}wb48Pf z@!|Bp_cg?M-ylhUn11kzrtQBLt@}wa~Zb)e}L4kkNEjO9N#|&;c$m zRiMSqFt-2niTkC+r~jEG!owIh-Ph8nxfN%9O5 z`!W*yIuiRH68jMn`{nexn^?p{K}W;DoB~=J4P*aDQpzlf@C_Fdn-7UCio}+jzVMo+ zvY|GTkUkO{w4xejjT4f%*YvrMG{mRxk(A(>p0SyQXS&LDO^N9}n^|}m^QS+Qln|G! zLQ>s`#BQCwaWjiJFK8Jy%;4$MWu+v<85d3OwKfpvU5li6N7* zVmD3Kys4>N4?2Yh=8+jl(({qni;>u?kl5>x*jtgPT!o2z&bR zry7FwmJklujkZW^7bLa^65Ah%9gM^Vos$G}L>!WMDiS*ri49t9FAQ3m1+uvWNunBw z4Z8aTW@sCdco!0TA`*Kl5_=93djS#~bS4wb%+=!4r^!kv)q|Gy!^93EsXc+jK7+)* zjKl_Q1Aythha~<8iTx6Z{T7M+1;U1$i}?e>0fh&{|LMGP65{n-5{Q(*hr|{|VoM^i z6_D5}NNjB+HfX#N=3dZ^Eikqfk{ZzN1Ysry$V`Ci^jtX!<@#_W`Di3|5)wNNiJgnY zE<|EiAhBzZ*xg9%ekAsE2z&Z>ISE1D#SjiSjFwLqm6uSi-;E>$I`#_YpW{g4ps`e# z_+=z<&}A_&@%u>PkCE7~k=XB%*xw;+NKXF^;eg%6Es2OU{^@%`Zd5@M(m-PCBe9K< z*fvOP(9v8lH@YK`Vw75|6plQxznXRhuAUV5hbrvHOwOlc%p$kWfxs zi6jKt*8y_|Xs8*+2Av89W1mJ+e;$c_1BrbHiT!%It)hf@{dXjx-$-m`(4{fZ!69}e zHt38on3W<(;;Kk&O(eD<65AAsZ3|&TLdS9XQ$-1D$s~vvICRpG*!k0Sl_bRLYmkH* zkk}KE*i(_%bCK8!k=QGd*lUs4TaehGqtIa9JOB}g6ck4w9IzWNPu~l2*JC81=Sb|& zNbK)OY-VXh8f8af3nQ_`k=V*eY|wo?Fn57YU=wCy&;q-~97&Ng65Ab#9X!2OSwdVj z8A&J|iCvAvuAhEaSwgvfGLjHzOAXBG`AFi6k=Uz{*z1tkTanm1k=USHeqd%EK@z_V zVS`7r8LmS(U~k?>Vn0S=zd>SuKw^JKVuQBqz${~tLBs(E5*u{Y5KN7*Og%yoXm1Zp zLIFvG1`=Bbi48i+4yN85NgT8d2qx~0Bp!ss4ui2l2XmytI1CI7nMmwHBzEcaSXBvS z)lMX#UL^KZB=*ed8&xI56IUV$fp!eRoU{c=d^Zw%KN9;m68kg~`w9~K#&kzD330=> zNJ3wc*guijjIxMOW}RNECZSvp+Ij>#Yg`UVS_z4*jl|YRVmlzQU69y*NNmtCfiNFO zBZtO63NNWBgv66S5VVzr zaKPRK?ZAQ=q=zJKgv7Q)V%tvNs39Sq=!GN{j>L{eVy7UnGmzM&NbE`^b^{W-WxAuL zgt+HaB%ygo>_tfI)ky61NbGG$>|N6rYD$RLA4d|pg2cXo#D0jxeu~8Ygv16N_XrEL zzewVs-DEIvF6e|OD3tjiY)F|W4dH;q8RU`J8c1v%B(^CM+Y*WGfW&q|VuQA)!5k5a zBpxG=ROTfhNn|3iLEG41nnBa9Fm@f1npPxsClY%a5_=XBdohd++WxW}#$jM!0PT~5 zS+HYzptgj#=V2u23rK9xj4n*+T_o{`NbFZg?03`mYD-w#3M(M80B9#2%m~mfI~ZFP zNeyUM9ZcM4y04CevMT8ME0|C)l2Xtqn=tYC>9sl%*7dbW(x6#em~;n{IB3!qCJs7~ z6UGKj+QQiLk<^1GZDHc8ki=-0=0unnNi4EE?2(z*XNxTe+ z-3nntP6_LTaKL^A?J9(6o&y%2eojxqY`TEHghc&WumD&c!$lwO9 z<1Hxr7&8OI11KA$?-`U0+5_|!$_8Cn@)^nooelX5%&v#rg2KQ82^P>nkZe%401E>H z50oteqCuF6fdO<*qXd*~z{0>F2W3aGFff4j4S^gDIxJEPDh|471EdZl4m$7A3@W~b zg@M5av?mB8v7Lp1!3ip{lLc}O2S|K33j>2cRD3TB14Ag3eTapDAsWg)!ot9i2xWuL z6HSM*L5D-;LfN20O^c!IXDkd1l~8v5FBS%d1}Fz~2xJ?S4O$Kjy5j@n2+$@Y&~gb7 zTacB3VK!8a2rB~vXu}dn9JJwR15{jM26T!f$blg7VpawQepX2IRkAWL zNI}`410@xq>}jkF3_4Ku3RVUNGbno_Ydr%4$R!|yKqpFiKqU^dGB5-|*`U)RW1;Mu ztPBjvP&R0@T^^JTI$g39$_5=4*#Kq#XJugMg0ew}Lr#LSIoKE&W`NkB9l)UFuJb?~ zCI$vmHU@?zP__jd1H&pP+nSAmVFQ$H$Hu_04a#<8V_?_=WxKF3FdTxi-PsrzPC(f{ zYzz$Nplm-j28Jt8b|4!AL;Wo%CxnfG;Q^E#%f`U)49ZSmV_79Cn6!23;rzbiAN3lnuIu#|p|`$`u z*E2AH4&Vgs^8;xBoh%9p8W8(72LnSRSP=umJq`v2PzZp;L8m@Wf{KGqeFXUkB>t6y zfnh#W{5J;!!%`?4bP_4Z;UG1j(?>T##X+0vw%0>Bpi>}08wNp&G&mU;Ku4p3*jk(n z3>TqlKqo?ijyeU2>vJ+NJcWuIaxyTyhq6sL85n*-*`OVG|DkMKP6h^64oG5m=44<1 z?I{GAQ}4~mzyLbJ6vP2-(-Vg(3gcv8P=c~S$4crz+0mQ~44@N9LFz%rOgcivTR0gQ ze4y-hP6mcBD0>1Y0|V%UQILAjq3Bsq@p+sK3?-lwKtU4QI2jl~$A^O0pd%?kN0@@x z`#BjHCP6iTj;NdiWrKF(fldGgsR5m72|7U(#Qx05z_1Id26Tkw5h(i)Cj-MdC>wO7 zXk@2Az#5%n1qhcrFG8StuKHHYR8jr4Xq6Pv>G_0L39lLlzeUgEdr9 zE*ArXBa~gi#lYYWW!G^rFn~?~1*r#Z*#kuzhz&YR6STV$#Gc5-zyOLK5PLEg14ABE z?=&t3h6*SfbjD^q=preQBG6Vo(1lVUHt4{5(1lVU_BAdBh6PX!@3T)wMJcEeWGk{L&1RXsJk}&6HVE6@* zU;v%m$qKstiiv>%bcYl_lnuIDN)pQU=VoA#hq6HzQ-PufWDe-0Pw>rIObiUk+zbq$ z=mCkRaWgQOL)GVTGceeK;vXbY!p*?o43((jW?=AxvKzP=7$Tu;(BZzIU6dg8pyNY9 z(F0=laWgQKL)A>=W?*Q9vZrt}FtkJ2^SK!qdZFy4+zbq$C;{nRUC+(HFcT^PI&O46 zl)aUkfnh0>4Z0a?HIxlHn0F(T4ceW!9m+n%&A_l1$_5=z3c9WfWC7^F-jh)Ad)y2R z=b>!Sv87i*TRcIEKnLyKhDw00(s~GGgDxw317(8_Zw77U1gQs|bNUY|4%+0x!3{~_ zay$$SLQu9M4+Db?lnpxCR1L}w<$+uc1~MmshoPRq3M!Gx!@%GIW!LgBF!({)pfgS* zpzL-Y28JXk8+7Jr4wMbL#;Xj<2Az=#iZ+l1+j$rmx}f5qGf_cN0un#K!@w{PEMCvR z06HWUbowhu0)muUAOpa2yI z9j>YcWykX}FqlBupu<+}pls0HV;)d8=x|ohrDq^>Ch;;b#6ZPChq0zX+4XaH85jzn z9MI{kpyOph8bFtofp(jM*z0*27(hV}VsGMQVCaRa-^$CtFd53;&db06+K~!UvzM2F zVLnuRKQ99VXm=_|9JEh!4OILHF9XA7Q2c`=j`K1w?1W03wN)tP_+Cx}MDk$_5=f8wO=h;$vV)fU-fy%w|E^bNCn- zN}z1esk3z;HmLju-RIT;;(!Js_!t;KaRu@X==fPs^nloh_!t4x14AB^4LZ{n6vQB(?dE4-Xo8A^&baM?vOyQsO@p#QXW}k^vO$|d zLHlt*dO>I4f=(I+v48M0Fzf@xKZpZ56!!#F1L$VEOHelGVBR}WHfVkAGbkH$Htq)~ zTTy_4;TMz*IxClj9};q)Gj#c&>|_B322cot?8*>eU{HdJH`EI-Fz7%zphI)bpzKZo z1_n?NgEW8+(e;9ggKp6afwDn|?#4mcdjuF5K>Kt->OqI>7D2^N2rw{!P7nr(gAUyV z1wDuj+Gsk7pMjws!~tE$Hy5e_bPDhaC>wMrFX$dXkeX)#3=I3A;-Eu%PeR$C!+Sw{ zb3tm<1sNDXdvif-9YF?$S5P%Bf(#6xpa+SA4(kPlFo^9BI-Uh|i6DprI=vTkE-{D= zy5kQNtROb%v|k+oNRZYGGBB7!*`U*XouKRvK?VlU5yc?&pwoUqK@Vbs?gUJMssY^< zm=9%xZcVHLv+EfcKt}_&f;kKfpyPojK-r*Oq_d&y&7fn0q3msf3=AuvY|y#)ppCsC z%Rnav?}dtkP7FQ{WnU6xV7LfngH8|zg*He%=w#uip!f%Ic!U@j-a{3EjuZX~WlIS$ zFffCza%5s)FcM;5;Dxe5tA)j(Y-=F~21O_vbj+|elnvU$Y6@jX2r)2#_U3}@0xc8v zgo@XL&LIYE&IL)72r)3kLM1@Q5`&gfgTz6%Am%~ELAN88K-r*kl0i`dQUf}wxDzT4 zI;wawl)XrZfdLfUAT^+)i$VKzL2S@I*Nvd~2XR0fT|q0aL2S@D#z&!=UkfoXfPxYv z4%*y$6Dkfm&-gKv%_Gdf@D|Djop1ad$_CxO$S4F!H=y&7xuI+uVFm_J2!qUY60T=p zkcUdR3NtWhLfN46kd2{i&~=SgP&ViqM^F%hG=mOk_J)dsmMnu-WrM^)XC%i%#S1}a zDMQ(yGn0#;Gcc@& zvO(K#cS6~qdm=&WxU!fA9 zizWX+*`Nz2nS>$1s3XF_zzb!A4rCUGvOxzjD?-^eA`A@LP&Q}{vniAfI=C5h0Vc?< z8W9EtPl$LuL!$@-LokHHFjItqAr{I8oz$ETWiJ+CU;qUrNHb{9a5YpMbfIM{lnpxR zxgW{~Ep(mwO7bUc(jN0fmf6UtsF z%D_+zWiJtBV5ow!LDzP+g5n=!0qFLSey9ZK*lAESfW$$^PJ@CT#6Bs?z_1po=7uN( z!*(bebUgJzC>wMP^=T*@v@rQ9lx-}=z;GYR1|32D63Vs_W2k5N4CR2%qy_~m$THBq zpzNZMpoylGR};#v zFBWHD0L2eTGw6VATd2euaRvs^l5mjtMsWs)K&UuqMQ${deMp>vAr;ELCCf=;ZLbP&VlB?&nZ8=0E$RlbLnZ1N7+A$2 z!38?yn;*(=dxKVNgEUN&U;tk<3u3R6U|=wZssWww?FeOWmtbJj1+RrF`Xa%=&;ezGP6(d_WrOaDoeO1y zP7PlHWrI!)-wb8*OENI*gR*5L85mAN*`U+KuRz%Wk_-&>_o19nNd|^jP>%?eTs44`$%AU0^V>P4s`&`Q+XP&VkS^QTa@ zyA%ThXw5N5J?QLnQ1pPaz@QD~ zfDTJHgR()_?}8Q>gEWAy<@JJ!&yr$b2!XOeYgpr;>;+N`3>i@NGARazA}D)>6axb& zszBzfmSSK4Ei4AH*GVxjOaR3{h_gY8fngR@!)7T42GFtbAn~nI3=E))g+Xl4qE}E< zf!LrE-S1H(rsn-g?jFlb>hNRgs6 z0|ToRB{m6mJe?M@us>I6~Q=li|Ig>=bDR zhEOOQbW(f*m|f4n0J@Yo8_Z!~09{sG3S~D+GcbUn2jsYBX$FQas5t0|dC;C1!lz_xRr?D)Cim#DoU;r&E28n~#w62GWgDybc3bWug=w@W71n7u)P_TkD zJd|c&I1UvDT||Et$_Cw>d>zUL-JJXo$_6b(1+6m%=>;7<{}w8)DZ{`3x~>@{ZYaaR z@Ea;#4_b=~I(#1_0lIb>wCEVb2Ax&UCk=@XYZ(RxQ7GGAhJisE$_|uaU;srMNPWBv z0|O}9Ky1+Y_WDpY%`yxOR!}zRUMgPelMPwNmRzTUH^`o1h zY#G^l28KOQ4(P1=V^FrgECa&@C>wOP{VgavLY9Hy36u>w>;4^-4Z2hM2b2vue4j}M z5^|vP??F)oau8?@s2EhdS(bqTv{D#c{(}z0*Mdrb4$3!yvX96zFxWxaCuJEJJfQ4{ zvJ4EMCBYy=K}$M8HwA#$uVfh*(x7TU=j4N;0VEE(z#4R80Ei7bSHA_SMp}-6p}r5w zQIum~m;q(0$T2W1g0eve>#u>bK{sA+gR((4VIP39K_~s6g0eve?}HZmf-D0aynhcW z9w5iS@B+#P9mM|$!meikozVXW!eQu@V_;yDg#;bwP<{a@8??3jv3{FrsXvrq%ssfOMK!^K>LB&7IF)$>6;vXadI?X=|D)C2-fuRJ- z2Cc^g#UV%y=xXi`s5t0~fk{v{=#>9CP&Vk4|7B3NsyqY31}GbJ>i;e%8+020C~84w zCf3U{Fr0%*fR;|)fU=9^85kZx*BGA3zK~Og6dVyFd z8+5$@Xgw}S{cCv!hGM8V=(>SAC>yjuvJ=YIQD9)00%aR2Fff3Q`vK|o1f2=93Mv7* zc3>-%4O%970LliPh;tgs1}&Mq24#b;EdVXG1sT+=z`*bZDh|4);5(EJS}VyU4+$aA zwFSITHfY_XB$!>#zyP}89JGuUWDw|Tbx^#4*r00zwZV!Q7{K>2LfPvT7#M7zY|v>p zZcsMp?t=g*8+7+U6qJ2Hfq@|f%08>Wz>o)JgYHPE0L4GZvcC!p3{6l8Hbn-89w-}h zuj(`?TT79FVF8p4x*uT`lx?gCSqTd=2XtS;KBze8mh}@*Ht2qZOHg*6A_K!6D7zkX zp#mswL7K}I85ll5B|sM}{DQJU*Rr!HK!Oo;%L5;j4Z3te0?Gzmx&S&I31rSwNV{23@4beH@!C>wOu!~-ZBbn^pf0W8QM(9I8^OYcGKVkHKKe^51`n;R*GuF;+0x1k44|d2ATvR?Ni;ykRg@VR>bsyEO=Sj#DNr`( zc6(6Vf;52cxL5%d2VFC<3CadtGqDHC_Eu(KI0j{duAR65WrMDxxCLc*Dl;%VfwDnY zP`rb%>ltP#Gcf#sa2PfzGcbUb!-6aZT}1(ka}ayCG6RE{5+oQwS5tsiz=FgND>E=? zLB&B=S(rfC@0A%C?4WGW)fFC4_77zShF~Zgbd^OcDE>hPF{m&wfNu2wu|fA+fZ`Cu z2HkH_2h|{~!obi8WrOa!m;z;csxUCjhq6Od7#LPT*^w#?44`-hnNz63z;FO64!Zl| zG?ZOkufo7^4ax!CgYgi`ZdPGn0L3jxGw9-s?@;m8Dhv!v%8-!QsKUU&3uSLnVPF8= z8Uj)ex;H}^D!xaBfk7Y2KCZ&RU47*e5ZK2-*WY$zLaNk=J^4Z4)09?EuAWvFKW zB@B=wJX9GNCPO9sR2dlNLfIjz3=GSm>?l;A{6w2;VWnefBWzSM&V7Lfo*E2AHZtu7b<}fgcV1_o6qTSAS2K^w{j-40?1W!tDRFqlKx_G%0aj!?Fv z8Uuqjl}_fc42z)booWmWD?m5EfPBAOje%huRAR3h z0|O{EgT(i%F)-|ciXT*CU^ob6A5mjqI00oJS7TrR-4X**e^QNs;R;k7bZ6ErDEq1! z1H%I-`-U0=!!s!RKIjIRH&D(aH3o)HQ1(+b28JI{_H#7`hJR4@D>ViN7F9^RzE@*l z;DWNhsxdGKK-r)RR-~cqU!drNvO#yO=tJ3{ds0k6Y*6`crOv=$58^N}Fr=t6FnB`Q zpi5PPq3jBE28Ku|d!af51L)cskdKe5Gccq<#X%Rh6hhf|)FIc_fYgBQQmKQAYicks zv_RRQyHo1>p&WA!28O9ncA^FY!yG8PRfB`WfeMEjBnm<`85lsh1tbo-phXrc4!Sc%9m)pXd!h?v&(>sMFo&{tYBDf5g5n>f z8FWbs=u#aJ`>ZAdLm*Vq2TcZsa3~veX-YhlEw9DEkOgIfE@dfzvO)KxltbA?S_}-e zPE4Z4a1bU6>muI_p*28Mp9M4uJ|!&E36ba~1GC>wN{$|@*(p%w!JC`*Af zgYHAw1r3Ycnv|K-r*sikzYBx!Md2o=`UEA{EdTL?B1(&}LwW zf{Gu}hU6TO_*rcRhHR+#Wo-tAA}AYluL>v^fz*KR*{%nRn=mkd?yPPFa~K#vhl_SY z+59>T3=^SjF&zemsZh42&UUFUn#cLKdrp^#VV{2Sj<~R@hB#sujR_Ll0g3H1{o@^R z@%j`bA<#%XZ1YVGk~nD87bZRhNgQ-s2230@Ko4W@KvHuAiG2ZyeFwsZY#{`V#KSa$ z&bolHK_k5|Hjf0tPZCIM(D@ZGH3mrHHb`s_Bz6cAI{`G{3e%i}Bmp{n0w&&qBt8L& zJqL-s0*MV8e23{hfFynfi47WX2de@3{|S=B2P8IVU>&T8fq{Voc4{vJ1A_<>TLFo! zgT%H#V!I%*L4)itGeM_8)WbNS(OMWAG{_EPHy~;5L1NE9VuJ=~Vd_DH=`i*lBsC|H z*r0K8sG52P(119U!vGo(hp~SkX<(5;#69R#2bdZeBykNSwh0p30f`M78;9wQ0E>gd zpMik^G&l~EC;&@?ufUKx5r7@ij=|pdo6QIA|yu#s&>V!`OE~ z1FkU63na~;!BLnv1JdEjJVvf zRUnDCAh9PPvF9MML1UUQy<3pPLBq^2@iR!`H$VffFbU8&CX5X_76HcogQNj;`~gf{ zLe*gZ&W&=4g|?-C?& z&`2>%d=HZN2_!aXSduXYy1(%Ok_2c78K(IMk~j;}nbx48NSGQKBykNSw#jtHN8;l3 zpwUK{QqX`gjGcm{6f{T-6R$xM2MsX7#HS#MFF<0iL1OPfVuJ=189`erLCFgFZ05+Y8EJXOJXrAhDkyu|c=Ff;BKOFo4E5st!NfBu56(j)) z70{M-7#lP`2V++tsc%7IgN9pSYCyZuVQkQl9E`mMN&NvuhAjAg?lVXdpdIQk4WMy2 z82bZ~nmOn(qFt*P0+^6E=sxC-MK?A2S=@=yOjOi1fii_8S#zSG! zJxJ0skl0I**q|{}nEE|P;wO;USCH5bkl3K%Gnn2V5OK)iEiB3iuL&TrWsuk!NNf`% zw!`#~Ab)^HIbmj}AW0V>v1^dn9Z2jcNbChj>@`Si(BLP`{3A%>7a;6tP*P$5jd;Qo zy?{u7#lIl2K?9qx3uSmz7#Lt|2_&`(65Bv!dfY8xx9L|F^NCD* z=rS-&nVwj}sH_{q%E<7Cfq?-uWyQ|G0NQWO06I1Rv}>P{fq}sY>~aQ%3%U#pF4GqZ zY6?%Eq9ZH7Dx$~05HUSbL{gbGf{l^k!1RkcvdU~ZdJGJI6{i2vkyYZ9U}t0i9k3}s zz3{JuJZq*u1HIr*Cj$6JQevW?-;Yn!ZksS&8$H3?qYz69a?%^o8P#@~pn4 z3=9gBH(Co%+sG-vtPsyQU0y*|l{L4NfuR8;JiXwbgaD^YJR<`KBS_Q4BTVvaZ%Y{% zW~on~w?aXQ%_o78;eyfhE2II%+c2bPe+uzR7PbweRW;r3;#>g;ToAIXd3FaBfv*t`X`DX^_OfK(L?Tc;%C@tt@ z(O{DOV78D+DbbdPNh#4xNmve5u(WLc!>0nD%lJCb!SbZe9!w49ll5xj8YoTj?L4 z!o+6E;BF(*v>>B>3KN?SL(zgvkR|?%ZZDXxPhmQy$e^?!PkjngTMEOm$IRQOFtMHc z?`|zxw4eZFX*5I8f?^QEn!)W3GegsYiu(2`Ol^h?j}}xtpTgvJ_%~;W+g>CX(gpZJGL4BWmwp_7@WizuTSCdc6Um2 zVdG$6;E`wuRXDclf0H<)`wUL+=Z_Lycv{@*85nq696}Y2E%`qoRKacWe+8ul4oVZ0 z4qb3t@Lz{n-I#;hh)0Zv>DZkAO^UWMNtzsPphG4eGBEJ8JW6z96JTU$WMJS~*OchS zCdkO(w1~kWRKbyfL59OA(XA=bv5kk3N5mmifk&l{i;;ohk)jI^OOrUG6F7mzJaMm| z!Rg)3Bi95zHzhhBV`O-w=&b$jQKBB!=+mVZ7h z4xtK8iq4M|U9>?7%qgFt@QV99nQ=(}D+g8BZPDu(82lu|n#x|3Qk@pc{A? zloYL{gWMNEl(I=O$b5EJHd9ixk~Z;9GD^eN{Uu8&E8Hl9 zW?*1YQZ!S4%G9RGu*OV%F(=H$3`lO?wQ((T>hAweiq>wRMJkHc(yr=D5f1*Uu4-na zXeAxwog4&laBI41J!=ry#pbFkI?hIkRzZpuMv7JfN(-z^jSl}=!Mu_Q$;ClIiB{qG z9h{_SmlVZtOp}3uf&D3yn-ha^R7=`i(W2nT<1S2B}UQAz|Q3Yli_ zq@+YE9O(fXw*o2)Rx;Hi1ur;3;1Av=MY{%l!%i+uDK>SyL903&>{1y}ma~?QQ(p&h zsG9_X%qMpx6Sza`iFWBRPULipJ*7i^rle@6$rY4neXRT+sMb-IWKUvpV_|4gv{8}( z*GVU!byCfzdf5*Qpu%LyqeM5SL`TrkKcGCz!`B3^d3a3HpK7x)F)%bK+DUgbCE7s> zL`a2F%az8(bgY31!V6pV_t+GsG!-VdHwtpaiMF z@MejTqLoamcQPn~RO{J-AlVN!1AtS%1+!72Lo5>y^D%Y?hBH$hDLO2QV*nLK4${Wz zlX;G@F`N;0OJs^=c4L7S)DV}Xaa}z2*XGlTxCcRcKJ6lbi8B zhBs5-PN=s^SH+sD;V!UWwBQ1dFb^wF4-dD%ss;zs$q-u%3`*MMKn*tt_tJt3Qg;gu ze3>(OxsmuLMSFM+oXe%g#l#lO#AeRKuxY`aMT)jkyCGU~xzf1mncNJSmTwY))FlPm z6(CZe8$=lx?wFkA0&B5{X>oI8Vqi#6IuLY}aZV9xT&Qs|9lP`YjHsI|Q<9?HBSojE zG#(BfQHR#%u6Ie`)&twuzcrr~l$&LQ4O|7xrvw=(ItSG&x&}BOaBx)QVRmC+kTym( zGZqxTZu$)7lbGDpn35FT7i{=)mSvHmYtJ@_E5&a8VN+lNrI!aB3}IY#~A;#h?N z6ud3o!Jy(wh1CgCz^OnAI45uc*QDrdWny&r&!c)p*9_(d9v~OkvhlDUV`L}_W(YdU zs10?AQKAFHu39cNF1_$cOl}-ZO<Nndz#suR7 z28N1Ziv`)9GRm+xxC*>E0&*EF?QsMtTGqRP&iG=$k@~=K-^0T#wF+YFBSmK;MOPkC zgS-Pg%nrem0|H7C5*33IJ=mn6&RA})+@$C+=fTbY+FA&|JwiuJ!UBw<#&*gD4L+nL<1Gu}}_k9)TBj0_6u2hih4jZ2riNbFG2 zBXFoB90P~SJ*acUy1uy`VFXvhpeS1P_1FQ%Ge?vZ?VX$$oD`j;UFw|@oq`m>S;NjP z?=J�39sXm%tfj)n%n+n#=M>B!9U7Eqhmin$xzxBslN9aLr!le3 zgxPlL58E`5`Z+1m`w^6a1P1MhZbB&NGOpy zQ?TL7oU-LcqKg#mAwGuW?@C5CM@9w)P^3cB3Mh9o9kXU!zKO}Lgz=6DdUU$wFoNp8 z1f>Jg?;fEStZG~WVwWOe7KCXdy=Bpa$EU& zo7V7Ml{T#5QVz>aV|BKM&th^@V$it?q7@j}^cdP_F|jE#EVtkcI<p>=f zw7MvOE)iy!o}wDaz`&y&5F)_i6R>CUBSnW@k>T?Mg4X^Ia1oGU1sTr5pvI+))<$Px zc%>krOz|#GN;E4Xl`DBMu(dI$!(FL{$B|2Up^ki{2zFzfyIvy5jiwlGWJ`kBX^!N`dT{fckSnn} zGKzsMmciiwOOMPk69(}oOm4=Y5zHnRskf0Ckg8?^nFSB}(v`&_PU0_u2}dp8*+nl*ts9_@IWV*=_6 zRx-hxZ$(9kR^@Q@^(Z|RX#35RD^e;c(fXJgIEjeCI!J8D9VAd^L-pIgAVq7?vi&4T z*oV2Rfy18o^idBFdN(d;wT+rUP=nsUqyd~jz|G4hMJG_3(CLw)gOQ@0fZhS6Lk$Xw zidx1-iFR)HeuLYa8j_%q5Y$%ZBSoh(a&4)9l@x7^>V?>J`O+UVwHYzECH@7qP!*kw zG$h%Rnb`Cgj3n5mCo>(>W>^Gku*!Zoq-e93sZD|blJWMkLt{7X?;}O~tapzR?b-JH zh03u)L#{ zp>3)EniTD`I+_yg+a`dNFH*FHJ7)Sn*$><6SL(7dPJi%}jc2;RT5S$l2L=WP(DrN= zP=kZ-0+R@+?a08u0NTtRGX3Fl0TC9^R`%%!oq2@oHwZB>fJO%)iVYZ!i9tpU85md? zwU`(fc#2sW7_NW@X+ZNgkOTM`m{?dd^GX<)O&Ay$m~McM5M^OuKgh(uzzRA9lgWb- zG?v1`#4f|kz`(-6%E}0$SRR9TjBLUn9wWyN76t|urX1EYAew_6)bV9zWMyDrHUgQ) zyc(p6Ta|%!`f~O6{^k!jTU=>&n8rfuFtzl$fVBW>bz`&{w5@AhZVPIfNWdvzw zV`24r0_vH9&c|f+0i89_o0vVwbu8?!nHU&&>zEiAB-nIkf{gn% zU7=WAOe&a_fq~5c_ zdb|%<7#KL%ivNQ2sZQUwR#%ScFVpmUYjw5xdO$v!oeK&_?pC(x@=f|;Ootez>#x%l zua9PBVEDjR#JHJ-fk6axx*}T{Bghg4k=YFL7#LVYZi8%ua5zND5F8#6eUKY# z!FmKlKna?yo)I((FCyXq3XDdujD!g2z(%%aFh@qj5~Qbv(TI&<`q6c|0`;CCeg~r) z2LpqK2gF@%b1*Qx5U~Jp7J$uu!@$o1y7ZOzC(HB+Uv;$W!NI&87R(F` zY&$?~KEC**GzPYvH<%b01h@||Ffg#~0tK)HcNvJi7ZmU^+#w(v4;nEtFeq?O1^M|f z9~%RM3imRQeMfEC7#K9Tw}3+FL?RmlgAO-4$oD6!*%%lM_~J{G7}(C-m~OX0SC&s6 zWYEP%Hqc>HciE;Fw&;r)E&_?&09hKqy&J^7IfsFPA%wdTWcUMhMh1om?r@M}9)YrE z40rqV4I6Zgm`<`zfACFbn#preJjybHj%^hN9c{_OrpO37XHf*4Je9$01`#fhVJhI5 zVi7queceV~HH}=5i|rTb+t&9u|0&HQ7 zP3#N|B7q>|A{jxMn?YnYC`6*5W?O^29u1b^5Ge<_Ee5QGhk>63q+D2rje#M6ErC&$ zgMmTh56ISJaD*|4D1kUBj38gKh`a;&I2Fv{5z%F5h+<$%1M@^gmV#WD4mLnW1aufD zTL##26_HGkeVJfo8Y0&~CS-v*1|o++0hXMTZNs0fr9}QYa9&B5iAT0oU5uq zrD8v*7_|ZUfjJLU(k7L1Ffd5XWno}oyvPbRhJk?rR24DKG2&!kkj?{%t1>Wv=WQ8e z5(_|lkPKt6EGHXSmjx#S17nD+6NG06;)TkAj@)Ft%nEV~%W)0{27b`uUz*{28MYI3=E3N42%topsHj6BLjnyAjt4W zMjg*ADotZxoXYrL7$m=;PU$qrdD9p{;RZUPPiYy5KOM|Jz|Fv* zv>p_$Gr;D*=Vo9~EYD<_W6F&pP2?hp+KpqALr4yj|XDneUVPIeYof4*ThM$2!S&V@} ziHCtflQD{A;*xqcO~z>Ei5u!P8Dm){ZmDNu(7MLYz@R6_z@QB}LF*C+SS3?216bXJ zdQHX{P{eC8#)9ITfk6j!t{PJj1L%A|3?u7fSV|Ziq~K<2wDK`9fQ(!QaR5$ZH8lAl z3eOWzsJ&1CqEM4D3e&WDm@74M7#J8p=ag{qg8jo>e1m~O3l#UDb4fHFGB7asi!m@5 zLS$j#!ZL9|JsX3@8wLgjkTO?@EK?B!gN6Yz|q1(C&cH`Li0zAO;WT_&XPCsg4dRE6~z@udMeD;s3DDjzr?aN8Zo2C*A-h91_y zg*oyw8|284G>D08pg>cOV9+oC9R&;u)fR{ZS{T`}Lqc^1M3$+Xfk6XwL@r3jHi#IS z4jm2#h5|7LhHDU6Q1PM37|m45pvf4+Xu+Um&B4G>Eylp05yHX1&?d&fz*o-?_9Vib zHK6n!!&c6~_5VL8S%S=9(8zL^fHDkI83TjHP7X+lnSg2z)C3+*i0nG7vbvlM40FU77%oF(nJ2n{ zvMpOF)EhBOB@7H&ptFHNiZo_G&HBX;+KPe5=1{A4Kn;@@00$1tFj$yC3_Au@q#?rv z33PXu>V|qv#%M+ZXqek^LBl+X3lip_)6c*`04iGQ8KYSyg2N&P?$}sJn1fEg1)bTm z6lN|cq@v-fVj!w!a6tkVbmkq{RIp*upqSBQi~;AwwOo*p1D$3ERsymZWbcA{wt5DQ zGhC3M;}%5mEh6a37#Or}K^1AJazhL=f~ZC$9B@GZI=>cF5V%2PnTi=0G^Rpzq(H<_ zb<}IDv$lZl@$U94XV>MK!>q{JZlJ%1?SJZ zP#yjdF*F^ZQVNt)auKrN>;_GDJbaKsu^S=HrpX8^6HTGAE3wLgP7(wiOLPz-3om2( z1R-S)=yGdK#wfV>GC{Ci^$dR@8elF($+d?diWorWoFSzgNPz-%^cAQY@JVurtb}6D zC#afC&}nl>YDyVEnICEpXcz<(IQ2-17&OF$!0u#Nj3f#w(@GG9i#F(tK#)#kCn6O- z4E!vogc%ql>=;08RS*dtG59RZz#v`^>S>9C+D_tNKB!^Kz|R6|W%9FJ6=q=I0I3JH zeK@}`FfgbyaImv6Fo0S+U_Qt|P=Ah}1vKWt&k_h~s)GbT4H~ct0R{;}5e5c%30BZL zG;k{nJn{o>RJ|6dhc<-S7+;GpFsSo0Fvx%#x@?ReL>L$tgTbv}HgGGLX<~pJ=*9#_ zZg3l#F+|-t2GB^D=5)IY>h^;PQIibWQ#1=7TDMq;~dc4&4#V z4<%WMts&r0l7-o-4mumqfPsMlbRD%4I9!y$;j)yUfkFF(5Ca1&WQsujAMGDVAp#1D zXmFJeF69~Y-h;ZFiy7I(7#Jj&nL+J7P-kxmSU?YS2LRhrFk9b)k%0k(xu)nxvN14# zFxMpg6h;OH5N1)-uVG_g0AX$w{YDlB1`y_2nyJqN8i8S8VBp%OpTfq#0KzP)`f^MR z3?R%SslT0>fdPbho%KN%T7WQ@zrHR50|N;2i0iAfFff2HSF=9o&HxbR^3zXXU|;}Y zwq*vOTSSt!f83{Q&NzLottQv>J-oua(;XJ`b4+gy))JRvWnkvB;6lRiOV30FG z;mS`J^yHMDUf`+9A_G$;&cL8(!obV{)g;cqpky*VuSZyRI{RZabuU3iAvQ)~1_q7J zCPqdhrC43 zK7qzw874Y_Xa+`xdIm;@AO=PT2GBSP6T?IY5Us_?z`)250-```OF_aQ8q_mnWC#IK z)l3WwAYl*7)GAes}DEf^v|loT@q12efx?qO?IBRfdQVCI*K3Ffcm=L`8y`5g-aQ`pU=<0-~}Y zq9Ce(iGhKWp`2j~6R6EOAH=N%%Z7j`Pyiswg1DeNofsKH`anlVLlkadVqiD}4m{)e zAiDlMMEnvH18Dw@fr-I*KIopTM-X8U^%T|CH%tr+A0e_JN{|`FO_t0I44@03m>3M_ zgJ{qNP>c*AVayB+G0dP~GnfyelOUm)%3ROD02&1ZSqq|TAmSiuHkcW)gqeXs64ZNT zhyY!m6aZ$1gs?C$L_(M#>Li#MafO9}0dz|dBSQ!SXiywflQ4v^vNAA$Zr?!;8&(E} zCa`RM2#8(|W=CvbWnfSR4FWJk7=cH@K*=y7f{lUU6hstsZwjc7!pIN;8Z-S177YPW z4D4XF9PA7X0$^qc=&F+_2=f641H&s0kb;muTnr3s++gM^9tMUrpcCF18A3p%;e9+;=GXz08fPvnFsuMa zv%&nO91ILAI2ah%7>XFm7-Z&y*lWSA39v#>&e-Y`WGcBC7}~fQ>KT|Bikas3b2Bi2 z#()?ZfH`L7su^#h6%>*$ZkrM}^e!;{+jK5$`aov0nLy!*~ zTp&uH52_i&05ww?>ls2pG^j`cTL>ysKqER}4k(U~3PQDj7@$yQWC#XP9H670L7@p^ zND6_h2nJE6FmZ4kFfcGoV`pHP1G=7v!C*d!z64H_!J-@t45pwPRzT5X$^q%1Gctt! z<6vNLfCM4va*i7iCNCEQg9R5P|AJ^xGG=552Bo?!;8+g@Q6MKmo%aNPI24qPg}^y76m(P8Y6ugQIYF0jF*1aLGAHOp zDn^D-Q09yUXTwlL<_ra8&RnoRLtn5%uD@Yq2nA)s&)|d~3i2W7k`z$*gS_|z?9EV+ zxBS4lGZd5z&On%;j0#E!j0~Y5Dis``Aq@Ns44nL+a1U9+&%kg4!fX&=U}zQqiz2dR zC@3_3gF_?~79yaeeTfrX|Ak>mo}i!vMKdEq7)%E!4?STAr-mK%AaC6T4SX_8+))qm z*j`9l-%$@@fE>@r5CNi=gLCM_9rYjv$R~^p!5|8n;_7$QtALJ8hoqVvkWp?11_n?) z9>@f?Y(+hY0g5(8h9D4iAL57=^&kewA&d+`AnF#xJuB)#43L``8Nxu+4=$)VAO

  • QhQ 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 z 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 QA2XO#ShyG%{6QO@00-v(X8-^I From f04e62e6ccf752883bbd3a23cf6d62a403b6a2a9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 10 Jan 2017 23:45:44 +0800 Subject: [PATCH 129/167] cxx: add iostreams test Checks that libstdc++ can use newlib IO functions --- components/cxx/test/test_cxx.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/cxx/test/test_cxx.cpp b/components/cxx/test/test_cxx.cpp index 8b790783ed0..3b653361f45 100644 --- a/components/cxx/test/test_cxx.cpp +++ b/components/cxx/test/test_cxx.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "unity.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" @@ -201,3 +202,9 @@ TEST_CASE("before scheduler has started, static initializers work correctly", "[ TEST_ASSERT_EQUAL(1, g_static_init_test3.index); TEST_ASSERT_EQUAL(2, StaticInitTestBeforeScheduler::order); } + + +TEST_CASE("can use iostreams", "[cxx]") +{ + std::cout << "hello world"; +} From 8d2199e36c068cffa7e935e371aa591227fdd2e3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sat, 3 Dec 2016 23:02:02 -0800 Subject: [PATCH 130/167] docs: update toolchain version --- docs/linux-setup.rst | 22 ++++++++++++---------- docs/macos-setup.rst | 13 +++++++------ docs/windows-setup.rst | 4 ++-- make/project.mk | 27 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/docs/linux-setup.rst b/docs/linux-setup.rst index 20f460aa62d..94d77f3605e 100644 --- a/docs/linux-setup.rst +++ b/docs/linux-setup.rst @@ -10,11 +10,11 @@ Install some packages To compile with ESP-IDF you need to get the following packages: - Ubuntu and Debian:: - - sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial + + sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial - Arch:: - + sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial Step 1: Download binary toolchain for the ESP32 @@ -24,17 +24,17 @@ ESP32 toolchain for Linux is available for download from Espressif website: - for 64-bit Linux:: - https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-59.tar.gz + https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz - for 32-bit Linux:: - https://dl.espressif.com/dl/xtensa-esp32-elf-linux32-1.22.0-59.tar.gz + https://dl.espressif.com/dl/xtensa-esp32-elf-linux32-1.22.0-61-gab8375a-5.2.0.tar.gz Download this file, then extract it to the location you prefer, for example:: mkdir -p ~/esp cd ~/esp - tar -xzf ~/Downloads/xtensa-esp32-elf-linux64-1.22.0-59.tar.gz + tar -xzf ~/Downloads/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz The toolchain will be extracted into ``~/esp/xtensa-esp32-elf/`` directory. @@ -61,12 +61,14 @@ To run the precompiled gdb (xtensa-esp32-elf-gdb) in Arch Linux requires ncurses Alternative Step 1: Compile the toolchain from source using crosstool-NG ======================================================================== -Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. +Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. If you can't think of a reason why you need to build it yourself, then probably it's better to stick with the binary version. However, here are some of the reasons why you might want to compile it from source: - if you want to customize toolchain build configuration +- if you want to use a different GCC version (such as 4.8.5) + - if you want to hack gcc or newlib or libstdc++ - if you are curious and/or have time to spare @@ -79,7 +81,7 @@ In any case, here are the steps to compile the toolchain yourself. - Ubuntu:: - sudo apt-get install gawk gperf grep gettext ncurses python python-dev automake bison flex texinfo help2man libtool + sudo apt-get install gawk gperf grep gettext libncurses-dev python python-dev automake bison flex texinfo help2man libtool - Debian:: @@ -113,7 +115,7 @@ Open terminal, navigate to the directory you want to clone ESP-IDF and clone it git clone --recursive https://github.com/espressif/esp-idf.git -ESP-IDF will be downloaded into ``~/esp/esp-idf``. +ESP-IDF will be downloaded into ``~/esp/esp-idf``. Note the ``--recursive`` option! If you have already cloned ESP-IDF without this option, run another command to get all the submodules:: @@ -142,7 +144,7 @@ In terminal, go to the application directory which was obtained on the previous cd ~/esp/myapp -Type a command like this to set the path to ESP-IDF directory:: +Type a command like this to set the path to ESP-IDF directory:: export IDF_PATH=~/esp/esp-idf diff --git a/docs/macos-setup.rst b/docs/macos-setup.rst index 67a1fa9906d..ac772c52984 100644 --- a/docs/macos-setup.rst +++ b/docs/macos-setup.rst @@ -23,13 +23,13 @@ Step 1: Download binary toolchain for the ESP32 ESP32 toolchain for macOS is available for download from Espressif website: -https://dl.espressif.com/dl/xtensa-esp32-elf-osx-1.22.0-59.tar.gz +https://dl.espressif.com/dl/xtensa-esp32-elf-osx-1.22.0-61-gab8375a-5.2.0.tar.gz Download this file, then extract it to the location you prefer, for example:: mkdir -p ~/esp cd ~/esp - tar -xzf ~/Downloads/xtensa-esp32-elf-osx-1.22.0-59.tar.gz + tar -xzf ~/Downloads/xtensa-esp32-elf-osx-1.22.0-61-gab8375a-5.2.0.tar.gz The toolchain will be extracted into ``~/esp/xtensa-esp32-elf/`` directory. @@ -46,12 +46,14 @@ Then when you need the toolchain you can type ``get_esp32`` on the command line Alternative Step 1: Compile the toolchain from source using crosstool-NG ======================================================================== -Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. +Instead of downloading binary toolchain from Espressif website (Step 1 above) you may build the toolchain yourself. If you can't think of a reason why you need to build it yourself, then probably it's better to stick with the binary version. However, here are some of the reasons why you might want to compile it from source: - if you want to customize toolchain build configuration +- if you want to use a different GCC version (such as 4.8.5) + - if you want to hack gcc or newlib or libstdc++ - if you are curious and/or have time to spare @@ -107,7 +109,7 @@ Open Terminal.app, navigate to the directory you want to clone ESP-IDF and clone git clone --recursive https://github.com/espressif/esp-idf.git -ESP-IDF will be downloaded into ``~/esp/esp-idf``. +ESP-IDF will be downloaded into ``~/esp/esp-idf``. Note the ``--recursive`` option! If you have already cloned ESP-IDF without this option, run another command to get all the submodules:: @@ -136,7 +138,7 @@ In Terminal.app, go to the application directory which was obtained on the previ cd ~/esp/myapp -Type a command like this to set the path to ESP-IDF directory:: +Type a command like this to set the path to ESP-IDF directory:: export IDF_PATH=~/esp/esp-idf @@ -160,4 +162,3 @@ Further reading =============== If you'd like to use the Eclipse IDE instead of running ``make``, check out the Eclipse setup guide in this directory. - diff --git a/docs/windows-setup.rst b/docs/windows-setup.rst index a425f5b3a05..00205fb78bb 100644 --- a/docs/windows-setup.rst +++ b/docs/windows-setup.rst @@ -9,7 +9,7 @@ Windows doesn't have a built-in "make" environment, so as well as installing the The quick setup is to download the Windows all-in-one toolchain & MSYS zip file from dl.espressif.com: -https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20160816.zip +https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20170111.zip Unzip the zip file to C:\ and it will create an "msys32" directory with a pre-prepared environment. @@ -38,7 +38,7 @@ Another Alternative Step 1: Just download a toolchain If you already have an MSYS2 install or want to do things differently, you can download just the toolchain here: -https://dl.espressif.com/dl/xtensa-esp32-elf-win32-1.22.0-59.zip +https://dl.espressif.com/dl/xtensa-esp32-elf-win32-1.22.0-61-gab8375a-5.2.0.zip If you followed one of the above options for Step 1, you won't need this download. diff --git a/make/project.mk b/make/project.mk index ee8c0779438..f41e434393a 100644 --- a/make/project.mk +++ b/make/project.mk @@ -421,3 +421,30 @@ endef # filter/subst in expression ensures all submodule paths begin with $(IDF_PATH), and then strips that prefix # so the argument is suitable for use with 'git submodule' commands $(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUBMODULES))),$(eval $(call GenerateSubmoduleCheckTarget,$(submodule)))) + + +# 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 +# The part in brackets is extracted into TOOLCHAIN_COMMIT_DESC variable, +# the part after the brackets is extracted into TOOLCHAIN_GCC_VER. +ifndef MAKE_RESTARTS +TOOLCHAIN_COMMIT_DESC := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc\ \(([^)]*).*|\1|gp') +TOOLCHAIN_GCC_VER := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc\ \(.*\)\ (.*)|\1|gp') + +# Officially supported version(s) +SUPPORTED_TOOLCHAIN_COMMIT_DESC := crosstool-NG crosstool-ng-1.22.0-61-gab8375a +SUPPORTED_TOOLCHAIN_GCC_VERSIONS := 5.2.0 + +ifneq ($(TOOLCHAIN_COMMIT_DESC), $(SUPPORTED_TOOLCHAIN_COMMIT_DESC)) +$(info WARNING: Toolchain version is not supported: $(TOOLCHAIN_COMMIT_DESC)) +$(info Expected to see version: $(SUPPORTED_TOOLCHAIN_COMMIT_DESC)) +$(info Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.) +endif +ifeq (,$(findstring $(TOOLCHAIN_GCC_VER), $(SUPPORTED_TOOLCHAIN_GCC_VERSIONS))) +$(warning WARNING: Compiler version is not supported: $(TOOLCHAIN_GCC_VER)) +$(info Expected to see version(s): $(SUPPORTED_TOOLCHAIN_GCC_VERSIONS)) +$(info Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.) +endif +endif #MAKE_RESTARTS + From 356e01545c08888463442fa3c3c7adbec1658a97 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 11 Jan 2017 16:13:33 +0800 Subject: [PATCH 131/167] Add test for spi clock, fix corner cases) --- components/driver/spi_master.c | 7 +-- components/driver/test/test_spi_master.c | 62 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 53be8b29de6..6162edba367 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -318,6 +318,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, spi_device_interface_config SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE); SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(dev_config->clock_speed_hz > 0, "invalid sclk speed", ESP_ERR_INVALID_ARG); for (freecs=0; freecsdevice[freecs], NULL, (spi_device_t *)1)) break; @@ -412,15 +413,15 @@ static void spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) { //with the higher n. int bestn=-1; int bestpre=-1; - int besterr=hz; + int besterr=0; int errval; for (n=1; n<=64; n++) { //Effectively, this does pre=round((fapb/n)/hz). pre=((fapb/n)+(hz/2))/hz; - if (pre<0) pre=0; + if (pre<=0) pre=1; if (pre>8192) pre=8192; errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz); - if (errval<=besterr) { + if (bestn==-1 || errval<=besterr) { besterr=errval; bestn=n; bestpre=pre; diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c index 119c94bc570..d54cc5a633f 100644 --- a/components/driver/test/test_spi_master.c +++ b/components/driver/test/test_spi_master.c @@ -15,8 +15,70 @@ #include "freertos/xtensa_api.h" #include "unity.h" #include "driver/spi_master.h" +#include "soc/dport_reg.h" +#include "soc/spi_reg.h" +#include "soc/spi_struct.h" +static void check_spi_pre_n_for(int clk, int pre, int n) +{ + esp_err_t ret; + spi_device_handle_t handle; + + spi_device_interface_config_t devcfg={ + .command_bits=0, + .address_bits=0, + .dummy_bits=0, + .clock_speed_hz=clk, + .duty_cycle_pos=128, + .mode=0, + .spics_io_num=21, + .queue_size=3 + }; + char sendbuf[16]=""; + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + + ret=spi_bus_add_device(HSPI_HOST, &devcfg, &handle); + TEST_ASSERT(ret==ESP_OK); + + t.length=16*8; + t.tx_buffer=sendbuf; + ret=spi_device_transmit(handle, &t); + + printf("Checking clk rate %dHz. expect pre %d n %d, got pre %d n %d\n", clk, pre, n, SPI2.clock.clkdiv_pre+1, SPI2.clock.clkcnt_n+1); + + TEST_ASSERT(SPI2.clock.clkcnt_n+1==n); + TEST_ASSERT(SPI2.clock.clkdiv_pre+1==pre); + + ret=spi_bus_remove_device(handle); + TEST_ASSERT(ret==ESP_OK); +} + + +TEST_CASE("SPI Master clockdiv calculation routines", "[spi]") +{ + spi_bus_config_t buscfg={ + .mosi_io_num=4, + .miso_io_num=16, + .sclk_io_num=25, + .quadwp_io_num=-1, + .quadhd_io_num=-1 + }; + esp_err_t ret; + ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1); + TEST_ASSERT(ret==ESP_OK); + + check_spi_pre_n_for(8000000, 1, 10); + check_spi_pre_n_for(800000, 2, 50); + check_spi_pre_n_for(100000, 16, 50); + check_spi_pre_n_for(333333, 4, 60); + check_spi_pre_n_for(1, 8192, 64); //Actually should generate the minimum clock speed, 152Hz + + ret=spi_bus_free(HSPI_HOST); + TEST_ASSERT(ret==ESP_OK); +} + TEST_CASE("SPI Master test", "[spi]") { From d512d6100cb65804305b4f33084df0142811f771 Mon Sep 17 00:00:00 2001 From: Yulong Date: Fri, 23 Dec 2016 11:28:47 -0500 Subject: [PATCH 132/167] component bt:Added the create attribute table method to the new API --- components/bt/bluedroid/api/esp_blufi_api.c | 14 +- components/bt/bluedroid/api/esp_bt_main.c | 12 +- components/bt/bluedroid/api/esp_gap_ble_api.c | 28 +- components/bt/bluedroid/api/esp_gattc_api.c | 50 +-- components/bt/bluedroid/api/esp_gatts_api.c | 124 +++++-- .../bluedroid/api/include/esp_gap_ble_api.h | 225 ++++++------ .../bt/bluedroid/api/include/esp_gatt_defs.h | 290 ++++++++++----- .../bt/bluedroid/api/include/esp_gatts_api.h | 276 ++++++++------ .../bt/bluedroid/bta/gatt/bta_gatts_act.c | 61 +++- .../bt/bluedroid/bta/gatt/bta_gatts_api.c | 66 +++- .../bt/bluedroid/bta/gatt/bta_gatts_main.c | 13 +- .../bt/bluedroid/bta/include/bta_gatt_api.h | 174 +++++---- .../bt/bluedroid/bta/include/bta_gatts_int.h | 24 +- .../btc/profile/esp/blufi/blufi_prf.c | 10 +- .../btc/profile/std/gatt/btc_gatts.c | 291 ++++++++++++++- .../btc/profile/std/include/btc_gatts.h | 37 +- .../bt/bluedroid/hci/packet_fragmenter.c | 9 +- components/bt/bluedroid/stack/gap/gap_ble.c | 12 +- components/bt/bluedroid/stack/gatt/gatt_api.c | 112 ++++-- .../bt/bluedroid/stack/gatt/gatt_attr.c | 11 +- components/bt/bluedroid/stack/gatt/gatt_db.c | 333 +++++++++++++++-- .../bt/bluedroid/stack/gatt/gatt_main.c | 6 +- components/bt/bluedroid/stack/gatt/gatt_sr.c | 96 +++-- .../bt/bluedroid/stack/gatt/gatt_utils.c | 30 +- .../bluedroid/stack/gatt/include/gatt_int.h | 120 ++++--- .../bt/bluedroid/stack/include/gatt_api.h | 91 +++-- components/bt/bluedroid/stack/smp/smp_main.c | 2 +- docs/api/esp_blufi.rst | 3 +- docs/api/esp_gatt_defs.rst | 23 ++ docs/api/esp_gatts.rst | 9 + examples/14_gatt_server/main/gatts_demo.c | 39 +- examples/30_gatt_server_table_create/Makefile | 11 + .../30_gatt_server_table_create/README.rst | 10 + .../main/component.mk | 8 + .../main/gatts_table_creat_demo.c | 340 ++++++++++++++++++ .../main/gatts_table_creat_demo.h | 57 +++ .../sdkconfig.defaults | 14 + 37 files changed, 2360 insertions(+), 671 deletions(-) create mode 100644 examples/30_gatt_server_table_create/Makefile create mode 100644 examples/30_gatt_server_table_create/README.rst create mode 100644 examples/30_gatt_server_table_create/main/component.mk create mode 100644 examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c create mode 100644 examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h create mode 100644 examples/30_gatt_server_table_create/sdkconfig.defaults diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index 70f5c9ce8f3..00fbeffbc98 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -25,9 +25,9 @@ esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) { if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + if (callbacks == NULL) { return ESP_FAIL; } @@ -42,9 +42,9 @@ esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn btc_blufi_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_SEND_CFG_REPORT; @@ -62,7 +62,7 @@ esp_err_t esp_blufi_profile_init(void) btc_msg_t msg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -77,9 +77,9 @@ esp_err_t esp_blufi_profile_deinit(void) btc_msg_t msg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_DEINIT; diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index 3093b56403d..c9fe4fc060e 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -24,13 +24,13 @@ static bool esp_already_init = false; esp_bluedroid_status_t esp_bluedroid_get_status(void) { if (esp_already_init) { - if (esp_already_enable) { - return ESP_BLUEDROID_STATUS_ENABLED; - } else { - return ESP_BLUEDROID_STATUS_INITIALIZED; - } + if (esp_already_enable) { + return ESP_BLUEDROID_STATUS_ENABLED; + } else { + return ESP_BLUEDROID_STATUS_INITIALIZED; + } } else { - return ESP_BLUEDROID_STATUS_UNINITIALIZED; + return ESP_BLUEDROID_STATUS_UNINITIALIZED; } } diff --git a/components/bt/bluedroid/api/esp_gap_ble_api.c b/components/bt/bluedroid/api/esp_gap_ble_api.c index f9401d8c42c..56bedc42421 100644 --- a/components/bt/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/bluedroid/api/esp_gap_ble_api.c @@ -25,7 +25,7 @@ esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback) { if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } return (btc_profile_cb_set(BTC_PID_GAP_BLE, callback) == 0 ? ESP_OK : ESP_FAIL); } @@ -37,7 +37,7 @@ esp_err_t esp_ble_gap_config_adv_data(esp_ble_adv_data_t *adv_data) btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } if (adv_data == NULL) { @@ -64,7 +64,7 @@ esp_err_t esp_ble_gap_set_scan_params(esp_ble_scan_params_t *scan_params) btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } if (scan_params == NULL) { @@ -85,7 +85,7 @@ esp_err_t esp_ble_gap_start_scanning(uint32_t duration) btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -102,7 +102,7 @@ esp_err_t esp_ble_gap_stop_scanning(void) btc_msg_t msg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -116,8 +116,8 @@ esp_err_t esp_ble_gap_start_advertising(esp_ble_adv_params_t *adv_params) btc_msg_t msg; btc_ble_gap_args_t arg; - if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -133,7 +133,7 @@ esp_err_t esp_ble_gap_stop_advertising(void) btc_msg_t msg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -150,7 +150,7 @@ esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params) btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -167,7 +167,7 @@ esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_ btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -186,7 +186,7 @@ esp_err_t esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -204,7 +204,7 @@ esp_err_t esp_ble_gap_config_local_privacy (bool privacy_enable) btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -221,9 +221,9 @@ esp_err_t esp_ble_gap_set_device_name(const char *name) btc_ble_gap_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + if (strlen(name) > ESP_GAP_DEVICE_NAME_MAX) { return ESP_ERR_INVALID_ARG; } diff --git a/components/bt/bluedroid/api/esp_gattc_api.c b/components/bt/bluedroid/api/esp_gattc_api.c index 8b9cc99e87e..4f68d81cc0c 100644 --- a/components/bt/bluedroid/api/esp_gattc_api.c +++ b/components/bt/bluedroid/api/esp_gattc_api.c @@ -23,9 +23,9 @@ esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback) { if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + if (callback == NULL) { return ESP_FAIL; } @@ -40,9 +40,9 @@ esp_err_t esp_ble_gattc_app_register(uint16_t app_id) btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + if (app_id > ESP_APP_ID_MAX) { return ESP_ERR_INVALID_ARG; } @@ -61,7 +61,7 @@ esp_err_t esp_ble_gattc_app_unregister(esp_gatt_if_t gattc_if) btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -78,9 +78,9 @@ esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, b btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_OPEN; @@ -97,7 +97,7 @@ esp_err_t esp_ble_gattc_close (esp_gatt_if_t gattc_if, uint16_t conn_id) btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -114,7 +114,7 @@ esp_err_t esp_ble_gattc_config_mtu (esp_gatt_if_t gattc_if, uint16_t conn_id, ui btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } if ((mtu < ESP_GATT_DEF_BLE_MTU_SIZE) || (mtu > ESP_GATT_MAX_MTU_SIZE)) { @@ -136,7 +136,7 @@ esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -163,7 +163,7 @@ esp_err_t esp_ble_gattc_get_characteristic(esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -192,9 +192,9 @@ esp_err_t esp_ble_gattc_get_descriptor(esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; @@ -223,7 +223,7 @@ esp_err_t esp_ble_gattc_get_included_service(esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -253,7 +253,7 @@ esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -278,7 +278,7 @@ esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -299,14 +299,14 @@ esp_err_t esp_ble_gattc_write_char( esp_gatt_if_t gattc_if, esp_gatt_id_t *char_id, uint16_t value_len, uint8_t *value, - esp_gatt_write_type_t write_type, + esp_gatt_write_type_t write_type, esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -330,16 +330,16 @@ esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, esp_gatt_id_t *descr_id, uint16_t value_len, uint8_t *value, - esp_gatt_write_type_t write_type, + esp_gatt_write_type_t write_type, esp_gatt_auth_req_t auth_req) { btc_msg_t msg; btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR_DESCR; @@ -369,7 +369,7 @@ esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -392,7 +392,7 @@ esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -413,7 +413,7 @@ esp_gatt_status_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -436,7 +436,7 @@ esp_gatt_status_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, btc_ble_gattc_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index 71b5a4338c0..f5ebe59a2d3 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -22,10 +22,11 @@ #define COPY_TO_GATTS_ARGS(_gatt_args, _arg, _arg_type) memcpy(_gatt_args, _arg, sizeof(_arg_type)) + esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback) { if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } return (btc_profile_cb_set(BTC_PID_GATTS, callback) == 0 ? ESP_OK : ESP_FAIL); } @@ -36,7 +37,7 @@ esp_err_t esp_ble_gatts_app_register(uint16_t app_id) btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } //if (app_id < ESP_APP_ID_MIN || app_id > ESP_APP_ID_MAX) { @@ -59,9 +60,9 @@ esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if) btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_APP_UNREGISTER; @@ -77,7 +78,7 @@ esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -90,6 +91,26 @@ esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } +esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint8_t max_nb_attr, + uint8_t srvc_inst_id) +{ + btc_msg_t msg; + btc_ble_gatts_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_CREATE_ATTR_TAB; + arg.create_attr_tab.gatts_if = gatts_if; + arg.create_attr_tab.max_nb_attr = max_nb_attr; + arg.create_attr_tab.srvc_inst_id = srvc_inst_id; + arg.create_attr_tab.gatts_attr_db = (esp_gatts_attr_db_t *)gatts_attr_db; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t included_service_handle) { @@ -97,7 +118,7 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -111,46 +132,69 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid, - esp_gatt_perm_t perm, esp_gatt_char_prop_t property) + esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val, + esp_attr_control_t *control) { btc_msg_t msg; btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_CHAR; arg.add_char.service_handle = service_handle; arg.add_char.perm = perm; arg.add_char.property = property; + if (char_val != NULL) { + arg.add_char.char_val.attr_max_len = char_val->attr_max_len; + arg.add_char.char_val.attr_len = char_val->attr_len; + arg.add_char.char_val.attr_value = char_val->attr_value; + } + + if (control != NULL) { + arg.add_char.attr_control.auto_rsp = control->auto_rsp; + } memcpy(&arg.add_char.char_uuid, char_uuid, sizeof(esp_bt_uuid_t)); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, esp_bt_uuid_t *descr_uuid, - esp_gatt_perm_t perm) + esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val, + esp_attr_control_t *control) { btc_msg_t msg; btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_ADD_CHAR_DESCR; arg.add_descr.service_handle = service_handle; arg.add_descr.perm = perm; + + if (char_descr_val != NULL) { + arg.add_descr.descr_val.attr_max_len = char_descr_val->attr_max_len; + arg.add_descr.descr_val.attr_len = char_descr_val->attr_len; + arg.add_descr.descr_val.attr_value = char_descr_val->attr_value; + } + + if (control != NULL) { + arg.add_descr.attr_control.auto_rsp = control->auto_rsp; + } memcpy(&arg.add_descr.descr_uuid, descr_uuid, sizeof(esp_bt_uuid_t)); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle) @@ -159,7 +203,7 @@ esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle) btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -176,7 +220,7 @@ esp_err_t esp_ble_gatts_start_service(uint16_t service_handle) btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -193,9 +237,9 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle) btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_STOP_SERVICE; @@ -212,7 +256,7 @@ esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -224,7 +268,8 @@ esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, arg.send_ind.value_len = value_len; arg.send_ind.value = value; - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), + btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id, @@ -234,7 +279,7 @@ esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -245,7 +290,32 @@ esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, arg.send_rsp.status = status; arg.send_rsp.rsp = rsp; - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), + btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, const uint8_t *value) +{ + btc_msg_t msg; + btc_ble_gatts_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GATTS; + msg.act = BTC_GATTS_ACT_SET_ATTR_VALUE; + arg.set_attr_val.length = length; + arg.set_attr_val.value = (uint8_t *)value; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), + btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value) +{ + if (attr_handle == ESP_GATT_ILLEGAL_HANDLE) { + return ESP_FAIL; + } + btc_gatts_get_attr_value(attr_handle, length, (uint8_t **)value); + return ESP_OK; } esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct) @@ -254,7 +324,7 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, b btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } msg.sig = BTC_SIG_API_CALL; @@ -264,7 +334,8 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, b arg.open.is_direct = is_direct; memcpy(&arg.open.remote_bda, remote_bda, sizeof(esp_bd_addr_t)); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id) @@ -273,13 +344,14 @@ esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id) btc_ble_gatts_args_t arg; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; + return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CLOSE; arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id); - return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } 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 f92143e6af8..1d27edb8812 100644 --- a/components/bt/bluedroid/api/include/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/esp_gap_ble_api.h @@ -40,42 +40,44 @@ extern "C" { /// GAP BLE callback event type typedef enum { - ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT = 0, /*!< When advertising data set complete, the event comes */ - ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT , /*!< When scan response data set complete, the event comes */ - ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, /*!< When scan parameters set complete, the event comes */ - ESP_GAP_BLE_SCAN_RESULT_EVT, /*!< When one scan result ready, the event comes each time */ + ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT = 0, /*!< When advertising data set complete, the event comes */ + ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT, /*!< When scan response data set complete, the event comes */ + ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, /*!< When scan parameters set complete, the event comes */ + ESP_GAP_BLE_SCAN_RESULT_EVT, /*!< When one scan result ready, the event comes each time */ } esp_gap_ble_cb_event_t; /// Advertising data maximum length -#define ESP_BLE_ADV_DATA_LEN_MAX 31 +#define ESP_BLE_ADV_DATA_LEN_MAX 31 +/// Scan response data maximum length +#define ESP_BLE_SCAN_RSP_DATA_LEN_MAX 31 /// The type of advertising data(not adv_type) typedef enum { - ESP_BLE_AD_TYPE_FLAG = 0x01, - ESP_BLE_AD_TYPE_16SRV_PART = 0x02, - ESP_BLE_AD_TYPE_16SRV_CMPL = 0x03, - ESP_BLE_AD_TYPE_32SRV_PART = 0x04, - ESP_BLE_AD_TYPE_32SRV_CMPL = 0x05, - ESP_BLE_AD_TYPE_128SRV_PART = 0x06, - ESP_BLE_AD_TYPE_128SRV_CMPL = 0x07, - ESP_BLE_AD_TYPE_NAME_SHORT = 0x08, - ESP_BLE_AD_TYPE_NAME_CMPL = 0x09, - ESP_BLE_AD_TYPE_TX_PWR = 0x0A, - ESP_BLE_AD_TYPE_DEV_CLASS = 0x0D, - ESP_BLE_AD_TYPE_SM_TK = 0x10, - ESP_BLE_AD_TYPE_SM_OOB_FLAG = 0x11, - ESP_BLE_AD_TYPE_INT_RANGE = 0x12, - ESP_BLE_AD_TYPE_SOL_SRV_UUID = 0x14, - ESP_BLE_AD_TYPE_128SOL_SRV_UUID = 0x15, - ESP_BLE_AD_TYPE_SERVICE_DATA = 0x16, - ESP_BLE_AD_TYPE_PUBLIC_TARGET = 0x17, - ESP_BLE_AD_TYPE_RANDOM_TARGET = 0x18, - ESP_BLE_AD_TYPE_APPEARANCE = 0x19, - ESP_BLE_AD_TYPE_ADV_INT = 0x1A, - ESP_BLE_AD_TYPE_32SOL_SRV_UUID = 0x1B, - ESP_BLE_AD_TYPE_32SERVICE_DATA = 0x1C, - ESP_BLE_AD_TYPE_128SERVICE_DATA = 0x1D, - ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE = 0xFF, + ESP_BLE_AD_TYPE_FLAG = 0x01, + ESP_BLE_AD_TYPE_16SRV_PART = 0x02, + ESP_BLE_AD_TYPE_16SRV_CMPL = 0x03, + ESP_BLE_AD_TYPE_32SRV_PART = 0x04, + ESP_BLE_AD_TYPE_32SRV_CMPL = 0x05, + ESP_BLE_AD_TYPE_128SRV_PART = 0x06, + ESP_BLE_AD_TYPE_128SRV_CMPL = 0x07, + ESP_BLE_AD_TYPE_NAME_SHORT = 0x08, + ESP_BLE_AD_TYPE_NAME_CMPL = 0x09, + ESP_BLE_AD_TYPE_TX_PWR = 0x0A, + ESP_BLE_AD_TYPE_DEV_CLASS = 0x0D, + ESP_BLE_AD_TYPE_SM_TK = 0x10, + ESP_BLE_AD_TYPE_SM_OOB_FLAG = 0x11, + ESP_BLE_AD_TYPE_INT_RANGE = 0x12, + ESP_BLE_AD_TYPE_SOL_SRV_UUID = 0x14, + ESP_BLE_AD_TYPE_128SOL_SRV_UUID = 0x15, + ESP_BLE_AD_TYPE_SERVICE_DATA = 0x16, + ESP_BLE_AD_TYPE_PUBLIC_TARGET = 0x17, + ESP_BLE_AD_TYPE_RANDOM_TARGET = 0x18, + ESP_BLE_AD_TYPE_APPEARANCE = 0x19, + ESP_BLE_AD_TYPE_ADV_INT = 0x1A, + ESP_BLE_AD_TYPE_32SOL_SRV_UUID = 0x1B, + ESP_BLE_AD_TYPE_32SERVICE_DATA = 0x1C, + ESP_BLE_AD_TYPE_128SERVICE_DATA = 0x1D, + ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE = 0xFF, } esp_ble_adv_data_type; /// Advertising mode @@ -109,37 +111,37 @@ typedef enum { /// Advertising parameters typedef struct { - uint16_t adv_int_min; /*!< Minimum advertising interval for - undirected and low duty cycle directed advertising. - Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) - Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec */ - uint16_t adv_int_max; /*!< Maximum advertising interval for - undirected and low duty cycle directed advertising. - Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) - Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec Advertising max interval */ - esp_ble_adv_type_t adv_type; /*!< Advertising type */ - esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */ - esp_bd_addr_t peer_addr; /*!< Peer device bluetooth device address */ - esp_ble_addr_type_t peer_addr_type; /*!< Peer device bluetooth device address type */ - esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */ - esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */ + uint16_t adv_int_min; /*!< Minimum advertising interval for + undirected and low duty cycle directed advertising. + Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) + Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec */ + uint16_t adv_int_max; /*!< Maximum advertising interval for + undirected and low duty cycle directed advertising. + Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second) + Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec Advertising max interval */ + esp_ble_adv_type_t adv_type; /*!< Advertising type */ + esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */ + esp_bd_addr_t peer_addr; /*!< Peer device bluetooth device address */ + esp_ble_addr_type_t peer_addr_type; /*!< Peer device bluetooth device address type */ + esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */ + esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */ } esp_ble_adv_params_t; /// Advertising data content, according to "Supplement to the Bluetooth Core Specification" typedef struct { - bool set_scan_rsp; /*!< Set this advertising data as scan response or not*/ - bool include_name; /*!< Advertising data include device name or not */ - bool include_txpower; /*!< Advertising data include TX power */ - int min_interval; /*!< Advertising data show advertising min interval */ - int max_interval; /*!< Advertising data show advertising max interval */ - int appearance; /*!< External appearance of device */ - uint16_t manufacturer_len; /*!< Manufacturer data length */ - uint8_t *p_manufacturer_data; /*!< Manufacturer data point */ - uint16_t service_data_len; /*!< Service data length */ - uint8_t *p_service_data; /*!< Service data point */ - uint16_t service_uuid_len; /*!< Service uuid length */ - uint8_t *p_service_uuid; /*!< Service uuid array point */ - uint8_t flag; /*!< Advertising flag of discovery mode, see BLE_ADV_DATA_FLAG detail */ + bool set_scan_rsp; /*!< Set this advertising data as scan response or not*/ + bool include_name; /*!< Advertising data include device name or not */ + bool include_txpower; /*!< Advertising data include TX power */ + int min_interval; /*!< Advertising data show advertising min interval */ + int max_interval; /*!< Advertising data show advertising max interval */ + int appearance; /*!< External appearance of device */ + uint16_t manufacturer_len; /*!< Manufacturer data length */ + uint8_t *p_manufacturer_data; /*!< Manufacturer data point */ + uint16_t service_data_len; /*!< Service data length */ + uint8_t *p_service_data; /*!< Service data point */ + uint16_t service_uuid_len; /*!< Service uuid length */ + uint8_t *p_service_uuid; /*!< Service uuid array point */ + uint8_t flag; /*!< Advertising flag of discovery mode, see BLE_ADV_DATA_FLAG detail */ } esp_ble_adv_data_t; /// Own BD address source of the device @@ -160,53 +162,53 @@ typedef enum { /// Ble scan type typedef enum { - BLE_SCAN_TYPE_PASSIVE = 0x0, /*!< Passive scan */ - BLE_SCAN_TYPE_ACTIVE = 0x1, /*!< Active scan */ + BLE_SCAN_TYPE_PASSIVE = 0x0, /*!< Passive scan */ + BLE_SCAN_TYPE_ACTIVE = 0x1, /*!< Active scan */ } esp_ble_scan_type_t; /// Ble scan filter type typedef enum { - BLE_SCAN_FILTER_ALLOW_ALL = 0x0, /*!< Accept all : - 1. advertisement packets except directed advertising packets not addressed to this device (default). */ + BLE_SCAN_FILTER_ALLOW_ALL = 0x0, /*!< Accept all : + 1. advertisement packets except directed advertising packets not addressed to this device (default). */ BLE_SCAN_FILTER_ALLOW_ONLY_WLST = 0x1, /*!< Accept only : - 1. advertisement packets from devices where the advertiser’s address is in the White list. - 2. Directed advertising packets which are not addressed for this device shall be ignored. */ + 1. advertisement packets from devices where the advertiser’s address is in the White list. + 2. Directed advertising packets which are not addressed for this device shall be ignored. */ BLE_SCAN_FILTER_ALLOW_UND_RPA_DIR = 0x2, /*!< Accept all : - 1. undirected advertisement packets, and - 2. directed advertising packets where the initiator address is a resolvable private address, and - 3. directed advertising packets addressed to this device. */ + 1. undirected advertisement packets, and + 2. directed advertising packets where the initiator address is a resolvable private address, and + 3. directed advertising packets addressed to this device. */ BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR = 0x3, /*!< Accept all : - 1. advertisement packets from devices where the advertiser’s address is in the White list, and - 2. directed advertising packets where the initiator address is a resolvable private address, and - 3. directed advertising packets addressed to this device.*/ + 1. advertisement packets from devices where the advertiser’s address is in the White list, and + 2. directed advertising packets where the initiator address is a resolvable private address, and + 3. directed advertising packets addressed to this device.*/ } esp_ble_scan_filter_t; /// Ble scan parameters typedef struct { - esp_ble_scan_type_t scan_type; /*!< Scan type */ - esp_ble_addr_type_t own_addr_type; /*!< Owner address type */ - esp_ble_scan_filter_t scan_filter_policy; /*!< Scan filter policy */ - uint16_t scan_interval; /*!< Scan interval. This is defined as the time interval from - when the Controller started its last LE scan until it begins the subsequent LE scan. - Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) - Time = N * 0.625 msec - Time Range: 2.5 msec to 10.24 seconds*/ - uint16_t scan_window; /*!< Scan window. The duration of the LE scan. LE_Scan_Window - shall be less than or equal to LE_Scan_Interval - Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) - Time = N * 0.625 msec - Time Range: 2.5 msec to 10240 msec */ + esp_ble_scan_type_t scan_type; /*!< Scan type */ + esp_ble_addr_type_t own_addr_type; /*!< Owner address type */ + esp_ble_scan_filter_t scan_filter_policy; /*!< Scan filter policy */ + uint16_t scan_interval; /*!< Scan interval. This is defined as the time interval from + when the Controller started its last LE scan until it begins the subsequent LE scan. + Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) + Time = N * 0.625 msec + Time Range: 2.5 msec to 10.24 seconds*/ + uint16_t scan_window; /*!< Scan window. The duration of the LE scan. LE_Scan_Window + shall be less than or equal to LE_Scan_Interval + Range: 0x0004 to 0x4000 Default: 0x0010 (10 ms) + Time = N * 0.625 msec + Time Range: 2.5 msec to 10240 msec */ } esp_ble_scan_params_t; /// Connection update parameters typedef struct { - esp_bd_addr_t bda; /*!< Bluetooth device address */ - uint16_t min_int; /*!< Min connection interval */ - uint16_t max_int; /*!< Max connection interval */ - uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ - uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. - Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec - Time Range: 100 msec to 32 seconds */ + esp_bd_addr_t bda; /*!< Bluetooth device address */ + uint16_t min_int; /*!< Min connection interval */ + uint16_t max_int; /*!< Max connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec + Time Range: 100 msec to 32 seconds */ } esp_ble_conn_update_params_t; /// Sub Event of ESP_GAP_BLE_SCAN_RESULT_EVT @@ -225,11 +227,11 @@ typedef enum { * result is scan response or advertising data or other */ typedef enum { - ESP_BLE_EVT_CONN_ADV = 0x00, /*!< Connectable undirected advertising (ADV_IND) */ - ESP_BLE_EVT_CONN_DIR_ADV = 0x01, /*!< Connectable directed advertising (ADV_DIRECT_IND) */ - ESP_BLE_EVT_DISC_ADV = 0x02, /*!< Scannable undirected advertising (ADV_SCAN_IND) */ - ESP_BLE_EVT_NON_CONN_ADV = 0x03, /*!< Non connectable undirected advertising (ADV_NONCONN_IND) */ - ESP_BLE_EVT_SCAN_RSP = 0x04, /*!< Scan Response (SCAN_RSP) */ + ESP_BLE_EVT_CONN_ADV = 0x00, /*!< Connectable undirected advertising (ADV_IND) */ + ESP_BLE_EVT_CONN_DIR_ADV = 0x01, /*!< Connectable directed advertising (ADV_DIRECT_IND) */ + ESP_BLE_EVT_DISC_ADV = 0x02, /*!< Scannable undirected advertising (ADV_SCAN_IND) */ + ESP_BLE_EVT_NON_CONN_ADV = 0x03, /*!< Non connectable undirected advertising (ADV_NONCONN_IND) */ + ESP_BLE_EVT_SCAN_RSP = 0x04, /*!< Scan Response (SCAN_RSP) */ } esp_ble_evt_type_t; /** @@ -240,34 +242,34 @@ typedef union { * @brief ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */ struct ble_adv_data_cmpl_evt_param { - esp_bt_status_t status; /*!< Indicate the set advertising data operation success status */ - } adv_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */ + esp_bt_status_t status; /*!< Indicate the set advertising data operation success status */ + } adv_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */ struct ble_scan_rsp_data_cmpl_evt_param { - esp_bt_status_t status; /*!< Indicate the set scan response data operation success status */ - } scan_rsp_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */ + esp_bt_status_t status; /*!< Indicate the set scan response data operation success status */ + } scan_rsp_data_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */ struct ble_scan_param_cmpl_evt_param { - esp_bt_status_t status; /*!< Indicate the set scan param operation success status */ - } scan_param_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */ + esp_bt_status_t status; /*!< Indicate the set scan param operation success status */ + } scan_param_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_SCAN_RESULT_EVT */ struct ble_scan_result_evt_param { - esp_gap_search_evt_t search_evt; /*!< Search event type */ - esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */ - esp_bt_dev_type_t dev_type; /*!< Device type */ - esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */ - esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */ - int rssi; /*!< Searched device's RSSI */ - uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX]; /*!< Received EIR */ - int flag; /*!< Advertising data flag bit */ - int num_resps; /*!< Scan result number */ - } scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */ + esp_gap_search_evt_t search_evt; /*!< Search event type */ + esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */ + esp_bt_dev_type_t dev_type; /*!< Device type */ + esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */ + esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */ + int rssi; /*!< Searched device's RSSI */ + uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */ + int flag; /*!< Advertising data flag bit */ + int num_resps; /*!< Scan result number */ + } scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */ } esp_ble_gap_cb_param_t; /** @@ -440,7 +442,8 @@ esp_err_t esp_ble_gap_set_device_name(const char *name); * @param[in] type - finding ADV data type * @param[out] length - return the length of ADV data not including type * - * @return pointer of ADV data + * @return - ESP_OK : success + * - other : failed * */ uint8_t *esp_ble_resolve_adv_data(uint8_t *adv_data, uint8_t type, uint8_t *length); diff --git a/components/bt/bluedroid/api/include/esp_gatt_defs.h b/components/bt/bluedroid/api/include/esp_gatt_defs.h index 7b119a62cd1..f8b2302111d 100644 --- a/components/bt/bluedroid/api/include/esp_gatt_defs.h +++ b/components/bt/bluedroid/api/include/esp_gatt_defs.h @@ -22,106 +22,141 @@ extern "C" { #endif /// GATT INVALID UUID -#define ESP_GATT_ILLEGAL_UUID 0 +#define ESP_GATT_ILLEGAL_UUID 0 +/// GATT INVALID HANDLE +#define ESP_GATT_ILLEGAL_HANDLE 0 +/// GATT attribute max handle +#define ESP_GATT_ATTR_HANDLE_MAX 100 + /**@{ * All "ESP_GATT_UUID_xxx" is attribute types */ -#define ESP_GATT_UUID_PRI_SERVICE 0x2800 -#define ESP_GATT_UUID_SEC_SERVICE 0x2801 -#define ESP_GATT_UUID_INCLUDE_SERVICE 0x2802 -#define ESP_GATT_UUID_CHAR_DECLARE 0x2803 /* Characteristic Declaration*/ +#define ESP_GATT_UUID_IMMEDIATE_ALERT_SVC 0x1802 /* Immediate alert Service*/ +#define ESP_GATT_UUID_LINK_LOSS_SVC 0x1803 /* Link Loss Service*/ +#define ESP_GATT_UUID_TX_POWER_SVC 0x1804 /* TX Power Service*/ +#define ESP_GATT_UUID_CURRENT_TIME_SVC 0x1805 /* Current Time Service Service*/ +#define ESP_GATT_UUID_REF_TIME_UPDATE_SVC 0x1806 /* Reference Time Update Service*/ +#define ESP_GATT_UUID_NEXT_DST_CHANGE_SVC 0x1807 /* Next DST Change Service*/ +#define ESP_GATT_UUID_GLUCOSE_SVC 0x1808 /* Glucose Service*/ +#define ESP_GATT_UUID_HEALTH_THERMOM_SVC 0x1809 /* Health Thermometer Service*/ +#define ESP_GATT_UUID_DEVICE_INFO_SVC 0x180A /* Device Information Service*/ +#define ESP_GATT_UUID_HEART_RATE_SVC 0x180D /* Heart Rate Service*/ +#define ESP_GATT_UUID_PHONE_ALERT_STATUS_SVC 0x180E /* Phone Alert Status Service*/ +#define ESP_GATT_UUID_BATTERY_SERVICE_SVC 0x180F /* Battery Service*/ +#define ESP_GATT_UUID_BLOOD_PRESSURE_SVC 0x1810 /* Blood Pressure Service*/ +#define ESP_GATT_UUID_ALERT_NTF_SVC 0x1811 /* Alert Notification Service*/ +#define ESP_GATT_UUID_HID_SVC 0x1812 /* HID Service*/ +#define ESP_GATT_UUID_SCAN_PARAMETERS_SVC 0x1813 /* Scan Parameters Service*/ +#define ESP_GATT_UUID_RUNNING_SPEED_CADENCE_SVC 0x1814 /* Running Speed and Cadence Service*/ +#define ESP_GATT_UUID_CYCLING_SPEED_CADENCE_SVC 0x1816 /* Cycling Speed and Cadence Service*/ +#define ESP_GATT_UUID_CYCLING_POWER_SVC 0x1818 /* Cycling Power Service*/ +#define ESP_GATT_UUID_LOCATION_AND_NAVIGATION_SVC 0x1819 /* Location and Navigation Service*/ +#define ESP_GATT_UUID_USER_DATA_SVC 0x181C /* User Data Service*/ +#define ESP_GATT_UUID_WEIGHT_SCALE_SVC 0x181D /* Weight Scale Service*/ -#define ESP_GATT_UUID_CHAR_EXT_PROP 0x2900 /* Characteristic Extended Properties */ -#define ESP_GATT_UUID_CHAR_DESCRIPTION 0x2901 /* Characteristic User Description*/ -#define ESP_GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ -#define ESP_GATT_UUID_CHAR_SRVR_CONFIG 0x2903 /* Server Characteristic Configuration */ -#define ESP_GATT_UUID_CHAR_PRESENT_FORMAT 0x2904 /* Characteristic Presentation Format*/ -#define ESP_GATT_UUID_CHAR_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ -#define ESP_GATT_UUID_CHAR_VALID_RANGE 0x2906 /* Characteristic Valid Range */ -#define ESP_GATT_UUID_EXT_RPT_REF_DESCR 0x2907 -#define ESP_GATT_UUID_RPT_REF_DESCR 0x2908 +#define ESP_GATT_UUID_PRI_SERVICE 0x2800 +#define ESP_GATT_UUID_SEC_SERVICE 0x2801 +#define ESP_GATT_UUID_INCLUDE_SERVICE 0x2802 +#define ESP_GATT_UUID_CHAR_DECLARE 0x2803 /* Characteristic Declaration*/ + +#define ESP_GATT_UUID_CHAR_EXT_PROP 0x2900 /* Characteristic Extended Properties */ +#define ESP_GATT_UUID_CHAR_DESCRIPTION 0x2901 /* Characteristic User Description*/ +#define ESP_GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ +#define ESP_GATT_UUID_CHAR_SRVR_CONFIG 0x2903 /* Server Characteristic Configuration */ +#define ESP_GATT_UUID_CHAR_PRESENT_FORMAT 0x2904 /* Characteristic Presentation Format*/ +#define ESP_GATT_UUID_CHAR_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ +#define ESP_GATT_UUID_CHAR_VALID_RANGE 0x2906 /* Characteristic Valid Range */ +#define ESP_GATT_UUID_EXT_RPT_REF_DESCR 0x2907 +#define ESP_GATT_UUID_RPT_REF_DESCR 0x2908 /* GAP Profile Attributes */ -#define ESP_GATT_UUID_GAP_DEVICE_NAME 0x2A00 -#define ESP_GATT_UUID_GAP_ICON 0x2A01 -#define ESP_GATT_UUID_GAP_PREF_CONN_PARAM 0x2A04 -#define ESP_GATT_UUID_GAP_CENTRAL_ADDR_RESOL 0x2AA6 +#define ESP_GATT_UUID_GAP_DEVICE_NAME 0x2A00 +#define ESP_GATT_UUID_GAP_ICON 0x2A01 +#define ESP_GATT_UUID_GAP_PREF_CONN_PARAM 0x2A04 +#define ESP_GATT_UUID_GAP_CENTRAL_ADDR_RESOL 0x2AA6 /* Attribute Profile Attribute UUID */ -#define ESP_GATT_UUID_GATT_SRV_CHGD 0x2A05 +#define ESP_GATT_UUID_GATT_SRV_CHGD 0x2A05 /* Link ESP_Loss Service */ -#define ESP_GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ -#define ESP_GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ +#define ESP_GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ +#define ESP_GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ /* Current Time Service */ -#define ESP_GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ -#define ESP_GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ -#define ESP_GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ +#define ESP_GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ +#define ESP_GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ +#define ESP_GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ /* Network availability Profile */ -#define ESP_GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ -#define ESP_GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ +#define ESP_GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ +#define ESP_GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ /* Phone alert */ -#define ESP_GATT_UUID_ALERT_STATUS 0x2A3F /* alert status */ -#define ESP_GATT_UUID_RINGER_CP 0x2A40 /* ringer control point */ -#define ESP_GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ +#define ESP_GATT_UUID_ALERT_STATUS 0x2A3F /* alert status */ +#define ESP_GATT_UUID_RINGER_CP 0x2A40 /* ringer control point */ +#define ESP_GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ /* Glucose Service */ -#define ESP_GATT_UUID_GM_MEASUREMENT 0x2A18 -#define ESP_GATT_UUID_GM_CONTEXT 0x2A34 -#define ESP_GATT_UUID_GM_CONTROL_POINT 0x2A52 -#define ESP_GATT_UUID_GM_FEATURE 0x2A51 +#define ESP_GATT_UUID_GM_MEASUREMENT 0x2A18 +#define ESP_GATT_UUID_GM_CONTEXT 0x2A34 +#define ESP_GATT_UUID_GM_CONTROL_POINT 0x2A52 +#define ESP_GATT_UUID_GM_FEATURE 0x2A51 /* device information characteristic */ -#define ESP_GATT_UUID_SYSTEM_ID 0x2A23 -#define ESP_GATT_UUID_MODEL_NUMBER_STR 0x2A24 -#define ESP_GATT_UUID_SERIAL_NUMBER_STR 0x2A25 -#define ESP_GATT_UUID_FW_VERSION_STR 0x2A26 -#define ESP_GATT_UUID_HW_VERSION_STR 0x2A27 -#define ESP_GATT_UUID_SW_VERSION_STR 0x2A28 -#define ESP_GATT_UUID_MANU_NAME 0x2A29 -#define ESP_GATT_UUID_IEEE_DATA 0x2A2A -#define ESP_GATT_UUID_PNP_ID 0x2A50 +#define ESP_GATT_UUID_SYSTEM_ID 0x2A23 +#define ESP_GATT_UUID_MODEL_NUMBER_STR 0x2A24 +#define ESP_GATT_UUID_SERIAL_NUMBER_STR 0x2A25 +#define ESP_GATT_UUID_FW_VERSION_STR 0x2A26 +#define ESP_GATT_UUID_HW_VERSION_STR 0x2A27 +#define ESP_GATT_UUID_SW_VERSION_STR 0x2A28 +#define ESP_GATT_UUID_MANU_NAME 0x2A29 +#define ESP_GATT_UUID_IEEE_DATA 0x2A2A +#define ESP_GATT_UUID_PNP_ID 0x2A50 /* HID characteristics */ -#define ESP_GATT_UUID_HID_INFORMATION 0x2A4A -#define ESP_GATT_UUID_HID_REPORT_MAP 0x2A4B -#define ESP_GATT_UUID_HID_CONTROL_POINT 0x2A4C -#define ESP_GATT_UUID_HID_REPORT 0x2A4D -#define ESP_GATT_UUID_HID_PROTO_MODE 0x2A4E -#define ESP_GATT_UUID_HID_BT_KB_INPUT 0x2A22 -#define ESP_GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 -#define ESP_GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 +#define ESP_GATT_UUID_HID_INFORMATION 0x2A4A +#define ESP_GATT_UUID_HID_REPORT_MAP 0x2A4B +#define ESP_GATT_UUID_HID_CONTROL_POINT 0x2A4C +#define ESP_GATT_UUID_HID_REPORT 0x2A4D +#define ESP_GATT_UUID_HID_PROTO_MODE 0x2A4E +#define ESP_GATT_UUID_HID_BT_KB_INPUT 0x2A22 +#define ESP_GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 +#define ESP_GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 + + /// Heart Rate Measurement +#define ESP_GATT_HEART_RATE_MEAS 0x2A37 +/// Body Sensor Location +#define ESP_GATT_BODY_SENSOR_LOCATION 0x2A38 +/// Heart Rate Control Point +#define ESP_GATT_HEART_RATE_CNTL_POINT 0x2A39 /* Battery Service characteristics */ -#define ESP_GATT_UUID_BATTERY_LEVEL 0x2A19 +#define ESP_GATT_UUID_BATTERY_LEVEL 0x2A19 /* Sensor Service */ -#define ESP_GATT_UUID_SC_CONTROL_POINT 0x2A55 -#define ESP_GATT_UUID_SENSOR_LOCATION 0x2A5D +#define ESP_GATT_UUID_SC_CONTROL_POINT 0x2A55 +#define ESP_GATT_UUID_SENSOR_LOCATION 0x2A5D /* Runners speed and cadence service */ -#define ESP_GATT_UUID_RSC_MEASUREMENT 0x2A53 -#define ESP_GATT_UUID_RSC_FEATURE 0x2A54 +#define ESP_GATT_UUID_RSC_MEASUREMENT 0x2A53 +#define ESP_GATT_UUID_RSC_FEATURE 0x2A54 /* Cycling speed and cadence service */ -#define ESP_GATT_UUID_CSC_MEASUREMENT 0x2A5B -#define ESP_GATT_UUID_CSC_FEATURE 0x2A5C +#define ESP_GATT_UUID_CSC_MEASUREMENT 0x2A5B +#define ESP_GATT_UUID_CSC_FEATURE 0x2A5C /* Scan ESP_Parameter characteristics */ -#define ESP_GATT_UUID_SCAN_INT_WINDOW 0x2A4F -#define ESP_GATT_UUID_SCAN_REFRESH 0x2A31 +#define ESP_GATT_UUID_SCAN_INT_WINDOW 0x2A4F +#define ESP_GATT_UUID_SCAN_REFRESH 0x2A31 /** * @} */ /// Attribute write data type from the client typedef enum { - ESP_GATT_PREP_WRITE_CANCEL = 0x00, /*!< Prepare write cancel */ - ESP_GATT_PREP_WRITE_EXEC = 0x01, /*!< Prepare write execute */ + ESP_GATT_PREP_WRITE_CANCEL = 0x00, /*!< Prepare write cancel */ + ESP_GATT_PREP_WRITE_EXEC = 0x01, /*!< Prepare write execute */ } esp_gatt_prep_write_type; /** @@ -178,23 +213,23 @@ typedef enum { * @brief Gatt Connection reason enum */ typedef enum { - ESP_GATT_CONN_UNKNOWN = 0, /*!< Gatt connection unknown */ - ESP_GATT_CONN_L2C_FAILURE = 1, /*!< General L2cap failure */ - ESP_GATT_CONN_TIMEOUT = 0x08, /*!< Connection timeout */ - ESP_GATT_CONN_TERMINATE_PEER_USER = 0x13, /*!< Connection terminate by peer user */ - ESP_GATT_CONN_TERMINATE_LOCAL_HOST = 0x16, /*!< Connectionterminated by local host */ - ESP_GATT_CONN_FAIL_ESTABLISH = 0x3e, /*!< Connection fail to establish */ - ESP_GATT_CONN_LMP_TIMEOUT = 0x22, /*!< Connection fail for LMP response tout */ - ESP_GATT_CONN_CONN_CANCEL = 0x0100, /*!< L2CAP connection cancelled */ - ESP_GATT_CONN_NONE = 0x0101 /*!< No connection to cancel */ + ESP_GATT_CONN_UNKNOWN = 0, /*!< Gatt connection unknown */ + ESP_GATT_CONN_L2C_FAILURE = 1, /*!< General L2cap failure */ + ESP_GATT_CONN_TIMEOUT = 0x08, /*!< Connection timeout */ + ESP_GATT_CONN_TERMINATE_PEER_USER = 0x13, /*!< Connection terminate by peer user */ + ESP_GATT_CONN_TERMINATE_LOCAL_HOST = 0x16, /*!< Connectionterminated by local host */ + ESP_GATT_CONN_FAIL_ESTABLISH = 0x3e, /*!< Connection fail to establish */ + ESP_GATT_CONN_LMP_TIMEOUT = 0x22, /*!< Connection fail for LMP response tout */ + ESP_GATT_CONN_CONN_CANCEL = 0x0100, /*!< L2CAP connection cancelled */ + ESP_GATT_CONN_NONE = 0x0101 /*!< No connection to cancel */ } esp_gatt_conn_reason_t; /** * @brief Gatt id, include uuid and instance id */ typedef struct { - esp_bt_uuid_t uuid; /*!< UUID */ - uint8_t inst_id; /*!< Instance id */ + esp_bt_uuid_t uuid; /*!< UUID */ + uint8_t inst_id; /*!< Instance id */ } __attribute__((packed)) esp_gatt_id_t; /** @@ -202,19 +237,19 @@ typedef struct { * (uuid and instance id) and primary flag */ typedef struct { - esp_gatt_id_t id; /*!< Gatt id, include uuid and instance */ - bool is_primary; /*!< This service is primary or not */ + esp_gatt_id_t id; /*!< Gatt id, include uuid and instance */ + bool is_primary; /*!< This service is primary or not */ } __attribute__((packed)) esp_gatt_srvc_id_t; /** * @brief Gatt authentication request type */ typedef enum { - ESP_GATT_AUTH_REQ_NONE = 0, - ESP_GATT_AUTH_REQ_NO_MITM = 1, /* unauthenticated encryption */ - ESP_GATT_AUTH_REQ_MITM = 2, /* authenticated encryption */ - ESP_GATT_AUTH_REQ_SIGNED_NO_MITM = 3, - ESP_GATT_AUTH_REQ_SIGNED_MITM = 4, + ESP_GATT_AUTH_REQ_NONE = 0, + ESP_GATT_AUTH_REQ_NO_MITM = 1, /* unauthenticated encryption */ + ESP_GATT_AUTH_REQ_MITM = 2, /* authenticated encryption */ + ESP_GATT_AUTH_REQ_SIGNED_NO_MITM = 3, + ESP_GATT_AUTH_REQ_SIGNED_MITM = 4, } esp_gatt_auth_req_t; /** @@ -246,32 +281,109 @@ typedef enum { /// GATT maximum attribute length #define ESP_GATT_MAX_ATTR_LEN 600 //as same as GATT_MAX_ATTR_LEN + +/** + * @brief Attribute description (used to create database) + */ + typedef struct + { + esp_bt_uuid_t uuid; /*!< Element UUID */ + uint16_t perm; /*!< Attribute permission */ + uint16_t max_length; /*!< Maximum length of the element*/ + uint16_t length; /*!< Current length of the element*/ + uint8_t* value; /*!< Element value array*/ + }esp_attr_desc_t; + + +/** + * @brief attribute auto respose flag + */ +typedef struct +{ +#define ESP_GATT_RSP_BY_APP 0 +#define ESP_GATT_AUTO_RSP 1 + uint8_t auto_rsp; /*!< need the app response to the client if need_rsp set to 1*/ +}esp_attr_control_t; + + +/** + * @brief attribute type added to the gatt server database + */ +typedef struct +{ + esp_attr_control_t attr_control; /*!< The attribue control type*/ + esp_attr_desc_t att_desc; /*!< The attribue type*/ +}esp_gatts_attr_db_t; + + +/** + * @brief set the attribute value type + */ +typedef struct +{ + uint16_t attr_max_len; /*!< attribute max value length */ + uint16_t attr_len; /*!< attribute current value length */ + uint8_t *attr_value; /*!< the pointer to attribute value */ +}esp_attr_value_t; + + +/** + * @brief Gatt include service entry element + */ +typedef struct +{ + uint16_t start_hdl; /*!< Gatt start handle value of included service */ + uint16_t end_hdl; /*!< Gatt end handle value of included service */ + uint16_t uuid; /*!< Gatt attribute value UUID of included service */ +}esp_gatts_incl_svc_desc_t; /*!< Gatt include service entry element */ + +/** + * @brief Gatt include 128 bit service entry element + */ +typedef struct +{ + uint16_t start_hdl; /*!< Gatt start handle value of included 128 bit service */ + uint16_t end_hdl; /*!< Gatt end handle value of included 128 bit service */ +}esp_gatts_incl128_svc_desc_t; /*!< Gatt include 128 bit service entry element */ + + +/** + * @brief Gatt characteristic entry element + */ +typedef struct +{ + uint8_t prop; /*!< Gatt attribute properties */ + uint16_t attr_hdl; /*!< Gatt attribute handle */ + esp_bt_uuid_t attr_uuid; /*!< Gatt attribute uuid typedle */ +}esp_gatts_char_desc_t; /*!< Gatt characteristic value descriptor */ + + /// Gatt attribute value typedef struct { - uint8_t value[ESP_GATT_MAX_ATTR_LEN]; /*!< Gatt attribute value */ - uint16_t handle; /*!< Gatt attribute handle */ - uint16_t offset; /*!< Gatt attribute value offset */ - uint16_t len; /*!< Gatt attribute value length */ - uint8_t auth_req; /*!< Gatt authentication request */ + uint8_t value[ESP_GATT_MAX_ATTR_LEN]; /*!< Gatt attribute value */ + uint16_t handle; /*!< Gatt attribute handle */ + uint16_t offset; /*!< Gatt attribute value offset */ + uint16_t len; /*!< Gatt attribute value length */ + uint8_t auth_req; /*!< Gatt authentication request */ } esp_gatt_value_t; /// GATT remote read request response type typedef union { - esp_gatt_value_t attr_value; /*!< Gatt attribute structure */ - uint16_t handle; /*!< Gatt attribute handle */ + esp_gatt_value_t attr_value; /*!< Gatt attribute structure */ + uint16_t handle; /*!< Gatt attribute handle */ } esp_gatt_rsp_t; /** * @brief Gatt write type */ typedef enum { - ESP_GATT_WRITE_TYPE_NO_RSP = 1, /*!< Gatt write attribute need no response */ - ESP_GATT_WRITE_TYPE_RSP, /*!< Gatt write attribute need remote response */ + ESP_GATT_WRITE_TYPE_NO_RSP = 1, /*!< Gatt write attribute need no response */ + ESP_GATT_WRITE_TYPE_RSP, /*!< Gatt write attribute need remote response */ } esp_gatt_write_type_t; #define ESP_GATT_IF_NONE 0xff /*!< If callback report gattc_if/gatts_if as this macro, means this event is not correspond to any app */ -typedef uint8_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ +typedef uint8_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ #ifdef __cplusplus } diff --git a/components/bt/bluedroid/api/include/esp_gatts_api.h b/components/bt/bluedroid/api/include/esp_gatts_api.h index d0fc055a7fd..86a539597ac 100644 --- a/components/bt/bluedroid/api/include/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/esp_gatts_api.h @@ -25,29 +25,31 @@ extern "C" { /// GATT Server callback function events typedef enum { - ESP_GATTS_REG_EVT = 0, /*!< When register application id, the event comes */ - ESP_GATTS_READ_EVT = 1, /*!< When gatt client request read operation, the event comes */ - ESP_GATTS_WRITE_EVT = 2, /*!< When gatt client request write operation, the event comes */ - ESP_GATTS_EXEC_WRITE_EVT = 3, /*!< When gatt client request execute write, the event comes */ - ESP_GATTS_MTU_EVT = 4, /*!< When set mtu complete, the event comes */ - ESP_GATTS_CONF_EVT = 5, /*!< When receive confirm, the event comes */ - ESP_GATTS_UNREG_EVT = 6, /*!< When unregister application id, the event comes */ - ESP_GATTS_CREATE_EVT = 7, /*!< When create service complete, the event comes */ - ESP_GATTS_ADD_INCL_SRVC_EVT = 8, /*!< When add included service complete, the event comes */ - ESP_GATTS_ADD_CHAR_EVT = 9, /*!< When add characteristic complete, the event comes */ - ESP_GATTS_ADD_CHAR_DESCR_EVT = 10, /*!< When add descriptor complete, the event comes */ - ESP_GATTS_DELETE_EVT = 11, /*!< When delete service complete, the event comes */ - ESP_GATTS_START_EVT = 12, /*!< When start service complete, the event comes */ - ESP_GATTS_STOP_EVT = 13, /*!< When stop service complete, the event comes */ - ESP_GATTS_CONNECT_EVT = 14, /*!< When gatt client connect, the event comes */ - ESP_GATTS_DISCONNECT_EVT = 15, /*!< When gatt client disconnect, the event comes */ - ESP_GATTS_OPEN_EVT = 16, /*!< When connect to peer, the event comes */ - ESP_GATTS_CANCEL_OPEN_EVT = 17, /*!< When disconnect from peer, the event comes */ - ESP_GATTS_CLOSE_EVT = 18, /*!< When gatt server close, the event comes */ - ESP_GATTS_LISTEN_EVT = 19, /*!< When gatt listen to be connected the event comes */ - ESP_GATTS_CONGEST_EVT = 20, /*!< When congest happen, the event comes */ - /* following is extra event */ - ESP_GATTS_RESPONSE_EVT = 21, /*!< When gatt send response complete, the event comes */ + ESP_GATTS_REG_EVT = 0, /*!< When register application id, the event comes */ + ESP_GATTS_READ_EVT = 1, /*!< When gatt client request read operation, the event comes */ + ESP_GATTS_WRITE_EVT = 2, /*!< When gatt client request write operation, the event comes */ + ESP_GATTS_EXEC_WRITE_EVT = 3, /*!< When gatt client request execute write, the event comes */ + ESP_GATTS_MTU_EVT = 4, /*!< When set mtu complete, the event comes */ + ESP_GATTS_CONF_EVT = 5, /*!< When receive confirm, the event comes */ + ESP_GATTS_UNREG_EVT = 6, /*!< When unregister application id, the event comes */ + ESP_GATTS_CREATE_EVT = 7, /*!< When create service complete, the event comes */ + ESP_GATTS_ADD_INCL_SRVC_EVT = 8, /*!< When add included service complete, the event comes */ + ESP_GATTS_ADD_CHAR_EVT = 9, /*!< When add characteristic complete, the event comes */ + ESP_GATTS_ADD_CHAR_DESCR_EVT = 10, /*!< When add descriptor complete, the event comes */ + ESP_GATTS_DELETE_EVT = 11, /*!< When delete service complete, the event comes */ + ESP_GATTS_START_EVT = 12, /*!< When start service complete, the event comes */ + ESP_GATTS_STOP_EVT = 13, /*!< When stop service complete, the event comes */ + ESP_GATTS_CONNECT_EVT = 14, /*!< When gatt client connect, the event comes */ + ESP_GATTS_DISCONNECT_EVT = 15, /*!< When gatt client disconnect, the event comes */ + ESP_GATTS_OPEN_EVT = 16, /*!< When connect to peer, the event comes */ + ESP_GATTS_CANCEL_OPEN_EVT = 17, /*!< When disconnect from peer, the event comes */ + ESP_GATTS_CLOSE_EVT = 18, /*!< When gatt server close, the event comes */ + ESP_GATTS_LISTEN_EVT = 19, /*!< When gatt listen to be connected the event comes */ + ESP_GATTS_CONGEST_EVT = 20, /*!< When congest happen, the event comes */ + /* following is extra event */ + ESP_GATTS_RESPONSE_EVT = 21, /*!< When gatt send response complete, the event comes */ + ESP_GATTS_CREAT_ATTR_TAB_EVT = 22, + ESP_GATTS_SET_ATTR_VAL_EVT = 23, } esp_gatts_cb_event_t; /** @@ -58,64 +60,64 @@ typedef union { * @brief ESP_GATTS_REG_EVT */ struct gatts_reg_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t app_id; /*!< Application id which input in register API */ - } reg; /*!< Gatt server callback param of ESP_GATTS_REG_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t app_id; /*!< Application id which input in register API */ + } reg; /*!< Gatt server callback param of ESP_GATTS_REG_EVT */ /** * @brief ESP_GATTS_READ_EVT */ struct gatts_read_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint32_t trans_id; /*!< Transfer id */ - esp_bd_addr_t bda; /*!< The bluetooth device address which been read */ - uint16_t handle; /*!< The attribute handle */ - uint16_t offset; /*!< Offset of the value, if the value is too long */ - bool is_long; /*!< The value is too long or not */ - } read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been read */ + uint16_t handle; /*!< The attribute handle */ + uint16_t offset; /*!< Offset of the value, if the value is too long */ + bool is_long; /*!< The value is too long or not */ + } read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */ /** * @brief ESP_GATTS_WRITE_EVT */ struct gatts_write_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint32_t trans_id; /*!< Transfer id */ - esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ - uint16_t handle; /*!< The attribute handle */ - uint16_t offset; /*!< Offset of the value, if the value is too long */ - bool need_rsp; /*!< The write operation need to do response */ - bool is_prep; /*!< This write operation is prepare write */ - uint16_t len; /*!< The write attribute value length */ - uint8_t *value; /*!< The write attribute value */ - } write; /*!< Gatt server callback param of ESP_GATTS_WRITE_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ + uint16_t handle; /*!< The attribute handle */ + uint16_t offset; /*!< Offset of the value, if the value is too long */ + bool need_rsp; /*!< The write operation need to do response */ + bool is_prep; /*!< This write operation is prepare write */ + uint16_t len; /*!< The write attribute value length */ + uint8_t *value; /*!< The write attribute value */ + } write; /*!< Gatt server callback param of ESP_GATTS_WRITE_EVT */ /** * @brief ESP_GATTS_EXEC_WRITE_EVT */ struct gatts_exec_write_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint32_t trans_id; /*!< Transfer id */ - esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ -#define ESP_GATT_PREP_WRITE_CANCEL 0x00 -#define ESP_GATT_PREP_WRITE_EXEC 0x01 - uint8_t exec_write_flag; /*!< Execute write flag */ - } exec_write; /*!< Gatt server callback param of ESP_GATTS_EXEC_WRITE_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint32_t trans_id; /*!< Transfer id */ + esp_bd_addr_t bda; /*!< The bluetooth device address which been written */ +#define ESP_GATT_PREP_WRITE_CANCEL 0x00 /*!< Prepare write flag to indicate cancel prepare write */ +#define ESP_GATT_PREP_WRITE_EXEC 0x01 /*!< Prepare write flag to indicate execute prepare write */ + uint8_t exec_write_flag; /*!< Execute write flag */ + } exec_write; /*!< Gatt server callback param of ESP_GATTS_EXEC_WRITE_EVT */ /** * @brief ESP_GATTS_MTU_EVT */ struct gatts_mtu_evt_param { - uint16_t conn_id; /*!< Connection id */ - uint16_t mtu; /*!< MTU size */ - } mtu; /*!< Gatt server callback param of ESP_GATTS_MTU_EVT */ + uint16_t conn_id; /*!< Connection id */ + uint16_t mtu; /*!< MTU size */ + } mtu; /*!< Gatt server callback param of ESP_GATTS_MTU_EVT */ /** * @brief ESP_GATTS_CONF_EVT */ struct gatts_conf_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t conn_id; /*!< Connection id */ - } conf; /*!< Gatt server callback param of ESP_GATTS_CONF_EVT (confirm) */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t conn_id; /*!< Connection id */ + } conf; /*!< Gatt server callback param of ESP_GATTS_CONF_EVT (confirm) */ /** * @brief ESP_GATTS_UNREG_EVT @@ -125,81 +127,81 @@ typedef union { * @brief ESP_GATTS_CREATE_EVT */ struct gatts_create_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t service_handle; /*!< Service attribute handle */ - esp_gatt_srvc_id_t service_id; /*!< Service id, include service uuid and other information */ - } create; /*!< Gatt server callback param of ESP_GATTS_CREATE_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_gatt_srvc_id_t service_id; /*!< Service id, include service uuid and other information */ + } create; /*!< Gatt server callback param of ESP_GATTS_CREATE_EVT */ /** * @brief ESP_GATTS_ADD_INCL_SRVC_EVT */ struct gatts_add_incl_srvc_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t attr_handle; /*!< Included service attribute handle */ - uint16_t service_handle; /*!< Service attribute handle */ - } add_incl_srvc; /*!< Gatt server callback param of ESP_GATTS_ADD_INCL_SRVC_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Included service attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + } add_incl_srvc; /*!< Gatt server callback param of ESP_GATTS_ADD_INCL_SRVC_EVT */ /** * @brief ESP_GATTS_ADD_CHAR_EVT */ struct gatts_add_char_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t attr_handle; /*!< Characteristic attribute handle */ - uint16_t service_handle; /*!< Service attribute handle */ - esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ - } add_char; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Characteristic attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ + } add_char; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_EVT */ /** * @brief ESP_GATTS_ADD_CHAR_DESCR_EVT */ struct gatts_add_char_descr_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t attr_handle; /*!< Descriptor attribute handle */ - uint16_t service_handle; /*!< Service attribute handle */ - esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ - } add_char_descr; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_DESCR_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t attr_handle; /*!< Descriptor attribute handle */ + uint16_t service_handle; /*!< Service attribute handle */ + esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */ + } add_char_descr; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_DESCR_EVT */ /** * @brief ESP_GATTS_DELETE_EVT */ struct gatts_delete_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t service_handle; /*!< Service attribute handle */ - } del; /*!< Gatt server callback param of ESP_GATTS_DELETE_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } del; /*!< Gatt server callback param of ESP_GATTS_DELETE_EVT */ /** * @brief ESP_GATTS_START_EVT */ struct gatts_start_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t service_handle; /*!< Service attribute handle */ - } start; /*!< Gatt server callback param of ESP_GATTS_START_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } start; /*!< Gatt server callback param of ESP_GATTS_START_EVT */ /** * @brief ESP_GATTS_STOP_EVT */ struct gatts_stop_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t service_handle; /*!< Service attribute handle */ - } stop; /*!< Gatt server callback param of ESP_GATTS_STOP_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t service_handle; /*!< Service attribute handle */ + } stop; /*!< Gatt server callback param of ESP_GATTS_STOP_EVT */ /** * @brief ESP_GATTS_CONNECT_EVT */ 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 */ + 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 */ /** * @brief ESP_GATTS_DISCONNECT_EVT */ 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 */ - } disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */ + 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 */ + } disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */ /** * @brief ESP_GATTS_OPEN_EVT @@ -217,17 +219,38 @@ typedef union { * @brief ESP_GATTS_CONGEST_EVT */ struct gatts_congest_evt_param { - uint16_t conn_id; /*!< Connection id */ - bool congested; /*!< Congested or not */ - } congest; /*!< Gatt server callback param of ESP_GATTS_CONGEST_EVT */ + uint16_t conn_id; /*!< Connection id */ + bool congested; /*!< Congested or not */ + } congest; /*!< Gatt server callback param of ESP_GATTS_CONGEST_EVT */ /** * @brief ESP_GATTS_RESPONSE_EVT */ struct gatts_rsp_evt_param { - esp_gatt_status_t status; /*!< Operation status */ - uint16_t handle; /*!< Attribute handle which send response */ - } rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */ + esp_gatt_status_t status; /*!< Operation status */ + uint16_t handle; /*!< Attribute handle which send response */ + } rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */ + + /** + * @brief ESP_GATTS_CREAT_ATTR_TAB_EVT + */ + struct gatts_add_attr_tab_evt_param{ + esp_gatt_status_t status; /*!< Operation status */ + esp_bt_uuid_t svc_uuid; /*!< Service uuid type */ + uint16_t num_handle; /*!< The number of the attribute handle to be added to the gatts database */ + uint16_t *handles; /*!< The number to the handles */ + } add_attr_tab; /*!< Gatt server callback param of ESP_GATTS_CREAT_ATTR_TAB_EVT */ + + + /** + * @brief ESP_GATTS_SET_ATTR_VAL_EVT + */ + struct gatts_set_attr_val_evt_param{ + uint16_t srvc_handle; /*!< The service handle */ + uint16_t attr_handle; /*!< The attribute handle */ + esp_gatt_status_t status; /*!< Operation status*/ + } set_attr_val; /*!< Gatt server callback param of ESP_GATTS_SET_ATTR_VAL_EVT */ + } esp_ble_gatts_cb_param_t; /** @@ -294,7 +317,22 @@ esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, esp_gatt_srvc_id_t *service_id, uint16_t num_handle); - +/** + * @brief Create a service attribute tab. + * @param[in] gatts_attr_db: the pointer to the service attr tab + * @param[in] gatts_if: GATT server access interface + * @param[in] max_nb_attr: the number of attribute to be added to the service database. + * @param[in] srvc_inst_id: the instance id of the service + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint8_t max_nb_attr, + uint8_t srvc_inst_id); /** * @brief This function is called to add an included service. After included * service is included, a callback event BTA_GATTS_ADD_INCL_SRVC_EVT @@ -321,6 +359,8 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i * @param[in] char_uuid : Characteristic UUID. * @param[in] perm : Characteristic value declaration attribute permission. * @param[in] property : Characteristic Properties + * @param[in] char_val : Characteristic value + * @param[in] control : attribute response control byte * * @return * - ESP_OK : success @@ -328,7 +368,8 @@ esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t i * */ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid, - esp_gatt_perm_t perm, esp_gatt_char_prop_t property); + esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val, + esp_attr_control_t *control); /** @@ -340,15 +381,17 @@ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_ * be added. * @param[in] perm: descriptor access permission. * @param[in] descr_uuid: descriptor UUID. - * + * @param[in] char_descr_val : Characteristic descriptor value + * @param[in] control : attribute response control byte * @return * - ESP_OK : success * - other : failed * */ esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, - esp_bt_uuid_t *descr_uuid, - esp_gatt_perm_t perm); + esp_bt_uuid_t *descr_uuid, + esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val, + esp_attr_control_t *control); @@ -432,6 +475,35 @@ esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, esp_gatt_status_t status, esp_gatt_rsp_t *rsp); +/** + * @brief This function is called to set the attribute value by the application + * + * @param[in] attr_handle: the attribute handle which to be set + * @param[in] length: the value length + * @param[in] value: the pointer to the attribute value + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, const uint8_t *value); + +/** + * @brief Retrieve attribute value + * + * @param[in] attr_handle: Attribute handle. + * @param[out] length: pointer to the attribute value length + * @param[out] value: Pointer to attribute value payload, the value cannot be modified by user + * + * @return + * - ESP_OK : success + * - other : failed + * + */ +esp_err_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value); + + /** * @brief Open a direct open connection or add a background auto connection * diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index 04bb3ee7cba..9dd2ea4039e 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -34,7 +34,6 @@ #include "bta_gatts_int.h" #include "bta_gatts_co.h" #include "btm_ble_api.h" -// #include "btif/include/btif_debug_conn.h" #include static void bta_gatts_nv_save_cback(BOOLEAN is_saved, tGATTS_HNDL_RANGE *p_hndl_range); @@ -404,10 +403,20 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) UINT16 attr_id = 0; tBTA_GATTS cb_data; + tGATT_ATTR_VAL *p_attr_val = NULL; + tGATTS_ATTR_CONTROL *p_control = NULL; + + if (p_msg->api_add_char_descr.attr_val.attr_max_len != 0) { + p_attr_val = &p_msg->api_add_char_descr.attr_val; + } + + if (p_msg->api_add_char_descr.control.auto_rsp != 0) { + p_control = &p_msg->api_add_char_descr.control; + } attr_id = GATTS_AddCharacteristic(p_msg->api_add_char.hdr.layer_specific, &p_msg->api_add_char.char_uuid, p_msg->api_add_char.perm, - p_msg->api_add_char.property); + p_msg->api_add_char.property, p_attr_val, p_control); cb_data.add_result.server_if = p_rcb->gatt_if; cb_data.add_result.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; cb_data.add_result.attr_id = attr_id; @@ -425,6 +434,7 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) (*p_rcb->p_cback)(BTA_GATTS_ADD_CHAR_EVT, &cb_data); } } + /******************************************************************************* ** ** Function bta_gatts_add_char_descr @@ -439,10 +449,20 @@ void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; UINT16 attr_id = 0; tBTA_GATTS cb_data; + tGATT_ATTR_VAL *p_attr_val = NULL; + tGATTS_ATTR_CONTROL *p_control = NULL; + if (p_msg->api_add_char_descr.attr_val.attr_max_len != 0) { + p_attr_val = &p_msg->api_add_char_descr.attr_val; + } + + if (p_msg->api_add_char_descr.control.auto_rsp != 0) { + p_control = &p_msg->api_add_char_descr.control; + } attr_id = GATTS_AddCharDescriptor(p_msg->api_add_char_descr.hdr.layer_specific, p_msg->api_add_char_descr.perm, - &p_msg->api_add_char_descr.descr_uuid); + &p_msg->api_add_char_descr.descr_uuid, p_attr_val, + p_control); cb_data.add_result.server_if = p_rcb->gatt_if; cb_data.add_result.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; @@ -462,6 +482,41 @@ void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ } } + +/******************************************************************************* +** +** Function bta_gatts_add_char_descr +** +** Description action function to add characteristic descriptor. +** +** Returns none. +** +*******************************************************************************/ +void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) +{ + tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; + UINT16 attr_id = 0; + tBTA_GATTS cb_data; + tBTA_GATT_STATUS gatts_status; + gatts_status = GATTS_SetAttributeValue(p_msg->api_add_char_descr.hdr.layer_specific, + p_msg->api_set_val.length, + p_msg->api_set_val.value); + + cb_data.attr_val.server_if = p_rcb->gatt_if; + cb_data.attr_val.service_id = p_msg->api_set_val.hdr.layer_specific; + cb_data.attr_val.attr_id = attr_id; + cb_data.attr_val.status = gatts_status; + + if (p_rcb->p_cback) { + (*p_rcb->p_cback)(BTA_GATTS_SET_ATTR_VAL_EVT, &cb_data); + } +} + +void bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + GATTS_GetAttributeValue(attr_handle, length, value); +} + /******************************************************************************* ** ** Function bta_gatts_delete_service diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c index d54935120c1..58e226c839e 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c @@ -215,10 +215,14 @@ void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_service_id) ** *******************************************************************************/ void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, - tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property) + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control) { tBTA_GATTS_API_ADD_CHAR *p_buf; - + UINT16 len = 0; + if(attr_val != NULL){ + len = attr_val->attr_len; + } if ((p_buf = (tBTA_GATTS_API_ADD_CHAR *) GKI_getbuf(sizeof(tBTA_GATTS_API_ADD_CHAR))) != NULL) { memset(p_buf, 0, sizeof(tBTA_GATTS_API_ADD_CHAR)); @@ -226,6 +230,19 @@ void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, p_buf->hdr.layer_specific = service_id; p_buf->perm = perm; p_buf->property = property; + if(control !=NULL){ + p_buf->control.auto_rsp = control->auto_rsp; + } + if(attr_val != NULL){ + APPL_TRACE_DEBUG("!!!!!!attr_val->attr_len = %x\n",attr_val->attr_len); + APPL_TRACE_DEBUG("!!!!!!!attr_val->attr_max_len = %x\n",attr_val->attr_max_len); + p_buf->attr_val.attr_len = attr_val->attr_len; + p_buf->attr_val.attr_max_len = attr_val->attr_max_len; + p_buf->attr_val.attr_val = (uint8_t *)GKI_getbuf(len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } if (p_char_uuid) { memcpy(&p_buf->char_uuid, p_char_uuid, sizeof(tBT_UUID)); @@ -253,11 +270,16 @@ void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, *******************************************************************************/ void BTA_GATTS_AddCharDescriptor (UINT16 service_id, tBTA_GATT_PERM perm, - tBT_UUID *p_descr_uuid) + tBT_UUID *p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control) { tBTA_GATTS_API_ADD_DESCR *p_buf; - UINT16 len = sizeof(tBTA_GATTS_API_ADD_DESCR); - + UINT16 len = 0; + if(attr_val != NULL) { + len = sizeof(tBTA_GATTS_API_ADD_DESCR) + attr_val->attr_len; + } else { + len = sizeof(tBTA_GATTS_API_ADD_DESCR); + } if ((p_buf = (tBTA_GATTS_API_ADD_DESCR *) GKI_getbuf(len)) != NULL) { memset(p_buf, 0, len); @@ -269,10 +291,19 @@ void BTA_GATTS_AddCharDescriptor (UINT16 service_id, if (p_descr_uuid) { memcpy(&p_buf->descr_uuid, p_descr_uuid, sizeof(tBT_UUID)); } + + if(attr_val->attr_len != 0) { + p_buf->attr_val.attr_len = attr_val->attr_len; + p_buf->attr_val.attr_max_len= attr_val->attr_max_len; + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + + if(control != NULL) { + p_buf->control.auto_rsp = control->auto_rsp; + } bta_sys_sendmsg(p_buf); } return; - } /******************************************************************************* @@ -433,6 +464,29 @@ void BTA_GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, } +void BTA_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) +{ + tBTA_GATTS_API_SET_ATTR_VAL *p_buf; + if((p_buf = (tBTA_GATTS_API_SET_ATTR_VAL *)GKI_getbuf( + sizeof(tBTA_GATTS_API_SET_ATTR_VAL))) != NULL){ + p_buf->hdr.event = BTA_GATTS_API_SET_ATTR_VAL_EVT; + p_buf->hdr.layer_specific = attr_handle; + p_buf->length = length; + if(value != NULL){ + if((p_buf->value = (UINT8 *)GKI_getbuf(length)) != NULL){ + memcpy(p_buf->value, value, length); + } + } + + bta_sys_sendmsg(p_buf); + } + +} + +void BTA_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + bta_gatts_get_attr_value(attr_handle, length, value); +} /******************************************************************************* ** diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_main.c b/components/bt/bluedroid/bta/gatt/bta_gatts_main.c index d59115d42de..4306709c8ca 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_main.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_main.c @@ -104,29 +104,30 @@ BOOLEAN bta_gatts_hdl_event(BT_HDR *p_msg) case BTA_GATTS_API_RSP_EVT: bta_gatts_send_rsp(p_cb, (tBTA_GATTS_DATA *) p_msg); break; - + case BTA_GATTS_API_SET_ATTR_VAL_EVT:{ + UINT16 attr_id = ((tBTA_GATTS_DATA *) p_msg)->api_set_val.hdr.layer_specific; + p_srvc_cb = bta_gatts_find_srvc_cb_by_attr_id(p_cb, attr_id); + bta_gatts_set_attr_value(p_srvc_cb, (tBTA_GATTS_DATA *) p_msg); + break; + } case BTA_GATTS_API_LISTEN_EVT: bta_gatts_listen(p_cb, (tBTA_GATTS_DATA *) p_msg); break; - - case BTA_GATTS_API_ADD_INCL_SRVC_EVT: case BTA_GATTS_API_ADD_CHAR_EVT: case BTA_GATTS_API_ADD_DESCR_EVT: case BTA_GATTS_API_DEL_SRVC_EVT: case BTA_GATTS_API_START_SRVC_EVT: case BTA_GATTS_API_STOP_SRVC_EVT: - p_srvc_cb = bta_gatts_find_srvc_cb_by_srvc_id(p_cb, ((tBTA_GATTS_DATA *)p_msg)->api_add_incl_srvc.hdr.layer_specific); if (p_srvc_cb != NULL) { bta_gatts_srvc_build_act[p_msg->event - BTA_GATTS_API_ADD_INCL_SRVC_EVT](p_srvc_cb, (tBTA_GATTS_DATA *) p_msg); } else { - APPL_TRACE_ERROR("service not created"); + APPL_TRACE_ERROR("service not created\n"); } break; - default: break; } diff --git a/components/bt/bluedroid/bta/include/bta_gatt_api.h b/components/bt/bluedroid/bta/include/bta_gatt_api.h index d10ca46aeea..f4c0e06f078 100644 --- a/components/bt/bluedroid/bta/include/bta_gatt_api.h +++ b/components/bt/bluedroid/bta/include/bta_gatt_api.h @@ -109,41 +109,41 @@ typedef UINT8 tBTA_GATT_STATUS; /* Client callback function events */ -#define BTA_GATTC_REG_EVT 0 /* GATT client is registered. */ -#define BTA_GATTC_DEREG_EVT 1 /* GATT client deregistered event */ -#define BTA_GATTC_OPEN_EVT 2 /* GATTC open request status event */ -#define BTA_GATTC_READ_CHAR_EVT 3 /* GATT read characteristic event */ -#define BTA_GATTC_WRITE_CHAR_EVT 4 /* GATT write characteristic or char descriptor event */ -#define BTA_GATTC_CLOSE_EVT 5 /* GATTC close request status event */ -#define BTA_GATTC_SEARCH_CMPL_EVT 6 /* GATT discovery complete event */ -#define BTA_GATTC_SEARCH_RES_EVT 7 /* GATT discovery result event */ -#define BTA_GATTC_READ_DESCR_EVT 8 /* GATT read characterisitc descriptor event */ -#define BTA_GATTC_WRITE_DESCR_EVT 9 /* GATT write characteristic descriptor event */ -#define BTA_GATTC_NOTIF_EVT 10 /* GATT attribute notification event */ -#define BTA_GATTC_PREP_WRITE_EVT 11 /* GATT prepare write event */ -#define BTA_GATTC_EXEC_EVT 12 /* execute write complete event */ -#define BTA_GATTC_ACL_EVT 13 /* ACL up event */ -#define BTA_GATTC_CANCEL_OPEN_EVT 14 /* cancel open event */ -#define BTA_GATTC_SRVC_CHG_EVT 15 /* service change event */ -#define BTA_GATTC_LISTEN_EVT 16 /* listen event */ -#define BTA_GATTC_ENC_CMPL_CB_EVT 17 /* encryption complete callback event */ -#define BTA_GATTC_CFG_MTU_EVT 18 /* configure MTU complete event */ -#define BTA_GATTC_ADV_DATA_EVT 19 /* ADV data event */ -#define BTA_GATTC_MULT_ADV_ENB_EVT 20 /* Enable Multi ADV event */ -#define BTA_GATTC_MULT_ADV_UPD_EVT 21 /* Update parameter event */ -#define BTA_GATTC_MULT_ADV_DATA_EVT 22 /* Multi ADV data event */ -#define BTA_GATTC_MULT_ADV_DIS_EVT 23 /* Disable Multi ADV event */ -#define BTA_GATTC_CONGEST_EVT 24 /* Congestion event */ -#define BTA_GATTC_BTH_SCAN_ENB_EVT 25 /* Enable batch scan event */ -#define BTA_GATTC_BTH_SCAN_CFG_EVT 26 /* Config storage event */ -#define BTA_GATTC_BTH_SCAN_RD_EVT 27 /* Batch scan reports read event */ -#define BTA_GATTC_BTH_SCAN_THR_EVT 28 /* Batch scan threshold event */ -#define BTA_GATTC_BTH_SCAN_PARAM_EVT 29 /* Batch scan param event */ -#define BTA_GATTC_BTH_SCAN_DIS_EVT 30 /* Disable batch scan event */ -#define BTA_GATTC_SCAN_FLT_CFG_EVT 31 /* Scan filter config event */ -#define BTA_GATTC_SCAN_FLT_PARAM_EVT 32 /* Param filter event */ -#define BTA_GATTC_SCAN_FLT_STATUS_EVT 33 /* Filter status event */ -#define BTA_GATTC_ADV_VSC_EVT 34 /* ADV VSC event */ +#define BTA_GATTC_REG_EVT 0 /* GATT client is registered. */ +#define BTA_GATTC_DEREG_EVT 1 /* GATT client deregistered event */ +#define BTA_GATTC_OPEN_EVT 2 /* GATTC open request status event */ +#define BTA_GATTC_READ_CHAR_EVT 3 /* GATT read characteristic event */ +#define BTA_GATTC_WRITE_CHAR_EVT 4 /* GATT write characteristic or char descriptor event */ +#define BTA_GATTC_CLOSE_EVT 5 /* GATTC close request status event */ +#define BTA_GATTC_SEARCH_CMPL_EVT 6 /* GATT discovery complete event */ +#define BTA_GATTC_SEARCH_RES_EVT 7 /* GATT discovery result event */ +#define BTA_GATTC_READ_DESCR_EVT 8 /* GATT read characterisitc descriptor event */ +#define BTA_GATTC_WRITE_DESCR_EVT 9 /* GATT write characteristic descriptor event */ +#define BTA_GATTC_NOTIF_EVT 10 /* GATT attribute notification event */ +#define BTA_GATTC_PREP_WRITE_EVT 11 /* GATT prepare write event */ +#define BTA_GATTC_EXEC_EVT 12 /* execute write complete event */ +#define BTA_GATTC_ACL_EVT 13 /* ACL up event */ +#define BTA_GATTC_CANCEL_OPEN_EVT 14 /* cancel open event */ +#define BTA_GATTC_SRVC_CHG_EVT 15 /* service change event */ +#define BTA_GATTC_LISTEN_EVT 16 /* listen event */ +#define BTA_GATTC_ENC_CMPL_CB_EVT 17 /* encryption complete callback event */ +#define BTA_GATTC_CFG_MTU_EVT 18 /* configure MTU complete event */ +#define BTA_GATTC_ADV_DATA_EVT 19 /* ADV data event */ +#define BTA_GATTC_MULT_ADV_ENB_EVT 20 /* Enable Multi ADV event */ +#define BTA_GATTC_MULT_ADV_UPD_EVT 21 /* Update parameter event */ +#define BTA_GATTC_MULT_ADV_DATA_EVT 22 /* Multi ADV data event */ +#define BTA_GATTC_MULT_ADV_DIS_EVT 23 /* Disable Multi ADV event */ +#define BTA_GATTC_CONGEST_EVT 24 /* Congestion event */ +#define BTA_GATTC_BTH_SCAN_ENB_EVT 25 /* Enable batch scan event */ +#define BTA_GATTC_BTH_SCAN_CFG_EVT 26 /* Config storage event */ +#define BTA_GATTC_BTH_SCAN_RD_EVT 27 /* Batch scan reports read event */ +#define BTA_GATTC_BTH_SCAN_THR_EVT 28 /* Batch scan threshold event */ +#define BTA_GATTC_BTH_SCAN_PARAM_EVT 29 /* Batch scan param event */ +#define BTA_GATTC_BTH_SCAN_DIS_EVT 30 /* Disable batch scan event */ +#define BTA_GATTC_SCAN_FLT_CFG_EVT 31 /* Scan filter config event */ +#define BTA_GATTC_SCAN_FLT_PARAM_EVT 32 /* Param filter event */ +#define BTA_GATTC_SCAN_FLT_STATUS_EVT 33 /* Filter status event */ +#define BTA_GATTC_ADV_VSC_EVT 34 /* ADV VSC event */ typedef UINT8 tBTA_GATTC_EVT; @@ -151,7 +151,7 @@ typedef tGATT_IF tBTA_GATTC_IF; typedef struct { UINT16 unit; /* as UUIUD defined by SIG */ - UINT16 descr; /* as UUID as defined by SIG */ + UINT16 descr; /* as UUID as defined by SIG */ tGATT_FORMAT format; INT8 exp; UINT8 name_spc; /* The name space of the description */ @@ -165,7 +165,7 @@ typedef UINT16 tBTA_GATT_CLT_CHAR_CONFIG; /* characteristic descriptor: server configuration value */ #define BTA_GATT_SVR_CONFIG_NONE GATT_SVR_CONFIG_NONE /* 0x0000 */ -#define BTA_GATT_SVR_CONFIG_BROADCAST GATT_SVR_CONFIG_BROADCAST /* 0x0001 */ +#define BTA_GATT_SVR_CONFIG_BROADCAST GATT_SVR_CONFIG_BROADCAST /* 0x0001 */ typedef UINT16 tBTA_GATT_SVR_CHAR_CONFIG; /* Characteristic Aggregate Format attribute value @@ -367,8 +367,8 @@ typedef struct { // btla-specific -- typedef struct { - tBTA_GATTC_IF client_if; - BD_ADDR remote_bda; + tBTA_GATTC_IF client_if; + BD_ADDR remote_bda; } tBTA_GATTC_ENC_CMPL_CB; typedef union { @@ -395,7 +395,6 @@ typedef void (tBTA_GATTC_ENB_CBACK)(tBTA_GATT_STATUS status); /* Client callback function */ typedef void (tBTA_GATTC_CBACK)(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); - /* GATT Server Data Structure */ /* Server callback function events */ #define BTA_GATTS_REG_EVT 0 @@ -419,6 +418,7 @@ typedef void (tBTA_GATTC_CBACK)(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); #define BTA_GATTS_CLOSE_EVT 18 #define BTA_GATTS_LISTEN_EVT 19 #define BTA_GATTS_CONGEST_EVT 20 +#define BTA_GATTS_SET_ATTR_VAL_EVT 21 typedef UINT8 tBTA_GATTS_EVT; typedef tGATT_IF tBTA_GATTS_IF; @@ -434,20 +434,22 @@ typedef tGATT_IF tBTA_GATTS_IF; #define BTA_GATT_PERM_WRITE_SIGNED GATT_PERM_WRITE_SIGNED /* bit 7 - 0x0080 */ #define BTA_GATT_PERM_WRITE_SIGNED_MITM GATT_PERM_WRITE_SIGNED_MITM /* bit 8 - 0x0100 */ typedef UINT16 tBTA_GATT_PERM; +typedef tGATT_ATTR_VAL tBTA_GATT_ATTR_VAL; +typedef tGATTS_ATTR_CONTROL tBTA_GATTS_ATTR_CONTROL; #define BTA_GATTS_INVALID_APP 0xff #define BTA_GATTS_INVALID_IF 0 /* definition of characteristic properties */ -#define BTA_GATT_CHAR_PROP_BIT_BROADCAST GATT_CHAR_PROP_BIT_BROADCAST /* 0x01 */ -#define BTA_GATT_CHAR_PROP_BIT_READ GATT_CHAR_PROP_BIT_READ /* 0x02 */ -#define BTA_GATT_CHAR_PROP_BIT_WRITE_NR GATT_CHAR_PROP_BIT_WRITE_NR /* 0x04 */ -#define BTA_GATT_CHAR_PROP_BIT_WRITE GATT_CHAR_PROP_BIT_WRITE /* 0x08 */ -#define BTA_GATT_CHAR_PROP_BIT_NOTIFY GATT_CHAR_PROP_BIT_NOTIFY /* 0x10 */ -#define BTA_GATT_CHAR_PROP_BIT_INDICATE GATT_CHAR_PROP_BIT_INDICATE /* 0x20 */ -#define BTA_GATT_CHAR_PROP_BIT_AUTH GATT_CHAR_PROP_BIT_AUTH /* 0x40 */ -#define BTA_GATT_CHAR_PROP_BIT_EXT_PROP GATT_CHAR_PROP_BIT_EXT_PROP /* 0x80 */ +#define BTA_GATT_CHAR_PROP_BIT_BROADCAST GATT_CHAR_PROP_BIT_BROADCAST /* 0x01 */ +#define BTA_GATT_CHAR_PROP_BIT_READ GATT_CHAR_PROP_BIT_READ /* 0x02 */ +#define BTA_GATT_CHAR_PROP_BIT_WRITE_NR GATT_CHAR_PROP_BIT_WRITE_NR /* 0x04 */ +#define BTA_GATT_CHAR_PROP_BIT_WRITE GATT_CHAR_PROP_BIT_WRITE /* 0x08 */ +#define BTA_GATT_CHAR_PROP_BIT_NOTIFY GATT_CHAR_PROP_BIT_NOTIFY /* 0x10 */ +#define BTA_GATT_CHAR_PROP_BIT_INDICATE GATT_CHAR_PROP_BIT_INDICATE /* 0x20 */ +#define BTA_GATT_CHAR_PROP_BIT_AUTH GATT_CHAR_PROP_BIT_AUTH /* 0x40 */ +#define BTA_GATT_CHAR_PROP_BIT_EXT_PROP GATT_CHAR_PROP_BIT_EXT_PROP /* 0x80 */ typedef UINT8 tBTA_GATT_CHAR_PROP; #ifndef BTA_GATTC_CHAR_DESCR_MAX @@ -476,8 +478,8 @@ typedef tGATTS_SRV_CHG tBTA_GATTS_SRV_CHG; typedef tGATTS_SRV_CHG_REQ tBTA_GATTS_SRV_CHG_REQ; typedef tGATTS_SRV_CHG_RSP tBTA_GATTS_SRV_CHG_RSP; -#define BTA_GATT_TRANSPORT_LE GATT_TRANSPORT_LE -#define BTA_GATT_TRANSPORT_BR_EDR GATT_TRANSPORT_BR_EDR +#define BTA_GATT_TRANSPORT_LE GATT_TRANSPORT_LE +#define BTA_GATT_TRANSPORT_BR_EDR GATT_TRANSPORT_BR_EDR #define BTA_GATT_TRANSPORT_LE_BR_EDR GATT_TRANSPORT_LE_BR_EDR typedef UINT8 tBTA_GATT_TRANSPORT; @@ -539,6 +541,13 @@ typedef struct { // btla-specific -- } tBTA_GATTS_ADD_RESULT; +typedef struct{ + tBTA_GATTS_IF server_if; + UINT16 service_id; + UINT16 attr_id; + tBTA_GATT_STATUS status; +}tBAT_GATTS_ATTR_VAL_RESULT; + typedef struct { tBTA_GATTS_IF server_if; UINT16 service_id; @@ -566,17 +575,18 @@ typedef struct { /* GATTS callback data */ typedef union { - tBTA_GATTS_REG_OPER reg_oper; - tBTA_GATTS_CREATE create; - tBTA_GATTS_SRVC_OPER srvc_oper; - tBTA_GATT_STATUS status; /* BTA_GATTS_LISTEN_EVT */ - tBTA_GATTS_ADD_RESULT add_result; /* add included service: BTA_GATTS_ADD_INCL_SRVC_EVT - add char : BTA_GATTS_ADD_CHAR_EVT - add char descriptor: BTA_GATTS_ADD_CHAR_DESCR_EVT */ - tBTA_GATTS_REQ req_data; - tBTA_GATTS_CONN conn; /* BTA_GATTS_CONN_EVT */ - tBTA_GATTS_CONGEST congest; /* BTA_GATTS_CONGEST_EVT callback data */ - tBTA_GATTS_CONF confirm; /* BTA_GATTS_CONF_EVT callback data */ + tBTA_GATTS_REG_OPER reg_oper; + tBTA_GATTS_CREATE create; + tBTA_GATTS_SRVC_OPER srvc_oper; + tBTA_GATT_STATUS status; /* BTA_GATTS_LISTEN_EVT */ + tBTA_GATTS_ADD_RESULT add_result; /* add included service: BTA_GATTS_ADD_INCL_SRVC_EVT + add char : BTA_GATTS_ADD_CHAR_EVT + add char descriptor: BTA_GATTS_ADD_CHAR_DESCR_EVT */ + tBAT_GATTS_ATTR_VAL_RESULT attr_val; + tBTA_GATTS_REQ req_data; + tBTA_GATTS_CONN conn; /* BTA_GATTS_CONN_EVT */ + tBTA_GATTS_CONGEST congest; /* BTA_GATTS_CONGEST_EVT callback data */ + tBTA_GATTS_CONF confirm; /* BTA_GATTS_CONF_EVT callback data */ } tBTA_GATTS; /* GATTS enable callback function */ @@ -1193,8 +1203,9 @@ extern void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_servi ** Returns None ** *******************************************************************************/ -extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, - tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property); +extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -1214,8 +1225,9 @@ extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_ ** *******************************************************************************/ extern void BTA_GATTS_AddCharDescriptor (UINT16 service_id, - tBTA_GATT_PERM perm, - tBT_UUID *p_descr_uuid); + tBTA_GATT_PERM perm, + tBT_UUID *p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + tBTA_GATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -1296,6 +1308,38 @@ extern void BTA_GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, tBTA_GATT_STATUS status, tBTA_GATTS_RSP *p_msg); + +/******************************************************************************* +** +** Function BTA_SetAttributeValue +** +** Description This function is called to set the attribute value in the gatt database +** +** Parameters attr_handle - the attribute value handle. +** length - the value length which has been set to the attribute. +** value - the pointer to the value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value); + + +/******************************************************************************* +** +** Function BTA_GetAttributeValue +** +** Description This function is called to get the attribute value in the gatt database +** +** Parameters attr_handle - the attribute value handle. +** length - the value length which has been set to the attribute. +** value - the pointer to the value +** +** Returns None +** +*******************************************************************************/ +extern void BTA_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value); + /******************************************************************************* ** ** Function BTA_GATTS_Open diff --git a/components/bt/bluedroid/bta/include/bta_gatts_int.h b/components/bt/bluedroid/bta/include/bta_gatts_int.h index f934c4ac6a6..0f9e689acea 100644 --- a/components/bt/bluedroid/bta/include/bta_gatts_int.h +++ b/components/bt/bluedroid/bta/include/bta_gatts_int.h @@ -48,6 +48,7 @@ enum { BTA_GATTS_API_START_SRVC_EVT, BTA_GATTS_API_STOP_SRVC_EVT, BTA_GATTS_API_RSP_EVT, + BTA_GATTS_API_SET_ATTR_VAL_EVT, BTA_GATTS_API_OPEN_EVT, BTA_GATTS_API_CANCEL_OPEN_EVT, BTA_GATTS_API_CLOSE_EVT, @@ -91,19 +92,21 @@ typedef struct { tBT_UUID char_uuid; tBTA_GATT_PERM perm; tBTA_GATT_CHAR_PROP property; - + tBTA_GATTS_ATTR_CONTROL control; + tBTA_GATT_ATTR_VAL attr_val; } tBTA_GATTS_API_ADD_CHAR; typedef struct { BT_HDR hdr; UINT16 included_service_id; - } tBTA_GATTS_API_ADD_INCL_SRVC; typedef struct { - BT_HDR hdr; - tBT_UUID descr_uuid; - tBTA_GATT_PERM perm; + BT_HDR hdr; + tBT_UUID descr_uuid; + tBTA_GATT_PERM perm; + tBTA_GATTS_ATTR_CONTROL control; + tBTA_GATT_ATTR_VAL attr_val; } tBTA_GATTS_API_ADD_DESCR; typedef struct { @@ -121,6 +124,12 @@ typedef struct { tBTA_GATTS_RSP *p_rsp; } tBTA_GATTS_API_RSP; +typedef struct{ + BT_HDR hdr; + UINT16 length; + UINT8 *value; +}tBTA_GATTS_API_SET_ATTR_VAL; + typedef struct { BT_HDR hdr; tBTA_GATT_TRANSPORT transport; @@ -156,6 +165,7 @@ typedef union { tBTA_GATTS_API_START api_start; tBTA_GATTS_API_INDICATION api_indicate; tBTA_GATTS_API_RSP api_rsp; + tBTA_GATTS_API_SET_ATTR_VAL api_set_val; tBTA_GATTS_API_OPEN api_open; tBTA_GATTS_API_CANCEL_OPEN api_cancel_open; @@ -169,7 +179,7 @@ typedef struct { BOOLEAN in_use; tBT_UUID app_uuid; tBTA_GATTS_CBACK *p_cback; - tBTA_GATTS_IF gatt_if; + tBTA_GATTS_IF gatt_if; } tBTA_GATTS_RCB; /* service registration control block */ @@ -219,6 +229,8 @@ extern void bta_gatts_create_srvc(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_add_include_srvc(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); +extern void bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value); extern void bta_gatts_delete_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_start_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); extern void bta_gatts_stop_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg); diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index e88c775f5ff..7866ca0f806 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -88,6 +88,7 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) tBTA_GATTS_RSP rsp; LOG_DEBUG("blufi profile cb event = %x\n", event); + switch (event) { case BTA_GATTS_REG_EVT: LOG_DEBUG("REG: status %d, app_uuid %04x, gatt_if %d\n", p_data->reg_oper.status, p_data->reg_oper.uuid.uu.uuid16, p_data->reg_oper.server_if); @@ -187,7 +188,8 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) //add the frist blufi characteristic --> write characteristic BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_p2e, (GATT_PERM_WRITE), - (GATT_CHAR_PROP_BIT_WRITE)); + (GATT_CHAR_PROP_BIT_WRITE), + NULL, NULL); break; case BTA_GATTS_ADD_CHAR_EVT: switch (p_data->add_result.char_uuid.uu.uuid16) { @@ -196,14 +198,16 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_e2p, (GATT_PERM_READ), - (GATT_PERM_READ | GATT_CHAR_PROP_BIT_NOTIFY)); + (GATT_PERM_READ | GATT_CHAR_PROP_BIT_NOTIFY), + NULL, NULL); break; case BLUFI_CHAR_E2P_UUID: /* ESP32 to Phone */ blufi_env.handle_char_e2p = p_data->add_result.attr_id; BTA_GATTS_AddCharDescriptor (blufi_env.handle_srvc, (GATT_PERM_READ | GATT_PERM_WRITE), - &blufi_descr_uuid_e2p); + &blufi_descr_uuid_e2p, + NULL, NULL); break; default: break; 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 9bc41d2ab1d..255484fbc4b 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -20,17 +20,30 @@ #include "btc_manage.h" #include "btc_gatts.h" #include "btc_gatt_util.h" - +#include "future.h" +#include "btc_main.h" #include "esp_gatts_api.h" #define A2C_GATTS_EVT(_bta_event) (_bta_event) //BTA TO BTC EVT #define C2A_GATTS_EVT(_btc_event) (_btc_event) //BTC TO BTA EVT +typedef struct { + future_t *complete_future; + uint16_t svc_start_hdl; + esp_bt_uuid_t svc_uuid; + bool is_tab_creat_svc; + uint8_t num_handle; + uint8_t handle_idx; + uint16_t handles[ESP_GATT_ATTR_HANDLE_MAX]; +} esp_btc_creat_tab_t; + +static esp_btc_creat_tab_t btc_creat_tab_env; + static inline void btc_gatts_cb_to_app(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { esp_gatts_cb_t btc_gatts_cb = (esp_gatts_cb_t)btc_profile_cb_get(BTC_PID_GATTS); if (btc_gatts_cb) { - btc_gatts_cb(event, gatts_if, param); + btc_gatts_cb(event, gatts_if, param); } } @@ -59,6 +72,56 @@ void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } } break; + + } + case BTC_GATTS_ACT_ADD_CHAR:{ + if (src->add_char.char_val.attr_value != NULL){ + dst->add_char.char_val.attr_value = (uint8_t *)GKI_getbuf(src->add_char.char_val.attr_len); + if(dst->add_char.char_val.attr_value != NULL){ + memcpy(dst->add_char.char_val.attr_value, src->add_char.char_val.attr_value, + src->add_char.char_val.attr_len); + }else{ + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_ADD_CHAR_DESCR:{ + if(src->add_descr.descr_val.attr_value != NULL){ + dst->add_descr.descr_val.attr_value = (uint8_t *)GKI_getbuf(src->add_descr.descr_val.attr_len); + if(dst->add_descr.descr_val.attr_value != NULL){ + memcpy(dst->add_descr.descr_val.attr_value, src->add_descr.descr_val.attr_value, + src->add_descr.descr_val.attr_len); + }else{ + LOG_ERROR("%s %d no mem\n", __func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_CREATE_ATTR_TAB:{ + uint8_t num_attr = src->create_attr_tab.max_nb_attr; + if(src->create_attr_tab.gatts_attr_db != NULL){ + dst->create_attr_tab.gatts_attr_db = (esp_gatts_attr_db_t *)GKI_getbuf(sizeof(esp_gatts_attr_db_t)*num_attr); + if(dst->create_attr_tab.gatts_attr_db != NULL){ + memcpy(dst->create_attr_tab.gatts_attr_db, src->create_attr_tab.gatts_attr_db, + sizeof(esp_gatts_attr_db_t)*num_attr); + }else{ + LOG_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + case BTC_GATTS_ACT_SET_ATTR_VALUE:{ + uint8_t len = src->set_attr_val.length; + if(src->set_attr_val.value){ + dst->set_attr_val.value = (uint8_t *)GKI_getbuf(len); + if(dst->set_attr_val.value != NULL){ + memcpy(dst->set_attr_val.value, src->set_attr_val.value, len); + }else{ + LOG_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; } default: LOG_DEBUG("%s Unhandled deep copy %d\n", __func__, msg->act); @@ -91,6 +154,163 @@ void btc_gatts_arg_deep_free(btc_msg_t *msg) } +static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, + esp_gatt_if_t gatts_if, + uint8_t max_nb_attr, + uint8_t srvc_inst_id) +{ + uint16_t uuid = 0; + future_t *future_p; + esp_ble_gatts_cb_param_t param; + + //set the attribute table create service flag to ture + btc_creat_tab_env.is_tab_creat_svc = true; + btc_creat_tab_env.num_handle = max_nb_attr; + for(int i = 0; i < max_nb_attr; i++){ + if(gatts_attr_db[i].att_desc.uuid.len == ESP_UUID_LEN_16){ + uuid = gatts_attr_db[i].att_desc.uuid.uuid.uuid16; + } + future_p = future_new(); + if (future_p == NULL) { + LOG_ERROR("%s failed:no mem\n", __func__); + return ; + } + btc_creat_tab_env.complete_future = future_p; + btc_creat_tab_env.handle_idx = i; + switch(uuid) + { + case ESP_GATT_UUID_PRI_SERVICE:{ + tBTA_GATT_SRVC_ID srvc_id; + esp_gatt_srvc_id_t esp_srvc_id; + + esp_srvc_id.id.inst_id = srvc_inst_id; + memcpy(&esp_srvc_id.id.uuid, &gatts_attr_db[i].att_desc.uuid, sizeof(esp_bt_uuid_t)); + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, + srvc_inst_id, max_nb_attr, true); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + break; + } + case ESP_GATT_UUID_SEC_SERVICE:{ + tBTA_GATT_SRVC_ID srvc_id; + esp_gatt_srvc_id_t esp_srvc_id; + + esp_srvc_id.id.inst_id = srvc_inst_id; + memcpy(&esp_srvc_id.id.uuid, &gatts_attr_db[i].att_desc.uuid, sizeof(esp_bt_uuid_t)); + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, + srvc_inst_id, max_nb_attr, false); + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + break; + } + case ESP_GATT_UUID_INCLUDE_SERVICE:{ + esp_gatts_incl_svc_desc_t *incl_svc_desc = (esp_gatts_incl_svc_desc_t *)gatts_attr_db[i].att_desc.value; + + if(incl_svc_desc!= NULL){ + if(btc_creat_tab_env.svc_start_hdl != 0){ + BTA_GATTS_AddIncludeService(btc_creat_tab_env.svc_start_hdl, + incl_svc_desc->start_hdl); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + } + } + break; + } + case ESP_GATT_UUID_CHAR_DECLARE:{ + uint16_t svc_hal = 0; + tBT_UUID bta_char_uuid; + tGATT_ATTR_VAL attr_val; + tBTA_GATT_PERM perm; + tBTA_GATTS_ATTR_CONTROL control; + + if(btc_creat_tab_env.svc_start_hdl != 0){ + svc_hal = btc_creat_tab_env.svc_start_hdl; + esp_gatts_char_desc_t *char_desc = (esp_gatts_char_desc_t *)gatts_attr_db[i].att_desc.value; + if(char_desc != NULL){ + perm = gatts_attr_db[i+1].att_desc.perm; + attr_val.attr_len = gatts_attr_db[i+1].att_desc.length; + attr_val.attr_max_len = gatts_attr_db[i+1].att_desc.max_length; + btc_to_bta_uuid(&bta_char_uuid, &char_desc->attr_uuid); + attr_val.attr_val = gatts_attr_db[i+1].att_desc.value; + control.auto_rsp = gatts_attr_db[i+1].attr_control.auto_rsp; + BTA_GATTS_AddCharacteristic (svc_hal, &bta_char_uuid, + perm, char_desc->prop, &attr_val, &control); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + } + } + + break; + } + case ESP_GATT_UUID_CHAR_EXT_PROP: + case ESP_GATT_UUID_CHAR_DESCRIPTION: + case ESP_GATT_UUID_CHAR_CLIENT_CONFIG: + case ESP_GATT_UUID_CHAR_SRVR_CONFIG: + case ESP_GATT_UUID_CHAR_PRESENT_FORMAT: + case ESP_GATT_UUID_CHAR_AGG_FORMAT: + case ESP_GATT_UUID_CHAR_VALID_RANGE: + case ESP_GATT_UUID_EXT_RPT_REF_DESCR: + case ESP_GATT_UUID_RPT_REF_DESCR:{ + uint16_t svc_hal = btc_creat_tab_env.svc_start_hdl; + tBT_UUID bta_char_uuid; + tGATT_ATTR_VAL attr_val; + tBTA_GATT_PERM perm = gatts_attr_db[i].att_desc.perm; + tBTA_GATTS_ATTR_CONTROL control; + + if(svc_hal != 0){ + attr_val.attr_len = gatts_attr_db[i].att_desc.length; + attr_val.attr_max_len = gatts_attr_db[i].att_desc.max_length; + attr_val.attr_val = gatts_attr_db[i].att_desc.value; + btc_to_bta_uuid(&bta_char_uuid, &gatts_attr_db[i].att_desc.uuid); + control.auto_rsp = gatts_attr_db[i].attr_control.auto_rsp; + BTA_GATTS_AddCharDescriptor(svc_hal, perm, &bta_char_uuid, &attr_val, &control); + + if (future_await(future_p) == FUTURE_FAIL) { + LOG_ERROR("%s failed\n", __func__); + return; + } + } + break; + } + default: + break; + } + + + } + + param.add_attr_tab.status = ESP_GATT_OK; + param.add_attr_tab.num_handle = max_nb_attr; + param.add_attr_tab.handles = btc_creat_tab_env.handles; + memcpy(¶m.add_attr_tab.svc_uuid, &btc_creat_tab_env.svc_uuid, sizeof(esp_bt_uuid_t)); + + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + //reset the env after sent the data to app + memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); + + //release the flag vaule to false after finish the service created. + btc_creat_tab_env.is_tab_creat_svc = false; +} + +void btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value) +{ + + BTA_GetAttributeValue(attr_handle, length, value); +} + static void btc_gatts_cb_param_copy_req(btc_msg_t *msg, void *p_dest, void *p_src) { @@ -137,7 +357,6 @@ static void btc_gatts_cb_param_copy_free(btc_msg_t *msg, tBTA_GATTS *p_data) GKI_freebuf(p_data->req_data.p_data); } break; - default: break; } @@ -148,11 +367,43 @@ static void btc_gatts_inter_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) { bt_status_t status; btc_msg_t msg; - + msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_GATTS; msg.act = event; + if(btc_creat_tab_env.is_tab_creat_svc && btc_creat_tab_env.complete_future){ + switch(event){ + case BTA_GATTS_CREATE_EVT:{ + //save the service handle to the btc module after used + //the attribute table method to creat a service + bta_to_btc_uuid(&btc_creat_tab_env.svc_uuid, &p_data->create.uuid); + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.svc_start_hdl = p_data->create.service_id; + btc_creat_tab_env.handles[index] = p_data->create.service_id; + break; + } + case BTA_GATTS_ADD_INCL_SRVC_EVT:{ + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id; + break; + } + case BTA_GATTS_ADD_CHAR_EVT:{ + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id - 1; + btc_creat_tab_env.handles[index+1] = p_data->add_result.attr_id; + break; + } + case BTA_GATTS_ADD_CHAR_DESCR_EVT:{ + uint8_t index = btc_creat_tab_env.handle_idx; + btc_creat_tab_env.handles[index] = p_data->add_result.attr_id; + break; + } + default: + break; + } + future_ready(btc_creat_tab_env.complete_future, FUTURE_SUCCESS); + } status = btc_transfer_context(&msg, p_data, sizeof(tBTA_GATTS), btc_gatts_cb_param_copy_req); @@ -187,6 +438,12 @@ void btc_gatts_call_handler(btc_msg_t *msg) srvc_id.is_primary); break; } + case BTC_GATTS_ACT_CREATE_ATTR_TAB: + btc_gatts_act_create_attr_tab(arg->create_attr_tab.gatts_attr_db, + arg->create_attr_tab.gatts_if, + arg->create_attr_tab.max_nb_attr, + arg->create_attr_tab.srvc_inst_id); + break; case BTC_GATTS_ACT_DELETE_SERVICE: BTA_GATTS_DeleteService(arg->delete_srvc.service_handle); break; @@ -204,13 +461,17 @@ void btc_gatts_call_handler(btc_msg_t *msg) btc_to_bta_uuid(&uuid, &arg->add_char.char_uuid); BTA_GATTS_AddCharacteristic(arg->add_char.service_handle, &uuid, - arg->add_char.perm, arg->add_char.property); + arg->add_char.perm, arg->add_char.property, + (tGATT_ATTR_VAL *)&arg->add_char.char_val, + (tBTA_GATTS_ATTR_CONTROL *)&arg->add_char.attr_control); break; } case BTC_GATTS_ACT_ADD_CHAR_DESCR: { tBT_UUID uuid; btc_to_bta_uuid(&uuid, &arg->add_descr.descr_uuid); - BTA_GATTS_AddCharDescriptor(arg->add_descr.service_handle, arg->add_descr.perm, &uuid); + BTA_GATTS_AddCharDescriptor(arg->add_descr.service_handle, arg->add_descr.perm, &uuid, + (tBTA_GATT_ATTR_VAL *)&arg->add_descr.descr_val, + (tBTA_GATTS_ATTR_CONTROL *)&arg->add_descr.attr_control); break; } case BTC_GATTS_ACT_SEND_INDICATE: @@ -236,6 +497,9 @@ void btc_gatts_call_handler(btc_msg_t *msg) btc_gatts_cb_to_app(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m); break; } + case BTC_GATTS_ACT_SET_ATTR_VALUE: + + break; case BTC_GATTS_ACT_OPEN: { // Ensure device is in inquiry database tBTA_GATT_TRANSPORT transport = BTA_GATT_TRANSPORT_LE; @@ -359,6 +623,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.create.service_id.is_primary = p_data->create.is_primary; param.create.service_id.id.inst_id = p_data->create.svc_instance; bta_to_btc_uuid(¶m.create.service_id.id.uuid, &p_data->create.uuid); + btc_gatts_cb_to_app(ESP_GATTS_CREATE_EVT, gatts_if, ¶m); break; case BTA_GATTS_ADD_INCL_SRVC_EVT: @@ -391,6 +656,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) gatts_if = p_data->srvc_oper.server_if; param.del.status = p_data->srvc_oper.status; param.del.service_handle = p_data->srvc_oper.service_id; + btc_gatts_cb_to_app(ESP_GATTS_DELETE_EVT, gatts_if, ¶m); break; case BTA_GATTS_START_EVT: @@ -424,11 +690,11 @@ void btc_gatts_cb_handler(btc_msg_t *msg) btc_gatts_cb_to_app(ESP_GATTS_DISCONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_OPEN_EVT: - // do nothing + // do nothing case BTA_GATTS_CANCEL_OPEN_EVT: - // do nothing + // do nothing case BTA_GATTS_CLOSE_EVT: - // do nothing + // do nothing case BTA_GATTS_LISTEN_EVT: // do nothing break; @@ -438,6 +704,13 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.congest.congested = p_data->congest.congested; btc_gatts_cb_to_app(ESP_GATTS_CONGEST_EVT, gatts_if, ¶m); break; + case BTA_GATTS_SET_ATTR_VAL_EVT: + gatts_if = p_data->attr_val.server_if; + param.set_attr_val.srvc_handle = p_data->attr_val.service_id; + param.set_attr_val.attr_handle = p_data->attr_val.attr_id; + param.set_attr_val.status = p_data->attr_val.status; + btc_gatts_cb_to_app(ESP_GATTS_SET_ATTR_VAL_EVT, gatts_if, ¶m); + break; default: // do nothing break; diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h index 4ad276f3eca..caae44de44c 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h @@ -24,6 +24,7 @@ typedef enum { BTC_GATTS_ACT_APP_REGISTER = 0, BTC_GATTS_ACT_APP_UNREGISTER, BTC_GATTS_ACT_CREATE_SERVICE, + BTC_GATTS_ACT_CREATE_ATTR_TAB, BTC_GATTS_ACT_DELETE_SERVICE, BTC_GATTS_ACT_START_SERVICE, BTC_GATTS_ACT_STOP_SERVICE, @@ -32,6 +33,7 @@ typedef enum { BTC_GATTS_ACT_ADD_CHAR_DESCR, BTC_GATTS_ACT_SEND_INDICATE, BTC_GATTS_ACT_SEND_RESPONSE, + BTC_GATTS_ACT_SET_ATTR_VALUE, BTC_GATTS_ACT_OPEN, BTC_GATTS_ACT_CLOSE, } btc_gatts_act_t; @@ -42,46 +44,67 @@ typedef union { struct app_reg_args { uint16_t app_id; } app_reg; + //BTC_GATTS_ACT_APP_UNREGISTER, struct app_unreg_args { esp_gatt_if_t gatts_if; } app_unreg; + //BTC_GATTS_ACT_CREATE_SERVICE, struct create_srvc_args { esp_gatt_if_t gatts_if; esp_gatt_srvc_id_t service_id; uint16_t num_handle; } create_srvc; + + //BTC_GATTS_ACT_CREATE_ATTR_TAB + struct create_attr_tab_args{ + esp_gatt_if_t gatts_if; + uint8_t srvc_inst_id; + uint8_t max_nb_attr; + esp_gatts_attr_db_t *gatts_attr_db; + }create_attr_tab; + //BTC_GATTS_ACT_DELETE_SERVICE, struct delete_srvc_args { uint16_t service_handle; } delete_srvc; + //BTC_GATTS_ACT_START_SERVICE, struct start_srvc_args { uint16_t service_handle; } start_srvc; + //BTC_GATTS_ACT_STOP_SERVICE, struct stop_srvc_args { uint16_t service_handle; } stop_srvc; + //BTC_GATTS_ACT_ADD_INCLUDE_SERVICE, struct add_incl_srvc_args { uint16_t service_handle; uint16_t included_service_handle; } add_incl_srvc; + //BTC_GATTS_ACT_ADD_CHAR, struct add_char_args { uint16_t service_handle; esp_bt_uuid_t char_uuid; esp_gatt_perm_t perm; esp_gatt_char_prop_t property; + esp_attr_control_t attr_control; + esp_attr_value_t char_val; } add_char; + //BTC_GATTS_ACT_ADD_CHAR_DESCR, struct add_descr_args { - uint16_t service_handle; + uint16_t service_handle; esp_bt_uuid_t descr_uuid; esp_gatt_perm_t perm; + esp_attr_control_t attr_control; + esp_attr_value_t descr_val; } add_descr; + //BTC_GATTS_ACT_SEND_INDICATE, struct send_indicate_args { uint16_t conn_id; @@ -90,6 +113,7 @@ typedef union { uint16_t value_len; uint8_t *value; } send_ind; + //BTC_GATTS_ACT_SEND_RESPONSE, struct send_rsp_args { uint16_t conn_id; @@ -97,21 +121,32 @@ typedef union { esp_gatt_status_t status; esp_gatt_rsp_t *rsp; } send_rsp; + + //BTC_GATTS_SET_ATTR_VALUE + struct set_attr_val_args{ + uint16_t length; + uint8_t *value; + } set_attr_val; + //BTC_GATTS_ACT_OPEN, struct open_args { esp_gatt_if_t gatts_if; esp_bd_addr_t remote_bda; bool is_direct; } open; + //BTC_GATTS_ACT_CLOSE, struct close_args { uint16_t conn_id; } close; + } btc_ble_gatts_args_t; void btc_gatts_call_handler(btc_msg_t *msg); void btc_gatts_cb_handler(btc_msg_t *msg); void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value); + #endif /* __BTC_GATTS_H__ */ diff --git a/components/bt/bluedroid/hci/packet_fragmenter.c b/components/bt/bluedroid/hci/packet_fragmenter.c index bd46041c3b7..9294cb5bbbe 100644 --- a/components/bt/bluedroid/hci/packet_fragmenter.c +++ b/components/bt/bluedroid/hci/packet_fragmenter.c @@ -150,13 +150,10 @@ static void reassemble_and_dispatch(BT_HDR *packet) LOG_DEBUG("%s found unfinished packet for handle with start packet. Dropping old.\n", __func__); LOG_DEBUG("partial_packet->len = %x, offset = %x\n", partial_packet->len, partial_packet->len); - //for (int i = 0; i < partial_packet->len; i++) { - // LOG_ERROR("%x", partial_packet->data[i]); - //} - //LOG_ERROR("\n"); + hash_map_erase(partial_packets, (void *)(uintptr_t)handle); - //buffer_allocator->free(partial_packet); - //LOG_ERROR("+++++++++++++++++++\n"); + + } uint16_t full_length = l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE; diff --git a/components/bt/bluedroid/stack/gap/gap_ble.c b/components/bt/bluedroid/stack/gap/gap_ble.c index b560fad6519..bf9ea08369f 100644 --- a/components/bt/bluedroid/stack/gap/gap_ble.c +++ b/components/bt/bluedroid/stack/gap/gap_ble.c @@ -392,7 +392,8 @@ void gap_attr_db_init(void) */ uuid.len = LEN_UUID_16; uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_DEVICE_NAME; - p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ); + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr ++; /* add Icon characteristic @@ -401,7 +402,8 @@ void gap_attr_db_init(void) p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, - GATT_CHAR_PROP_BIT_READ); + GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr ++; #if ((defined BTM_PERIPHERAL_ENABLED) && (BTM_PERIPHERAL_ENABLED == TRUE)) @@ -416,7 +418,8 @@ void gap_attr_db_init(void) p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, - GATT_CHAR_PROP_BIT_READ); + GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr ++; #endif @@ -424,7 +427,8 @@ void gap_attr_db_init(void) uuid.len = LEN_UUID_16; uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_CENTRAL_ADDR_RESOL; p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, - GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ); + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + NULL, NULL); p_db_attr->attr_value.addr_resolution = 0; p_db_attr++; diff --git a/components/bt/bluedroid/stack/gatt/gatt_api.c b/components/bt/bluedroid/stack/gatt/gatt_api.c index c464508da91..7a9d125a974 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_api.c +++ b/components/bt/bluedroid/stack/gatt/gatt_api.c @@ -151,10 +151,10 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, tBT_UUID *p_app_uuid128; - GATT_TRACE_API ("GATTS_CreateService" ); + GATT_TRACE_API ("GATTS_CreateService\n" ); if (p_reg == NULL) { - GATT_TRACE_ERROR ("Inavlid gatt_if=%d", gatt_if); + GATT_TRACE_ERROR ("Inavlid gatt_if=%d\n", gatt_if); return (0); } @@ -162,7 +162,7 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) != NULL) { s_hdl = p_list->asgn_range.s_handle; - GATT_TRACE_DEBUG ("Service already been created!!"); + GATT_TRACE_DEBUG ("Service already been created!!\n"); } else { if ( (p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GATT_SERVER)) { s_hdl = gatt_cb.hdl_cfg.gatt_start_hdl; @@ -184,13 +184,13 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, /* check for space */ if (num_handles > (0xFFFF - s_hdl + 1)) { - GATT_TRACE_ERROR ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u", s_hdl, num_handles); + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u\n", s_hdl, num_handles); return (0); } if ( (p_list = gatt_alloc_hdl_buffer()) == NULL) { /* No free entry */ - GATT_TRACE_ERROR ("GATTS_ReserveHandles: no free handle blocks"); + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no free handle blocks\n"); return (0); } @@ -210,7 +210,7 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, /* add a pending new service change item to the list */ if ( (p_buf = gatt_add_pending_new_srv_start(&p_list->asgn_range)) == NULL) { /* No free entry */ - GATT_TRACE_ERROR ("gatt_add_pending_new_srv_start: no free blocks"); + GATT_TRACE_ERROR ("gatt_add_pending_new_srv_start: no free blocks\n"); if (p_list) { gatt_remove_an_item_from_list(p_list_info, p_list); @@ -219,12 +219,12 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, return (0); } - GATT_TRACE_DEBUG ("Add a new srv chg item"); + GATT_TRACE_DEBUG ("Add a new srv chg item\n"); } } if (!gatts_init_service_db(&p_list->svc_db, p_svc_uuid, is_pri, s_hdl , num_handles)) { - GATT_TRACE_ERROR ("GATTS_ReserveHandles: service DB initialization failed"); + GATT_TRACE_ERROR ("GATTS_ReserveHandles: service DB initialization failed\n"); if (p_list) { gatt_remove_an_item_from_list(p_list_info, p_list); gatt_free_hdl_buffer(p_list); @@ -236,12 +236,6 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, return (0); } - GATT_TRACE_DEBUG ("GATTS_CreateService(success): handles needed:%u s_hdl=%u e_hdl=%u %s[%x] is_primary=%d", - num_handles, p_list->asgn_range.s_handle , p_list->asgn_range.e_handle, - ((p_list->asgn_range.svc_uuid.len == 2) ? "uuid16" : "uuid128" ), - p_list->asgn_range.svc_uuid.uu.uuid16, - p_list->asgn_range.is_primary); - return (s_hdl); } @@ -295,25 +289,27 @@ UINT16 GATTS_AddIncludeService (UINT16 service_handle, UINT16 include_svc_handle ** *******************************************************************************/ UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, - tGATT_PERM perm, tGATT_CHAR_PROP property) + tGATT_PERM perm, tGATT_CHAR_PROP property, + tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_HDL_LIST_ELEM *p_decl; if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { - GATT_TRACE_DEBUG("Service not created"); + GATT_TRACE_DEBUG("Service not created\n"); return 0; } /* data validity checking */ if ( ((property & GATT_CHAR_PROP_BIT_AUTH) && !(perm & GATT_WRITE_SIGNED_PERM)) || ((perm & GATT_WRITE_SIGNED_PERM) && !(property & GATT_CHAR_PROP_BIT_AUTH)) ) { - GATT_TRACE_DEBUG("Invalid configuration property=0x%x perm=0x%x ", property, perm); + GATT_TRACE_DEBUG("Invalid configuration property=0x%x perm=0x%x\n ", property, perm); return 0; } return gatts_add_characteristic(&p_decl->svc_db, perm, property, - p_char_uuid); + p_char_uuid, + attr_val, control); } /******************************************************************************* ** @@ -336,7 +332,7 @@ UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, *******************************************************************************/ UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, tGATT_PERM perm, - tBT_UUID *p_descr_uuid) + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_HDL_LIST_ELEM *p_decl; @@ -353,7 +349,8 @@ UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, return gatts_add_char_descr(&p_decl->svc_db, perm, - p_descr_uuid); + p_descr_uuid, + attr_val, control); } /******************************************************************************* @@ -493,9 +490,9 @@ tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, gatt_add_a_srv_to_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[i_sreg]); - GATT_TRACE_DEBUG ("allocated i_sreg=%d ", i_sreg); + GATT_TRACE_DEBUG ("allocated i_sreg=%d\n", i_sreg); - GATT_TRACE_DEBUG ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x", + GATT_TRACE_DEBUG ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x\n", p_sreg->s_hdl, p_sreg->e_hdl, p_sreg->type, p_sreg->service_instance, p_sreg->sdp_handle); @@ -676,16 +673,16 @@ tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, tGATT_REG *p_reg = gatt_get_regcb(gatt_if); tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); - GATT_TRACE_API ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x", + GATT_TRACE_API ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x\n", conn_id, trans_id, status); if ( (p_reg == NULL) || (p_tcb == NULL)) { - GATT_TRACE_ERROR ("GATTS_SendRsp Unknown conn_id: %u ", conn_id); + GATT_TRACE_ERROR ("GATTS_SendRsp Unknown conn_id: %u\n", conn_id); return (tGATT_STATUS) GATT_INVALID_CONN_ID; } if (p_tcb->sr_cmd.trans_id != trans_id) { - GATT_TRACE_ERROR ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x", + GATT_TRACE_ERROR ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x\n", conn_id, p_tcb->sr_cmd.op_code); return (GATT_WRONG_STATE); @@ -696,6 +693,69 @@ tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, return cmd_sent; } + +/******************************************************************************* +** +** Function GATTS_SetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle:the attribute handle +** length: the attribute length +** value: the value to be set to the attribute in the database +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl = NULL; + + GATT_TRACE_DEBUG("GATTS_SetAttributeValue: attr_handle: %u length: %u \n", + attr_handle, length); + + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return GATT_INVALID_HANDLE; + } + + status = gatts_set_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; + +} + + +/******************************************************************************* +** +** Function GATTS_GetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl; + + GATT_TRACE_DEBUG("GATTS_GetAttributeValue: attr_handle: %u\n", + attr_handle); + + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_ERROR("Service not created\n"); + return GATT_INVALID_HANDLE; + } + + status = gatts_get_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; +} + /*******************************************************************************/ /* GATT Profile Srvr Functions */ /*******************************************************************************/ @@ -1132,7 +1192,7 @@ tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, tGATT_CBACK *p_cb_info) break; } } - GATT_TRACE_API ("allocated gatt_if=%d", gatt_if); + GATT_TRACE_API ("allocated gatt_if=%d\n", gatt_if); return gatt_if; } diff --git a/components/bt/bluedroid/stack/gatt/gatt_attr.c b/components/bt/bluedroid/stack/gatt/gatt_attr.c index 49688283947..421b17cab76 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_attr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_attr.c @@ -279,21 +279,24 @@ void gatt_profile_db_init (void) GATT_StartIf(gatt_cb.gatt_if); service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); + GATT_TRACE_ERROR ("GATTS_CreateService: handle of service handle%x", service_handle); + /* add Service Changed characteristic */ uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; gatt_cb.gattp_attr.service_change = 0; gatt_cb.gattp_attr.handle = - gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE); + gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE, + NULL, NULL); - GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d", - gatt_cb.handle_of_h_r ); + GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d\n", + gatt_cb.handle_of_h_r); /* start service */ status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATTP_TRANSPORT_SUPPORTED ); - GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d", + GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d\n", gatt_cb.gatt_if, status); } diff --git a/components/bt/bluedroid/stack/gatt/gatt_db.c b/components/bt/bluedroid/stack/gatt/gatt_db.c index 2e74c7d2dfb..b95934a8fa5 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_db.c +++ b/components/bt/bluedroid/stack/gatt/gatt_db.c @@ -65,12 +65,12 @@ BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN GKI_init_q(&p_db->svc_buffer); if (!allocate_svc_db_buf(p_db)) { - GATT_TRACE_ERROR("gatts_init_service_db failed, no resources"); + GATT_TRACE_ERROR("gatts_init_service_db failed, no resources\n"); return FALSE; } - GATT_TRACE_DEBUG("gatts_init_service_db"); - GATT_TRACE_DEBUG("s_hdl = %d num_handle = %d", s_hdl, num_handle ); + GATT_TRACE_DEBUG("gatts_init_service_db\n"); + GATT_TRACE_DEBUG("s_hdl = %d num_handle = %d\n", s_hdl, num_handle ); /* update service database information */ p_db->next_handle = s_hdl; @@ -94,7 +94,7 @@ BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN tBT_UUID *gatts_get_service_uuid (tGATT_SVC_DB *p_db) { if (!p_db || !p_db->p_attr_list) { - GATT_TRACE_ERROR("service DB empty"); + GATT_TRACE_ERROR("service DB empty\n"); return NULL; } else { @@ -127,28 +127,28 @@ static tGATT_STATUS gatts_check_attr_readability(tGATT_ATTR16 *p_attr, } if (!(perm & GATT_READ_ALLOWED)) { - GATT_TRACE_ERROR( "GATT_READ_NOT_PERMIT"); + GATT_TRACE_ERROR( "GATT_READ_NOT_PERMIT\n"); return GATT_READ_NOT_PERMIT; } if ((perm & GATT_READ_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED) && !(sec_flag & BTM_SEC_FLAG_ENCRYPTED)) { - GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION"); + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION\n"); return GATT_INSUF_AUTHENTICATION; } if ((perm & GATT_READ_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) { - GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION: MITM Required"); + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION: MITM Required\n"); return GATT_INSUF_AUTHENTICATION; } if ((perm & GATT_READ_ENCRYPTED_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { - GATT_TRACE_ERROR( "GATT_INSUF_ENCRYPTION"); + GATT_TRACE_ERROR( "GATT_INSUF_ENCRYPTION\n"); return GATT_INSUF_ENCRYPTION; } if ( (perm & GATT_READ_ENCRYPTED_REQUIRED) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) { - GATT_TRACE_ERROR( "GATT_INSUF_KEY_SIZE"); + GATT_TRACE_ERROR( "GATT_INSUF_KEY_SIZE\n"); return GATT_INSUF_KEY_SIZE; } @@ -163,7 +163,7 @@ static tGATT_STATUS gatts_check_attr_readability(tGATT_ATTR16 *p_attr, case GATT_UUID_CHAR_CLIENT_CONFIG: case GATT_UUID_CHAR_SRVR_CONFIG: case GATT_UUID_CHAR_PRESENT_FORMAT: - GATT_TRACE_ERROR("GATT_NOT_LONG"); + GATT_TRACE_ERROR("GATT_NOT_LONG\n"); return GATT_NOT_LONG; default: @@ -206,7 +206,7 @@ static tGATT_STATUS read_attr_value (void *p_attr, tGATT_STATUS status; tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr; - GATT_TRACE_DEBUG("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d", + GATT_TRACE_DEBUG("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d\n", p_attr16->uuid, p_attr16->permission, sec_flag, @@ -268,7 +268,22 @@ static tGATT_STATUS read_attr_value (void *p_attr, status = GATT_SUCCESS; } } else { /* characteristic description or characteristic value */ - status = GATT_PENDING; + if (p_attr16->control.auto_rsp == GATT_RSP_BY_STACK) { + GATT_TRACE_DEBUG("before characteristic description or characteristic value\n"); + if (p_attr16->p_value != NULL && p_attr16->p_value->attr_val.attr_val != NULL) { + uint8_t *value = p_attr16->p_value->attr_val.attr_val + offset; + GATT_TRACE_DEBUG("after characteristic description or characteristic value\n"); + if (mtu >= p_attr16->p_value->attr_val.attr_len) { + ARRAY_TO_STREAM(p, value, p_attr16->p_value->attr_val.attr_len); + } else { + ARRAY_TO_STREAM(p, value, mtu); + } + } + status = GATT_STACK_RSP; + + } else { + status = GATT_PENDING; + } } *p_len = len; @@ -341,7 +356,7 @@ tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, status = read_attr_value ((void *)p_attr, 0, &p, FALSE, (UINT16)(*p_len - 2), &len, sec_flag, key_size); - if (status == GATT_PENDING) { + if (status == GATT_PENDING || status == GATT_STACK_RSP) { status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id); /* one callback at a time */ @@ -445,12 +460,12 @@ UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e *******************************************************************************/ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, tGATT_CHAR_PROP property, - tBT_UUID *p_char_uuid) + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_ATTR16 *p_char_decl, *p_char_val; tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_CHAR_DECLARE}}; - GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x", perm, property); + GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x\n", perm, property); if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_decl->p_value, sizeof(tGATT_CHAR_DECL))) { @@ -467,8 +482,30 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, p_char_decl->p_value->char_decl.property = property; p_char_decl->p_value->char_decl.char_val_handle = p_char_val->handle; + if (control != NULL) { + p_char_val->control.auto_rsp = control->auto_rsp; + } else { + p_char_val->control.auto_rsp = GATT_RSP_DEFAULT; - p_char_val->p_value = NULL; + } + + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_val->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_val); + return 0; + } + GATT_TRACE_DEBUG("attr_val->attr_len = %x, attr_val->attr_max_len = %x\n", attr_val->attr_len, attr_val->attr_max_len); + GATT_TRACE_DEBUG("attribute handle = %x\n", p_char_val->handle); + p_char_val->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_val->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + p_char_val->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); + if (p_char_val->p_value->attr_val.attr_val != NULL) { + GATT_TRACE_DEBUG("attribute value not NULL"); + memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } else { + p_char_val->p_value = NULL; + } return p_char_val->handle; } @@ -542,24 +579,221 @@ UINT8 gatt_convertchar_descr_type(tBT_UUID *p_descr_uuid) ** *******************************************************************************/ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, - tBT_UUID *p_descr_uuid) + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) { tGATT_ATTR16 *p_char_dscptr; - GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x", p_descr_uuid->uu.uuid16); + GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x\n", p_descr_uuid->uu.uuid16); /* Add characteristic descriptors */ - if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, - p_descr_uuid, - perm)) - == NULL) { + if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_descr_uuid, perm)) == NULL) { GATT_TRACE_DEBUG("gatts_add_char_descr Fail for adding char descriptors."); return 0; } else { + if (control != NULL) { + p_char_dscptr->control.auto_rsp = control->auto_rsp; + } + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_dscptr->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_dscptr); + return 0; + } + p_char_dscptr->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_dscptr->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + if (attr_val->attr_val != NULL) { + p_char_dscptr->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); + if (p_char_dscptr->p_value->attr_val.attr_val != NULL) { + memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + memcpy(p_char_dscptr->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } + } return p_char_dscptr->handle; } } + +/******************************************************************************* +** +** Function gatts_set_attribute_value +** +** Description This function add the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be set to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value) +{ + tGATT_ATTR16 *p_cur, *p_next; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db is NULL.\n"); + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + + for (; p_cur != NULL; p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + if (p_cur->handle == attr_handle) { + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + return GATT_NOT_FOUND; + default: + if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); + } else { + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } + break; + } + } else { + if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); + } else { + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } + } + break; + } + } + + return GATT_SUCCESS; +} + + +/******************************************************************************* +** +** Function gatts_get_attribute_value +** +** Description This function get the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be get to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value) +{ + tGATT_ATTR16 *p_cur, *p_next; + GATT_TRACE_DEBUG("***********%s*************\n", __func__); + GATT_TRACE_DEBUG("attr_handle = %x\n", attr_handle); + if (p_db == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db is NULL.\n"); + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + + for (; p_cur != NULL; p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + LOG_ERROR("p_ur->handle = %x\n", p_cur->handle); + if (p_cur->handle == attr_handle) { + + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + break; + default: + if (p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + GATT_TRACE_ERROR("gatts_get_attribute_vaule failt:the value length is 0"); + return GATT_INVALID_ATTR_LEN; + } + break; + } + } else { + if (p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + GATT_TRACE_ERROR("gatts_get_attribute_vaule failt:the value length is 0"); + return GATT_INVALID_ATTR_LEN; + } + + } + + break; + + } + + + } + + return GATT_SUCCESS; +} + +BOOLEAN gatts_is_auto_response(UINT16 attr_handle) +{ + tGATT_HDL_LIST_ELEM *p_decl = NULL; + BOOLEAN rsp = FALSE; + tGATT_SVC_DB *p_db = NULL; + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return rsp; + } + + p_db = &p_decl->svc_db; + + tGATT_ATTR16 *p_cur, *p_next; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db is NULL.\n"); + return rsp; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return rsp; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + for (; p_cur != NULL && p_next != NULL; + p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + if (p_cur->handle == attr_handle) { + if (p_cur->p_value != NULL && p_cur->control.auto_rsp == GATT_RSP_BY_STACK) { + rsp = true; + return rsp; + } + + } + + } + + return rsp; + +} + /*******************************************************************************/ /* Service Attribute Database Query Utility Functions */ /*******************************************************************************/ @@ -617,6 +851,41 @@ tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, return status; } +tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle ) { + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + return GATT_APP_RSP; + } + + if (p_attr->p_value != NULL && (p_attr->p_value->attr_val.attr_max_len > + offset + len)) { + memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); + p_attr->p_value->attr_val.attr_len = len + offset; + return GATT_SUCCESS; + } else { + return GATT_NOT_LONG; + } + } + + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + + } + + } + + return status; +} + /******************************************************************************* ** ** Function gatts_read_attr_perm_check @@ -661,6 +930,8 @@ tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, return status; } + + /******************************************************************************* ** ** Function gatts_write_attr_perm_check @@ -835,7 +1106,7 @@ static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PER UINT16 len = sizeof(tGATT_ATTR128); if (p_uuid == NULL) { - GATT_TRACE_ERROR("illegal UUID"); + GATT_TRACE_ERROR("illegal UUID\n"); return NULL; } @@ -845,17 +1116,17 @@ static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PER len = sizeof(tGATT_ATTR32); } - GATT_TRACE_DEBUG("allocate attr %d bytes ", len); + GATT_TRACE_DEBUG("allocate attr %d bytes\n", len); if (p_db->end_handle <= p_db->next_handle) { - GATT_TRACE_DEBUG("handle space full. handle_max = %d next_handle = %d", + GATT_TRACE_DEBUG("handle space full. handle_max = %d next_handle = %d\n", p_db->end_handle, p_db->next_handle); return NULL; } if (p_db->mem_free < len) { if (!allocate_svc_db_buf(p_db)) { - GATT_TRACE_ERROR("allocate_attr_in_db failed, no resources"); + GATT_TRACE_ERROR("allocate_attr_in_db failed, no resources\n"); return NULL; } } @@ -896,19 +1167,21 @@ static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PER } if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { - GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid16 = [0x%04x] perm=0x%02x ", + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid16 = [0x%04x] perm=0x%02x\n", p_attr16->handle, p_attr16->uuid, p_attr16->permission); } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_32) { - GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid32 = [0x%08x] perm=0x%02x ", + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid32 = [0x%08x] perm=0x%02x\n", p_attr32->handle, p_attr32->uuid, p_attr32->permission); } else { - GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x ", + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x\n", p_attr128->handle, p_attr128->uuid[0], p_attr128->uuid[1], p_attr128->permission); } return (void *)p_attr16; } + + /******************************************************************************* ** ** Function deallocate_attr_in_db @@ -974,7 +1247,7 @@ static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 le if (p_db->mem_free < len) { if (!allocate_svc_db_buf(p_db)) { - GATT_TRACE_ERROR("copy_extra_byte_in_db failed, no resources"); + GATT_TRACE_ERROR("copy_extra_byte_in_db failed, no resources\n"); return FALSE; } } diff --git a/components/bt/bluedroid/stack/gatt/gatt_main.c b/components/bt/bluedroid/stack/gatt/gatt_main.c index 8b40ed0e519..d7997a847fc 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_main.c +++ b/components/bt/bluedroid/stack/gatt/gatt_main.c @@ -508,7 +508,7 @@ static void gatt_le_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf) GKI_freebuf (p_buf); if (p_tcb != NULL) { - GATT_TRACE_WARNING ("ATT - Ignored L2CAP data while in state: %d", + GATT_TRACE_WARNING ("ATT - Ignored L2CAP data while in state: %d\n", gatt_get_ch_state(p_tcb)); } } @@ -906,10 +906,10 @@ void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf) } } } else { - GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x", op_code); + GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x\n", op_code); } } else { - GATT_TRACE_ERROR ("invalid data length, ignore"); + GATT_TRACE_ERROR ("invalid data length, ignore\n"); } GKI_freebuf (p_buf); diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index 4846d4ad3e5..6f21e8c45d1 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -239,7 +239,7 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, tGATT_STATUS ret_code = GATT_SUCCESS; UNUSED(trans_id); - GATT_TRACE_DEBUG("gatt_sr_process_app_rsp gatt_if=%d", gatt_if); + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp gatt_if=%d\n", gatt_if); gatt_sr_update_cback_cnt(p_tcb, gatt_if, FALSE, FALSE); @@ -264,7 +264,7 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, if (p_tcb->sr_cmd.p_rsp_msg == NULL) { p_tcb->sr_cmd.p_rsp_msg = attp_build_sr_msg (p_tcb, (UINT8)(op_code + 1), (tGATT_SR_MSG *)p_msg); } else { - GATT_TRACE_ERROR("Exception!!! already has respond message"); + GATT_TRACE_ERROR("Exception!!! already has respond message\n"); } } } @@ -279,7 +279,7 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, gatt_dequeue_sr_cmd(p_tcb); } - GATT_TRACE_DEBUG("gatt_sr_process_app_rsp ret_code=%d", ret_code); + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp ret_code=%d\n", ret_code); return ret_code; } @@ -371,7 +371,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U #if GATT_CONFORMANCE_TESTING == TRUE if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { - GATT_TRACE_DEBUG("Conformance tst: forced err rspvofr ReadMultiple: error status=%d", gatt_cb.err_status); + GATT_TRACE_DEBUG("Conformance tst: forced err rspvofr ReadMultiple: error status=%d\n", gatt_cb.err_status); STREAM_TO_UINT16(handle, p); @@ -871,7 +871,7 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data) ** - discover characteristic by UUID ** - relationship discovery ** -** Returns void +** Returns void ** *******************************************************************************/ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) @@ -889,10 +889,10 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, tGATT_SRV_LIST_ELEM *p_srv = NULL; reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); - + GATT_TRACE_DEBUG("%s, op_code =%x, len = %x\n", __func__, op_code, len); #if GATT_CONFORMANCE_TESTING == TRUE if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { - GATT_TRACE_DEBUG("Conformance tst: forced err rsp for ReadByType: error status=%d", gatt_cb.err_status); + GATT_TRACE_DEBUG("Conformance tst: forced err rsp for ReadByType: error status=%d\n", gatt_cb.err_status); gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, s_hdl, FALSE); @@ -902,7 +902,7 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, if (reason == GATT_SUCCESS) { if ((p_msg = (BT_HDR *)GKI_getbuf(msg_len)) == NULL) { - GATT_TRACE_ERROR("gatts_process_find_info failed. no resources."); + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); reason = GATT_NO_RESOURCES; } else { @@ -959,7 +959,7 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, p_msg->offset = L2CAP_MIN_OFFSET; } } - if (reason != GATT_SUCCESS) { + if (reason != GATT_SUCCESS && reason != GATT_STACK_RSP) { if (p_msg) { GKI_freebuf(p_msg); } @@ -987,19 +987,32 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, UINT8 op_code, UINT16 len, UINT8 *p_data) { - tGATTS_DATA sr_data; - UINT32 trans_id; - tGATT_STATUS status; - UINT8 sec_flag, key_size, *p = p_data; - tGATT_SR_REG *p_sreg; - UINT16 conn_id; - + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + tGATTS_DATA sr_data; + UINT32 trans_id; + tGATT_STATUS status; + UINT8 sec_flag, key_size, *p = p_data, *p_m; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + BT_HDR *p_msg = NULL; memset(&sr_data, 0, sizeof(tGATTS_DATA)); + if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_write_req failed. no resources.\n"); + } + + memset(p_msg, 0, buf_len); + p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p_m ++ = op_code + 1; + p_msg->len = 1; + buf_len = p_tcb->payload_size - 1; + switch (op_code) { case GATT_REQ_PREPARE_WRITE: sr_data.write_req.is_prep = TRUE; STREAM_TO_UINT16(sr_data.write_req.offset, p); + UINT16_TO_STREAM(p_m, sr_data.write_req.is_prep); + offset = sr_data.write_req.offset; len -= 2; /* fall through */ case GATT_SIGN_CMD_WRITE: @@ -1012,11 +1025,16 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, case GATT_REQ_WRITE: if (op_code == GATT_REQ_WRITE || op_code == GATT_REQ_PREPARE_WRITE) { sr_data.write_req.need_rsp = TRUE; + if(op_code == GATT_REQ_PREPARE_WRITE){ + memcpy(p_m, p, len); + p_msg->len += len; + } } sr_data.write_req.handle = handle; sr_data.write_req.len = len; if (len != 0 && p != NULL) { memcpy (sr_data.write_req.value, p, len); + } break; } @@ -1035,18 +1053,26 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, sec_flag, key_size); + p_msg->len += len; if (status == GATT_SUCCESS) { if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { p_sreg = &gatt_cb.sr_reg[i_rcb]; conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, + handle, offset, p, len); gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE, &sr_data); - - status = GATT_PENDING; + + if(status == GATT_SUCCESS){ + attp_send_sr_msg(p_tcb, p_msg); + }else if(status == GATT_NOT_LONG){ + gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); + } + status = GATT_PENDING; } else { - GATT_TRACE_ERROR("max pending command, send error"); + GATT_TRACE_ERROR("max pending command, send error\n"); status = GATT_BUSY; /* max pending command, application error */ } } @@ -1072,15 +1098,15 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 op_code, UINT16 handle, UINT16 len, UINT8 *p_data) { - UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); - tGATT_STATUS reason; - BT_HDR *p_msg = NULL; - UINT8 sec_flag, key_size, *p; - UINT16 offset = 0, value_len = 0; + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + tGATT_STATUS reason; + BT_HDR *p_msg = NULL; + UINT8 sec_flag, key_size, *p; + UINT16 offset = 0, value_len = 0; UNUSED (len); if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { - GATT_TRACE_ERROR("gatts_process_find_info failed. no resources."); + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); reason = GATT_NO_RESOURCES; } else { @@ -1114,13 +1140,13 @@ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 p_msg->len += value_len; } - if (reason != GATT_SUCCESS) { + if (reason != GATT_SUCCESS && reason != GATT_PENDING) { if (p_msg) { GKI_freebuf(p_msg); } /* in theroy BUSY is not possible(should already been checked), protected check */ - if (reason != GATT_PENDING && reason != GATT_BUSY) { + if (reason != GATT_BUSY) { gatt_send_error_rsp (p_tcb, reason, op_code, handle, FALSE); } } else { @@ -1149,7 +1175,7 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, tGATT_ATTR16 *p_attr; if (len < 2) { - GATT_TRACE_ERROR("Illegal PDU length, discard request"); + GATT_TRACE_ERROR("Illegal PDU length, discard request\n"); status = GATT_INVALID_PDU; } else { STREAM_TO_UINT16(handle, p); @@ -1159,7 +1185,7 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, #if GATT_CONFORMANCE_TESTING == TRUE gatt_cb.handle = handle; if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { - GATT_TRACE_DEBUG("Conformance tst: forced err rsp: error status=%d", gatt_cb.err_status); + GATT_TRACE_DEBUG("Conformance tst: forced err rsp: error status=%d\n", gatt_cb.err_status); gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); @@ -1350,24 +1376,24 @@ void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, /* otherwise, ignore the pkt */ } else { switch (op_code) { - case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ - case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ + case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ + case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ gatts_process_primary_service_req (p_tcb, op_code, len, p_data); break; - case GATT_REQ_FIND_INFO:/* discover char descrptor */ + case GATT_REQ_FIND_INFO: /* discover char descrptor */ gatts_process_find_info(p_tcb, op_code, len, p_data); break; - case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ + case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ /* discover characteristic, discover char by UUID */ gatts_process_read_by_type_req(p_tcb, op_code, len, p_data); break; - case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ: /* read char/char descriptor value */ case GATT_REQ_READ_BLOB: - case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_REQ_WRITE: /* write char/char descriptor value */ case GATT_CMD_WRITE: case GATT_SIGN_CMD_WRITE: case GATT_REQ_PREPARE_WRITE: diff --git a/components/bt/bluedroid/stack/gatt/gatt_utils.c b/components/bt/bluedroid/stack/gatt/gatt_utils.c index 7b55a097c9d..09f4e8df2fa 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/bluedroid/stack/gatt/gatt_utils.c @@ -318,6 +318,34 @@ tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle) } return NULL; } + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_attr_handle +** +** Description Find handle range buffer by attribute handle. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if (p_list->in_use && (p_list->asgn_range.s_handle <= attr_handle) + && (p_list->asgn_range.e_handle >= attr_handle)) { + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} + + /******************************************************************************* ** ** Function gatt_find_hdl_buffer_by_app_id @@ -1476,7 +1504,7 @@ tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if) p_reg = &gatt_cb.cl_rcb[ii - 1]; if (!p_reg->in_use) { - GATT_TRACE_WARNING("gatt_if found but not in use."); + GATT_TRACE_WARNING("gatt_if found but not in use.\n"); return NULL; } diff --git a/components/bt/bluedroid/stack/gatt/include/gatt_int.h b/components/bt/bluedroid/stack/gatt/include/gatt_int.h index 1ac8431b3b9..c9622a24c1d 100644 --- a/components/bt/bluedroid/stack/gatt/include/gatt_int.h +++ b/components/bt/bluedroid/stack/gatt/include/gatt_int.h @@ -35,7 +35,10 @@ #define GATT_GET_GATT_IF(conn_id) ((tGATT_IF)((UINT8) (conn_id))) #define GATT_GET_SR_REG_PTR(index) (&gatt_cb.sr_reg[(UINT8) (index)]); -#define GATT_TRANS_ID_MAX 0x0fffffff /* 4 MSB is reserved */ +#define GATT_TRANS_ID_MAX 0x0fffffff /* 4 MSB is reserved */ +#define GATT_RSP_BY_APP 0x00 +#define GATT_RSP_BY_STACK 0x01 +#define GATT_RSP_DEFAULT GATT_RSP_BY_APP //need to rsp by the app. /* security action for GATT write and read request */ #define GATT_SEC_NONE 0 @@ -61,16 +64,16 @@ typedef UINT8 tGATT_SEC_ACTION; #define GATT_ATTR_OP_SPT_PREP_WRITE (0x00000001 << 10) #define GATT_ATTR_OP_SPT_EXE_WRITE (0x00000001 << 11) #define GATT_ATTR_OP_SPT_HDL_VALUE_CONF (0x00000001 << 12) -#define GATT_ATTR_OP_SP_SIGN_WRITE (0x00000001 << 13) +#define GATT_ATTR_OP_SP_SIGN_WRITE (0x00000001 << 13) -#define GATT_INDEX_INVALID 0xff +#define GATT_INDEX_INVALID 0xff -#define GATT_PENDING_REQ_NONE 0 +#define GATT_PENDING_REQ_NONE 0 -#define GATT_WRITE_CMD_MASK 0xc0 /*0x1100-0000*/ -#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/ -#define GATT_AUTH_SIGN_LEN 12 +#define GATT_WRITE_CMD_MASK 0xc0 /*0x1100-0000*/ +#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/ +#define GATT_AUTH_SIGN_LEN 12 #define GATT_HDR_SIZE 3 /* 1B opcode + 2B handle */ @@ -154,7 +157,7 @@ typedef union { tBT_UUID uuid; /* service declaration */ tGATT_CHAR_DECL char_decl; /* characteristic declaration */ tGATT_INCL_SRVC incl_handle; /* included service */ - + tGATT_ATTR_VAL attr_val; } tGATT_ATTR_VALUE; /* Attribute UUID type @@ -167,50 +170,49 @@ typedef UINT8 tGATT_ATTR_UUID_TYPE; /* 16 bits UUID Attribute in server database */ typedef struct { - void *p_next; /* pointer to the next attribute, - either tGATT_ATTR16 or tGATT_ATTR128 */ - tGATT_ATTR_VALUE *p_value; - tGATT_ATTR_UUID_TYPE uuid_type; - tGATT_PERM permission; - UINT16 handle; - UINT16 uuid; + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + UINT16 handle; + UINT16 uuid; } tGATT_ATTR16; /* 32 bits UUID Attribute in server database */ typedef struct { - void *p_next; /* pointer to the next attribute, - either tGATT_ATTR16, tGATT_ATTR32 or tGATT_ATTR128 */ - tGATT_ATTR_VALUE *p_value; - tGATT_ATTR_UUID_TYPE uuid_type; - tGATT_PERM permission; - UINT16 handle; - UINT32 uuid; + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16, tGATT_ATTR32 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + UINT16 handle; + UINT32 uuid; } tGATT_ATTR32; /* 128 bits UUID Attribute in server database */ typedef struct { - void *p_next; /* pointer to the next attribute, - either tGATT_ATTR16 or tGATT_ATTR128 */ - tGATT_ATTR_VALUE *p_value; - tGATT_ATTR_UUID_TYPE uuid_type; - tGATT_PERM permission; - UINT16 handle; - UINT8 uuid[LEN_UUID_128]; + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + UINT16 handle; + UINT8 uuid[LEN_UUID_128]; } tGATT_ATTR128; /* Service Database definition */ typedef struct { - void *p_attr_list; /* pointer to the first attribute, - either tGATT_ATTR16 or tGATT_ATTR128 */ - UINT8 *p_free_mem; /* Pointer to free memory */ - BUFFER_Q svc_buffer; /* buffer queue used for service database */ - UINT32 mem_free; /* Memory still available */ - UINT16 end_handle; /* Last handle number */ - UINT16 next_handle; /* Next usable handle value */ + void *p_attr_list; /* pointer to the first attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + UINT8 *p_free_mem; /* Pointer to free memory */ + BUFFER_Q svc_buffer; /* buffer queue used for service database */ + UINT32 mem_free; /* Memory still available */ + UINT16 end_handle; /* Last handle number */ + UINT16 next_handle; /* Next usable handle value */ } tGATT_SVC_DB; /* Data Structure used for GATT server */ @@ -218,14 +220,14 @@ typedef struct { /* A service registration information record consists of beginning and ending */ /* attribute handle, service UUID and a set of GATT server callback. */ typedef struct { - tGATT_SVC_DB *p_db; /* pointer to the service database */ + tGATT_SVC_DB *p_db; /* pointer to the service database */ tBT_UUID app_uuid; /* applicatino UUID */ - UINT32 sdp_handle; /* primamry service SDP handle */ + UINT32 sdp_handle; /* primamry service SDP handle */ UINT16 service_instance; /* service instance number */ - UINT16 type; /* service type UUID, primary or secondary */ - UINT16 s_hdl; /* service starting handle */ - UINT16 e_hdl; /* service ending handle */ - tGATT_IF gatt_if; /* this service is belong to which application */ + UINT16 type; /* service type UUID, primary or secondary */ + UINT16 s_hdl; /* service starting handle */ + UINT16 e_hdl; /* service ending handle */ + tGATT_IF gatt_if; /* this service is belong to which application */ BOOLEAN in_use; } tGATT_SR_REG; @@ -340,7 +342,7 @@ typedef struct { tGATT_CH_STATE ch_state; UINT8 ch_flags; - tGATT_IF app_hold_link[GATT_MAX_APPS]; + tGATT_IF app_hold_link[GATT_MAX_APPS]; /* server needs */ /* server response data */ @@ -350,13 +352,13 @@ typedef struct { TIMER_LIST_ENT conf_timer_ent; /* peer confirm to indication timer */ - UINT8 prep_cnt[GATT_MAX_APPS]; - UINT8 ind_count; + UINT8 prep_cnt[GATT_MAX_APPS]; + UINT8 ind_count; - tGATT_CMD_Q cl_cmd_q[GATT_CL_MAX_LCB]; - TIMER_LIST_ENT ind_ack_timer_ent; /* local app confirm to indication timer */ - UINT8 pending_cl_req; - UINT8 next_slot_inq; /* index of next available slot in queue */ + tGATT_CMD_Q cl_cmd_q[GATT_CL_MAX_LCB]; + TIMER_LIST_ENT ind_ack_timer_ent; /* local app confirm to indication timer */ + UINT8 pending_cl_req; + UINT8 next_slot_inq; /* index of next available slot in queue */ BOOLEAN in_use; UINT8 tcb_idx; @@ -579,6 +581,7 @@ extern BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb); /* reserved handle list */ extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle); +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle); extern tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void); extern void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p); extern BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value); @@ -664,12 +667,27 @@ extern void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act); /* gatt_db.c */ extern BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri, UINT16 s_hdl, UINT16 num_handle); extern UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, tBT_UUID service); -extern UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, tGATT_CHAR_PROP property, tBT_UUID *p_char_uuid); -extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, tBT_UUID *p_dscp_uuid); +extern UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tGATT_CHAR_PROP property, + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); +extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tBT_UUID *p_dscp_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); + +extern tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value); + +extern tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value); +extern BOOLEAN gatts_is_auto_response(UINT16 attr_handle); extern tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, BT_HDR *p_rsp, UINT16 s_handle, UINT16 e_handle, tBT_UUID type, UINT16 *p_len, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id, UINT16 *p_cur_handle); extern tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, UINT8 *p_value, UINT16 *p_len, UINT16 mtu, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id); +extern tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len); extern tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, UINT8 *p_data, UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size); extern tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, BOOLEAN is_long, UINT16 handle, tGATT_SEC_FLAG sec_flag, UINT8 key_size); diff --git a/components/bt/bluedroid/stack/include/gatt_api.h b/components/bt/bluedroid/stack/include/gatt_api.h index a365ca9deec..c460a655387 100644 --- a/components/bt/bluedroid/stack/include/gatt_api.h +++ b/components/bt/bluedroid/stack/include/gatt_api.h @@ -63,6 +63,8 @@ #define GATT_ENCRYPED_NO_MITM 0x8d #define GATT_NOT_ENCRYPTED 0x8e #define GATT_CONGESTED 0x8f +#define GATT_STACK_RSP 0x90 +#define GATT_APP_RSP 0x91 /* 0xE0 ~ 0xFC reserved for future use */ #define GATT_CCC_CFG_ERR 0xFD /* Client Characteristic Configuration Descriptor Improperly Configured */ @@ -117,36 +119,36 @@ typedef UINT16 tGATT_DISCONN_REASON; /* MAX GATT MTU size */ #ifndef GATT_MAX_MTU_SIZE -#define GATT_MAX_MTU_SIZE 517 +#define GATT_MAX_MTU_SIZE 517 #endif /* max legth of an attribute value */ #ifndef GATT_MAX_ATTR_LEN -#define GATT_MAX_ATTR_LEN 600 +#define GATT_MAX_ATTR_LEN 600 #endif /* default GATT MTU size over LE link */ -#define GATT_DEF_BLE_MTU_SIZE 23 +#define GATT_DEF_BLE_MTU_SIZE 23 /* invalid connection ID */ -#define GATT_INVALID_CONN_ID 0xFFFF +#define GATT_INVALID_CONN_ID 0xFFFF #ifndef GATT_CL_MAX_LCB -#define GATT_CL_MAX_LCB 12 // 22 +#define GATT_CL_MAX_LCB 12 // 22 #endif #ifndef GATT_MAX_SCCB -#define GATT_MAX_SCCB 10 +#define GATT_MAX_SCCB 10 #endif /* GATT notification caching timer, default to be three seconds */ #ifndef GATTC_NOTIF_TIMEOUT -#define GATTC_NOTIF_TIMEOUT 3 +#define GATTC_NOTIF_TIMEOUT 3 #endif /***************************************************************************** @@ -155,22 +157,22 @@ typedef UINT16 tGATT_DISCONN_REASON; /* Attribute permissions */ -#define GATT_PERM_READ (1 << 0) /* bit 0 */ -#define GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 */ -#define GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 */ -#define GATT_PERM_WRITE (1 << 4) /* bit 4 */ -#define GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 */ -#define GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 */ -#define GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 */ -#define GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 */ +#define GATT_PERM_READ (1 << 0) /* bit 0 */ +#define GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 */ +#define GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 */ +#define GATT_PERM_WRITE (1 << 4) /* bit 4 */ +#define GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 */ +#define GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 */ +#define GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 */ +#define GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 */ typedef UINT16 tGATT_PERM; #define GATT_ENCRYPT_KEY_SIZE_MASK (0xF000) /* the MS nibble of tGATT_PERM; key size 7=0; size 16=9 */ -#define GATT_READ_ALLOWED (GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) -#define GATT_READ_AUTH_REQUIRED (GATT_PERM_READ_ENCRYPTED) -#define GATT_READ_MITM_REQUIRED (GATT_PERM_READ_ENC_MITM) -#define GATT_READ_ENCRYPTED_REQUIRED (GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) +#define GATT_READ_ALLOWED (GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) +#define GATT_READ_AUTH_REQUIRED (GATT_PERM_READ_ENCRYPTED) +#define GATT_READ_MITM_REQUIRED (GATT_PERM_READ_ENC_MITM) +#define GATT_READ_ENCRYPTED_REQUIRED (GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) #define GATT_WRITE_ALLOWED (GATT_PERM_WRITE | GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_ENC_MITM | \ @@ -312,6 +314,16 @@ typedef struct { UINT8 value[GATT_MAX_ATTR_LEN]; /* the actual attribute value */ } tGATT_VALUE; +typedef struct{ + UINT16 attr_max_len; + UINT16 attr_len; + UINT8 *attr_val; +}tGATT_ATTR_VAL; + +typedef struct{ + uint8_t auto_rsp; +}tGATTS_ATTR_CONTROL; + /* Union of the event data which is used in the server respond API to carry the server response information */ typedef union { @@ -740,8 +752,9 @@ extern UINT16 GATTS_AddIncludeService (UINT16 service_handle, ** characteristic failed. ** *******************************************************************************/ -extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *char_uuid, - tGATT_PERM perm, tGATT_CHAR_PROP property); +extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, + tGATT_PERM perm, tGATT_CHAR_PROP property, + tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -763,7 +776,8 @@ extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *char_uui ** *******************************************************************************/ extern UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, tGATT_PERM perm, - tBT_UUID *p_descr_uuid); + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); /******************************************************************************* ** @@ -866,6 +880,39 @@ extern tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, tGATT_STATUS status, tGATTS_RSP *p_msg); +/******************************************************************************* +** +** Function GATTS_SetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle:the attribute handle +** length: the attribute length +** value: the value to be set to the attribute in the database +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value); + + +/******************************************************************************* +** +** Function GATTS_GetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value); + + + /*******************************************************************************/ /* GATT Profile Client Functions */ /*******************************************************************************/ diff --git a/components/bt/bluedroid/stack/smp/smp_main.c b/components/bt/bluedroid/stack/smp/smp_main.c index 85b232aea8a..cad6427464f 100644 --- a/components/bt/bluedroid/stack/smp/smp_main.c +++ b/components/bt/bluedroid/stack/smp/smp_main.c @@ -419,7 +419,7 @@ static const UINT8 smp_master_create_local_sec_conn_oob_data[][SMP_SM_NUM_COLS] static const UINT8 smp_slave_entry_map[][SMP_STATE_MAX] = { /* state name: Idle WaitApp SecReq Pair Wait Confirm Rand PublKey SCPhs1 Wait Wait SCPhs2 Wait DHKChk Enc Bond CrLocSc Rsp Pend ReqRsp Cfm Exch Strt Cmtm Nonce Strt DHKChk Pend Pend OobData */ - /* PAIR_REQ */{ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* PAIR_REQ */{ 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PAIR_RSP */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* CONFIRM */{ 0, 4, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* RAND */{ 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, diff --git a/docs/api/esp_blufi.rst b/docs/api/esp_blufi.rst index 9dd80851665..13432dc1542 100644 --- a/docs/api/esp_blufi.rst +++ b/docs/api/esp_blufi.rst @@ -6,8 +6,7 @@ Overview BLUFI is a profile based GATT to config ESP32 WIFI to connect/disconnect AP or setup a softap and etc. Use should concern these things: 1. The event sent from profile. Then you need to do something as the event indicate. -2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. -Even you can define the "Key Exchange/Negotiation" procedure. +2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. Even you can define the "Key Exchange/Negotiation" procedure. Application Example ------------------- diff --git a/docs/api/esp_gatt_defs.rst b/docs/api/esp_gatt_defs.rst index 637acaab990..9eeac6193ca 100644 --- a/docs/api/esp_gatt_defs.rst +++ b/docs/api/esp_gatt_defs.rst @@ -84,6 +84,8 @@ Macros .. doxygendefine:: ESP_GATT_UUID_SCAN_INT_WINDOW .. doxygendefine:: ESP_GATT_UUID_SCAN_REFRESH .. doxygendefine:: ESP_GATT_ILLEGAL_UUID +.. doxygendefine:: ESP_GATT_ILLEGAL_HANDLE +.. doxygendefine:: ESP_GATT_ATTR_HANDLE_MAX .. doxygendefine:: ESP_GATT_MAX_ATTR_LEN .. doxygendefine:: ESP_GATT_IF_NONE @@ -106,6 +108,27 @@ Enumerations Structures ^^^^^^^^^^ +.. doxygenstruct:: esp_attr_desc_t + :members: + +.. doxygenstruct:: esp_attr_control_t + :members: + +.. doxygenstruct:: esp_gatts_attr_db_t + :members: + +.. doxygenstruct:: esp_attr_value_t + :members: + +.. doxygenstruct:: esp_gatts_incl_svc_desc_t + :members: + +.. doxygenstruct:: esp_gatts_incl128_svc_desc_t + :members: + +.. doxygenstruct:: esp_gatts_char_desc_t + :members: + .. doxygenstruct:: esp_gatt_value_t :members: diff --git a/docs/api/esp_gatts.rst b/docs/api/esp_gatts.rst index fbaa1c236db..9e3749df0ac 100644 --- a/docs/api/esp_gatts.rst +++ b/docs/api/esp_gatts.rst @@ -80,6 +80,9 @@ Structures .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_add_char_descr_evt_param :members: +.. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_add_attr_tab_evt_param + :members: + .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_delete_evt_param :members: @@ -101,6 +104,9 @@ Structures .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_rsp_evt_param :members: +.. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_set_attr_val_evt_param + :members: + Functions ^^^^^^^^^ @@ -109,6 +115,7 @@ Functions .. doxygenfunction:: esp_ble_gatts_app_register .. doxygenfunction:: esp_ble_gatts_app_unregister .. doxygenfunction:: esp_ble_gatts_create_service +.. doxygenfunction:: esp_ble_gatts_create_attr_tab .. doxygenfunction:: esp_ble_gatts_add_included_service .. doxygenfunction:: esp_ble_gatts_add_char .. doxygenfunction:: esp_ble_gatts_add_char_descr @@ -117,6 +124,8 @@ Functions .. doxygenfunction:: esp_ble_gatts_stop_service .. doxygenfunction:: esp_ble_gatts_send_indicate .. doxygenfunction:: esp_ble_gatts_send_response +.. doxygenfunction:: esp_ble_gatts_set_attr_value +.. doxygenfunction:: esp_ble_gatts_get_attr_value .. doxygenfunction:: esp_ble_gatts_open .. doxygenfunction:: esp_ble_gatts_close diff --git a/examples/14_gatt_server/main/gatts_demo.c b/examples/14_gatt_server/main/gatts_demo.c index 9b3b6cc802a..98feaa22403 100644 --- a/examples/14_gatt_server/main/gatts_demo.c +++ b/examples/14_gatt_server/main/gatts_demo.c @@ -48,6 +48,19 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i #define TEST_DEVICE_NAME "ESP_GATTS_DEMO" #define TEST_MANUFACTURER_DATA_LEN 17 + +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 + +uint8_t char1_str[] ={0x11,0x22,0x33}; + +esp_attr_value_t gatts_demo_char1_val = +{ + .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX, + .attr_len = sizeof(char1_str), + .attr_value = char1_str, +}; + + static uint8_t test_service_uuid128[32] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value @@ -175,20 +188,30 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, - ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY, + &gatts_demo_char1_val, NULL); break; case ESP_GATTS_ADD_INCL_SRVC_EVT: break; - case ESP_GATTS_ADD_CHAR_EVT: - ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", - param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + case ESP_GATTS_ADD_CHAR_EVT: { + uint16_t length = 0; + const uint8_t *prf_char; + ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + esp_ble_gatts_get_attr_value(param->add_char.attr_handle, &length, &prf_char); + + ESP_LOGI(GATTS_TAG, "the gatts demo char length = %x\n", length); + for(int i = 0; i < length; i++){ + ESP_LOGI(GATTS_TAG, "prf_char[%x] =%x\n",i,prf_char[i]); + } esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid, - ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL); break; + } case ESP_GATTS_ADD_CHAR_DESCR_EVT: ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); @@ -269,7 +292,8 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, - ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY, + NULL, NULL); break; case ESP_GATTS_ADD_INCL_SRVC_EVT: break; @@ -281,7 +305,8 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid, - ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + NULL, NULL); break; case ESP_GATTS_ADD_CHAR_DESCR_EVT: ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", diff --git a/examples/30_gatt_server_table_create/Makefile b/examples/30_gatt_server_table_create/Makefile new file mode 100644 index 00000000000..a6e41ff33ac --- /dev/null +++ b/examples/30_gatt_server_table_create/Makefile @@ -0,0 +1,11 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := gatt_server_table_creat_demo + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/30_gatt_server_table_create/README.rst b/examples/30_gatt_server_table_create/README.rst new file mode 100644 index 00000000000..7a763998d74 --- /dev/null +++ b/examples/30_gatt_server_table_create/README.rst @@ -0,0 +1,10 @@ +ESP-IDF GATT SERVER create attribute table demo +=============================================== + +This is the demo for user to use ESP_APIs to create a GATT Server attribute table. +The table is easy to use to create GATT server service database without use each "attribute create" functions. +Actually, there are two way to create server service and characteristics. +One is use the esp_gatts_create_service or esp_ble_gatts_add_char and etc. +The other way is use esp_ble_gatts_create_attr_tab. +The important things: the two ways cannot use in the same service, but can use in different service. + diff --git a/examples/30_gatt_server_table_create/main/component.mk b/examples/30_gatt_server_table_create/main/component.mk new file mode 100644 index 00000000000..79edf031d3d --- /dev/null +++ b/examples/30_gatt_server_table_create/main/component.mk @@ -0,0 +1,8 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# diff --git a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c new file mode 100644 index 00000000000..f4511f68a59 --- /dev/null +++ b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c @@ -0,0 +1,340 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "bt.h" +#include "bta_api.h" + +#include "esp_gap_ble_api.h" +#include "esp_gatts_api.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "esp_bt_main.h" +#include "gatts_table_creat_demo.h" + + +#define HEART_PROFILE_NUM 1 +#define HEART_PROFILE_APP_IDX 0 +#define ESP_HEART_RATE_APP_ID 0x55 +#define SAMPLE_DEVICE_NAME "ESP_HEART_RATE" +#define SAMPLE_MANUFACTURER_DATA_LEN 17 +#define HEART_RATE_SVC_INST_ID 0 + +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 + +uint8_t char1_str[] ={0x11,0x22,0x33}; + +uint16_t heart_rate_handle_table[HRS_IDX_NB]; + +esp_attr_value_t gatts_demo_char1_val = +{ + .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX, + .attr_len = sizeof(char1_str), + .attr_value = char1_str, +}; + + +static uint8_t heart_rate_service_uuid[16] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x18, 0x0D, 0x00, 0x00, +}; + + +static esp_ble_adv_data_t heart_rate_adv_config = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x20, + .max_interval = 0x40, + .appearance = 0x00, + .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, + .p_manufacturer_data = NULL, //&test_manufacturer[0], + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 32, + .p_service_uuid = heart_rate_service_uuid, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), +}; + +static esp_ble_adv_params_t heart_rate_adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x40, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + //.peer_addr = + //.peer_addr_type = + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +struct gatts_profile_inst { + esp_gatts_cb_t gatts_cb; + uint16_t gatts_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_handle; + esp_gatt_srvc_id_t service_id; + uint16_t char_handle; + esp_bt_uuid_t char_uuid; + esp_gatt_perm_t perm; + esp_gatt_char_prop_t property; + uint16_t descr_handle; + esp_bt_uuid_t descr_uuid; +}; + +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ +static struct gatts_profile_inst heart_rate_profile_tab[HEART_PROFILE_NUM] = { + [HEART_PROFILE_APP_IDX] = { + .gatts_cb = gatts_profile_event_handler, + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, + +}; + +/* + * HTPT PROFILE ATTRIBUTES + **************************************************************************************** + */ + +/// Full HRS Database Description - Used to add attributes into the database +const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = +{ + // Heart Rate Service Declaration + [HRS_IDX_SVC] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_UUID_PRI_SERVICE}}, + ESP_GATT_PERM_READ, + sizeof(heart_rate_svc), + sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}}, + + // Heart Rate Measurement Characteristic Declaration + [HRS_IDX_HR_MEAS_CHAR] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_DECLARE}}, + ESP_GATT_PERM_READ, + sizeof(heart_rate_meas_char), + sizeof(heart_rate_meas_char), (uint8_t *)&heart_rate_meas_char}}, + // Heart Rate Measurement Characteristic Value + [HRS_IDX_HR_MEAS_VAL] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_MEAS}}, + ESP_GATT_PERM_READ, + HRPS_HT_MEAS_MAX_LEN, + 0, NULL}}, + + // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor + [HRS_IDX_HR_MEAS_NTF_CFG] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_SRVR_CONFIG}}, + ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, + sizeof(uint16_t), + 0, NULL}}, + + // Body Sensor Location Characteristic Declaration + [HRS_IDX_BOBY_SENSOR_LOC_CHAR] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_DECLARE}}, + ESP_GATT_PERM_READ, + sizeof(heart_rate_body_sensor_loc_char), + sizeof(heart_rate_body_sensor_loc_char), (uint8_t *)&heart_rate_body_sensor_loc_char}}, + + // Body Sensor Location Characteristic Value + [HRS_IDX_BOBY_SENSOR_LOC_VAL] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_BODY_SENSOR_LOCATION}}, + ESP_GATT_PERM_READ, + sizeof(uint8_t), + 0, NULL}}, + + // Heart Rate Control Point Characteristic Declaration + [HRS_IDX_HR_CTNL_PT_CHAR] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_DECLARE}}, + ESP_GATT_PERM_READ, + sizeof(heart_rate_cntl_point_char), + sizeof(heart_rate_cntl_point_char), (uint8_t *)&heart_rate_cntl_point_char}}, + + // Heart Rate Control Point Characteristic Value + [HRS_IDX_HR_CTNL_PT_VAL] = {{ESP_GATT_AUTO_RSP}, { + {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_CNTL_POINT}}, + ESP_GATT_PERM_WRITE, + sizeof(uint8_t), + 0, NULL}}, +}; + +/* + * Heart Rate PROFILE ATTRIBUTES + **************************************************************************************** + */ + +/// Heart Rate Sensor Service +const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC; + +/// Heart Rate Sensor Service - Heart Rate Measurement Characteristic +const esp_gatts_char_desc_t heart_rate_meas_char = +{ + .prop = ESP_GATT_CHAR_PROP_BIT_NOTIFY, + .attr_hdl = 0, + .attr_uuid = {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_MEAS}}, +}; + +/// Heart Rate Sensor Service -Body Sensor Location characteristic +const esp_gatts_char_desc_t heart_rate_body_sensor_loc_char = +{ + .prop = ESP_GATT_CHAR_PROP_BIT_READ, + .attr_hdl = 0, + .attr_uuid = {ESP_UUID_LEN_16, {ESP_GATT_BODY_SENSOR_LOCATION}}, +}; + +/// Heart Rate Sensor Service - Heart Rate Control Point characteristic +const esp_gatts_char_desc_t heart_rate_cntl_point_char = +{ + .prop = ESP_GATT_CHAR_PROP_BIT_WRITE, + .attr_hdl = 0, + .attr_uuid = {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_CNTL_POINT}}, +}; + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + LOG_ERROR("GAP_EVT, event %d\n", event); + + switch (event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + esp_ble_gap_start_advertising(&heart_rate_adv_params); + break; + default: + break; + } +} + +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + LOG_ERROR("event = %x\n",event); + switch (event) { + case ESP_GATTS_REG_EVT: + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gap_config_adv_data(&heart_rate_adv_config); + + LOG_INFO("%s %d\n", __func__, __LINE__); + esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if, + HRS_IDX_NB, HEART_RATE_SVC_INST_ID); + break; + case ESP_GATTS_READ_EVT: + + break; + case ESP_GATTS_WRITE_EVT: + break; + case ESP_GATTS_EXEC_WRITE_EVT: + break; + case ESP_GATTS_MTU_EVT: + break; + case ESP_GATTS_CONF_EVT: + break; + case ESP_GATTS_UNREG_EVT: + break; + case ESP_GATTS_DELETE_EVT: + break; + case ESP_GATTS_START_EVT: + break; + case ESP_GATTS_STOP_EVT: + break; + case ESP_GATTS_CONNECT_EVT: + break; + case ESP_GATTS_DISCONNECT_EVT: + break; + case ESP_GATTS_OPEN_EVT: + break; + case ESP_GATTS_CANCEL_OPEN_EVT: + break; + case ESP_GATTS_CLOSE_EVT: + break; + case ESP_GATTS_LISTEN_EVT: + break; + case ESP_GATTS_CONGEST_EVT: + break; + case ESP_GATTS_CREAT_ATTR_TAB_EVT:{ + LOG_ERROR("The number handle =%x\n",param->add_attr_tab.num_handle); + if(param->add_attr_tab.num_handle == HRS_IDX_NB){ + memcpy(heart_rate_handle_table, param->add_attr_tab.handles, + sizeof(heart_rate_handle_table)); + esp_ble_gatts_start_service(heart_rate_handle_table[HRS_IDX_SVC]); + } + + break; + } + + default: + break; + } +} + + +static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t *param) +{ + LOG_INFO("EVT %d, gatts if %d\n", event, gatts_if); + + /* If event is register event, store the gatts_if for each profile */ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + heart_rate_profile_tab[HEART_PROFILE_APP_IDX].gatts_if = gatts_if; + } else { + LOG_INFO("Reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; + } + } + + do { + int idx; + for (idx = 0; idx < HEART_PROFILE_NUM; idx++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == heart_rate_profile_tab[idx].gatts_if) { + if (heart_rate_profile_tab[idx].gatts_cb) { + heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param); + } + } + } + } while (0); +} + +void app_main() +{ + esp_err_t ret; + + esp_bt_controller_init(); + LOG_INFO("%s init bluetooth\n", __func__); + ret = esp_bluedroid_init(); + if (ret) { + LOG_ERROR("%s init bluetooth failed\n", __func__); + return; + } + ret = esp_bluedroid_enable(); + if (ret) { + LOG_ERROR("%s enable bluetooth failed\n", __func__); + return; + } + + esp_ble_gatts_register_callback(gatts_event_handler); + esp_ble_gap_register_callback(gap_event_handler); + esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID); + return; +} diff --git a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h new file mode 100644 index 00000000000..ecce340df7c --- /dev/null +++ b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h @@ -0,0 +1,57 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include +#include +#include + +/* + * DEFINES + **************************************************************************************** + */ + +#define HRPS_HT_MEAS_MAX_LEN (13) + +#define HRPS_MANDATORY_MASK (0x0F) +#define HRPS_BODY_SENSOR_LOC_MASK (0x30) +#define HRPS_HR_CTNL_PT_MASK (0xC0) + + +///Attributes State Machine +enum +{ + HRS_IDX_SVC, + + HRS_IDX_HR_MEAS_CHAR, + HRS_IDX_HR_MEAS_VAL, + HRS_IDX_HR_MEAS_NTF_CFG, + + HRS_IDX_BOBY_SENSOR_LOC_CHAR, + HRS_IDX_BOBY_SENSOR_LOC_VAL, + + HRS_IDX_HR_CTNL_PT_CHAR, + HRS_IDX_HR_CTNL_PT_VAL, + + HRS_IDX_NB, +}; + + +extern const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB]; +/// Heart Rate Sensor Service - only one instance for now +extern const uint16_t heart_rate_svc; + +extern const esp_gatts_char_desc_t heart_rate_meas_char; +extern const esp_gatts_char_desc_t heart_rate_body_sensor_loc_char; +extern const esp_gatts_char_desc_t heart_rate_cntl_point_char; diff --git a/examples/30_gatt_server_table_create/sdkconfig.defaults b/examples/30_gatt_server_table_create/sdkconfig.defaults new file mode 100644 index 00000000000..e435f383c81 --- /dev/null +++ b/examples/30_gatt_server_table_create/sdkconfig.defaults @@ -0,0 +1,14 @@ +# Override some defaults so BT stack is enabled +# in this example + +# +# BT config +# +CONFIG_BT_ENABLED=y + +# +# ESP32-specific config +# +CONFIG_ESP32_ENABLE_STACK_BT=y +# CONFIG_ESP32_ENABLE_STACK_NONE is not set +CONFIG_MEMMAP_BT=y From 1eed54c7cd0d69378c24640739460b203d5297b3 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Wed, 11 Jan 2017 17:19:25 +0800 Subject: [PATCH 133/167] component/bt : modify bluedroid task to core0 --- components/bt/bluedroid/btc/core/btc_task.c | 2 +- components/bt/bluedroid/hci/hci_hal_h4.c | 2 +- components/bt/bluedroid/hci/hci_layer.c | 2 +- components/bt/bluedroid/stack/btu/btu_init.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 773d7889bb8..a245f54d287 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -120,7 +120,7 @@ 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)); - xTaskCreate(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle); + xTaskCreatePinnedToCore(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle, 0); /* TODO: initial the profile_tab */ diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index eb78cab5d31..e621450423f 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -102,7 +102,7 @@ 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)); - xTaskCreate(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle); + xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, 0); //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 747a5829531..14a36eac813 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -109,7 +109,7 @@ int hci_start_up(void) } xHciHostQueue = xQueueCreate(HCI_HOST_QUEUE_NUM, sizeof(BtTaskEvt_t)); - xTaskCreate(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle); + xTaskCreatePinnedToCore(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle, 0); packet_fragmenter->init(&packet_fragmenter_callbacks); hal->open(&hal_callbacks); diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index 0f13e318a41..2ed561bcf20 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -201,7 +201,7 @@ void BTU_StartUp(void) } xBtuQueue = xQueueCreate(BTU_QUEUE_NUM, sizeof(BtTaskEvt_t)); - xTaskCreate(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle); + xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, 0); btu_task_post(SIG_BTU_START_UP); /* // Continue startup on bt workqueue thread. From 2d8a8bc141a8f098f36f5a9de095e9185eeaee31 Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Wed, 11 Jan 2017 17:21:10 +0800 Subject: [PATCH 134/167] esp32: fixs about auto-reconnect and ap ssid configuration 1. Remove auto-reconnect 2. Use strnlen instead strlen to calculate ssid len in wifi lib --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index f688a5e1b2f..01351616820 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit f688a5e1b2f5e4cb8dd2cdbd8dedf63a74b1d063 +Subproject commit 01351616820811bc08e7f7bd24e448df7897eedf From d6842e537c1830b2d38b23a46effa2368ea5ad40 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 21:30:23 +0800 Subject: [PATCH 135/167] mbedtls: give SHA test slightly more time to run --- components/mbedtls/test/test_mbedtls_sha.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/mbedtls/test/test_mbedtls_sha.c b/components/mbedtls/test/test_mbedtls_sha.c index 721c59b7615..b18169827b9 100644 --- a/components/mbedtls/test/test_mbedtls_sha.c +++ b/components/mbedtls/test/test_mbedtls_sha.c @@ -166,7 +166,7 @@ TEST_CASE("mbedtls SHA self-tests multithreaded", "[mbedtls]") xTaskCreate(tskRunSHASelftests, "SHASelftests2", 8192, NULL, 3, NULL); for(int i = 0; i < 2; i++) { - if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) { + if(!xSemaphoreTake(done_sem, 12000/portTICK_PERIOD_MS)) { TEST_FAIL_MESSAGE("done_sem not released by test task"); } } From ff4a5a14061f4add94f05e416c286096b2dd3edd Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Wed, 11 Jan 2017 18:20:01 +0800 Subject: [PATCH 136/167] wpa2 enterprise: add example for wpa2 enterprise --- components/esp32/lib | 2 +- examples/31_wpa2_enterprise/Makefile | 9 + examples/31_wpa2_enterprise/README.md | 76 +++++++++ .../31_wpa2_enterprise/main/Kconfig.projbuild | 34 ++++ examples/31_wpa2_enterprise/main/component.mk | 12 ++ examples/31_wpa2_enterprise/main/wpa2_ca.pem | 23 +++ .../31_wpa2_enterprise/main/wpa2_client.crt | 70 ++++++++ .../31_wpa2_enterprise/main/wpa2_client.key | 27 +++ .../31_wpa2_enterprise/main/wpa2_client.pem | 57 +++++++ .../main/wpa2_enterprise_main.c | 154 ++++++++++++++++++ .../31_wpa2_enterprise/main/wpa2_server.crt | 70 ++++++++ .../31_wpa2_enterprise/main/wpa2_server.key | 27 +++ .../31_wpa2_enterprise/main/wpa2_server.pem | 57 +++++++ 13 files changed, 617 insertions(+), 1 deletion(-) create mode 100644 examples/31_wpa2_enterprise/Makefile create mode 100644 examples/31_wpa2_enterprise/README.md create mode 100644 examples/31_wpa2_enterprise/main/Kconfig.projbuild create mode 100644 examples/31_wpa2_enterprise/main/component.mk create mode 100644 examples/31_wpa2_enterprise/main/wpa2_ca.pem create mode 100644 examples/31_wpa2_enterprise/main/wpa2_client.crt create mode 100644 examples/31_wpa2_enterprise/main/wpa2_client.key create mode 100644 examples/31_wpa2_enterprise/main/wpa2_client.pem create mode 100644 examples/31_wpa2_enterprise/main/wpa2_enterprise_main.c create mode 100644 examples/31_wpa2_enterprise/main/wpa2_server.crt create mode 100644 examples/31_wpa2_enterprise/main/wpa2_server.key create mode 100644 examples/31_wpa2_enterprise/main/wpa2_server.pem diff --git a/components/esp32/lib b/components/esp32/lib index 01351616820..c01bfe9038e 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 01351616820811bc08e7f7bd24e448df7897eedf +Subproject commit c01bfe9038e59fc0dc15947c1bf4616de006e103 diff --git a/examples/31_wpa2_enterprise/Makefile b/examples/31_wpa2_enterprise/Makefile new file mode 100644 index 00000000000..ff23a939345 --- /dev/null +++ b/examples/31_wpa2_enterprise/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := wpa2-enterprise + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/31_wpa2_enterprise/README.md b/examples/31_wpa2_enterprise/README.md new file mode 100644 index 00000000000..f84f9e070af --- /dev/null +++ b/examples/31_wpa2_enterprise/README.md @@ -0,0 +1,76 @@ +# WPA2 Enterprise Example + +This example shows how ESP32 connects to AP with wpa2 enterprise encryption. Example does the following steps: + +1. Install CA certificate which is optional. +2. Install client certificate and client key which is required in TLS method and optional in PEAP and TTLS methods. +3. Set identity of phase 1 which is optional. +4. Set user name and password of phase 2 which is required in PEAP and TTLS methods. +5. Enable wpa2 enterprise. +6. Connect to AP. + +*Note:* certificate currently is generated when compiling the example and then stored in flash. + +## The file wpa2_ca.pem, wpa2_ca.key, wpa2_server.pem, wpa2_server.crt and wpa2_server.key can be used to configure AP with + wpa2 enterprise encryption. The steps how to generate new certificates and keys using openssl is as follows: + +1. wpa2_ca.pem wpa2_ca.key: + openssl req -new -x509 -keyout wpa2_ca.key -out wpa2_ca.pem +2. wpa2_server.key: + openssl req -new -key wpa2_server.key -out wpa2_server.csr +3. wpa2_csr: + openssl req -new -key server.key -out server.csr +4. wpa2_server.crt: + openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_server.csr -key ca1234 -out wpa2_server.crt -extensions xpserver_ext -extfile xpextensions +5. wpa2_server.p12: + openssl pkcs12 -export -in wpa2_server.crt -inkey wpa2_server.key -out wpa2_server.p12 -passin pass:sv1234 -passout pass:sv1234 +6. wpa2_server.pem: + openssl pkcs12 -in wpa2_server.p12 -out wpa2_server.pem -passin pass:sv1234 -passout pass:sv1234 +7. wpa2_client.key: + openssl genrsa -out wpa2_client.key 1024 +8. wpa2_client.csr: + openssl req -new -key wpa2_client.key -out wpa2_client.csr +9. wpa2_client.crt: + openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_client.csr -key ca1234 -out wpa2_client.crt -extensions xpclient_ext -extfile xpextensions +10. wpa2_client.p12: + openssl pkcs12 -export -in wpa2_client.crt -inkey wpa2_client.key -out wpa2_client.p12 +11. wpa2_client.pem: + openssl pkcs12 -in wpa2_client.p12 -out wpa2_client.pem + +### Example output + +Here is an example of wpa2 enterprise(PEAP method) console output. + +I (1352) example: Setting WiFi configuration SSID wpa2_test... +I (1362) wpa: WPA2 ENTERPRISE VERSION: [v2.0] enable + +I (1362) wifi: rx_ba=1 tx_ba=1 + +I (1372) wifi: mode : sta (24:0a:c4:03:b8:dc) +I (3002) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:11 +I (3642) wifi: state: init -> auth (b0) +I (3642) wifi: state: auth -> assoc (0) +I (3652) wifi: state: assoc -> run (10) +I (3652) wpa: wpa2_task prio:24, stack:6144 + +I (3972) wpa: >>>>>wpa2 FINISH + +I (3982) wpa: wpa2 task delete + +I (3992) wifi: connected with wpa2_test, channel 11 +I (5372) example: ~~~~~~~~~~~ +I (5372) example: IP:0.0.0.0 +I (5372) example: MASK:0.0.0.0 +I (5372) example: GW:0.0.0.0 +I (5372) example: ~~~~~~~~~~~ +I (6832) event: ip: 192.168.1.112, mask: 255.255.255.0, gw: 192.168.1.1 +I (7372) example: ~~~~~~~~~~~ +I (7372) example: IP:192.168.1.112 +I (7372) example: MASK:255.255.255.0 +I (7372) example: GW:192.168.1.1 +I (7372) example: ~~~~~~~~~~~ +I (9372) example: ~~~~~~~~~~~ +I (9372) example: IP:192.168.1.112 +I (9372) example: MASK:255.255.255.0 +I (9372) example: GW:192.168.1.1 +I (9372) example: ~~~~~~~~~~~ diff --git a/examples/31_wpa2_enterprise/main/Kconfig.projbuild b/examples/31_wpa2_enterprise/main/Kconfig.projbuild new file mode 100644 index 00000000000..06fab0bfa1f --- /dev/null +++ b/examples/31_wpa2_enterprise/main/Kconfig.projbuild @@ -0,0 +1,34 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "wpa2_test" + help + SSID (network name) for the example to connect to. + +config EAP_METHOD + int "EAP METHOD" + default 1 + help + EAP method (TLS, PEAP or TTLS) for the example to use. + TLS: 0, PEAP: 1, TTLS: 2 + +config EAP_ID + string "EAP ID" + default "example@espressif.com" + help + Identity in phase 1 of EAP procedure. + +config EAP_USERNAME + string "EAP USERNAME" + default "espressif" + help + Username for EAP method (PEAP and TTLS). + +config EAP_PASSWORD + string "EAP PASSWORD" + default "test11" + help + Password for EAP method (PEAP and TTLS). + +endmenu \ No newline at end of file diff --git a/examples/31_wpa2_enterprise/main/component.mk b/examples/31_wpa2_enterprise/main/component.mk new file mode 100644 index 00000000000..aab8ff8f386 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/component.mk @@ -0,0 +1,12 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := wpa2_ca.pem +COMPONENT_EMBED_TXTFILES += wpa2_client.crt +COMPONENT_EMBED_TXTFILES += wpa2_client.key + + diff --git a/examples/31_wpa2_enterprise/main/wpa2_ca.pem b/examples/31_wpa2_enterprise/main/wpa2_ca.pem new file mode 100644 index 00000000000..c36b97e974f --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_ca.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCA0WgAwIBAgIJANe5ZSCKoB8fMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD +VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT +BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs +ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTE2MTEyMzAyNTUwN1oXDTE3MDEyMjAyNTUwN1owgZMxCzAJBgNVBAYTAkZSMQ8w +DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh +bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG +A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAL03y7N2GvNDO9BN8fVtdNonp0bMiqpj1D0He5+OTM+9 +3ZTIsJCNrbzhLQrRI3vMW7UDy8U7GeWORN9W4dWYlYiy/NFRp3hNMrbePhVmNIOV +ww4ovGzbD+Xo31gPVkhzQ8I5/jbOIQBmgKMAMZyOMlG9VD6yMmAeYqnZYz68WHKt +AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUf1MLQIzAEZcRsgZlS8sosfmVI+UwgcgG +A1UdIwSBwDCBvYAUf1MLQIzAEZcRsgZlS8sosfmVI+WhgZmkgZYwgZMxCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG +A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl +LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDX +uWUgiqAfHzAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93 +d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA +GepHc7TE/P+5t/cZPn5TTQkWQ/4/1lgQd82lF36RYWSIW3BdAc0zwYWYZaWixxyp +s0YOqwz6PZAGRV+SlYO2f8Kf+C3aZs4YHB0GsmksmFOb8r9d7xcDuOKHoA+QV0Zw +RaK6pttsBAxy7rw3kX/CgTp0Y2puaLdMXv/v9FisCP8= +-----END CERTIFICATE----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_client.crt b/examples/31_wpa2_enterprise/main/wpa2_client.crt new file mode 100644 index 00000000000..7499e6967a5 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_client.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 44 (0x2c) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Nov 23 02:55:07 2016 GMT + Not After : Jan 22 02:55:07 2017 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=user@example.com/emailAddress=user@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ac:41:d4:a2:46:0c:dc:67:1d:7b:89:36:7c:15: + be:a2:c1:fe:4c:f2:fa:af:5d:76:0e:ee:b5:ca:d4: + d3:01:c8:6b:30:50:df:2d:57:17:f4:43:47:97:ca: + f1:8d:f7:c0:9d:56:b3:e7:17:7c:58:59:de:f3:be: + b5:08:5d:f8:3a:ad:83:44:0d:31:c9:f1:3d:f1:9a: + cf:84:0c:ad:d3:be:5c:bd:3d:58:b5:1d:2c:fe:70: + 8d:c5:b0:17:87:d4:8e:85:f7:51:4c:0f:d1:e0:8c: + 7b:a0:25:ab:91:7c:7f:eb:47:73:c9:4b:6c:8b:e6: + c1:06:d5:94:30:63:ec:45:1a:f5:7f:46:2f:b3:84: + 78:5d:1c:37:1a:fa:57:ea:45:5e:45:40:ab:14:c7: + 81:b0:26:3d:7e:cf:da:db:f0:f1:40:a7:a1:4b:54: + f3:96:1b:c9:30:3c:3c:d8:19:ba:c7:df:b1:ad:a2: + d6:17:0a:d6:ed:31:b5:cb:12:39:f5:6e:92:6b:85: + f2:9e:c7:06:6b:bb:89:ed:a7:5f:ec:56:12:46:fd: + 3a:74:d1:d2:31:30:1d:58:19:25:33:ff:11:ea:3a: + 52:33:b1:fb:d3:75:8d:1f:5e:36:a5:35:e0:11:5a: + 4a:2d:97:58:2c:3d:62:3c:32:af:83:69:a9:1a:32: + 1b:b7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 8b:8d:b6:19:ce:6f:6b:9e:1d:03:8b:6b:10:fc:99:d0:7a:2f: + e0:37:ce:b8:a4:e4:b9:a1:c2:36:ff:76:b2:ad:d7:d0:df:d1: + 03:27:93:a7:4e:1e:bf:ed:d2:b7:65:2a:c9:c3:ab:20:aa:e3: + 10:4c:75:3b:c4:02:ab:34:08:6e:61:91:cf:e3:02:35:6a:e5: + f3:25:96:51:92:82:6e:52:81:c1:f1:7b:68:02:b0:ce:f4:ba: + fd:6e:68:35:b3:7e:77:cb:a0:1e:11:5e:58:bf:f3:2a:ed:b3: + 4c:82:21:5e:1b:47:b6:2f:f3:f5:c9:1b:6a:70:44:6d:ff:ad: + a6:e3 +-----BEGIN CERTIFICATE----- +MIIDTjCCAregAwIBAgIBLDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt +cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxB1KJGDNxnHXuJNnwVvqLB/kzy+q9d +dg7utcrU0wHIazBQ3y1XF/RDR5fK8Y33wJ1Ws+cXfFhZ3vO+tQhd+Dqtg0QNMcnx +PfGaz4QMrdO+XL09WLUdLP5wjcWwF4fUjoX3UUwP0eCMe6Alq5F8f+tHc8lLbIvm +wQbVlDBj7EUa9X9GL7OEeF0cNxr6V+pFXkVAqxTHgbAmPX7P2tvw8UCnoUtU85Yb +yTA8PNgZusffsa2i1hcK1u0xtcsSOfVukmuF8p7HBmu7ie2nX+xWEkb9OnTR0jEw +HVgZJTP/Eeo6UjOx+9N1jR9eNqU14BFaSi2XWCw9Yjwyr4NpqRoyG7cCAwEAAaNP +ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov +L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB +gQCLjbYZzm9rnh0Di2sQ/JnQei/gN864pOS5ocI2/3ayrdfQ39EDJ5OnTh6/7dK3 +ZSrJw6sgquMQTHU7xAKrNAhuYZHP4wI1auXzJZZRkoJuUoHB8XtoArDO9Lr9bmg1 +s353y6AeEV5Yv/Mq7bNMgiFeG0e2L/P1yRtqcERt/62m4w== +-----END CERTIFICATE----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_client.key b/examples/31_wpa2_enterprise/main/wpa2_client.key new file mode 100644 index 00000000000..0b8f0d0199d --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEArEHUokYM3Gcde4k2fBW+osH+TPL6r112Du61ytTTAchrMFDf +LVcX9ENHl8rxjffAnVaz5xd8WFne8761CF34Oq2DRA0xyfE98ZrPhAyt075cvT1Y +tR0s/nCNxbAXh9SOhfdRTA/R4Ix7oCWrkXx/60dzyUtsi+bBBtWUMGPsRRr1f0Yv +s4R4XRw3GvpX6kVeRUCrFMeBsCY9fs/a2/DxQKehS1TzlhvJMDw82Bm6x9+xraLW +FwrW7TG1yxI59W6Sa4XynscGa7uJ7adf7FYSRv06dNHSMTAdWBklM/8R6jpSM7H7 +03WNH142pTXgEVpKLZdYLD1iPDKvg2mpGjIbtwIDAQABAoIBAQCMhO9GqUpYia2d +VyOhOcPX1dTzRMuHPwDN0aFvIwo2zB3UvkQxInkiA7hldWJz44W3VEFR5PDEyht8 +Tzgy6SVUCLOqUfEpwag8bYOXPxiWQRY6Mc8pf/FyZrLgb3PilFznoAcru0QEn9VB +oTlCZ4OalSE5NlQIFGemgZhvmTPmcm4OwPW2diBjLtb3AA8eaaw8okWZwr8g4Bcd +el5KX6pZpDRpGQueh3iKaKxYWbxLYK+c30gKWD65tsAqKyVg2Tm1R2c+kFXgizZt +EexD95SGMjSkGg3R05sKv6m71iJhlOzVQ4ZCKm18Kqa7wZuZ4SIehVmKIV0gaupz +gjyr7+NBAoGBAOGjjGI3nxJTZY3O+KeaQo/jqrKowqZxzMOsCgAvW56xDuAvx9TJ +m4428NGubMl/0RwX6TnxJDm6oe+tnOxLIgE/VnsQLiNzQuFJxrs5JYctdGc4uvk2 +KuXDr7tPEYlU/7OLRReov9emydIXJnsGejkIPllUj+DGNjNFqtXh2VoHAoGBAMNv +eSgJSkcM6AUaDuUKaXBL2nkKHNoTtRQ0eCEUds6arKyMo0mSP753FNEuOWToVz1O +oaddSFw81J9t+Xd6XSRbhMj63bQ9nvFKBA1lJfLu+xe3ts0f+vmp1PguOuUHsgNP +aAm/gLPSKUpBO46NG6KhUrZ2ej6AEg7SuGXrDITRAoGBAKK7s6m6d81dvGZ0GT23 +sb3Y8ul7cTdd59JPp77OaQOgqxvhGfxLkxcUZMa1R9xjhMsAK8MQOZIxGk2kJwL8 +hP/lUFfdKYmDvX6CGQQ6iOhfTg6MCb1m5bVkVr9+nSUw2mIBVclkeUftEK2m6Kfd +2hR774u5wzLXgYuk+TrcckfNAoGBAJ9X8hacjH0lnr8aIe7I8HLoxbZOcnuz+b4B +kbiW8M8++W6uNCw2G9b1THnJEG6fqRGJXPASdH8P8eQTTIUHtY2BOOCM+dqNK1xc +FrW9NJXAF+WcmmTgoEaTG9tGBirafV+JjK/1/b+fqJ6sVRzDHDcbBU9ThhQTY6XG +VSZz4H8hAoGBAMeQQjiUlKBnpGt1oTgKDZo58b7ui61yftg+dEAwIKs6eb5X20vZ +Ca4v/zg06k9lKTzyspQjJZuzpMjFUvDK4ReamEvmwQTIc+oYVJm9Af1HUytzrHJH +u0/dDt0eYpZpzrFqxlP+0oXxlegD8REMVvwNCy+4isyCvjogDaYRfJqi +-----END RSA PRIVATE KEY----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_client.pem b/examples/31_wpa2_enterprise/main/wpa2_client.pem new file mode 100644 index 00000000000..37bf7091953 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_client.pem @@ -0,0 +1,57 @@ +Bag Attributes + localKeyID: E1 2F DD 9A 78 71 54 6D 59 57 AA 6A 9F 92 3B 5C CC AB A3 64 +subject=/C=FR/ST=Radius/O=Example Inc./CN=user@example.com/emailAddress=user@example.com +issuer=/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.com/CN=Example Certificate Authority +-----BEGIN CERTIFICATE----- +MIIDTjCCAregAwIBAgIBLDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt +cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxB1KJGDNxnHXuJNnwVvqLB/kzy+q9d +dg7utcrU0wHIazBQ3y1XF/RDR5fK8Y33wJ1Ws+cXfFhZ3vO+tQhd+Dqtg0QNMcnx +PfGaz4QMrdO+XL09WLUdLP5wjcWwF4fUjoX3UUwP0eCMe6Alq5F8f+tHc8lLbIvm +wQbVlDBj7EUa9X9GL7OEeF0cNxr6V+pFXkVAqxTHgbAmPX7P2tvw8UCnoUtU85Yb +yTA8PNgZusffsa2i1hcK1u0xtcsSOfVukmuF8p7HBmu7ie2nX+xWEkb9OnTR0jEw +HVgZJTP/Eeo6UjOx+9N1jR9eNqU14BFaSi2XWCw9Yjwyr4NpqRoyG7cCAwEAAaNP +ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov +L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB +gQCLjbYZzm9rnh0Di2sQ/JnQei/gN864pOS5ocI2/3ayrdfQ39EDJ5OnTh6/7dK3 +ZSrJw6sgquMQTHU7xAKrNAhuYZHP4wI1auXzJZZRkoJuUoHB8XtoArDO9Lr9bmg1 +s353y6AeEV5Yv/Mq7bNMgiFeG0e2L/P1yRtqcERt/62m4w== +-----END CERTIFICATE----- +Bag Attributes + localKeyID: E1 2F DD 9A 78 71 54 6D 59 57 AA 6A 9F 92 3B 5C CC AB A3 64 +Key Attributes: +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIdQ9mH9ZzfJECAggA +MBQGCCqGSIb3DQMHBAhDtvRRe/rZHgSCBMhNLBUX5dw3KAHBXkTJDqa9EjOCbD0i +kMGxvoVE3Vrotoh5rMiGlPg+qOTaKT7kd3Yl6bwxF1sV0GyzuSJn+legjV9oXuVK +rI3NWeZr7KG18IP+ZGPs4fDRKnCiKK6cCopF1mzvRhzts5stlOqQkp1Dv9+A7Gyp +OarBK6x3p5db1mznOMdwzD8vyOxGhE98Zq6yDaDz1yKm6mtOLc8RQ1zbwEE1nkcc +J1R73A77E8dY6Rq7E4ec6d+rHki79yskq6eu30ECzC3VUSn6wdKXKKJgvn1V3dNf +QyRdMwXFVXySaGcBaq3zQp9qW+ISesvEstH9fN/GSzoDqe1OqTbe9pLCUtmVksAg +9z4et+OHYdk1c6X+5VI0ywg4t0qjOCOTacJSzw0/lC8OhNTH0jQDFAoIMOIvLuyO +pdNUcaGiWPKS1WjWEMiPPusrPiDXZ5T88go35rGnZJRUK6ighSdtKPKG3qPAslMo +Rn+eOiA2YJ5AorfkR63PI9MfCJbNVfmeTV/VoPXgjrtVNGtvrV54KeHwwLSJtyfj +xqS1g7aSgTgzfoTgYtzxVwy1g3jJZjYlOd6hKBj+Zzl/7C4cyilI3rrKU1OCokAn +xXFwKu2clrh1n3VHD+TdUP1lJD44uHuhgev2MmwOBGKGGNsGNqK9Jo7PCBeWyBSq +d0vzlbo95mMJ5BxKEJex3pxNeTTtfRBjr0AJLuneUBDvjSfhltIvOeMXEfIPQCtU +MTqjGmpgOTbMI4fEgprH7dULOHyn+Mn37sBMbtYHHk2D4FSJD2FkIP3KrudDf/Yr +ePn2ACne2ot9mW9McvDZGDOq0fyrIMcCNTwxP52Z3CuJucwuAoTwiwSg9ZM3t4E9 +K1Fz10xwzctFip9XxpxADA9M/QRa9VBgt7pt114Z2y+/ba0hOZ9mCrdpcHGH8xkO +kJhcCCg1tOLVl2AmUIVtdMDaJMgskq8tfeRTD6qp/JzKQCiz2OvcOG5ixgAhuOSn +9WjCnM4hARhZ/OVDYvsugwELil6E4zpc7mawtNiMj4P7ad1O+/hDN5yVQiU/XzIg +OJEV//FfnApcigay5Ne/n9K2morPIIDSQdet/2L6Y6DmtwM9B8qbzb/DjHfXK4nf +hvvPXD+DUCOfrj3VdkgwY338anWFFQpYbju8nrIv7+vINys+y8HeiPQp5I9zw1XF +q5tfEt4YfTO8hwqBNZL9yhS46CLgvvWavP1ZfvknvNqcvVvRMo60j0DDeZBW676v +U251knuWH16m+/cozbelscwZxikhzxdYzudWjFlH+phOIIvnmCgEZKtN8OWxFoN/ +6YBLPO1jOkibGKV1GHVg0PHdSwYmc1H5CefPobow9XP/RCd8C9+eBmynzErMmeme +4R2etPlsBt3mpakFoG7U+iNu89e4eOK2pzSrydfBSS6tUFXg5L7W5UrdWQ8vRyuc +aLkwuzdVbM8adlcAyb1MVc+1G5JMJVRfPNNAfJkSti7VUqONoOnoUAUSXGpNwoXa +ddctpxLmwsfjYEJ3OCLjFy06A2ZlikqtMi5H4sNytSp0Mfr06J4ZZmL8T1GHxYSP +Xf51VEqiZpKHeBo7ZqrxKvGvFxzm6mGMy8LPpRfSy88z4rPjmP5qrXTbo9qBeo9G +GlY= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_enterprise_main.c b/examples/31_wpa2_enterprise/main/wpa2_enterprise_main.c new file mode 100644 index 00000000000..7d325c76a08 --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_enterprise_main.c @@ -0,0 +1,154 @@ +/* WiFi Connection Example using WPA2 Enterprise + * + * Original Copyright (C) 2006-2016, ARM Limited, All Rights Reserved, Apache 2.0 License. + * Additions Copyright (C) Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD, Apache 2.0 License. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_wifi.h" +#include "esp_wpa2.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "tcpip_adapter.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" + + You can choose EAP method via 'make menuconfig' according to the + configuration of AP. +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_EAP_METHOD CONFIG_EAP_METHOD + +#define EXAMPLE_EAP_ID CONFIG_EAP_ID +#define EXAMPLE_EAP_USERNAME CONFIG_EAP_USERNAME +#define EXAMPLE_EAP_PASSWORD CONFIG_EAP_PASSWORD + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +/* Constants that aren't configurable in menuconfig */ +#define EAP_PEAP 1 +#define EAP_TTLS 2 + +static const char *TAG = "example"; + +/* CA cert, taken from wpa2_ca.pem + Client cert, taken from wpa2_client.crt + Client key, taken from wpa2_client.key + + The PEM, CRT and KEY file were provided by the person or organization + who configured the AP with wpa2 enterprise. + + To embed it in the app binary, the PEM, CRT and KEY file is named + in the component.mk COMPONENT_EMBED_TXTFILES variable. +*/ +extern uint8_t ca_pem_start[] asm("_binary_wpa2_ca_pem_start"); +extern uint8_t ca_pem_end[] asm("_binary_wpa2_ca_pem_end"); +extern uint8_t client_crt_start[] asm("_binary_wpa2_client_crt_start"); +extern uint8_t client_crt_end[] asm("_binary_wpa2_client_crt_end"); +extern uint8_t client_key_start[] asm("_binary_wpa2_client_key_start"); +extern uint8_t client_key_end[] asm("_binary_wpa2_client_key_end"); + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start; + unsigned int client_crt_bytes = client_crt_end - client_crt_start; + unsigned int client_key_bytes = client_key_end - client_key_start; + + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_ca_cert(ca_pem_start, ca_pem_bytes) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_cert_key(client_crt_start, client_crt_bytes,\ + client_key_start, client_key_bytes, NULL, 0) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EXAMPLE_EAP_ID, strlen(EXAMPLE_EAP_ID)) ); + if (EXAMPLE_EAP_METHOD == EAP_PEAP || EXAMPLE_EAP_METHOD == EAP_TTLS) { + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EXAMPLE_EAP_USERNAME, strlen(EXAMPLE_EAP_USERNAME)) ); + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EXAMPLE_EAP_PASSWORD, strlen(EXAMPLE_EAP_PASSWORD)) ); + } + ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable() ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static void wpa2_enterprise_task(void *pvParameters) +{ + tcpip_adapter_ip_info_t ip; + memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + + while (1) { + vTaskDelay(2000 / portTICK_PERIOD_MS); + + if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == 0) { + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP:"IPSTR, IP2STR(&ip.ip)); + ESP_LOGI(TAG, "MASK:"IPSTR, IP2STR(&ip.netmask)); + ESP_LOGI(TAG, "GW:"IPSTR, IP2STR(&ip.gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + } + } +} + +void app_main() +{ + nvs_flash_init(); + initialise_wifi(); + xTaskCreate(&wpa2_enterprise_task, "wpa2_enterprise_task", 4096, NULL, 5, NULL); +} diff --git a/examples/31_wpa2_enterprise/main/wpa2_server.crt b/examples/31_wpa2_enterprise/main/wpa2_server.crt new file mode 100644 index 00000000000..312a7d6aa8e --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_server.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 43 (0x2b) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Nov 23 02:55:07 2016 GMT + Not After : Jan 22 02:55:07 2017 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=Example Server Certificate/emailAddress=admin@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ca:5d:86:b7:7a:3a:bc:f4:4d:d8:69:8c:25:bf: + d1:d7:33:b7:76:ea:d9:ae:b6:78:14:d6:d5:ca:67: + fd:f4:5c:13:d3:01:b4:bc:92:d5:a0:51:f5:fe:81: + 6f:da:28:33:07:08:20:e9:26:27:c6:ab:43:0b:7f: + ce:7c:3b:c6:9c:a4:6c:85:22:3d:40:84:27:32:d6: + a2:94:ed:14:29:4c:ef:d9:ac:d5:a3:ea:7d:47:76: + 18:57:7c:0a:4a:fe:ba:8f:b8:44:44:a5:62:e8:b0: + dd:59:6b:d2:20:69:f1:64:e1:f6:d0:e5:9e:88:da: + 10:e5:58:18:fc:87:ce:2f:67:f6:9d:f8:ac:da:0f: + 2b:f5:58:30:04:13:1c:b5:71:ce:3d:26:c7:34:03: + 66:38:ca:8d:11:75:f0:0b:14:ab:98:b1:dc:cd:81: + d2:68:33:96:d6:50:4f:a7:19:d0:20:15:5e:e0:18: + 8b:07:83:11:2d:3d:51:14:68:73:cd:f2:70:c6:59: + 50:cf:e1:f5:12:88:d5:71:de:1d:92:2e:7d:d1:8b: + 09:fe:b4:17:bd:7e:73:07:c0:a1:6a:f3:af:80:3b: + e4:d7:62:6d:1c:15:93:92:47:25:bd:f6:50:02:3e: + 9c:00:7d:15:89:f2:38:10:95:f2:ef:09:fa:b5:cf: + 90:63 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 92:f8:06:4b:32:4f:f1:22:18:9c:67:dc:28:03:82:ee:14:0a: + 59:a9:07:bb:1e:44:80:8e:a0:69:28:03:bd:04:87:5f:0c:86: + 80:63:db:47:ea:06:25:9e:7f:67:ef:16:77:37:02:8f:00:6a: + 59:cd:00:06:19:b0:43:34:8c:14:bf:29:fd:e0:8c:57:0f:63: + 7b:73:68:70:8a:13:ff:2d:39:a5:a7:69:fb:7b:13:3a:2c:09: + b5:c1:15:d8:7a:2f:77:33:16:7f:41:08:be:23:61:ac:aa:92: + 3f:38:40:06:87:3c:63:86:16:ba:4a:2d:ea:04:36:5a:fd:c7: + 80:8d +-----BEGIN CERTIFICATE----- +MIIDWTCCAsKgAwIBAgIBKzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT +ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyl2Gt3o6vPRN2GmM +Jb/R1zO3durZrrZ4FNbVymf99FwT0wG0vJLVoFH1/oFv2igzBwgg6SYnxqtDC3/O +fDvGnKRshSI9QIQnMtailO0UKUzv2azVo+p9R3YYV3wKSv66j7hERKVi6LDdWWvS +IGnxZOH20OWeiNoQ5VgY/IfOL2f2nfis2g8r9VgwBBMctXHOPSbHNANmOMqNEXXw +CxSrmLHczYHSaDOW1lBPpxnQIBVe4BiLB4MRLT1RFGhzzfJwxllQz+H1EojVcd4d +ki590YsJ/rQXvX5zB8ChavOvgDvk12JtHBWTkkclvfZQAj6cAH0VifI4EJXy7wn6 +tc+QYwIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG +SIb3DQEBBQUAA4GBAJL4BksyT/EiGJxn3CgDgu4UClmpB7seRICOoGkoA70Eh18M +hoBj20fqBiWef2fvFnc3Ao8AalnNAAYZsEM0jBS/Kf3gjFcPY3tzaHCKE/8tOaWn +aft7EzosCbXBFdh6L3czFn9BCL4jYayqkj84QAaHPGOGFrpKLeoENlr9x4CN +-----END CERTIFICATE----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_server.key b/examples/31_wpa2_enterprise/main/wpa2_server.key new file mode 100644 index 00000000000..e7d9d26200a --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAyl2Gt3o6vPRN2GmMJb/R1zO3durZrrZ4FNbVymf99FwT0wG0 +vJLVoFH1/oFv2igzBwgg6SYnxqtDC3/OfDvGnKRshSI9QIQnMtailO0UKUzv2azV +o+p9R3YYV3wKSv66j7hERKVi6LDdWWvSIGnxZOH20OWeiNoQ5VgY/IfOL2f2nfis +2g8r9VgwBBMctXHOPSbHNANmOMqNEXXwCxSrmLHczYHSaDOW1lBPpxnQIBVe4BiL +B4MRLT1RFGhzzfJwxllQz+H1EojVcd4dki590YsJ/rQXvX5zB8ChavOvgDvk12Jt +HBWTkkclvfZQAj6cAH0VifI4EJXy7wn6tc+QYwIDAQABAoIBAHN/BZBaNoP5eyR0 +uQQigoUsgx1f1HWettJN+x7WU17P3pCjfJ/TvhEONjPXdBsyrkzpGr92g2UkAdPi +Udsd0rE8NqOVcxgLVMPzND+DP+qCerHlrtkqz+2lPSdRKB69W4UDShNTwSvFJdAP +dZICZT+Kp+953g2FR/ddXIVkjQ0IaKN8gD4j+JKpfVGMpKlYdUf8gK6Ag71VtUa5 +XzRDS4A9ytrpNcmaXaOhCVPdgCg20CULgpg6B6dG3XWzC16Erf8rMl0fsAB31K9a +qF87QS14JavdW1vdUFXCyqre9N58NUKxQjuhNADSB2sJEXkP2uhPcL+72Li8deRO +8MJgckECgYEA8ryoRI7bKn907Kx4Go7G4NS3hwuTd9jhhS8iQ1tatkQdqnBXnuWU +X20i02vYQGoZGsOl+DTgY2IWRYBPkGT+RwklJxCvYkZvOCgHaipoWsF+EOptv5Au +4ZERlBCWXzWdEgnx5nCFJQFfWBm77iC+muPpB+SryQmTld/RwvihxBsCgYEA1Wwp +Qq/5urpzz/uwNdMiMJ1lePi2HBvJeZOnw0LU+xUqXUkt6291CLhHNn8okPVeoNZP +iKP0Lid6IO2yp/3iCT5w9NNOMFlyhrVMAxYOkrM8AxlYnCwoCOoqN5x+4RrJLVFL +zrg+VN9vexfkOWdH9t8g+0gNn7MCX3adqy1/WVkCgYA4yXIEN/eGBbNw+xhN/kEA +sEMPUOH7E74OzmwRnfmm0mCuUwHspoEDoiCXnY9F4oxk+oiFfLlSBsx3DgGPIlFq +hPUwInMlZpz2Ykb5y1oGgWXgxzdNrYmKM8oM/aRwOba5VaJF6uT7N0r67WpN11NA +ITmPIywdKCRi163XExulKQKBgHyMgI/AbrbANPH9adofeuZwFFXCn1RMCwn+V3sm +N3DH609Bc6DgDKaoFDcDgkMGTtECAKw3Mjr1ItqwnQBYs169p+HYptqkeKeQiemL +J7oJC06rrgCF7F83eKe3lnv7y8e8l8bt0sJpGn/1c2TklyTFFlROulSmfQ4FBQJu +rNERAoGAec+0Wi5qYT917CPHqXcCUTg35kvtlLlgGdX6kNZRNszZUIF7O+wH4EJx +yxu3cgxZ2FL95Kf/oyOOnlOkRJ/clJbNBVSEHvJh64GL0PZ5V5szsscoGr6KY7SO +/kkJKC3OS/3fpto1/9yjJpoqJp9pzGU48PM0IKgd3ITQE6oOCCg= +-----END RSA PRIVATE KEY----- diff --git a/examples/31_wpa2_enterprise/main/wpa2_server.pem b/examples/31_wpa2_enterprise/main/wpa2_server.pem new file mode 100644 index 00000000000..97d16aec59c --- /dev/null +++ b/examples/31_wpa2_enterprise/main/wpa2_server.pem @@ -0,0 +1,57 @@ +Bag Attributes + localKeyID: 4E 12 CF 3A FA D4 03 64 00 BB 98 1C 78 35 56 4A AC C3 1E 17 +subject=/C=FR/ST=Radius/O=Example Inc./CN=Example Server Certificate/emailAddress=admin@example.com +issuer=/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.com/CN=Example Certificate Authority +-----BEGIN CERTIFICATE----- +MIIDWTCCAsKgAwIBAgIBKzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjExMjMw +MjU1MDdaFw0xNzAxMjIwMjU1MDdaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT +ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyl2Gt3o6vPRN2GmM +Jb/R1zO3durZrrZ4FNbVymf99FwT0wG0vJLVoFH1/oFv2igzBwgg6SYnxqtDC3/O +fDvGnKRshSI9QIQnMtailO0UKUzv2azVo+p9R3YYV3wKSv66j7hERKVi6LDdWWvS +IGnxZOH20OWeiNoQ5VgY/IfOL2f2nfis2g8r9VgwBBMctXHOPSbHNANmOMqNEXXw +CxSrmLHczYHSaDOW1lBPpxnQIBVe4BiLB4MRLT1RFGhzzfJwxllQz+H1EojVcd4d +ki590YsJ/rQXvX5zB8ChavOvgDvk12JtHBWTkkclvfZQAj6cAH0VifI4EJXy7wn6 +tc+QYwIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG +SIb3DQEBBQUAA4GBAJL4BksyT/EiGJxn3CgDgu4UClmpB7seRICOoGkoA70Eh18M +hoBj20fqBiWef2fvFnc3Ao8AalnNAAYZsEM0jBS/Kf3gjFcPY3tzaHCKE/8tOaWn +aft7EzosCbXBFdh6L3czFn9BCL4jYayqkj84QAaHPGOGFrpKLeoENlr9x4CN +-----END CERTIFICATE----- +Bag Attributes + localKeyID: 4E 12 CF 3A FA D4 03 64 00 BB 98 1C 78 35 56 4A AC C3 1E 17 +Key Attributes: +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIxxT4lUY0dvgCAggA +MBQGCCqGSIb3DQMHBAiQ8/bzpj1InASCBMjU0Nl0/CMHnNAWZ6t1yb93vHJUHHsv +0acQNqeN8ToS1Wz0delbNnJMZ5lkO3ei542d2HwbGW4KYgWuMBPh2qgpdnnUfBPE +C6iCtGCDugVgZl3W7VPjQzMjsExuwYaZf7dhBkQEbuzuGXDrsZL3sauAeOla8V34 +yYESS9P7Jyd0prMgDG5yap9acorjiSLpqHsCogp/vlERSl8f/+yxY5dJuLo+54Z6 +Q8GjlpoiEUijy/Gs/BKcyTX3zddJm/yA3PF0JM8ZSH9K0pBa3l2VJYdizWGl3G59 +uozlMVZrg/KxDgfoe7QGit/WUGiq4fYXIUVKSKOfRmpD8YaTaUkGtoao9VjUYXul +01yU3DSRct9O/r1MG9QQjmYSi05t+Cohp/FNd0WqLlKPilDK86Wu4xECjOI+KsOe +kccUDBuaTMxdoYsq5Ge+V79YR1yABYtgkGymR9mS357Za9IlXxExhDBb2ky0MMlx +DfgimcWOlc4IWGrhheezZaTjgUO/Q0izrxD/ZBYNUmSnYDpRj4mk/sxQ6aDytlEu +ZSnTo+jbyXTh8nVwxhOB2PdWNmFOkRcGOJci8MErd8ArdWniw68MoYwhHTabDpwo +0nEs1MhqoiMAgn7iluN+cscV7pz4n5zriZX3Rw1ivcUPQ8RR6y5h/nR9Du5fCyRm +v5vF01w+o+XejU54DyqRptWiR6yHDJer2TIUqTjKt4NQENZyXEMh+ls6arjZ0mRp +i8rv3M/Z8NLcTQ7lD/gAGTWxzsNIfMcBQX9X9R6V4BmzhmQQA+/pbLh2IOOVPXO3 +sUGXChyQSa5xE1VpVle28Q3GqKq03W0W+8EtGy889px9/MGWLfCMrJNCv4zH7d/j +FTRkiNdtBZrmWTyUgBVgQoTVPxyzBBC11HXzRGxiEQft/NFwd0dzKoNfNsgGGa0Q +AzKmPJselaJdqol961z3RbzEpfyqvSnMbui/iIMV2lTEc/EQWoaQ3SGHf3qdhlQ5 +MScGocq+hskrMmgW1XVG3HDowdC5K5sKXUYJmixNGHWXFao/FZUAVBmQD+290Qkl +EMKQ2xw3PJ2tk47EpdscixayDpiOJQEdgys3oA1W06bIpxNaERYObqp8M62fet9I +wuRZyNWuKVyyilyB9sTjmgD55BXt6B+jkmkwHq15RA85AbsHHttugyMA//V+oiYt +B0BmSKpzSr6nEnr+0NiWybWlN97yLHUrtFiLMyKb6+qAMbxdsET6vfWsASuC21Lq +ZHgW3EofIEDz7r59w4NHwCLGBttpgKLkmrvjt17cMdmsEXbWFgw+9pi/uqEaRaq5 +/ekb9uPzbS7o4ArGQ+WBFUA6ONV98i2ZMOmfvq/dgBAQcRLlI7Cp+yikcMbzJ3Aq +VJc53y0Gl8awDXMbOiH0l1ij+3mQ5xPZbuv2ofcg+4enoK1cclG6ryWWGPMDcAg2 +JivgEzn6eFsiPRnlidrJUy6zJYxCsjSPodcbFH7DXnwPA8+C9P1it2bnqPdQWzXA +JiUFtvmRgEVmOAVCbZLkNPa+K0K8Ymzu3ZYchVMduxJh1xNKId+FM2BGOmlYqDUJ +jnGqHciOxd+0crjaPd0isxUgS7bTd3XdQEIkT/yESS9aJEHsnBFkEXsXSDpxpf7a +dE4= +-----END ENCRYPTED PRIVATE KEY----- From dd73a405561ba41649252f87a40eb2285d8061cd Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 22:47:44 +0800 Subject: [PATCH 137/167] build system: fix IDF_VER for projects outside of ESP-IDF tree --- make/project.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/project.mk b/make/project.mk index ee8c0779438..43dab767a47 100644 --- a/make/project.mk +++ b/make/project.mk @@ -178,7 +178,7 @@ endif # Git version of ESP-IDF (of the form v1.0-285-g5c4f707) -IDF_VER := $(shell git describe) +IDF_VER := $(shell git -C $(IDF_PATH) describe) # Set default LDFLAGS From 4a3e160888792809996c5325a1d6fd9d652b3252 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Thu, 22 Dec 2016 02:56:23 +0300 Subject: [PATCH 138/167] esp32: Add core dump saving to flash feature Complimentary changes: 1) Partition table definitions files with core dump partition 2) Special sub-type for core dump partition 3) Special version of spi_flash_xxx 4) espcoredump.py is script to get core dump from flash and print useful info 5) FreeRTOS API was extended to get tasks snapshots --- components/esp32/Kconfig | 18 + components/esp32/core_dump.c | 276 +++++ components/esp32/cpu_start.c | 5 + components/esp32/include/esp_core_dump.h | 21 + components/esp32/include/esp_panic.h | 4 + components/esp32/panic.c | 125 +- components/espcoredump/espcoredump.py | 1013 +++++++++++++++++ .../freertos/include/freertos/FreeRTOS.h | 4 +- .../include/freertos/FreeRTOSConfig.h | 4 +- components/freertos/include/freertos/task.h | 15 + components/freertos/tasks.c | 104 +- components/partition_table/Kconfig.projbuild | 6 +- .../partitions_singleapp_coredump.csv | 6 + .../partitions_two_ota_coredump.csv | 9 + components/spi_flash/cache_utils.c | 39 + components/spi_flash/cache_utils.h | 9 + components/spi_flash/flash_ops.c | 82 +- components/spi_flash/include/esp_partition.h | 1 + components/spi_flash/include/esp_spi_flash.h | 50 + 19 files changed, 1715 insertions(+), 76 deletions(-) create mode 100644 components/esp32/core_dump.c create mode 100644 components/esp32/include/esp_core_dump.h create mode 100755 components/espcoredump/espcoredump.py create mode 100644 components/partition_table/partitions_singleapp_coredump.csv create mode 100644 components/partition_table/partitions_two_ota_coredump.csv diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index bc35ca0e8f6..98115851406 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -54,6 +54,24 @@ config TRACEMEM_RESERVE_DRAM default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS default 0x0 +choice ESP32_COREDUMP_TO_FLASH_OR_UART + prompt "Core dump destination" + default ESP32_ENABLE_COREDUMP_TO_NONE + help + Select place to store core dump: flash, uart or none (to disable core dumps generation). + + If core dump is configured to be stored in flash and custom partition table is used add + corresponding entry to your CSV. For examples, please see predefined partition table CSV descriptions + in the components/partition_table directory. + +config ESP32_ENABLE_COREDUMP_TO_FLASH + bool "Flash" +config ESP32_ENABLE_COREDUMP_TO_UART + bool "UART" +config ESP32_ENABLE_COREDUMP_TO_NONE + bool "None" +endchoice + # Not implemented and/or needs new silicon rev to work config MEMMAP_SPISRAM bool "Use external SPI SRAM chip as main memory" diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c new file mode 100644 index 00000000000..165ec741927 --- /dev/null +++ b/components/esp32/core_dump.c @@ -0,0 +1,276 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +//#include "esp_attr.h" +#include "esp_panic.h" +#include "esp_partition.h" + +#ifdef ESP_PLATFORM +// Uncomment this line to force output from this module +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#include "esp_log.h" +static const char* TAG = "esp_core_dump_init"; +#else +#define ESP_LOGD(...) +#endif + +// TODO: allow user to set this in menuconfig or get tasks iteratively +#define COREDUMP_MAX_TASKS_NUM 32 + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + +// magic numbers to control core dump data consistency +#define COREDUMP_FLASH_MAGIC_START 0xDEADBEEFUL +#define COREDUMP_FLASH_MAGIC_END 0xACDCFEEDUL + +// core dump partition start +static uint32_t s_core_part_start; +// core dump partition size +static uint32_t s_core_part_size; + +static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size) +{ + esp_err_t err; + uint32_t data_len = 0, k, len; + union + { + uint8_t data8[4]; + uint32_t data32; + } rom_data; + + data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); + err = spi_flash_write_panic(off, data, data_len); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write data"); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return 0; + } + + len = data_size % sizeof(uint32_t); + if (len) { + // write last bytes with padding, actual TCB len can be retrieved by esptool from core dump header + rom_data.data32 = 0; + for (k = 0; k < len; k++) + rom_data.data8[k] = *(data + data_len + k); + err = spi_flash_write_panic(off + data_len, &rom_data, sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write data end"); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return 0; + } + data_len += sizeof(uint32_t); + } + + return data_len; +} + +/* + * | MAGIC1 | + * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | + * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | + * . . . . + * . . . . + * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | + * | MAGIC2 | + */ +void esp_core_dump_to_flash(XtExcFrame *frame) +{ + union + { + uint8_t data8[16]; + uint32_t data32[4]; + } rom_data; + //const esp_partition_t *core_part; + esp_err_t err; + TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; + UBaseType_t tcb_sz, task_num; + uint32_t data_len = 0, i, len, sec_num; + size_t off; + + esp_panicPutStr("Save core dump to flash...\r\n"); + task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); + // take TCB padding into account, actual TCB size will be stored in header + if (tcb_sz % sizeof(uint32_t)) + len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); + else + len = tcb_sz; + // header + magic2 + tasknum*(tcb + stack start/end + tcb addr) + data_len = 5*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); + for (i = 0; i < task_num; i++) { + if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { + // set correct stack top for current task + tasks[i].pxTopOfStack = (StackType_t *)frame; + esp_panicPutStr("Current task PC/A0/SP "); + esp_panicPutHex(frame->pc); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a0); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a1); + esp_panicPutStr("\r\n"); + } +#if( portSTACK_GROWTH < 0 ) + len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; +#else + len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; +#endif + esp_panicPutStr("stack len = "); + esp_panicPutHex(len); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxTopOfStack); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxEndOfStack); + esp_panicPutStr("\r\n"); + // take stack padding into account + if (len % sizeof(uint32_t)) + len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); + data_len += len; + } + esp_panicPutStr("Core dump len ="); + esp_panicPutHex(data_len); + esp_panicPutStr("\r\n"); + if (data_len > s_core_part_size) { + esp_panicPutStr("ERROR: Not enough space to save core dump!"); + return; + } + + // TEST READ START + err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to read flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + else { + esp_panicPutStr("Data from flash:\r\n"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + esp_panicPutHex(rom_data.data32[i]); + esp_panicPutStr("\r\n"); + } +// rom_data[4] = 0; +// esp_panicPutStr(rom_data); +// esp_panicPutStr("\r\n"); + } + // TEST READ END + + sec_num = data_len / SPI_FLASH_SEC_SIZE; + if (data_len % SPI_FLASH_SEC_SIZE) + sec_num++; + err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to erase flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + + rom_data.data32[0] = COREDUMP_FLASH_MAGIC_START; + rom_data.data32[1] = data_len; + rom_data.data32[2] = task_num; + rom_data.data32[3] = tcb_sz; + err = spi_flash_write_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write core dump header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + off = sizeof(rom_data); + + for (i = 0; i < task_num; i++) { + esp_panicPutStr("Dump task "); + esp_panicPutHex((int)tasks[i].pxTCB); + esp_panicPutStr("\r\n"); + + // save TCB address, stack base and stack top addr + rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; + rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; + rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; + err = spi_flash_write_panic(s_core_part_start + off, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + off += 3*sizeof(uint32_t); + // save TCB + len = esp_core_dump_write_flash_padded(s_core_part_start + off, tasks[i].pxTCB, tcb_sz); + if (len == 0) + return; + off += len; + // save task stack + /*int k; + for (k = 0; k < 8*4; k++) { + esp_panicPutStr("stack["); + esp_panicPutDec(k); + esp_panicPutStr("] = "); + esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); + esp_panicPutStr("\r\n"); + }*/ + len = esp_core_dump_write_flash_padded(s_core_part_start + off, +#if( portSTACK_GROWTH < 0 ) + tasks[i].pxTopOfStack, + (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack +#else + tasks[i].pxEndOfStack, + (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack +#endif + ); + if (len == 0) + return; + off += len; + } + + rom_data.data32[0] = COREDUMP_FLASH_MAGIC_END; + err = spi_flash_write_panic(s_core_part_start + off, &rom_data, sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("Failed to write to flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + + esp_panicPutStr("Core dump has been saved to flash partition.\r\n"); +} +#endif + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +void esp_core_dump_to_uart(XtExcFrame *frame) +{ +} +#endif + +void esp_core_dump_init() +{ +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + const esp_partition_t *core_part; + + core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); + if (!core_part) { + ESP_LOGE(TAG, "No core dump partition found!"); + return; + } + ESP_LOGI(TAG, "Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size); + s_core_part_start = core_part->address; + s_core_part_size = core_part->size; +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#endif +} + diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 864a17a1b6f..95d5c5e6a2a 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -55,6 +55,7 @@ #include "esp_task_wdt.h" #include "esp_phy_init.h" #include "esp_coexist.h" +#include "esp_core_dump.h" #include "trax.h" #define STRINGIFY(s) STRINGIFY2(s) @@ -214,6 +215,10 @@ void start_cpu0_default(void) } #endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + esp_core_dump_init(); +#endif + xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); diff --git a/components/esp32/include/esp_core_dump.h b/components/esp32/include/esp_core_dump.h new file mode 100644 index 00000000000..d130aa237bc --- /dev/null +++ b/components/esp32/include/esp_core_dump.h @@ -0,0 +1,21 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef ESP_CORE_DUMP_H_ +#define ESP_CORE_DUMP_H_ + +void esp_core_dump_init(); +void esp_core_dump_to_flash(); +void esp_core_dump_to_uart(); + +#endif diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index aa83c6d3811..25816d31f28 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -24,6 +24,10 @@ */ void esp_set_breakpoint_if_jtag(void *fn); +void esp_panicPutchar(char c); +void esp_panicPutStr(const char *c); +void esp_panicPutHex(int a); +void esp_panicPutDec(int a); #define ESP_WATCHPOINT_LOAD 0x40000000 #define ESP_WATCHPOINT_STORE 0x80000000 diff --git a/components/esp32/panic.c b/components/esp32/panic.c index c5b18870a93..94aebfbac46 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -33,6 +33,7 @@ #include "esp_panic.h" #include "esp_attr.h" #include "esp_err.h" +#include "esp_core_dump.h" /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level @@ -46,61 +47,61 @@ #if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... -inline static void panicPutChar(char c) +void esp_panicPutChar(char c) { while (((READ_PERI_REG(UART_STATUS_REG(0)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ; WRITE_PERI_REG(UART_FIFO_REG(0), c); } -inline static void panicPutStr(const char *c) +void esp_panicPutStr(const char *c) { int x = 0; while (c[x] != 0) { - panicPutChar(c[x]); + esp_panicPutChar(c[x]); x++; } } -inline static void panicPutHex(int a) +void esp_panicPutHex(int a) { int x; int c; for (x = 0; x < 8; x++) { c = (a >> 28) & 0xf; if (c < 10) { - panicPutChar('0' + c); + esp_panicPutChar('0' + c); } else { - panicPutChar('a' + c - 10); + esp_panicPutChar('a' + c - 10); } a <<= 4; } } -inline static void panicPutDec(int a) +void esp_panicPutDec(int a) { int n1, n2; n1 = a % 10; n2 = a / 10; if (n2 == 0) { - panicPutChar(' '); + esp_panicPutChar(' '); } else { - panicPutChar(n2 + '0'); + esp_panicPutChar(n2 + '0'); } - panicPutChar(n1 + '0'); + esp_panicPutChar(n1 + '0'); } #else //No printing wanted. Stub out these functions. -inline static void panicPutChar(char c) { } -inline static void panicPutStr(const char *c) { } -inline static void panicPutHex(int a) { } -inline static void panicPutDec(int a) { } +void esp_panicPutChar(char c) { } +void esp_panicPutStr(const char *c) { } +void esp_panicPutHex(int a) { } +void esp_panicPutDec(int a) { } #endif void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { - panicPutStr("***ERROR*** A stack overflow in task "); - panicPutStr((char *)pcTaskName); - panicPutStr(" has been detected.\r\n"); + esp_panicPutStr("***ERROR*** A stack overflow in task "); + esp_panicPutStr((char *)pcTaskName); + esp_panicPutStr(" has been detected.\r\n"); abort(); } @@ -161,39 +162,39 @@ void panicHandler(XtExcFrame *frame) reason = reasons[regs[20]]; } haltOtherCore(); - panicPutStr("Guru Meditation Error: Core "); - panicPutDec(xPortGetCoreID()); - panicPutStr(" panic'ed ("); + esp_panicPutStr("Guru Meditation Error: Core "); + esp_panicPutDec(xPortGetCoreID()); + esp_panicPutStr(" panic'ed ("); if (!abort_called) { - panicPutStr(reason); - panicPutStr(")\r\n"); + esp_panicPutStr(reason); + esp_panicPutStr(")\r\n"); if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) { int debugRsn; asm("rsr.debugcause %0":"=r"(debugRsn)); - panicPutStr("Debug exception reason: "); - if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep "); - if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint "); + esp_panicPutStr("Debug exception reason: "); + if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) esp_panicPutStr("SingleStep "); + if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) esp_panicPutStr("HwBreakpoint "); if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) { //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0. if (debugRsn&(1<<8)) { #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK - panicPutStr("Stack canary watchpoint triggered "); + esp_panicPutStr("Stack canary watchpoint triggered "); #else - panicPutStr("Watchpoint 1 triggered "); + esp_panicPutStr("Watchpoint 1 triggered "); #endif } else { - panicPutStr("Watchpoint 0 triggered "); + esp_panicPutStr("Watchpoint 0 triggered "); } } - if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr "); - panicPutStr("\r\n"); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) esp_panicPutStr("BREAK instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) esp_panicPutStr("BREAKN instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) esp_panicPutStr("DebugIntr "); + esp_panicPutStr("\r\n"); } } else { - panicPutStr("abort)\r\n"); + esp_panicPutStr("abort)\r\n"); } if (esp_cpu_in_ocd_debug_mode()) { @@ -219,25 +220,25 @@ void xt_unhandled_exception(XtExcFrame *frame) int x; haltOtherCore(); - panicPutStr("Guru Meditation Error of type "); + esp_panicPutStr("Guru Meditation Error of type "); x = regs[20]; if (x < 40) { - panicPutStr(edesc[x]); + esp_panicPutStr(edesc[x]); } else { - panicPutStr("Unknown"); + esp_panicPutStr("Unknown"); } - panicPutStr(" occurred on core "); - panicPutDec(xPortGetCoreID()); + esp_panicPutStr(" occurred on core "); + esp_panicPutDec(xPortGetCoreID()); if (esp_cpu_in_ocd_debug_mode()) { - panicPutStr(" at pc="); - panicPutHex(regs[1]); - panicPutStr(". Setting bp and returning..\r\n"); + esp_panicPutStr(" at pc="); + esp_panicPutHex(regs[1]); + esp_panicPutStr(". Setting bp and returning..\r\n"); //Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger //will kick in exactly at the context the error happened. setFirstBreakpoint(regs[1]); return; } - panicPutStr(". Exception was unhandled.\r\n"); + esp_panicPutStr(". Exception was unhandled.\r\n"); commonErrorHandler(frame); } @@ -292,16 +293,16 @@ static void putEntry(uint32_t pc, uint32_t sp) if (pc & 0x80000000) { pc = (pc & 0x3fffffff) | 0x40000000; } - panicPutStr(" 0x"); - panicPutHex(pc); - panicPutStr(":0x"); - panicPutHex(sp); + esp_panicPutStr(" 0x"); + esp_panicPutHex(pc); + esp_panicPutStr(":0x"); + esp_panicPutHex(sp); } static void doBacktrace(XtExcFrame *frame) { uint32_t i = 0, pc = frame->pc, sp = frame->a1; - panicPutStr("\nBacktrace:"); + esp_panicPutStr("\r\nBacktrace:"); /* Do not check sanity on first entry, PC could be smashed. */ putEntry(pc, sp); pc = frame->a0; @@ -317,7 +318,7 @@ static void doBacktrace(XtExcFrame *frame) break; } } - panicPutStr("\n\n"); + esp_panicPutStr("\r\n\r\n"); } /* @@ -341,18 +342,18 @@ static void commonErrorHandler(XtExcFrame *frame) the register window is no longer useful. */ if (!abort_called) { - panicPutStr("Register dump:\r\n"); + esp_panicPutStr("Register dump:\r\n"); for (x = 0; x < 24; x += 4) { for (y = 0; y < 4; y++) { if (sdesc[x + y][0] != 0) { - panicPutStr(sdesc[x + y]); - panicPutStr(": 0x"); - panicPutHex(regs[x + y + 1]); - panicPutStr(" "); + esp_panicPutStr(sdesc[x + y]); + esp_panicPutStr(": 0x"); + esp_panicPutHex(regs[x + y + 1]); + esp_panicPutStr(" "); } + esp_panicPutStr("\r\n"); } - panicPutStr("\r\n"); } } @@ -361,19 +362,27 @@ static void commonErrorHandler(XtExcFrame *frame) #if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); - panicPutStr("Entering gdb stub now.\r\n"); + esp_panicPutStr("Entering gdb stub now.\r\n"); esp_gdbstub_panic_handler(frame); -#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT - panicPutStr("Rebooting...\r\n"); +#else +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + esp_core_dump_to_flash(frame); +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP32_PANIC_SILENT_REBOOT + esp_core_dump_to_uart(frame); +#endif +#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT + esp_panicPutStr("Rebooting...\r\n"); for (x = 0; x < 100; x++) { ets_delay_us(1000); } software_reset(); #else disableAllWdts(); - panicPutStr("CPU halted.\r\n"); + esp_panicPutStr("CPU halted.\r\n"); while (1); #endif +#endif } diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py new file mode 100755 index 00000000000..065b323aa19 --- /dev/null +++ b/components/espcoredump/espcoredump.py @@ -0,0 +1,1013 @@ +#!/usr/bin/env python +# +# ESP32 core dump Utility + +import sys +import os +import argparse +import subprocess +import tempfile +import struct +import array +import errno + +try: + import esptool +except ImportError: + idf_path = os.getenv('IDF_PATH') + if idf_path is None: + print "Esptool is not found! Install it or set proper $IDF_PATH in environment." + sys.exit(2) + sys.path.append('%s/components/esptool_py/esptool' % idf_path) + import esptool + +__version__ = "0.1-dev" + +ESP32_COREDUMP_HDR_FMT = '<4L' +ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF +ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED + + +class Struct(object): + def __init__(self, buf=None): + if buf is None: + buf = b'\0' * self.sizeof() + fields = struct.unpack(self.__class__.fmt, buf[:self.sizeof()]) + self.__dict__.update(zip(self.__class__.fields, fields)) + + def sizeof(self): + return struct.calcsize(self.__class__.fmt) + + def dump(self): + keys = self.__class__.fields + if sys.version_info > (3, 0): + # Convert strings into bytearrays if this is Python 3 + for k in keys: + if type(self.__dict__[k]) is str: + self.__dict__[k] = bytearray(self.__dict__[k], encoding='ascii') + return struct.pack(self.__class__.fmt, *(self.__dict__[k] for k in keys)) + + def __str__(self): + keys = self.__class__.fields + return (self.__class__.__name__ + "({" + + ", ".join("%s:%r" % (k, self.__dict__[k]) for k in keys) + + "})") + + +class Elf32FileHeader(Struct): + """ELF32 File header""" + fields = ("e_ident", + "e_type", + "e_machine", + "e_version", + "e_entry", + "e_phoff", + "e_shoff", + "e_flags", + "e_ehsize", + "e_phentsize", + "e_phnum", + "e_shentsize", + "e_shnum", + "e_shstrndx") + fmt = "<16sHHLLLLLHHHHHH" + + def __init__(self, buf=None): + super(Elf32FileHeader, self).__init__(buf) + if buf is None: + # Fill in sane ELF header for LSB32 + self.e_ident = "\x7fELF\1\1\1\0\0\0\0\0\0\0\0\0" + self.e_version = ESPCoreDumpFile.EV_CURRENT + self.e_ehsize = self.sizeof() + + +class Elf32ProgramHeader(Struct): + """ELF32 Program Header""" + fields = ("p_type", + "p_offset", + "p_vaddr", + "p_paddr", + "p_filesz", + "p_memsz", + "p_flags", + "p_align") + fmt = " 0: + self._read_sections(f, shoff, shstrndx) + else: + self.sections = [] + if phnum > 0: + self._read_program_segments(f, phoff, phentsize, phnum) + else: + self.program_segments = [] + + def _read_sections(self, f, section_header_offs, shstrndx): + f.seek(section_header_offs) + section_header = f.read() + LEN_SEC_HEADER = 0x28 + if len(section_header) == 0: + raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) + if len(section_header) % LEN_SEC_HEADER != 0: + print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs,sec_type,flags,lma,sec_offs,size = struct.unpack_from("= ps.addr and addr < (ps.addr + seg_len): + raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) + if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len): + raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) + # append + self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags)) + + # currently dumps only program segments. + # dumping sections is not supported yet + def dump(self, f): + print "dump to '%s'" % f + # write ELF header + ehdr = Elf32FileHeader() + ehdr.e_type = self.e_type + ehdr.e_machine = self.e_machine + ehdr.e_entry = 0 + ehdr.e_phoff = ehdr.sizeof() + ehdr.e_shoff = 0 + ehdr.e_flags = 0 + ehdr.e_phentsize = Elf32ProgramHeader().sizeof() + ehdr.e_phnum = len(self.program_segments) + ehdr.e_shentsize = 0 + ehdr.e_shnum = 0 + ehdr.e_shstrndx = self.SHN_UNDEF + f.write(ehdr.dump()) + # write program header table + cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize +# print "" % (ehdr.e_ehsize, ehdr.e_phnum, ehdr.e_phentsize) + for i in range(len(self.program_segments)): + print "dump header for seg '%s'" % self.program_segments[i] + phdr = Elf32ProgramHeader() + phdr.p_type = self.program_segments[i].type + phdr.p_offset = cur_off + phdr.p_vaddr = self.program_segments[i].addr + phdr.p_paddr = phdr.p_vaddr # TODO + phdr.p_filesz = len(self.program_segments[i].data) + phdr.p_memsz = phdr.p_filesz # TODO + phdr.p_flags = self.program_segments[i].flags + phdr.p_align = 0 # TODO +# print "header '%s'" % phdr + f.write(phdr.dump()) + cur_off += phdr.p_filesz + # write program segments + for i in range(len(self.program_segments)): + print "dump seg '%s'" % self.program_segments[i] + f.write(self.program_segments[i].data) + + +class ESPCoreDumpError(RuntimeError): + """ + TBD + """ + def __init__(self, message): + super(ESPCoreDumpError, self).__init__(message) + + +class ESPCoreDumpLoaderError(ESPCoreDumpError): + """ + TBD + """ + def __init__(self, message): + super(ESPCoreDumpLoaderError, self).__init__(message) + + +class ESPCoreDumpLoader(object): + """ + TBD + """ + FLASH_READ_BLOCK_SZ = 0x2000 + def __init__(self, off, path=None, chip='esp32', port=None, baud=None): +# print "esptool.__file__ %s" % esptool.__file__ + if not path: + self.path = esptool.__file__ + self.path = self.path[:-1] + else: + self.path = path + self.port = port + self.baud = baud + self.chip = chip + self.fcores = [] + self.fgdbcore = None + self._load_coredump(off) + + def _load_coredump(self, off): + args = [self.path, '-c', self.chip] + if self.port: + args.extend(['-p', self.port]) + if self.baud: + args.extend(['-b', str(self.baud)]) + read_sz = self.FLASH_READ_BLOCK_SZ + read_off = off + args.extend(['read_flash', str(read_off), str(read_sz), '']) + try: + dump_sz = 0 + tot_len = 0 + while True: + fhnd,fname = tempfile.mkstemp() +# print "tmpname %s" % fname +# os.close(fhnd) + args[-1] = fname + et_out = subprocess.check_output(args) + print et_out + # data = os.fdopen(fhnd, 'r').read(sz) + self.fcores.append(os.fdopen(fhnd, 'r')) + if dump_sz == 0: + # read dump length from the first block + dump_sz = self._read_core_dump_length(self.fcores[0]) + tot_len += read_sz + if tot_len >= dump_sz: + break + read_off += read_sz + if dump_sz - tot_len >= self.FLASH_READ_BLOCK_SZ: + read_sz = self.FLASH_READ_BLOCK_SZ + else: + read_sz = dump_sz - tot_len + args[-3] = str(read_off) + args[-2] = str(read_sz) + + except subprocess.CalledProcessError as e: + print "esptool script execution failed with err %d" % e.returncode + print "Command ran: '%s'" % e.cmd + print "Command out:" + print e.output + self.cleanup() + return [] + + def _read_core_dump_length(self, f): + global ESP32_COREDUMP_HDR_FMT + global ESP32_COREDUMP_FLASH_MAGIC_START + print "Read core dump header from '%s'" % f.name + data = f.read(4*4) + mag1,tot_len,task_num,tcbsz = struct.unpack_from(ESP32_COREDUMP_HDR_FMT, data) + if mag1 != ESP32_COREDUMP_FLASH_MAGIC_START: + raise ESPCoreDumpLoaderError("Invalid start magic number!") + return tot_len + + def remove_tmp_file(self, fname): + try: + os.remove(fname) + except OSError as e: + if e.errno != errno.ENOENT: + print "Warning failed to remove temp file '%s'!" % fname + + def _get_registers_from_stack(self, data, grows_down): + # from "gdb/xtensa-tdep.h" + # typedef struct + # { + #0 xtensa_elf_greg_t pc; + #1 xtensa_elf_greg_t ps; + #2 xtensa_elf_greg_t lbeg; + #3 xtensa_elf_greg_t lend; + #4 xtensa_elf_greg_t lcount; + #5 xtensa_elf_greg_t sar; + #6 xtensa_elf_greg_t windowstart; + #7 xtensa_elf_greg_t windowbase; + #8..63 xtensa_elf_greg_t reserved[8+48]; + #64 xtensa_elf_greg_t ar[64]; + # } xtensa_elf_gregset_t; + REG_PC_IDX=0 + REG_PS_IDX=1 + REG_LB_IDX=2 + REG_LE_IDX=3 + REG_LC_IDX=4 + REG_SAR_IDX=5 + REG_WS_IDX=6 + REG_WB_IDX=7 + REG_AR_START_IDX=64 + REG_AR_NUM=64 + # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128, + # but gdb complanis when it less then 129 + REG_NUM=129 + + XT_SOL_EXIT=0 + XT_SOL_PC=1 + XT_SOL_PS=2 + XT_SOL_NEXT=3 + XT_SOL_AR_START=4 + XT_SOL_AR_NUM=4 + XT_SOL_FRMSZ=8 + + XT_STK_EXIT=0 + XT_STK_PC=1 + XT_STK_PS=2 + XT_STK_AR_START=3 + XT_STK_AR_NUM=16 + XT_STK_SAR=19 + XT_STK_EXCCAUSE=20 + XT_STK_EXCVADDR=21 + XT_STK_LBEG=22 + XT_STK_LEND=23 + XT_STK_LCOUNT=24 + XT_STK_FRMSZ=25 + + regs = [0] * REG_NUM + # TODO: support for growing up stacks + if not grows_down: + print "Growing up stacks are not supported for now!" + return regs + # for i in range(REG_NUM): + # regs[i] = i + # return regs + ex_struct = "<%dL" % XT_STK_FRMSZ + if len(data) < struct.calcsize(ex_struct): + print "Too small stack to keep frame: %d bytes!" % len(data) + return regs + + stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)]) + # Stack frame type indicator is always the first item + rc = stack[XT_STK_EXIT] + if rc != 0: + print "EXCSTACKFRAME %d" % rc + regs[REG_PC_IDX] = stack[XT_STK_PC] + regs[REG_PS_IDX] = stack[XT_STK_PS] + for i in range(XT_STK_AR_NUM): + regs[REG_AR_START_IDX + i] = stack[XT_STK_AR_START + i] + regs[REG_SAR_IDX] = stack[XT_STK_SAR] + regs[REG_LB_IDX] = stack[XT_STK_LBEG] + regs[REG_LE_IDX] = stack[XT_STK_LEND] + regs[REG_LC_IDX] = stack[XT_STK_LCOUNT] + print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( + regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], + regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) + else: + print "SOLSTACKFRAME %d" % rc + regs[REG_PC_IDX] = stack[XT_SOL_PC] + regs[REG_PS_IDX] = stack[XT_SOL_PS] + for i in range(XT_SOL_AR_NUM): + regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i] + nxt = stack[XT_SOL_NEXT] + print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( + regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], + regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) + + # TODO: remove magic hack with saved PC to get proper value + regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000) + if regs[REG_PC_IDX] & 0x80000000: + regs[REG_PC_IDX] = (regs[REG_PC_IDX] & 0x3fffffff) | 0x40000000; + if regs[REG_AR_START_IDX + 0] & 0x80000000: + regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000; + return regs + + def cleanup(self): +# if self.fgdbcore: +# self.fgdbcore.close() +# self.remove_tmp_file(self.fgdbcore.name) + for f in self.fcores: + if f: + f.close() + self.remove_tmp_file(f.name) + + def get_corefile_from_flash(self): + """ TBD + """ + global ESP32_COREDUMP_HDR_FMT + ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) + ESP32_COREDUMP_TSK_HDR_FMT = ' stack_top: + stack_len = stack_end - stack_top + stack_base = stack_top + else: + stack_len = stack_top - stack_end + stack_base = stack_end + print "tcb_addr=%x, stack_top=%x, stack_end=%x, stack_len=%d" % (tcb_addr,stack_top,stack_end,stack_len) + + stack_len_aligned = stack_len + if stack_len_aligned % 4: + stack_len_aligned = 4*(stack_len_aligned/4 + 1) + + core_off += ESP32_COREDUMP_TSK_HDR_SZ + print "Read task[%d] TCB" % i + data = self.read_flash(core_off, tcbsz_aligned, flash_progress) + if tcbsz != tcbsz_aligned: + core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + else: + core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + # print "tcb=%s" % data + core_off += tcbsz_aligned + print "Read task[%d] stack %d bytes" % (i,stack_len) + data = self.read_flash(core_off, stack_len_aligned, flash_progress) + # print "stk=%s" % data + if stack_len != stack_len_aligned: + data = data[:stack_len - stack_len_aligned] + core_elf.add_program_segment(stack_base, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_off += stack_len_aligned + + task_regs = self._get_registers_from_stack(data, stack_end > stack_top) + prstatus = XtensaPrStatus() + prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task + prstatus.pr_pid = i # TODO: use pid assigned by OS + note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() + print "NOTE_LEN %d" % len(note) + notes += note + + print "Read core dump endmarker" + data = self.read_flash(core_off, ESP32_COREDUMP_MAGIC_SZ, flash_progress) + mag = struct.unpack_from(ESP32_COREDUMP_MAGIC_FMT, data) + print "mag2=%x" % (mag) + + # add notes + core_elf.add_program_segment(0, notes, ESPCoreDumpFile.PT_NOTE, 0) + + core_elf.e_type = ESPCoreDumpFile.ET_CORE + core_elf.e_machine = ESPCoreDumpFile.EM_XTENSA + fhnd,fname = tempfile.mkstemp() + self.fgdbcore = os.fdopen(fhnd, 'wb') + core_elf.dump(self.fgdbcore) + return fname + ######################### END ########################### + + def read_flash(self, off, sz, progress=None): +# print "read_flash: %x %d" % (off, sz) + id = off / self.FLASH_READ_BLOCK_SZ + if id >= len(self.fcores): + return '' + self.fcores[id].seek(off % self.FLASH_READ_BLOCK_SZ) + data = self.fcores[id].read(sz) +# print "data1: %s" % data + return data + +class GDBMIOutRecordHandler(object): + """ TBD + """ + TAG = '' + + def __init__(self, f, verbose=False): + self.verbose = verbose + + def execute(self, ln): + if self.verbose: + print "%s.execute '%s'" % (self.__class__.__name__, ln) + + +class GDBMIOutStreamHandler(GDBMIOutRecordHandler): + """ TBD + """ + def __init__(self, f, verbose=False): + super(GDBMIOutStreamHandler, self).__init__(None, verbose) + self.func = f + + def execute(self, ln): + GDBMIOutRecordHandler.execute(self, ln) + if self.func: + # remove TAG / quotes and replace c-string \n with actual NL + self.func(ln[1:].strip('"').replace('\\n', '\n').replace('\\t', '\t')) + + +class GDBMIResultHandler(GDBMIOutRecordHandler): + """ TBD + """ + TAG = '^' + RC_DONE = 'done' + RC_RUNNING = 'running' + RC_CONNECTED = 'connected' + RC_ERROR = 'error' + RC_EXIT = 'exit' + + def __init__(self, verbose=False): + super(GDBMIResultHandler, self).__init__(None, verbose) + self.result_class = None + self.result_str = None + + def _parse_rc(self, ln, rc): + rc_str = "{0}{1}".format(self.TAG, rc) + if ln.startswith(rc_str): + self.result_class = rc + sl = len(rc_str) + if len(ln) > sl: + self.result_str = ln[sl:] + if self.result_str.startswith(','): + self.result_str = self.result_str[1:] + else: + print "Invalid result format: '%s'" % ln + else: + self.result_str = '' + return True + return False + + def execute(self, ln): + GDBMIOutRecordHandler.execute(self, ln) + if self._parse_rc(ln, self.RC_DONE): + return + if self._parse_rc(ln, self.RC_RUNNING): + return + if self._parse_rc(ln, self.RC_CONNECTED): + return + if self._parse_rc(ln, self.RC_ERROR): + return + if self._parse_rc(ln, self.RC_EXIT): + return + print "Unknown result: '%s'" % ln + + +class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler): + """ TBD + """ + TAG = '~' + + +def dbg_corefile(args): + """ TBD + """ + print "dbg_corefile %s %s %s" % (args.gdb, args.prog, args.core) + loader = None + if not args.core: + loader = ESPCoreDumpLoader(args.off, port=args.port) + core_fname = loader.get_corefile_from_flash() + loader.fgdbcore.close() +# core_fname = 'esp_core.elf' + else: + core_fname = args.core +# print core_fname +# return + + p = subprocess.Popen( + bufsize = 0, + args = [args.gdb, + '--nw', # ignore .gdbinit + '--core=%s' % core_fname, # core file + args.prog], + stdin = None, stdout = None, stderr = None, + close_fds = True + ) + p.wait() + if loader: + loader.remove_tmp_file(loader.fgdbcore.name) + loader.cleanup() + print 'Done!' + + +def info_corefile(args): +# def info_corefile(args): + """ TBD + """ + print "info_corefile %s %s %s" % (args.gdb, args.prog, args.core) + + + def gdbmi_console_stream_handler(ln): + # print ln + sys.stdout.write(ln) + sys.stdout.flush() + + + def gdbmi_read2prompt(f, out_handlers=None): + """ TBD + """ + while True: + ln = f.readline().rstrip(' \n') + # print "LINE='{0}'".format(ln) + if ln == '(gdb)': + break + elif len(ln) == 0: + break + elif out_handlers: + for h in out_handlers: + if ln.startswith(out_handlers[h].TAG): + out_handlers[h].execute(ln) + break + + loader = None + if not args.core: + loader = ESPCoreDumpLoader(args.off, port=args.port) + core_fname = loader.get_corefile_from_flash() + loader.fgdbcore.close() + else: + core_fname = args.core + + handlers = {} + handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False) + handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False) + p = subprocess.Popen( + bufsize = 0, + args = [args.gdb, + '--quiet', # inhibit dumping info at start-up + '--nx', # inhibit window interface + '--nw', # ignore .gdbinit + '--interpreter=mi2', # use GDB/MI v2 + '--core=%s' % core_fname, # core file + args.prog], +# ], + stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, + close_fds = True + ) + + gdbmi_read2prompt(p.stdout, handlers) + exe_elf = ESPCoreDumpFile(args.prog) + core_elf = ESPCoreDumpFile(core_fname) + merged_segs = []#[(s, 0) for s in exe_elf.sections if s.flags & (esptool.ELFSection.SHF_ALLOC | esptool.ELFSection.SHF_WRITE)] + for s in exe_elf.sections: + merged = False + for ps in core_elf.program_segments: + if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr: + # sec: |XXXXXXXXXX| + # seg: |...XXX.............| + seg_addr = ps.addr + if ps.addr + len(ps.data) <= s.addr + len(s.data): + # sec: |XXXXXXXXXX| + # seg: |XXXXXXXXXXX...| + # merged: |XXXXXXXXXXXXXX| + seg_len = len(s.data) + (s.addr - ps.addr) + else: + # sec: |XXXXXXXXXX| + # seg: |XXXXXXXXXXXXXXXXX| + # merged: |XXXXXXXXXXXXXXXXX| + seg_len = len(ps.data) + merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + merged = True + elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data): + # sec: |XXXXXXXXXX| + # seg: |...XXX.............| + seg_addr = s.addr + if (ps.addr + len(ps.data)) >= (s.addr + len(s.data)): + # sec: |XXXXXXXXXX| + # seg: |..XXXXXXXXXXX| + # merged: |XXXXXXXXXXXXX| + seg_len = len(s.data) + (ps.addr + len(ps.data)) - (s.addr + len(s.data)) + else: + # sec: |XXXXXXXXXX| + # seg: |XXXXXX| + # merged: |XXXXXXXXXX| + seg_len = len(s.data) + merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + merged = True + + if not merged: + merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False)) +# merged_segs.append(('None', ps.addr, len(ps.data), 'None')) + + print "===============================================================" + print "==================== ESP32 CORE DUMP START ====================" + + handlers[GDBMIResultHandler.TAG].result_class = None + handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler + print "\n================== CURRENT THREAD REGISTERS ===================" + p.stdin.write("-interpreter-exec console \"info registers\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n==================== CURRENT THREAD STACK =====================" + p.stdin.write("-interpreter-exec console \"bt\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n======================== THREADS INFO =========================" + p.stdin.write("-interpreter-exec console \"info threads\"\n") + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + print "\n======================= MEMORY REGIONS ========================" + print "Name Address Size Attrs" + for ms in merged_segs: + print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) + if args.print_mem: + print "\n====================== MEMORY CONTENTS ========================" + for ms in merged_segs: +# if ms[3].find('W') == -1: + if not ms[4]: + continue + print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) + p.stdin.write("-interpreter-exec console \"x/%dx 0x%x\"\n" % (ms[2]/4, ms[1])) + gdbmi_read2prompt(p.stdout, handlers) + if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: + print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) + + print "\n===================== ESP32 CORE DUMP END =====================" + print "===============================================================" + + p.terminate() + p.stdin.close() + p.stdout.close() + if loader: + loader.remove_tmp_file(loader.fgdbcore.name) + loader.cleanup() + + +def main(): + parser = argparse.ArgumentParser(description='coredumper.py v%s - ESP32 Core Dump Utility' % __version__, prog='coredumper') + + parser.add_argument('--chip', '-c', + help='Target chip type', + choices=['auto', 'esp32'], + default=os.environ.get('ESPTOOL_CHIP', 'auto')) + + parser.add_argument( + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT)) + + parser.add_argument( + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=int, + default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD)) + +# parser.add_argument( +# '--no-stub', +# help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", +# action='store_true') + + subparsers = parser.add_subparsers( + dest='operation', + help='Run coredumper {command} -h for additional help') + + parser_debug_coredump = subparsers.add_parser( + 'dbg_corefile', + help='Starts GDB debugging session with specified corefile') + parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') + parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) + parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) + parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) + + parser_info_coredump = subparsers.add_parser( + 'info_corefile', + help='Print core dump info from file') + parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') + parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true') + parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) + parser_info_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) + parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + args = parser.parse_args() + + print 'coredumper.py v%s' % __version__ + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + operation_func = globals()[args.operation] + operation_func(args) + + +if __name__ == '__main__': + try: + main() + except ESPCoreDumpError as e: + print '\nA fatal error occurred: %s' % e + sys.exit(2) diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index 4c60308f780..0e93acf2718 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -863,8 +863,8 @@ typedef struct xSTATIC_TCB void *pxDummy6; uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; UBaseType_t uxDummyCoreId; - #if ( portSTACK_GROWTH > 0 ) - void *pxDummy8; + #if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 ) + void *pxDummy8; #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxDummy9; diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 4f1012657a0..2bedaa02e85 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -268,7 +268,9 @@ #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 - +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH | CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#define configENABLE_TASK_SNAPSHOT 1 +#endif #endif /* FREERTOS_CONFIG_H */ diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index f7b9181fcfd..c6896e53863 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -181,6 +181,18 @@ typedef struct xTASK_STATUS uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; +/* + * Used with the uxTaskGetSnapshotAll() function to save memory snapshot of each task in the system. + * We need this struct because TCB_t is defined (hidden) in tasks.c. + */ +typedef struct xTASK_SNAPSHOT +{ + void *pxTCB; /* Address of task control block. */ + StackType_t *pxTopOfStack; /* Points to the location of the last item placed on the tasks stack. */ + StackType_t *pxEndOfStack; /* Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo + pxTopOfStack > pxEndOfStack, stack grows lo2hi*/ +} TaskSnapshot_t; + /* Possible return values for eTaskConfirmSleepModeStatus(). */ typedef enum { @@ -2173,6 +2185,9 @@ eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; */ void *pvTaskIncrementMutexHeldCount( void ); +/* Used by core dump facility to get list of task handles. */ +UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ); + #ifdef __cplusplus } #endif diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 64031cfbca4..100b6e470ba 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -181,7 +181,7 @@ typedef struct tskTaskControlBlock char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ BaseType_t xCoreID; /*< Core this task is pinned to */ /* If this moves around (other than pcTaskName size changes), please change the define in xtensa_vectors.S as well. */ - #if ( portSTACK_GROWTH > 0 ) + #if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 ) StackType_t *pxEndOfStack; /*< Points to the end of the stack on architectures where the stack grows up from low memory. */ #endif @@ -885,6 +885,12 @@ UBaseType_t x; /* Check the alignment of the calculated top of stack is correct. */ configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + #if ( configENABLE_TASK_SNAPSHOT == 1 ) + { + /* need stack end for core dumps */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } +#endif } #else /* portSTACK_GROWTH */ { @@ -4912,6 +4918,102 @@ TickType_t uxReturn; #endif /* configUSE_TASK_NOTIFICATIONS */ +#if ( configENABLE_TASK_SNAPSHOT == 1 ) + + static void prvTaskGetSnapshotsFromList( TaskSnapshot_t *pxTaskSnapshotArray, UBaseType_t *uxTask, const UBaseType_t uxArraySize, List_t *pxList ) + { + TCB_t *pxNextTCB, *pxFirstTCB; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + + if( *uxTask >= uxArraySize ) + break; + + pxTaskSnapshotArray[ *uxTask ].pxTCB = pxNextTCB; + pxTaskSnapshotArray[ *uxTask ].pxTopOfStack = (StackType_t *)pxNextTCB->pxTopOfStack; + #if( portSTACK_GROWTH < 0 ) + { + pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxEndOfStack; + } + #else + { + pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxStack; + } + #endif + + (*uxTask)++; + + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ) + { + UBaseType_t uxTask = 0, i = 0; + + *pxTcbSz = sizeof(TCB_t); + + //vTaskSuspendAll(); //WARNING: This only suspends one CPU. ToDo: suspend others as well. Mux using taskQueueMutex maybe? + { + /* Fill in an TaskStatus_t structure with information on each + task in the Ready state. */ + i = configMAX_PRIORITIES; + do + { + i--; + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( pxReadyTasksLists[ i ] ) ); + } while( i > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Fill in an TaskStatus_t structure with information on each + task in the Blocked state. */ + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxDelayedTaskList ); + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxOverflowDelayedTaskList ); + + #if( INCLUDE_vTaskDelete == 1 ) + { + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xTasksWaitingTermination ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xSuspendedTaskList ); + } + #endif + } + //( void ) xTaskResumeAll(); +#if 0 + /* Convention: First num_cpus slots will have current task for that cpu. */ + for (i = 0; i < portNUM_PROCESSORS; i++) { + if (pxCurrentTCB[i] == NULL || pxCurrentTCB == pxTaskSnapshotArray[i]) { + continue; + } else { + UBaseType_t j; + for (j = i; j < uxTask; j++) { + if (pxTaskSnapshotArray[j] == pxCurrentTCB[i]) { + TaskHandle_t tmp = pxTaskSnapshotArray[i]; + pxTaskSnapshotArray[i] = pxTaskSnapshotArray[j]; + pxTaskSnapshotArray[j] = tmp; + break; + } + } + } + } +#endif + return uxTask; + } + +#endif + #ifdef FREERTOS_MODULE_TEST #include "tasks_test_access_functions.h" #endif diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index 1f019a6e3fd..36e435ba2f8 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -45,8 +45,10 @@ config PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET config PARTITION_TABLE_FILENAME string - default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP - default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA + default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP && !ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_singleapp_coredump.csv if PARTITION_TABLE_SINGLE_APP && ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA && !ESP32_ENABLE_COREDUMP_TO_FLASH + default partitions_two_ota_coredump.csv if PARTITION_TABLE_TWO_OTA && ESP32_ENABLE_COREDUMP_TO_FLASH default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM config APP_OFFSET diff --git a/components/partition_table/partitions_singleapp_coredump.csv b/components/partition_table/partitions_singleapp_coredump.csv new file mode 100644 index 00000000000..96719a425a0 --- /dev/null +++ b/components/partition_table/partitions_singleapp_coredump.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000 +phy_init, data, phy, 0xf000, 0x1000 +factory, app, factory, 0x10000, 1M +coredump, data, 3, , 64K diff --git a/components/partition_table/partitions_two_ota_coredump.csv b/components/partition_table/partitions_two_ota_coredump.csv new file mode 100644 index 00000000000..3d2b34ec9a7 --- /dev/null +++ b/components/partition_table/partitions_two_ota_coredump.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x4000 +otadata, data, ota, 0xd000, 0x2000 +phy_init, data, phy, 0xf000, 0x1000 +factory, 0, 0, 0x10000, 1M +coredump, data, 3, , 64K +ota_0, 0, ota_0, , 1M +ota_1, 0, ota_1, , 1M diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index f30db80cd8d..748ccf9042e 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -141,6 +141,29 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() esp_intr_noniram_enable(); } +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_panic() +{ + const uint32_t cpuid = xPortGetCoreID(); + const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; + + // do not care about other CPU, it was halted upon entering panic handler + spi_flash_disable_cache(other_cpuid, &s_flash_op_cache_state[other_cpuid]); + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); + // Disable cache on this CPU as well + spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); +} + +void IRAM_ATTR spi_flash_enable_interrupts_caches_panic() +{ + const uint32_t cpuid = xPortGetCoreID(); + + // Re-enable cache on this CPU + spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); +} + #else // CONFIG_FREERTOS_UNICORE void spi_flash_init_lock() @@ -172,6 +195,22 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() esp_intr_noniram_enable(); } +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_panic() +{ + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); + // Disable cache on this CPU as well + spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); +} + +void IRAM_ATTR spi_flash_enable_interrupts_caches_panic() +{ + // Re-enable cache on this CPU + spi_flash_restore_cache(0, s_flash_op_cache_state[0]); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); +} + #endif // CONFIG_FREERTOS_UNICORE /** diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index 899a31c6515..a40bab04651 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -40,5 +40,14 @@ void spi_flash_disable_interrupts_caches_and_other_cpu(); // Enable cache, enable interrupts (to be added in future), resume scheduler void spi_flash_enable_interrupts_caches_and_other_cpu(); +// Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs. +// This function is implied to be called from panic handler +// when non-current CPU is halted and can not execute code from flash. +void spi_flash_disable_interrupts_caches_and_other_cpu_panic(); + +// Enable cache, enable interrupts (to be added in future) on current CPU. +// This function is implied to be called from panic handler +// when non-current CPU is halted and can not execute code from flash. +void spi_flash_enable_interrupts_caches_panic(); #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 993e68a5728..7f2d2d4d183 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -58,7 +58,35 @@ static spi_flash_counters_t s_flash_stats; #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS +/* SPI flash access critical section management functions */ +typedef void (*spi_flash_guard_start_func_t)(void); +typedef void (*spi_flash_guard_end_func_t)(void); + +/** + * Structure holding SPI flash access critical section management functions + */ +typedef struct { + spi_flash_guard_start_func_t start; // critical section start func + spi_flash_guard_end_func_t end; // critical section end func +} spi_flash_guard_funcs_t; + +#define FLASH_GUARD_START(_gp_) do{if((_gp_)) (_gp_)->start();}while(0) +#define FLASH_GUARD_END(_gp_) do{if((_gp_)) (_gp_)->end();}while(0) + static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); +static esp_err_t spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard); +static esp_err_t spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard); +static esp_err_t spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard); + +const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu, + .end = spi_flash_enable_interrupts_caches_and_other_cpu +}; + +const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_panic_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu_panic, + .end = spi_flash_enable_interrupts_caches_panic +}; void spi_flash_init() { @@ -92,6 +120,16 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) } esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) +{ + return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_ops); +} + +esp_err_t IRAM_ATTR spi_flash_erase_range_panic(uint32_t start_addr, uint32_t size) +{ + return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_panic_ops); +} + +static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard) { if (start_addr % SPI_FLASH_SEC_SIZE != 0) { return ESP_ERR_INVALID_ARG; @@ -106,7 +144,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) size_t end = start + size / SPI_FLASH_SEC_SIZE; const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE; COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { @@ -122,12 +160,22 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) } } } - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) +{ + return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_ops); +} + +esp_err_t IRAM_ATTR spi_flash_write_panic(size_t dst, const void *srcv, size_t size) +{ + return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_panic_ops); +} + +static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound writes are checked in ROM code, but we can give better // error code here @@ -160,9 +208,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (left_size > 0) { uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(left_off, &t, 4); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -178,9 +226,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) bool in_dram = true; #endif if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) { - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -194,9 +242,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t[8]; uint32_t write_size = MIN(mid_size, sizeof(t)); memcpy(t, srcc + mid_off, write_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(dst + mid_off, t, write_size); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -209,9 +257,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (right_size > 0) { uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); rc = SPIWrite(dst + right_off, &t, 4); - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -259,6 +307,16 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, } esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) +{ + return spi_flash_read_internal(src, dstv, size, &s_flash_guard_ops); +} + +esp_err_t IRAM_ATTR spi_flash_read_panic(size_t src, void *dstv, size_t size) +{ + return spi_flash_read_internal(src, dstv, size, &s_flash_guard_panic_ops); +} + +static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound reads are checked in ROM code, but we can give better // error code here @@ -271,7 +329,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_START(flash_guard); /* To simplify boundary checks below, we handle small reads separately. */ if (size < 16) { uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */ @@ -345,7 +403,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) memcpy(dstc + pad_right_off, t, pad_right_size); } out: - spi_flash_enable_interrupts_caches_and_other_cpu(); + FLASH_GUARD_END(flash_guard); COUNTER_STOP(read); return spi_flash_translate_rc(rc); } diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index b67891ae15f..b149e102340 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -69,6 +69,7 @@ typedef enum { ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition + ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03, //!< COREDUMP partition ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index bb3ec39b45e..e78c389b4e1 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -173,6 +173,56 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle); */ void spi_flash_mmap_dump(); +/** + * @brief Erase a range of flash sectors. + * + * @note This version of function is to be called from panic handler. + * It does not use any OS primitives and IPC and implies that + * only calling CPU is active. + * + * @param start_address Address where erase operation has to start. + * Must be 4kB-aligned + * @param size Size of erased range, in bytes. Must be divisible by 4kB. + * + * @return esp_err_t + */ +esp_err_t spi_flash_erase_range_panic(size_t start_address, size_t size); + + +/** + * @brief Write data to Flash. + * + * @note This version of function is to be called from panic handler. + * It does not use any OS primitives and IPC and implies that + * only calling CPU is active. + + * @note If source address is in DROM, this function will return + * ESP_ERR_INVALID_ARG. + * + * @param dest destination address in Flash. Must be a multiple of 4 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 4 bytes. + * + * @return esp_err_t + */ +esp_err_t spi_flash_write_panic(size_t dest, const void *src, size_t size); + + +/** + * @brief Read data from Flash. + * + * @note This version of function is to be called from panic handler. + * It does not use any OS primitives and IPC and implies that + * only calling CPU is active. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t spi_flash_read_panic(size_t src, void *dest, size_t size); + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS /** From 23f836659d1d46e579ddb57bdcbb66ac51b0b048 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Tue, 3 Jan 2017 14:16:41 +0300 Subject: [PATCH 139/167] esp32: Add core dump printing to UART feature --- components/esp32/core_dump.c | 456 +++++++++++++++++- components/esp32/cpu_start.c | 2 +- components/esp32/include/esp_panic.h | 2 +- components/espcoredump/espcoredump.py | 365 +++++++------- .../include/freertos/FreeRTOSConfig.h | 2 +- 5 files changed, 654 insertions(+), 173 deletions(-) diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index 165ec741927..eaab1871c30 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -18,24 +19,199 @@ #include "esp_panic.h" #include "esp_partition.h" -#ifdef ESP_PLATFORM -// Uncomment this line to force output from this module -#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART #include "esp_log.h" -static const char* TAG = "esp_core_dump_init"; -#else -#define ESP_LOGD(...) -#endif +const static char *TAG = "esp_core_dump"; // TODO: allow user to set this in menuconfig or get tasks iteratively #define COREDUMP_MAX_TASKS_NUM 32 +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); + +typedef struct _core_dump_write_config_t +{ + esp_core_dump_write_prepare_t prepare; + esp_core_dump_write_start_t start; + esp_core_dump_write_end_t end; + esp_core_dump_flash_write_data_t write; + void * priv; + +} core_dump_write_config_t; + +static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg, int verb) +{ + union + { + uint8_t data8[12]; + uint32_t data32[3]; + } rom_data; + //const esp_partition_t *core_part; + esp_err_t err; + TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; + UBaseType_t tcb_sz, task_num; + uint32_t data_len = 0, i, len; + //size_t off; + + task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); + // take TCB padding into account, actual TCB size will be stored in header + if (tcb_sz % sizeof(uint32_t)) + len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); + else + len = tcb_sz; + // header + tasknum*(tcb + stack start/end + tcb addr) + data_len = 3*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); + for (i = 0; i < task_num; i++) { + if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { + // set correct stack top for current task + tasks[i].pxTopOfStack = (StackType_t *)frame; + if (verb) { + esp_panicPutStr("Current task PC/A0/SP "); + esp_panicPutHex(frame->pc); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a0); + esp_panicPutStr(" "); + esp_panicPutHex(frame->a1); + esp_panicPutStr("\r\n"); + } + } +#if( portSTACK_GROWTH < 0 ) + len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; +#else + len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; +#endif + if (verb) { + esp_panicPutStr("stack len = "); + esp_panicPutHex(len); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxTopOfStack); + esp_panicPutStr(" "); + esp_panicPutHex((int)tasks[i].pxEndOfStack); + esp_panicPutStr("\r\n"); + } + // take stack padding into account + if (len % sizeof(uint32_t)) + len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); + data_len += len; + } + + // prepare write + if (write_cfg->prepare) { + err = write_cfg->prepare(write_cfg->priv, &data_len); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to prepare core dump "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } + + if (verb) { + esp_panicPutStr("Core dump len ="); + esp_panicPutHex(data_len); + esp_panicPutStr("\r\n"); + } + + // write start marker + if (write_cfg->start) { + err = write_cfg->start(write_cfg->priv); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to start core dump "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } + + // write header + rom_data.data32[0] = data_len; + rom_data.data32[1] = task_num; + rom_data.data32[2] = tcb_sz; + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write core dump header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + + // write tasks + for (i = 0; i < task_num; i++) { + if (verb) { + esp_panicPutStr("Dump task "); + esp_panicPutHex((int)tasks[i].pxTCB); + esp_panicPutStr("\r\n"); + } + // save TCB address, stack base and stack top addr + rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; + rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; + rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + // save TCB + err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + // save task stack + /*int k; + for (k = 0; k < 8*4; k++) { + esp_panicPutStr("stack["); + esp_panicPutDec(k); + esp_panicPutStr("] = "); + esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); + esp_panicPutStr("\r\n"); + }*/ + err = write_cfg->write(write_cfg->priv, +#if( portSTACK_GROWTH < 0 ) + tasks[i].pxTopOfStack, + (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack +#else + tasks[i].pxEndOfStack, + (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack +#endif + ); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to write task header "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } + + // write end marker + if (write_cfg->end) { + err = write_cfg->end(write_cfg->priv); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to end core dump "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return; + } + } +} + #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH // magic numbers to control core dump data consistency #define COREDUMP_FLASH_MAGIC_START 0xDEADBEEFUL #define COREDUMP_FLASH_MAGIC_END 0xACDCFEEDUL +typedef struct _core_dump_write_flash_data_t +{ + uint32_t off; +} core_dump_write_flash_data_t; + // core dump partition start static uint32_t s_core_part_start; // core dump partition size @@ -79,6 +255,115 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint return data_len; } +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) +{ + esp_err_t err; + uint32_t sec_num; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + esp_panicPutStr("Core dump len1 = "); + esp_panicPutHex(*data_len); + esp_panicPutStr("\r\n"); + + // add space for 2 magics. TODO: change to CRC + *data_len += 2*sizeof(uint32_t); + if (*data_len > s_core_part_size) { + esp_panicPutStr("ERROR: Not enough space to save core dump!"); + return ESP_ERR_NO_MEM; + } + + esp_panicPutStr("Core dump len2 = "); + esp_panicPutHex(*data_len); + esp_panicPutStr("\r\n"); + + wr_data->off = 0; + + sec_num = *data_len / SPI_FLASH_SEC_SIZE; + if (*data_len % SPI_FLASH_SEC_SIZE) + sec_num++; + err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to erase flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return err; + } + + return err; +} + +static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr_data, uint32_t word) +{ + esp_err_t err = ESP_OK; + uint32_t data32 = word; + + err = spi_flash_write_panic(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); + if (err != ESP_OK) { + esp_panicPutStr("Failed to write to flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return err; + } + wr_data->off += sizeof(uint32_t); + + return err; +} + +static esp_err_t esp_core_dump_flash_write_start(void *priv) +{ + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + // save magic 1 + return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START); +} + +static esp_err_t esp_core_dump_flash_write_end(void *priv) +{ + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + uint32_t i; + union + { + uint8_t data8[16]; + uint32_t data32[4]; + } rom_data; + + // TEST READ START + esp_err_t err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + esp_panicPutStr("ERROR: Failed to read flash "); + esp_panicPutHex(err); + esp_panicPutStr("!\r\n"); + return err; + } + else { + esp_panicPutStr("Data from flash:\r\n"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + esp_panicPutHex(rom_data.data32[i]); + esp_panicPutStr("\r\n"); + } +// rom_data[4] = 0; +// esp_panicPutStr(rom_data); +// esp_panicPutStr("\r\n"); + } + // TEST READ END + + // save magic 2 + return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END); +} + +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) +{ + esp_err_t err = ESP_OK; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + uint32_t len = esp_core_dump_write_flash_padded(s_core_part_start + wr_data->off, data, data_len); + if (len != data_len) + return ESP_FAIL; + + wr_data->off += len; + + return err; +} + /* * | MAGIC1 | * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | @@ -90,6 +375,19 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint */ void esp_core_dump_to_flash(XtExcFrame *frame) { +#if 1 + core_dump_write_config_t wr_cfg; + core_dump_write_flash_data_t wr_data; + + wr_cfg.prepare = esp_core_dump_flash_write_prepare; + wr_cfg.start = esp_core_dump_flash_write_start; + wr_cfg.end = esp_core_dump_flash_write_end; + wr_cfg.write = esp_core_dump_flash_write_data; + wr_cfg.priv = &wr_data; + + esp_panicPutStr("Save core dump to flash...\r\n"); + esp_core_dump_write(frame, &wr_cfg, 1); +#else union { uint8_t data8[16]; @@ -245,14 +543,150 @@ void esp_core_dump_to_flash(XtExcFrame *frame) esp_panicPutStr("!\r\n"); return; } - - esp_panicPutStr("Core dump has been saved to flash partition.\r\n"); +#endif + esp_panicPutStr("Core dump has been saved to flash.\r\n"); } #endif #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if 0 +#define BASE64_ENCODE_BODY(_src, _src_len, _dst) \ + do { \ + static const char *b64 = \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \ + int i, j, a, b, c; \ + \ + for (i = j = 0; i < _src_len; i += 3) { \ + a = _src[i]; \ + b = i + 1 >= _src_len ? 0 : _src[i + 1]; \ + c = i + 2 >= _src_len ? 0 : _src[i + 2]; \ + \ + /*BASE64_OUT(b64[a >> 2], _dst[j]);*/ \ + _dst[j++] = b64[a >> 2]; \ + /*BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)], _dst[j]);*/ \ + _dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; \ + j++; \ + if (i + 1 < _src_len) { \ + BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)], _dst[j]); \ + j++; \ + } \ + if (i + 2 < _src_len) { \ + BASE64_OUT(b64[c & 63], _dst[j]); \ + j++; \ + } \ + } \ + \ + while (j % 4 != 0) { \ + BASE64_OUT('=', _dst); \ + } \ + BASE64_FLUSH(_dst) \ + } while(0) + +#define BASE64_OUT(ch, _dst) \ + do { \ + _dst = (ch); \ + } while (0) + +#define BASE64_FLUSH(_dst) \ + do { \ + _dst = '\0'; \ + } while (0) +#endif +static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { +// BASE64_ENCODE_BODY(src, src_len, dst); + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 0x0F) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 0x3F]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} + +/*static esp_err_t esp_core_dump_uart_write_prepare(void *priv, uint32_t *data_len) +{ + esp_err_t err = ESP_OK; + return err; +}*/ + +static esp_err_t esp_core_dump_uart_write_start(void *priv) +{ +// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + esp_err_t err = ESP_OK; + esp_panicPutStr("================= CORE DUMP START =================\r\n"); + return err; +} + +static esp_err_t esp_core_dump_uart_write_end(void *priv) +{ +// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + esp_err_t err = ESP_OK; + esp_panicPutStr("================= CORE DUMP END =================\r\n"); + return err; +} + +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) +{ +// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + esp_err_t err = ESP_OK; + char buf[64 + 4], *addr = data; + char *end = addr + data_len; + +// esp_panicPutStr("CORE DUMP SEC: "); +// esp_panicPutDec(data_len); +// esp_panicPutStr("bytes\r\n"); + + while (addr < end) { + size_t len = end - addr; + if (len > 48) len = 48; + /* Copy to stack to avoid alignment restrictions. */ + char *tmp = buf + (sizeof(buf) - len); + memcpy(tmp, addr, len); + esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); + addr += len; + esp_panicPutStr(buf); +// for (size_t i = 0; buf[i] != '\0'; i++) { +// panicPutChar(buf[i]); +// } + //if (addr % 96 == 0) + esp_panicPutStr("\r\n"); + /* Feed the Cerberus. */ +// TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; +// TIMERG0.wdt_feed = 1; + } + + return err; +} + void esp_core_dump_to_uart(XtExcFrame *frame) { + core_dump_write_config_t wr_cfg; + //core_dump_write_flash_data_t wr_data; + + wr_cfg.prepare = NULL;//esp_core_dump_uart_write_prepare; + wr_cfg.start = esp_core_dump_uart_write_start; + wr_cfg.end = esp_core_dump_uart_write_end; + wr_cfg.write = esp_core_dump_uart_write_data; + wr_cfg.priv = NULL; + + esp_panicPutStr("Save core dump to flash...\r\n"); + esp_core_dump_write(frame, &wr_cfg, 0); + esp_panicPutStr("Core dump has been written to uart.\r\n"); } #endif @@ -261,6 +695,7 @@ void esp_core_dump_init() #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH const esp_partition_t *core_part; + ESP_LOGI(TAG, "Init core dump to flash"); core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); if (!core_part) { ESP_LOGE(TAG, "No core dump partition found!"); @@ -271,6 +706,9 @@ void esp_core_dump_init() s_core_part_size = core_part->size; #endif #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART + ESP_LOGI(TAG, "Init core dump to UART"); #endif } +#endif + diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 95d5c5e6a2a..1b8a95b75c3 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -215,7 +215,7 @@ void start_cpu0_default(void) } #endif -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART esp_core_dump_init(); #endif diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index 25816d31f28..3dd5d4b185e 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -24,7 +24,7 @@ */ void esp_set_breakpoint_if_jtag(void *fn); -void esp_panicPutchar(char c); +void esp_panicPutChar(char c); void esp_panicPutStr(const char *c); void esp_panicPutHex(int a); void esp_panicPutDec(int a); diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 065b323aa19..2f31d3f2926 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -10,6 +10,7 @@ import tempfile import struct import array import errno +import base64 try: import esptool @@ -23,10 +24,6 @@ except ImportError: __version__ = "0.1-dev" -ESP32_COREDUMP_HDR_FMT = '<4L' -ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF -ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED - class Struct(object): def __init__(self, buf=None): @@ -77,7 +74,7 @@ class Elf32FileHeader(Struct): if buf is None: # Fill in sane ELF header for LSB32 self.e_ident = "\x7fELF\1\1\1\0\0\0\0\0\0\0\0\0" - self.e_version = ESPCoreDumpFile.EV_CURRENT + self.e_version = ESPCoreDumpElfFile.EV_CURRENT self.e_ehsize = self.sizeof() @@ -200,7 +197,7 @@ class ESPCoreDumpSection(esptool.ELFSection): return str -class ESPCoreDumpFile(esptool.ELFFile): +class ESPCoreDumpElfFile(esptool.ELFFile): # ELF file type ET_NONE = 0x0 # No file type ET_REL = 0x1 # Relocatable file @@ -229,7 +226,7 @@ class ESPCoreDumpFile(esptool.ELFFile): def __init__(self, name=None): if name: - super(ESPCoreDumpFile, self).__init__(name) + super(ESPCoreDumpElfFile, self).__init__(name) else: self.sections = [] self.program_segments = [] @@ -409,80 +406,12 @@ class ESPCoreDumpLoader(object): """ TBD """ - FLASH_READ_BLOCK_SZ = 0x2000 - def __init__(self, off, path=None, chip='esp32', port=None, baud=None): -# print "esptool.__file__ %s" % esptool.__file__ - if not path: - self.path = esptool.__file__ - self.path = self.path[:-1] - else: - self.path = path - self.port = port - self.baud = baud - self.chip = chip - self.fcores = [] - self.fgdbcore = None - self._load_coredump(off) - - def _load_coredump(self, off): - args = [self.path, '-c', self.chip] - if self.port: - args.extend(['-p', self.port]) - if self.baud: - args.extend(['-b', str(self.baud)]) - read_sz = self.FLASH_READ_BLOCK_SZ - read_off = off - args.extend(['read_flash', str(read_off), str(read_sz), '']) - try: - dump_sz = 0 - tot_len = 0 - while True: - fhnd,fname = tempfile.mkstemp() -# print "tmpname %s" % fname -# os.close(fhnd) - args[-1] = fname - et_out = subprocess.check_output(args) - print et_out - # data = os.fdopen(fhnd, 'r').read(sz) - self.fcores.append(os.fdopen(fhnd, 'r')) - if dump_sz == 0: - # read dump length from the first block - dump_sz = self._read_core_dump_length(self.fcores[0]) - tot_len += read_sz - if tot_len >= dump_sz: - break - read_off += read_sz - if dump_sz - tot_len >= self.FLASH_READ_BLOCK_SZ: - read_sz = self.FLASH_READ_BLOCK_SZ - else: - read_sz = dump_sz - tot_len - args[-3] = str(read_off) - args[-2] = str(read_sz) - - except subprocess.CalledProcessError as e: - print "esptool script execution failed with err %d" % e.returncode - print "Command ran: '%s'" % e.cmd - print "Command out:" - print e.output - self.cleanup() - return [] - - def _read_core_dump_length(self, f): - global ESP32_COREDUMP_HDR_FMT - global ESP32_COREDUMP_FLASH_MAGIC_START - print "Read core dump header from '%s'" % f.name - data = f.read(4*4) - mag1,tot_len,task_num,tcbsz = struct.unpack_from(ESP32_COREDUMP_HDR_FMT, data) - if mag1 != ESP32_COREDUMP_FLASH_MAGIC_START: - raise ESPCoreDumpLoaderError("Invalid start magic number!") - return tot_len - - def remove_tmp_file(self, fname): - try: - os.remove(fname) - except OSError as e: - if e.errno != errno.ENOENT: - print "Warning failed to remove temp file '%s'!" % fname + ESP32_COREDUMP_HDR_FMT = '<3L' + ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) + ESP32_COREDUMP_TSK_HDR_FMT = '<3L' + ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT) + def __init__(self): + self.fcore = None def _get_registers_from_stack(self, data, grows_down): # from "gdb/xtensa-tdep.h" @@ -582,51 +511,36 @@ class ESPCoreDumpLoader(object): regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000; return regs - def cleanup(self): -# if self.fgdbcore: -# self.fgdbcore.close() -# self.remove_tmp_file(self.fgdbcore.name) - for f in self.fcores: - if f: - f.close() - self.remove_tmp_file(f.name) + def remove_tmp_file(self, fname): + try: + os.remove(fname) + except OSError as e: + if e.errno != errno.ENOENT: + print "Warning failed to remove temp file '%s'!" % fname - def get_corefile_from_flash(self): + def cleanup(self): + if self.fcore: + self.fcore.close() + self.remove_tmp_file(self.fcore.name) + + def create_corefile(self, core_fname=None, off=0): """ TBD """ - global ESP32_COREDUMP_HDR_FMT - ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) - ESP32_COREDUMP_TSK_HDR_FMT = ' stack_top: stack_len = stack_end - stack_top stack_base = stack_top @@ -639,23 +553,23 @@ class ESPCoreDumpLoader(object): if stack_len_aligned % 4: stack_len_aligned = 4*(stack_len_aligned/4 + 1) - core_off += ESP32_COREDUMP_TSK_HDR_SZ + core_off += self.ESP32_COREDUMP_TSK_HDR_SZ print "Read task[%d] TCB" % i - data = self.read_flash(core_off, tcbsz_aligned, flash_progress) + data = self.read_data(core_off, tcbsz_aligned) if tcbsz != tcbsz_aligned: - core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) else: - core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) # print "tcb=%s" % data core_off += tcbsz_aligned print "Read task[%d] stack %d bytes" % (i,stack_len) - data = self.read_flash(core_off, stack_len_aligned, flash_progress) + data = self.read_data(core_off, stack_len_aligned) # print "stk=%s" % data if stack_len != stack_len_aligned: data = data[:stack_len - stack_len_aligned] - core_elf.add_program_segment(stack_base, data, ESPCoreDumpFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) + core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) core_off += stack_len_aligned - + task_regs = self._get_registers_from_stack(data, stack_end > stack_top) prstatus = XtensaPrStatus() prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task @@ -663,33 +577,140 @@ class ESPCoreDumpLoader(object): note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() print "NOTE_LEN %d" % len(note) notes += note - - print "Read core dump endmarker" - data = self.read_flash(core_off, ESP32_COREDUMP_MAGIC_SZ, flash_progress) - mag = struct.unpack_from(ESP32_COREDUMP_MAGIC_FMT, data) - print "mag2=%x" % (mag) - - # add notes - core_elf.add_program_segment(0, notes, ESPCoreDumpFile.PT_NOTE, 0) - - core_elf.e_type = ESPCoreDumpFile.ET_CORE - core_elf.e_machine = ESPCoreDumpFile.EM_XTENSA - fhnd,fname = tempfile.mkstemp() - self.fgdbcore = os.fdopen(fhnd, 'wb') - core_elf.dump(self.fgdbcore) - return fname - ######################### END ########################### - def read_flash(self, off, sz, progress=None): -# print "read_flash: %x %d" % (off, sz) - id = off / self.FLASH_READ_BLOCK_SZ - if id >= len(self.fcores): - return '' - self.fcores[id].seek(off % self.FLASH_READ_BLOCK_SZ) - data = self.fcores[id].read(sz) + # add notes + core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0) + + core_elf.e_type = ESPCoreDumpElfFile.ET_CORE + core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA + if core_fname: + fce = open(core_fname, 'wb') + else: + fhnd,core_fname = tempfile.mkstemp() + fce = os.fdopen(fhnd, 'wb') + core_elf.dump(fce) + fce.close() + return core_fname + + def read_data(self, off, sz): +# print "read_data: %x %d" % (off, sz) + self.fcore.seek(off) + data = self.fcore.read(sz) # print "data1: %s" % data return data + +class ESPCoreDumpFileLoader(ESPCoreDumpLoader): + """ TBD + """ + def __init__(self, path, b64 = False): + super(ESPCoreDumpFileLoader, self).__init__() + self.fcore = self._load_coredump(path, b64) + + def _load_coredump(self, path, b64): + if b64: + fhnd,fname = tempfile.mkstemp() + print "tmpname %s" % fname + fcore = os.fdopen(fhnd, 'wb') + fb64 = open(path, 'r') + try: + while True: + line = fb64.readline() + if len(line) == 0: + break + data = base64.b64decode(line.rstrip('\r\n'))#, validate=True) + fcore.write(data) + fcore.close() + fcore = open(fname, 'r') + finally: + fb64.close() + else: + fcore = open(path, 'r') + return fcore + + +class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): + """ TBD + """ + ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF + ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED + ESP32_COREDUMP_FLASH_MAGIC_FMT = ' Date: Tue, 3 Jan 2017 22:01:40 +0300 Subject: [PATCH 140/167] esp32: Fixes several issues in core dump feature 1) PS is fixed up to allow GDB backtrace to work properly 2) MR!341 discussion: in core dump module: esp_panicPutXXX was replaced by ets_printf. 3) MR!341 discussion: core dump flash magic number was changed. 4) MR!341 discussion: SPI flash access API was redesigned to allow flexible critical section management. 5) test app for core dump feature was added 6) fixed base64 file reading issues on Windows platform 7) now raw bin core file is deleted upon core loader failure by epscoredump.py --- components/esp32/core_dump.c | 427 ++++--------------- components/esp32/cpu_start.c | 2 + components/esp32/include/esp_panic.h | 4 - components/esp32/panic.c | 82 ++-- components/espcoredump/espcoredump.py | 113 ++--- components/freertos/tasks.c | 20 - components/spi_flash/cache_utils.c | 8 +- components/spi_flash/cache_utils.h | 8 +- components/spi_flash/flash_ops.c | 84 +--- components/spi_flash/include/esp_spi_flash.h | 56 +-- 10 files changed, 241 insertions(+), 563 deletions(-) diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index eaab1871c30..e12fc1d71cc 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -15,7 +15,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -//#include "esp_attr.h" #include "esp_panic.h" #include "esp_partition.h" @@ -26,10 +25,10 @@ const static char *TAG = "esp_core_dump"; // TODO: allow user to set this in menuconfig or get tasks iteratively #define COREDUMP_MAX_TASKS_NUM 32 -typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); -typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); -typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); -typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len, int verb); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv, int verb); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv, int verb); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len, int verb); typedef struct _core_dump_write_config_t { @@ -67,14 +66,19 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { // set correct stack top for current task tasks[i].pxTopOfStack = (StackType_t *)frame; + if (verb) + ets_printf("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); + } + else { if (verb) { - esp_panicPutStr("Current task PC/A0/SP "); - esp_panicPutHex(frame->pc); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a0); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a1); - esp_panicPutStr("\r\n"); + XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; + if (task_frame->exit == 0) { + ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); + } + else { + XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; + ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); + } } } #if( portSTACK_GROWTH < 0 ) @@ -83,13 +87,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; #endif if (verb) { - esp_panicPutStr("stack len = "); - esp_panicPutHex(len); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxTopOfStack); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxEndOfStack); - esp_panicPutStr("\r\n"); + ets_printf("Stack len = %lu (%x %x)\r\n", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); } // take stack padding into account if (len % sizeof(uint32_t)) @@ -99,28 +97,22 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri // prepare write if (write_cfg->prepare) { - err = write_cfg->prepare(write_cfg->priv, &data_len); + err = write_cfg->prepare(write_cfg->priv, &data_len, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to prepare core dump "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to prepare core dump (%d)!\r\n", err); return; } } if (verb) { - esp_panicPutStr("Core dump len ="); - esp_panicPutHex(data_len); - esp_panicPutStr("\r\n"); + ets_printf("Core dump len = %lu\r\n", data_len); } - // write start marker + // write start if (write_cfg->start) { - err = write_cfg->start(write_cfg->priv); + err = write_cfg->start(write_cfg->priv, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to start core dump "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to start core dump (%d)!\r\n", err); return; } } @@ -129,49 +121,33 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri rom_data.data32[0] = data_len; rom_data.data32[1] = task_num; rom_data.data32[2] = tcb_sz; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write core dump header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write core dump header (%d)!\r\n", err); return; } // write tasks for (i = 0; i < task_num; i++) { if (verb) { - esp_panicPutStr("Dump task "); - esp_panicPutHex((int)tasks[i].pxTCB); - esp_panicPutStr("\r\n"); + ets_printf("Dump task %x\r\n", tasks[i].pxTCB); } // save TCB address, stack base and stack top addr rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write task header (%d)!\r\n", err); return; } // save TCB - err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); + err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write TCB (%d)!\r\n", err); return; } // save task stack - /*int k; - for (k = 0; k < 8*4; k++) { - esp_panicPutStr("stack["); - esp_panicPutDec(k); - esp_panicPutStr("] = "); - esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); - esp_panicPutStr("\r\n"); - }*/ err = write_cfg->write(write_cfg->priv, #if( portSTACK_GROWTH < 0 ) tasks[i].pxTopOfStack, @@ -180,22 +156,18 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri tasks[i].pxEndOfStack, (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack #endif - ); + , verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write task stack (%d)!\r\n", err); return; } } - // write end marker + // write end if (write_cfg->end) { - err = write_cfg->end(write_cfg->priv); + err = write_cfg->end(write_cfg->priv, verb); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to end core dump "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to end core dump (%d)!\r\n", err); return; } } @@ -204,8 +176,8 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH // magic numbers to control core dump data consistency -#define COREDUMP_FLASH_MAGIC_START 0xDEADBEEFUL -#define COREDUMP_FLASH_MAGIC_END 0xACDCFEEDUL +#define COREDUMP_FLASH_MAGIC_START 0xE32C04EDUL +#define COREDUMP_FLASH_MAGIC_END 0xE32C04EDUL typedef struct _core_dump_write_flash_data_t { @@ -228,11 +200,9 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint } rom_data; data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); - err = spi_flash_write_panic(off, data, data_len); + err = spi_flash_write(off, data, data_len); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write data"); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write data to flash (%d)!\r\n", err); return 0; } @@ -242,11 +212,9 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint rom_data.data32 = 0; for (k = 0; k < len; k++) rom_data.data8[k] = *(data + data_len + k); - err = spi_flash_write_panic(off + data_len, &rom_data, sizeof(uint32_t)); + err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t)); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write data end"); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to finish write data to flash (%d)!\r\n", err); return 0; } data_len += sizeof(uint32_t); @@ -255,37 +223,27 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint return data_len; } -static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len, int verb) { esp_err_t err; uint32_t sec_num; core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; - esp_panicPutStr("Core dump len1 = "); - esp_panicPutHex(*data_len); - esp_panicPutStr("\r\n"); - // add space for 2 magics. TODO: change to CRC - *data_len += 2*sizeof(uint32_t); - if (*data_len > s_core_part_size) { - esp_panicPutStr("ERROR: Not enough space to save core dump!"); + if ((*data_len + 2*sizeof(uint32_t)) > s_core_part_size) { + ets_printf("ERROR: Not enough space to save core dump!\r\n"); return ESP_ERR_NO_MEM; } - - esp_panicPutStr("Core dump len2 = "); - esp_panicPutHex(*data_len); - esp_panicPutStr("\r\n"); + *data_len += 2*sizeof(uint32_t); wr_data->off = 0; sec_num = *data_len / SPI_FLASH_SEC_SIZE; if (*data_len % SPI_FLASH_SEC_SIZE) sec_num++; - err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); + err = spi_flash_erase_range(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to erase flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to erase flash (%d)!\r\n", err); return err; } @@ -297,11 +255,9 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr esp_err_t err = ESP_OK; uint32_t data32 = word; - err = spi_flash_write_panic(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); + err = spi_flash_write(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); if (err != ESP_OK) { - esp_panicPutStr("Failed to write to flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); + ets_printf("ERROR: Failed to write to flash (%d)!\r\n", err); return err; } wr_data->off += sizeof(uint32_t); @@ -309,14 +265,14 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr return err; } -static esp_err_t esp_core_dump_flash_write_start(void *priv) +static esp_err_t esp_core_dump_flash_write_start(void *priv, int verb) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; // save magic 1 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START); } -static esp_err_t esp_core_dump_flash_write_end(void *priv) +static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; uint32_t i; @@ -326,31 +282,27 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv) uint32_t data32[4]; } rom_data; - // TEST READ START - esp_err_t err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to read flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return err; - } - else { - esp_panicPutStr("Data from flash:\r\n"); - for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { - esp_panicPutHex(rom_data.data32[i]); - esp_panicPutStr("\r\n"); + if (verb) { + // TEST READ START + esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + ets_printf("ERROR: Failed to read flash (%d)!\r\n", err); + return err; } -// rom_data[4] = 0; -// esp_panicPutStr(rom_data); -// esp_panicPutStr("\r\n"); + else { + ets_printf("Data from flash:\r\n"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + ets_printf("%x\r\n", rom_data.data32[i]); + } + } + // TEST READ END } - // TEST READ END // save magic 2 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END); } -static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len, int verb) { esp_err_t err = ESP_OK; core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; @@ -375,223 +327,25 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_ */ void esp_core_dump_to_flash(XtExcFrame *frame) { -#if 1 core_dump_write_config_t wr_cfg; core_dump_write_flash_data_t wr_data; + /* init non-OS flash access critical section */ + spi_flash_guard_set(&g_flash_guard_no_os_ops); + wr_cfg.prepare = esp_core_dump_flash_write_prepare; wr_cfg.start = esp_core_dump_flash_write_start; wr_cfg.end = esp_core_dump_flash_write_end; wr_cfg.write = esp_core_dump_flash_write_data; wr_cfg.priv = &wr_data; - esp_panicPutStr("Save core dump to flash...\r\n"); - esp_core_dump_write(frame, &wr_cfg, 1); -#else - union - { - uint8_t data8[16]; - uint32_t data32[4]; - } rom_data; - //const esp_partition_t *core_part; - esp_err_t err; - TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; - UBaseType_t tcb_sz, task_num; - uint32_t data_len = 0, i, len, sec_num; - size_t off; - - esp_panicPutStr("Save core dump to flash...\r\n"); - task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); - // take TCB padding into account, actual TCB size will be stored in header - if (tcb_sz % sizeof(uint32_t)) - len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); - else - len = tcb_sz; - // header + magic2 + tasknum*(tcb + stack start/end + tcb addr) - data_len = 5*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); - for (i = 0; i < task_num; i++) { - if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { - // set correct stack top for current task - tasks[i].pxTopOfStack = (StackType_t *)frame; - esp_panicPutStr("Current task PC/A0/SP "); - esp_panicPutHex(frame->pc); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a0); - esp_panicPutStr(" "); - esp_panicPutHex(frame->a1); - esp_panicPutStr("\r\n"); - } -#if( portSTACK_GROWTH < 0 ) - len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; -#else - len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; -#endif - esp_panicPutStr("stack len = "); - esp_panicPutHex(len); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxTopOfStack); - esp_panicPutStr(" "); - esp_panicPutHex((int)tasks[i].pxEndOfStack); - esp_panicPutStr("\r\n"); - // take stack padding into account - if (len % sizeof(uint32_t)) - len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); - data_len += len; - } - esp_panicPutStr("Core dump len ="); - esp_panicPutHex(data_len); - esp_panicPutStr("\r\n"); - if (data_len > s_core_part_size) { - esp_panicPutStr("ERROR: Not enough space to save core dump!"); - return; - } - - // TEST READ START - err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to read flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - else { - esp_panicPutStr("Data from flash:\r\n"); - for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { - esp_panicPutHex(rom_data.data32[i]); - esp_panicPutStr("\r\n"); - } -// rom_data[4] = 0; -// esp_panicPutStr(rom_data); -// esp_panicPutStr("\r\n"); - } - // TEST READ END - - sec_num = data_len / SPI_FLASH_SEC_SIZE; - if (data_len % SPI_FLASH_SEC_SIZE) - sec_num++; - err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to erase flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - - rom_data.data32[0] = COREDUMP_FLASH_MAGIC_START; - rom_data.data32[1] = data_len; - rom_data.data32[2] = task_num; - rom_data.data32[3] = tcb_sz; - err = spi_flash_write_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write core dump header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - off = sizeof(rom_data); - - for (i = 0; i < task_num; i++) { - esp_panicPutStr("Dump task "); - esp_panicPutHex((int)tasks[i].pxTCB); - esp_panicPutStr("\r\n"); - - // save TCB address, stack base and stack top addr - rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; - rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; - rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; - err = spi_flash_write_panic(s_core_part_start + off, &rom_data, 3*sizeof(uint32_t)); - if (err != ESP_OK) { - esp_panicPutStr("ERROR: Failed to write task header "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } - off += 3*sizeof(uint32_t); - // save TCB - len = esp_core_dump_write_flash_padded(s_core_part_start + off, tasks[i].pxTCB, tcb_sz); - if (len == 0) - return; - off += len; - // save task stack - /*int k; - for (k = 0; k < 8*4; k++) { - esp_panicPutStr("stack["); - esp_panicPutDec(k); - esp_panicPutStr("] = "); - esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]); - esp_panicPutStr("\r\n"); - }*/ - len = esp_core_dump_write_flash_padded(s_core_part_start + off, -#if( portSTACK_GROWTH < 0 ) - tasks[i].pxTopOfStack, - (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack -#else - tasks[i].pxEndOfStack, - (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack -#endif - ); - if (len == 0) - return; - off += len; - } - - rom_data.data32[0] = COREDUMP_FLASH_MAGIC_END; - err = spi_flash_write_panic(s_core_part_start + off, &rom_data, sizeof(uint32_t)); - if (err != ESP_OK) { - esp_panicPutStr("Failed to write to flash "); - esp_panicPutHex(err); - esp_panicPutStr("!\r\n"); - return; - } -#endif - esp_panicPutStr("Core dump has been saved to flash.\r\n"); + ets_printf("Save core dump to flash...\r\n"); + esp_core_dump_write(frame, &wr_cfg, 0); + ets_printf("Core dump has been saved to flash.\r\n"); } #endif #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART -#if 0 -#define BASE64_ENCODE_BODY(_src, _src_len, _dst) \ - do { \ - static const char *b64 = \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \ - int i, j, a, b, c; \ - \ - for (i = j = 0; i < _src_len; i += 3) { \ - a = _src[i]; \ - b = i + 1 >= _src_len ? 0 : _src[i + 1]; \ - c = i + 2 >= _src_len ? 0 : _src[i + 2]; \ - \ - /*BASE64_OUT(b64[a >> 2], _dst[j]);*/ \ - _dst[j++] = b64[a >> 2]; \ - /*BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)], _dst[j]);*/ \ - _dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; \ - j++; \ - if (i + 1 < _src_len) { \ - BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)], _dst[j]); \ - j++; \ - } \ - if (i + 2 < _src_len) { \ - BASE64_OUT(b64[c & 63], _dst[j]); \ - j++; \ - } \ - } \ - \ - while (j % 4 != 0) { \ - BASE64_OUT('=', _dst); \ - } \ - BASE64_FLUSH(_dst) \ - } while(0) - -#define BASE64_OUT(ch, _dst) \ - do { \ - _dst = (ch); \ - } while (0) - -#define BASE64_FLUSH(_dst) \ - do { \ - _dst = '\0'; \ - } while (0) -#endif static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { // BASE64_ENCODE_BODY(src, src_len, dst); static const char *b64 = @@ -618,38 +372,26 @@ static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8 dst[j++] = '\0'; } -/*static esp_err_t esp_core_dump_uart_write_prepare(void *priv, uint32_t *data_len) +static esp_err_t esp_core_dump_uart_write_start(void *priv, int verb) { esp_err_t err = ESP_OK; - return err; -}*/ - -static esp_err_t esp_core_dump_uart_write_start(void *priv) -{ -// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; - esp_err_t err = ESP_OK; - esp_panicPutStr("================= CORE DUMP START =================\r\n"); + ets_printf("================= CORE DUMP START =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_end(void *priv) +static esp_err_t esp_core_dump_uart_write_end(void *priv, int verb) { -// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; esp_err_t err = ESP_OK; - esp_panicPutStr("================= CORE DUMP END =================\r\n"); + ets_printf("================= CORE DUMP END =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len, int verb) { -// core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; esp_err_t err = ESP_OK; char buf[64 + 4], *addr = data; char *end = addr + data_len; -// esp_panicPutStr("CORE DUMP SEC: "); -// esp_panicPutDec(data_len); -// esp_panicPutStr("bytes\r\n"); while (addr < end) { size_t len = end - addr; @@ -659,15 +401,7 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t memcpy(tmp, addr, len); esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); addr += len; - esp_panicPutStr(buf); -// for (size_t i = 0; buf[i] != '\0'; i++) { -// panicPutChar(buf[i]); -// } - //if (addr % 96 == 0) - esp_panicPutStr("\r\n"); - /* Feed the Cerberus. */ -// TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; -// TIMERG0.wdt_feed = 1; + ets_printf("%s\r\n", buf); } return err; @@ -676,17 +410,16 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t void esp_core_dump_to_uart(XtExcFrame *frame) { core_dump_write_config_t wr_cfg; - //core_dump_write_flash_data_t wr_data; - wr_cfg.prepare = NULL;//esp_core_dump_uart_write_prepare; + wr_cfg.prepare = NULL; wr_cfg.start = esp_core_dump_uart_write_start; wr_cfg.end = esp_core_dump_uart_write_end; wr_cfg.write = esp_core_dump_uart_write_data; wr_cfg.priv = NULL; - esp_panicPutStr("Save core dump to flash...\r\n"); + ets_printf("Print core dump to uart...\r\n"); esp_core_dump_write(frame, &wr_cfg, 0); - esp_panicPutStr("Core dump has been written to uart.\r\n"); + ets_printf("Core dump has been written to uart.\r\n"); } #endif diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 1b8a95b75c3..2ae4260bd5b 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -203,6 +203,8 @@ void start_cpu0_default(void) #endif esp_ipc_init(); spi_flash_init(); + /* init default OS-aware flash access critical section */ + spi_flash_guard_set(&g_flash_guard_default_ops); #if CONFIG_ESP32_PHY_AUTO_INIT nvs_flash_init(); diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h index 3dd5d4b185e..aa83c6d3811 100644 --- a/components/esp32/include/esp_panic.h +++ b/components/esp32/include/esp_panic.h @@ -24,10 +24,6 @@ */ void esp_set_breakpoint_if_jtag(void *fn); -void esp_panicPutChar(char c); -void esp_panicPutStr(const char *c); -void esp_panicPutHex(int a); -void esp_panicPutDec(int a); #define ESP_WATCHPOINT_LOAD 0x40000000 #define ESP_WATCHPOINT_STORE 0x80000000 diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 94aebfbac46..09ce520dede 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -47,61 +47,61 @@ #if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... -void esp_panicPutChar(char c) +static void panicPutChar(char c) { while (((READ_PERI_REG(UART_STATUS_REG(0)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ; WRITE_PERI_REG(UART_FIFO_REG(0), c); } -void esp_panicPutStr(const char *c) +static void panicPutStr(const char *c) { int x = 0; while (c[x] != 0) { - esp_panicPutChar(c[x]); + panicPutChar(c[x]); x++; } } -void esp_panicPutHex(int a) +static void panicPutHex(int a) { int x; int c; for (x = 0; x < 8; x++) { c = (a >> 28) & 0xf; if (c < 10) { - esp_panicPutChar('0' + c); + panicPutChar('0' + c); } else { - esp_panicPutChar('a' + c - 10); + panicPutChar('a' + c - 10); } a <<= 4; } } -void esp_panicPutDec(int a) +static void panicPutDec(int a) { int n1, n2; n1 = a % 10; n2 = a / 10; if (n2 == 0) { - esp_panicPutChar(' '); + panicPutChar(' '); } else { - esp_panicPutChar(n2 + '0'); + panicPutChar(n2 + '0'); } - esp_panicPutChar(n1 + '0'); + panicPutChar(n1 + '0'); } #else //No printing wanted. Stub out these functions. -void esp_panicPutChar(char c) { } -void esp_panicPutStr(const char *c) { } -void esp_panicPutHex(int a) { } -void esp_panicPutDec(int a) { } +static void panicPutChar(char c) { } +static void panicPutStr(const char *c) { } +static void panicPutHex(int a) { } +static void panicPutDec(int a) { } #endif void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { - esp_panicPutStr("***ERROR*** A stack overflow in task "); - esp_panicPutStr((char *)pcTaskName); - esp_panicPutStr(" has been detected.\r\n"); + panicPutStr("***ERROR*** A stack overflow in task "); + panicPutStr((char *)pcTaskName); + panicPutStr(" has been detected.\r\n"); abort(); } @@ -220,25 +220,25 @@ void xt_unhandled_exception(XtExcFrame *frame) int x; haltOtherCore(); - esp_panicPutStr("Guru Meditation Error of type "); + panicPutStr("Guru Meditation Error of type "); x = regs[20]; if (x < 40) { - esp_panicPutStr(edesc[x]); + panicPutStr(edesc[x]); } else { - esp_panicPutStr("Unknown"); + panicPutStr("Unknown"); } - esp_panicPutStr(" occurred on core "); - esp_panicPutDec(xPortGetCoreID()); + panicPutStr(" occurred on core "); + panicPutDec(xPortGetCoreID()); if (esp_cpu_in_ocd_debug_mode()) { - esp_panicPutStr(" at pc="); - esp_panicPutHex(regs[1]); - esp_panicPutStr(". Setting bp and returning..\r\n"); + panicPutStr(" at pc="); + panicPutHex(regs[1]); + panicPutStr(". Setting bp and returning..\r\n"); //Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger //will kick in exactly at the context the error happened. setFirstBreakpoint(regs[1]); return; } - esp_panicPutStr(". Exception was unhandled.\r\n"); + panicPutStr(". Exception was unhandled.\r\n"); commonErrorHandler(frame); } @@ -293,16 +293,16 @@ static void putEntry(uint32_t pc, uint32_t sp) if (pc & 0x80000000) { pc = (pc & 0x3fffffff) | 0x40000000; } - esp_panicPutStr(" 0x"); - esp_panicPutHex(pc); - esp_panicPutStr(":0x"); - esp_panicPutHex(sp); + panicPutStr(" 0x"); + panicPutHex(pc); + panicPutStr(":0x"); + panicPutHex(sp); } static void doBacktrace(XtExcFrame *frame) { uint32_t i = 0, pc = frame->pc, sp = frame->a1; - esp_panicPutStr("\r\nBacktrace:"); + panicPutStr("\r\nBacktrace:"); /* Do not check sanity on first entry, PC could be smashed. */ putEntry(pc, sp); pc = frame->a0; @@ -318,7 +318,7 @@ static void doBacktrace(XtExcFrame *frame) break; } } - esp_panicPutStr("\r\n\r\n"); + panicPutStr("\r\n\r\n"); } /* @@ -342,17 +342,17 @@ static void commonErrorHandler(XtExcFrame *frame) the register window is no longer useful. */ if (!abort_called) { - esp_panicPutStr("Register dump:\r\n"); + panicPutStr("Register dump:\r\n"); for (x = 0; x < 24; x += 4) { for (y = 0; y < 4; y++) { if (sdesc[x + y][0] != 0) { - esp_panicPutStr(sdesc[x + y]); - esp_panicPutStr(": 0x"); - esp_panicPutHex(regs[x + y + 1]); - esp_panicPutStr(" "); + panicPutStr(sdesc[x + y]); + panicPutStr(": 0x"); + panicPutHex(regs[x + y + 1]); + panicPutStr(" "); } - esp_panicPutStr("\r\n"); + panicPutStr("\r\n"); } } } @@ -362,7 +362,7 @@ static void commonErrorHandler(XtExcFrame *frame) #if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); - esp_panicPutStr("Entering gdb stub now.\r\n"); + panicPutStr("Entering gdb stub now.\r\n"); esp_gdbstub_panic_handler(frame); #else #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH @@ -372,14 +372,14 @@ static void commonErrorHandler(XtExcFrame *frame) esp_core_dump_to_uart(frame); #endif #if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT - esp_panicPutStr("Rebooting...\r\n"); + panicPutStr("Rebooting...\r\n"); for (x = 0; x < 100; x++) { ets_delay_us(1000); } software_reset(); #else disableAllWdts(); - esp_panicPutStr("CPU halted.\r\n"); + panicPutStr("CPU halted.\r\n"); while (1); #endif #endif diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 2f31d3f2926..2942c609f8c 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -24,6 +24,11 @@ except ImportError: __version__ = "0.1-dev" +if os.name == 'nt': + CLOSE_FDS = False +else: + CLOSE_FDS = True + class Struct(object): def __init__(self, buf=None): @@ -492,6 +497,10 @@ class ESPCoreDumpLoader(object): print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) + # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set + # and GDB can not unwind callstack properly (it implies not windowed call0) + if regs[REG_PS_IDX] & (1 << 5): + regs[REG_PS_IDX] &= ~(1 << 4) else: print "SOLSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_SOL_PC] @@ -516,18 +525,18 @@ class ESPCoreDumpLoader(object): os.remove(fname) except OSError as e: if e.errno != errno.ENOENT: - print "Warning failed to remove temp file '%s'!" % fname + print "Warning failed to remove temp file '%s' (%d)!" % (fname, e.errno) def cleanup(self): if self.fcore: self.fcore.close() - self.remove_tmp_file(self.fcore.name) + if self.fcore_name: + self.remove_tmp_file(self.fcore_name) def create_corefile(self, core_fname=None, off=0): """ TBD """ core_off = off - print "Read core dump header" data = self.read_data(core_off, self.ESP32_COREDUMP_HDR_SZ) tot_len,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_HDR_FMT, data) tcbsz_aligned = tcbsz @@ -538,7 +547,6 @@ class ESPCoreDumpLoader(object): core_elf = ESPCoreDumpElfFile() notes = b'' for i in range(task_num): - print "Read task[%d] header" % i data = self.read_data(core_off, self.ESP32_COREDUMP_TSK_HDR_SZ) tcb_addr,stack_top,stack_end = struct.unpack_from(self.ESP32_COREDUMP_TSK_HDR_FMT, data) if stack_end > stack_top: @@ -554,7 +562,6 @@ class ESPCoreDumpLoader(object): stack_len_aligned = 4*(stack_len_aligned/4 + 1) core_off += self.ESP32_COREDUMP_TSK_HDR_SZ - print "Read task[%d] TCB" % i data = self.read_data(core_off, tcbsz_aligned) if tcbsz != tcbsz_aligned: core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) @@ -562,15 +569,17 @@ class ESPCoreDumpLoader(object): core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) # print "tcb=%s" % data core_off += tcbsz_aligned - print "Read task[%d] stack %d bytes" % (i,stack_len) data = self.read_data(core_off, stack_len_aligned) # print "stk=%s" % data if stack_len != stack_len_aligned: data = data[:stack_len - stack_len_aligned] core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) core_off += stack_len_aligned - - task_regs = self._get_registers_from_stack(data, stack_end > stack_top) + try: + task_regs = self._get_registers_from_stack(data, stack_end > stack_top) + except Exception as e: + print e + return None prstatus = XtensaPrStatus() prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task prstatus.pr_pid = i # TODO: use pid assigned by OS @@ -608,32 +617,38 @@ class ESPCoreDumpFileLoader(ESPCoreDumpLoader): self.fcore = self._load_coredump(path, b64) def _load_coredump(self, path, b64): + """Loads core dump from (raw binary or base64-encoded) file + """ + self.fcore_name = None if b64: - fhnd,fname = tempfile.mkstemp() - print "tmpname %s" % fname + fhnd,self.fcore_name = tempfile.mkstemp() fcore = os.fdopen(fhnd, 'wb') - fb64 = open(path, 'r') + fb64 = open(path, 'rb') try: while True: line = fb64.readline() if len(line) == 0: break - data = base64.b64decode(line.rstrip('\r\n'))#, validate=True) + data = base64.standard_b64decode(line.rstrip('\r\n')) fcore.write(data) fcore.close() - fcore = open(fname, 'r') + fcore = open(self.fcore_name, 'rb') + except Exception as e: + if self.fcore_name: + self.remove_tmp_file(self.fcore_name) + raise e finally: fb64.close() else: - fcore = open(path, 'r') + fcore = open(path, 'rb') return fcore class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): """ TBD """ - ESP32_COREDUMP_FLASH_MAGIC_START = 0xDEADBEEF - ESP32_COREDUMP_FLASH_MAGIC_END = 0xACDCFEED + ESP32_COREDUMP_FLASH_MAGIC_START = 0xE32C04ED + ESP32_COREDUMP_FLASH_MAGIC_END = 0xE32C04ED ESP32_COREDUMP_FLASH_MAGIC_FMT = 'start();}while(0) #define FLASH_GUARD_END(_gp_) do{if((_gp_)) (_gp_)->end();}while(0) static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); -static esp_err_t spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard); -static esp_err_t spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard); -static esp_err_t spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard); -const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_ops = { +const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = { .start = spi_flash_disable_interrupts_caches_and_other_cpu, .end = spi_flash_enable_interrupts_caches_and_other_cpu }; -const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_panic_ops = { - .start = spi_flash_disable_interrupts_caches_and_other_cpu_panic, - .end = spi_flash_enable_interrupts_caches_panic +const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = { + .start = spi_flash_disable_interrupts_caches_and_other_cpu_no_os, + .end = spi_flash_enable_interrupts_caches_no_os }; +static const spi_flash_guard_funcs_t *s_flash_guard_ops; + void spi_flash_init() { spi_flash_init_lock(); @@ -96,6 +83,11 @@ void spi_flash_init() #endif } +void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs) +{ + s_flash_guard_ops = funcs; +} + size_t spi_flash_get_chip_size() { return g_rom_flashchip.chip_size; @@ -120,16 +112,6 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) } esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) -{ - return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_ops); -} - -esp_err_t IRAM_ATTR spi_flash_erase_range_panic(uint32_t start_addr, uint32_t size) -{ - return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_panic_ops); -} - -static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard) { if (start_addr % SPI_FLASH_SEC_SIZE != 0) { return ESP_ERR_INVALID_ARG; @@ -144,7 +126,7 @@ static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, u size_t end = start + size / SPI_FLASH_SEC_SIZE; const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE; COUNTER_START(); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { @@ -160,22 +142,12 @@ static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, u } } } - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) -{ - return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_ops); -} - -esp_err_t IRAM_ATTR spi_flash_write_panic(size_t dst, const void *srcv, size_t size) -{ - return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_panic_ops); -} - -static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound writes are checked in ROM code, but we can give better // error code here @@ -208,9 +180,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv if (left_size > 0) { uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(left_off, &t, 4); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -226,9 +198,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv bool in_dram = true; #endif if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) { - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -242,9 +214,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv uint32_t t[8]; uint32_t write_size = MIN(mid_size, sizeof(t)); memcpy(t, srcc + mid_off, write_size); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(dst + mid_off, t, write_size); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -257,9 +229,9 @@ static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv if (right_size > 0) { uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); rc = SPIWrite(dst + right_off, &t, 4); - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -307,16 +279,6 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, } esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) -{ - return spi_flash_read_internal(src, dstv, size, &s_flash_guard_ops); -} - -esp_err_t IRAM_ATTR spi_flash_read_panic(size_t src, void *dstv, size_t size) -{ - return spi_flash_read_internal(src, dstv, size, &s_flash_guard_panic_ops); -} - -static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard) { // Out of bound reads are checked in ROM code, but we can give better // error code here @@ -329,7 +291,7 @@ static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_ SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; COUNTER_START(); - FLASH_GUARD_START(flash_guard); + FLASH_GUARD_START(s_flash_guard_ops); /* To simplify boundary checks below, we handle small reads separately. */ if (size < 16) { uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */ @@ -403,7 +365,7 @@ static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_ memcpy(dstc + pad_right_off, t, pad_right_size); } out: - FLASH_GUARD_END(flash_guard); + FLASH_GUARD_END(s_flash_guard_ops); COUNTER_STOP(read); return spi_flash_translate_rc(rc); } diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index e78c389b4e1..402cd6e6f79 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -173,12 +173,26 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle); */ void spi_flash_mmap_dump(); +/** + * @brief SPI flash critical section enter function. + */ +typedef void (*spi_flash_guard_start_func_t)(void); +/** + * @brief SPI flash critical section exit function. + */ +typedef void (*spi_flash_guard_end_func_t)(void); + +/** + * Structure holding SPI flash access critical section management functions + */ +typedef struct { + spi_flash_guard_start_func_t start; /**< critical section start func */ + spi_flash_guard_end_func_t end; /**< critical section end func */ +} spi_flash_guard_funcs_t; + /** * @brief Erase a range of flash sectors. * - * @note This version of function is to be called from panic handler. - * It does not use any OS primitives and IPC and implies that - * only calling CPU is active. * * @param start_address Address where erase operation has to start. * Must be 4kB-aligned @@ -186,42 +200,18 @@ void spi_flash_mmap_dump(); * * @return esp_err_t */ -esp_err_t spi_flash_erase_range_panic(size_t start_address, size_t size); +void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs); +/** Default OS-aware flash access critical section functions */ +extern const spi_flash_guard_funcs_t g_flash_guard_default_ops; -/** - * @brief Write data to Flash. +/** Non-OS flash access critical section functions * - * @note This version of function is to be called from panic handler. + * @note This version of functions is to be used when no OS is present or from panic handler. * It does not use any OS primitives and IPC and implies that * only calling CPU is active. - - * @note If source address is in DROM, this function will return - * ESP_ERR_INVALID_ARG. - * - * @param dest destination address in Flash. Must be a multiple of 4 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 4 bytes. - * - * @return esp_err_t */ -esp_err_t spi_flash_write_panic(size_t dest, const void *src, size_t size); - - -/** - * @brief Read data from Flash. - * - * @note This version of function is to be called from panic handler. - * It does not use any OS primitives and IPC and implies that - * only calling CPU is active. - * - * @param src source address of the data in Flash. - * @param dest pointer to the destination buffer - * @param size length of data - * - * @return esp_err_t - */ -esp_err_t spi_flash_read_panic(size_t src, void *dest, size_t size); +extern const spi_flash_guard_funcs_t g_flash_guard_no_os_ops; #if CONFIG_SPI_FLASH_ENABLE_COUNTERS From 50b3ce616f5132ccb8b25162932129e2f6d33aa0 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Fri, 6 Jan 2017 13:06:43 +0300 Subject: [PATCH 141/167] esp32: Adds documentation and comments to core dump feature files --- components/esp32/core_dump.c | 10 - components/esp32/include/esp_core_dump.h | 43 +++ components/espcoredump/espcoredump.py | 344 ++++++++++--------- components/freertos/include/freertos/task.h | 10 +- components/spi_flash/cache_utils.h | 10 +- components/spi_flash/include/esp_spi_flash.h | 25 +- docs/Doxyfile | 3 +- docs/core_dump.rst | 127 +++++++ 8 files changed, 391 insertions(+), 181 deletions(-) create mode 100644 docs/core_dump.rst diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index e12fc1d71cc..13519badb79 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -316,15 +316,6 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_ return err; } -/* - * | MAGIC1 | - * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | - * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | - * . . . . - * . . . . - * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | - * | MAGIC2 | - */ void esp_core_dump_to_flash(XtExcFrame *frame) { core_dump_write_config_t wr_cfg; @@ -347,7 +338,6 @@ void esp_core_dump_to_flash(XtExcFrame *frame) #if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { -// BASE64_ENCODE_BODY(src, src_len, dst); static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int i, j, a, b, c; diff --git a/components/esp32/include/esp_core_dump.h b/components/esp32/include/esp_core_dump.h index d130aa237bc..c6634364c52 100644 --- a/components/esp32/include/esp_core_dump.h +++ b/components/esp32/include/esp_core_dump.h @@ -14,8 +14,51 @@ #ifndef ESP_CORE_DUMP_H_ #define ESP_CORE_DUMP_H_ +/** + * @brief Initializes core dump module internal data. + * + * @note Should be called at system startup. + */ void esp_core_dump_init(); + +/** + * @brief Saves core dump to flash. + * + * The structure of data stored in flash is as follows: + * | MAGIC1 | + * | TOTAL_LEN | TASKS_NUM | TCB_SIZE | + * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | + * . . . . + * . . . . + * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | + * | MAGIC2 | + * Core dump in flash consists of header and data for every task in the system at the moment of crash. + * For flash data integrity control two magic numbers are used at the beginning and the end of core dump. + * The structure of core dump data is described below in details. + * 1) MAGIC1 and MAGIC2 are special numbers stored at the beginning and the end of core dump. + * They are used to control core dump data integrity. Size of every number is 4 bytes. + * 2) Core dump starts with header: + * 2.1) TOTAL_LEN is total length of core dump data in flash including magic numbers. Size is 4 bytes. + * 2.2) TASKS_NUM is the number of tasks for which data are stored. Size is 4 bytes. + * 2.3) TCB_SIZE is the size of task's TCB structure. Size is 4 bytes. + * 3) Core dump header is followed by the data for every task in the system. + * Task data are started with task header: + * 3.1) TCB_ADDR is the address of TCB in memory. Size is 4 bytes. + * 3.2) STACK_TOP is the top of task's stack (address of the topmost stack item). Size is 4 bytes. + * 3.2) STACK_END is the end of task's stack (address from which task's stack starts). Size is 4 bytes. + * 4) Task header is followed by TCB data. Size is TCB_SIZE bytes. + * 5) Task's stack is placed after TCB data. Size is (STACK_END - STACK_TOP) bytes. + */ void esp_core_dump_to_flash(); + +/** + * @brief Print base64-encoded core dump to UART. + * + * The structure of core dump data is the same as for data stored in flash (@see esp_core_dump_to_flash) with some notes: + * 1) Magic numbers are not present in core dump printed to UART. + * 2) Since magic numbers are omitted TOTAL_LEN does not include their size. + * 3) Printed base64 data are surrounded with special messages to help user recognize the start and end of actual data. + */ void esp_core_dump_to_uart(); #endif diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 2942c609f8c..1589a362b93 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -30,34 +30,63 @@ else: CLOSE_FDS = True -class Struct(object): +class ESPCoreDumpError(RuntimeError): + """Core dump runtime error class + """ + def __init__(self, message): + """Constructor for core dump error + """ + super(ESPCoreDumpError, self).__init__(message) + +class BinStruct(object): + """Binary structure representation + + Subclasses must specify actual structure layout using 'fields' and 'format' members. + For example, the following subclass represents structure with two fields: + f1 of size 2 bytes and 4 bytes f2. Little endian. + class SomeStruct(BinStruct): + fields = ("f1", + "f2") + format = " (3, 0): - # Convert strings into bytearrays if this is Python 3 - for k in keys: - if type(self.__dict__[k]) is str: - self.__dict__[k] = bytearray(self.__dict__[k], encoding='ascii') - return struct.pack(self.__class__.fmt, *(self.__dict__[k] for k in keys)) + return struct.pack(self.__class__.format, *(self.__dict__[k] for k in keys)) - def __str__(self): - keys = self.__class__.fields - return (self.__class__.__name__ + "({" + - ", ".join("%s:%r" % (k, self.__dict__[k]) for k in keys) + - "})") +# def __str__(self): +# keys = self.__class__.fields +# return (self.__class__.__name__ + "({" + +# ", ".join("%s:%r" % (k, self.__dict__[k]) for k in keys) + +# "})") -class Elf32FileHeader(Struct): - """ELF32 File header""" +class Elf32FileHeader(BinStruct): + """ELF32 file header + """ fields = ("e_ident", "e_type", "e_machine", @@ -72,9 +101,11 @@ class Elf32FileHeader(Struct): "e_shentsize", "e_shnum", "e_shstrndx") - fmt = "<16sHHLLLLLHHHHHH" + format = "<16sHHLLLLLHHHHHH" def __init__(self, buf=None): + """Constructor for ELF32 file header structure + """ super(Elf32FileHeader, self).__init__(buf) if buf is None: # Fill in sane ELF header for LSB32 @@ -83,8 +114,9 @@ class Elf32FileHeader(Struct): self.e_ehsize = self.sizeof() -class Elf32ProgramHeader(Struct): - """ELF32 Program Header""" +class Elf32ProgramHeader(BinStruct): + """ELF32 program header + """ fields = ("p_type", "p_offset", "p_vaddr", @@ -93,39 +125,37 @@ class Elf32ProgramHeader(Struct): "p_memsz", "p_flags", "p_align") - fmt = " 0: @@ -265,11 +314,13 @@ class ESPCoreDumpElfFile(esptool.ELFFile): self.program_segments = [] def _read_sections(self, f, section_header_offs, shstrndx): + """Reads core dump sections from ELF file + """ f.seek(section_header_offs) section_header = f.read() LEN_SEC_HEADER = 0x28 if len(section_header) == 0: - raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) + raise ESPCoreDumpError("No section header found at offset %04x in ELF file." % section_header_offs) if len(section_header) % LEN_SEC_HEADER != 0: print 'WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER) @@ -284,7 +335,7 @@ class ESPCoreDumpElfFile(esptool.ELFFile): # search for the string table section if not shstrndx * LEN_SEC_HEADER in section_header_offsets: - raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx) + raise ESPCoreDumpError("ELF file has no STRTAB section at shstrndx %d" % shstrndx) _,sec_type,_,_,sec_size,sec_offs = read_section_header(shstrndx * LEN_SEC_HEADER) if sec_type != esptool.ELFFile.SEC_TYPE_STRTAB: print 'WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type @@ -306,11 +357,13 @@ class ESPCoreDumpElfFile(esptool.ELFFile): self.sections = prog_sections def _read_program_segments(self, f, seg_table_offs, entsz, num): + """Reads core dump program segments from ELF file + """ f.seek(seg_table_offs) seg_table = f.read(entsz*num) LEN_SEG_HEADER = 0x20 if len(seg_table) == 0: - raise FatalError("No program header table found at offset %04x in ELF file." % seg_table_offs) + raise ESPCoreDumpError("No program header table found at offset %04x in ELF file." % seg_table_offs) if len(seg_table) % LEN_SEG_HEADER != 0: print 'WARNING: Unexpected ELF program header table length %04x is not mod-%02x' % (len(seg_table),LEN_SEG_HEADER) @@ -318,8 +371,8 @@ class ESPCoreDumpElfFile(esptool.ELFFile): seg_table_offs = range(0, len(seg_table), LEN_SEG_HEADER) def read_program_header(offs): - type,offset,vaddr,_paddr,filesz,_memsz,_flags,_align = struct.unpack_from("= ps.addr and addr < (ps.addr + seg_len): - raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len): - raise FatalError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % + raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." % (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1)) # append self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags)) - # currently dumps only program segments. - # dumping sections is not supported yet def dump(self, f): - print "dump to '%s'" % f + """Write core dump contents to file + """ + # TODO: currently dumps only program segments. + # dumping sections is not supported yet # write ELF header ehdr = Elf32FileHeader() ehdr.e_type = self.e_type @@ -370,9 +424,7 @@ class ESPCoreDumpElfFile(esptool.ELFFile): f.write(ehdr.dump()) # write program header table cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize -# print "" % (ehdr.e_ehsize, ehdr.e_phnum, ehdr.e_phentsize) for i in range(len(self.program_segments)): - print "dump header for seg '%s'" % self.program_segments[i] phdr = Elf32ProgramHeader() phdr.p_type = self.program_segments[i].type phdr.p_offset = cur_off @@ -382,57 +434,52 @@ class ESPCoreDumpElfFile(esptool.ELFFile): phdr.p_memsz = phdr.p_filesz # TODO phdr.p_flags = self.program_segments[i].flags phdr.p_align = 0 # TODO -# print "header '%s'" % phdr f.write(phdr.dump()) cur_off += phdr.p_filesz # write program segments for i in range(len(self.program_segments)): - print "dump seg '%s'" % self.program_segments[i] f.write(self.program_segments[i].data) -class ESPCoreDumpError(RuntimeError): - """ - TBD - """ - def __init__(self, message): - super(ESPCoreDumpError, self).__init__(message) - - class ESPCoreDumpLoaderError(ESPCoreDumpError): - """ - TBD + """Core dump loader error class """ def __init__(self, message): + """Constructor for core dump loader error + """ super(ESPCoreDumpLoaderError, self).__init__(message) class ESPCoreDumpLoader(object): - """ - TBD + """Core dump loader base class """ ESP32_COREDUMP_HDR_FMT = '<3L' ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT) ESP32_COREDUMP_TSK_HDR_FMT = '<3L' ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT) + def __init__(self): + """Base constructor for core dump loader + """ self.fcore = None def _get_registers_from_stack(self, data, grows_down): - # from "gdb/xtensa-tdep.h" - # typedef struct - # { - #0 xtensa_elf_greg_t pc; - #1 xtensa_elf_greg_t ps; - #2 xtensa_elf_greg_t lbeg; - #3 xtensa_elf_greg_t lend; - #4 xtensa_elf_greg_t lcount; - #5 xtensa_elf_greg_t sar; - #6 xtensa_elf_greg_t windowstart; - #7 xtensa_elf_greg_t windowbase; - #8..63 xtensa_elf_greg_t reserved[8+48]; - #64 xtensa_elf_greg_t ar[64]; - # } xtensa_elf_gregset_t; + """Returns list of registers (in GDB format) from xtensa stack frame + """ + # from "gdb/xtensa-tdep.h" + # typedef struct + # { + #0 xtensa_elf_greg_t pc; + #1 xtensa_elf_greg_t ps; + #2 xtensa_elf_greg_t lbeg; + #3 xtensa_elf_greg_t lend; + #4 xtensa_elf_greg_t lcount; + #5 xtensa_elf_greg_t sar; + #6 xtensa_elf_greg_t windowstart; + #7 xtensa_elf_greg_t windowbase; + #8..63 xtensa_elf_greg_t reserved[8+48]; + #64 xtensa_elf_greg_t ar[64]; + # } xtensa_elf_gregset_t; REG_PC_IDX=0 REG_PS_IDX=1 REG_LB_IDX=2 @@ -446,7 +493,7 @@ class ESPCoreDumpLoader(object): # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128, # but gdb complanis when it less then 129 REG_NUM=129 - + XT_SOL_EXIT=0 XT_SOL_PC=1 XT_SOL_PS=2 @@ -454,7 +501,7 @@ class ESPCoreDumpLoader(object): XT_SOL_AR_START=4 XT_SOL_AR_NUM=4 XT_SOL_FRMSZ=8 - + XT_STK_EXIT=0 XT_STK_PC=1 XT_STK_PS=2 @@ -467,25 +514,19 @@ class ESPCoreDumpLoader(object): XT_STK_LEND=23 XT_STK_LCOUNT=24 XT_STK_FRMSZ=25 - + regs = [0] * REG_NUM # TODO: support for growing up stacks if not grows_down: - print "Growing up stacks are not supported for now!" - return regs - # for i in range(REG_NUM): - # regs[i] = i - # return regs + raise ESPCoreDumpLoaderError("Growing up stacks are not supported for now!") ex_struct = "<%dL" % XT_STK_FRMSZ if len(data) < struct.calcsize(ex_struct): - print "Too small stack to keep frame: %d bytes!" % len(data) - return regs - + raise ESPCoreDumpLoaderError("Too small stack to keep frame: %d bytes!" % len(data)) + stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)]) # Stack frame type indicator is always the first item rc = stack[XT_STK_EXIT] if rc != 0: - print "EXCSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_STK_PC] regs[REG_PS_IDX] = stack[XT_STK_PS] for i in range(XT_STK_AR_NUM): @@ -494,23 +535,16 @@ class ESPCoreDumpLoader(object): regs[REG_LB_IDX] = stack[XT_STK_LBEG] regs[REG_LE_IDX] = stack[XT_STK_LEND] regs[REG_LC_IDX] = stack[XT_STK_LCOUNT] - print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( - regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], - regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set # and GDB can not unwind callstack properly (it implies not windowed call0) if regs[REG_PS_IDX] & (1 << 5): regs[REG_PS_IDX] &= ~(1 << 4) else: - print "SOLSTACKFRAME %d" % rc regs[REG_PC_IDX] = stack[XT_SOL_PC] regs[REG_PS_IDX] = stack[XT_SOL_PS] for i in range(XT_SOL_AR_NUM): regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i] nxt = stack[XT_SOL_NEXT] - print "get_registers_from_stack: pc %x ps %x a0 %x a1 %x a2 %x a3 %x" % ( - regs[REG_PC_IDX], regs[REG_PS_IDX], regs[REG_AR_NUM + 0], - regs[REG_AR_NUM + 1], regs[REG_AR_NUM + 2], regs[REG_AR_NUM + 3]) # TODO: remove magic hack with saved PC to get proper value regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000) @@ -521,6 +555,8 @@ class ESPCoreDumpLoader(object): return regs def remove_tmp_file(self, fname): + """Silently removes temporary file + """ try: os.remove(fname) except OSError as e: @@ -528,13 +564,15 @@ class ESPCoreDumpLoader(object): print "Warning failed to remove temp file '%s' (%d)!" % (fname, e.errno) def cleanup(self): + """Cleans up loader resources + """ if self.fcore: self.fcore.close() if self.fcore_name: self.remove_tmp_file(self.fcore_name) def create_corefile(self, core_fname=None, off=0): - """ TBD + """Creates core dump ELF file """ core_off = off data = self.read_data(core_off, self.ESP32_COREDUMP_HDR_SZ) @@ -542,7 +580,6 @@ class ESPCoreDumpLoader(object): tcbsz_aligned = tcbsz if tcbsz_aligned % 4: tcbsz_aligned = 4*(tcbsz_aligned/4 + 1) - print "tot_len=%d, task_num=%d, tcbsz=%d" % (tot_len,task_num,tcbsz) core_off += self.ESP32_COREDUMP_HDR_SZ core_elf = ESPCoreDumpElfFile() notes = b'' @@ -555,7 +592,6 @@ class ESPCoreDumpLoader(object): else: stack_len = stack_top - stack_end stack_base = stack_end - print "tcb_addr=%x, stack_top=%x, stack_end=%x, stack_len=%d" % (tcb_addr,stack_top,stack_end,stack_len) stack_len_aligned = stack_len if stack_len_aligned % 4: @@ -567,10 +603,8 @@ class ESPCoreDumpLoader(object): core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) else: core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) - # print "tcb=%s" % data core_off += tcbsz_aligned data = self.read_data(core_off, stack_len_aligned) - # print "stk=%s" % data if stack_len != stack_len_aligned: data = data[:stack_len - stack_len_aligned] core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) @@ -584,7 +618,6 @@ class ESPCoreDumpLoader(object): prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task prstatus.pr_pid = i # TODO: use pid assigned by OS note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump() - print "NOTE_LEN %d" % len(note) notes += note # add notes @@ -602,20 +635,22 @@ class ESPCoreDumpLoader(object): return core_fname def read_data(self, off, sz): -# print "read_data: %x %d" % (off, sz) + """Reads data from raw core dump got from flash or UART + """ self.fcore.seek(off) data = self.fcore.read(sz) -# print "data1: %s" % data return data class ESPCoreDumpFileLoader(ESPCoreDumpLoader): - """ TBD + """Core dump file loader class """ def __init__(self, path, b64 = False): + """Constructor for core dump file loader + """ super(ESPCoreDumpFileLoader, self).__init__() self.fcore = self._load_coredump(path, b64) - + def _load_coredump(self, path, b64): """Loads core dump from (raw binary or base64-encoded) file """ @@ -645,7 +680,7 @@ class ESPCoreDumpFileLoader(ESPCoreDumpLoader): class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): - """ TBD + """Core dump flash loader class """ ESP32_COREDUMP_FLASH_MAGIC_START = 0xE32C04ED ESP32_COREDUMP_FLASH_MAGIC_END = 0xE32C04ED @@ -655,7 +690,8 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): ESP32_COREDUMP_FLASH_HDR_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_HDR_FMT) def __init__(self, off, tool_path=None, chip='esp32', port=None, baud=None): -# print "esptool.__file__ %s" % esptool.__file__ + """Constructor for core dump flash loader + """ super(ESPCoreDumpFlashLoader, self).__init__() if not tool_path: self.path = esptool.__file__ @@ -667,7 +703,7 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): self.chip = chip self.dump_sz = 0 self.fcore = self._load_coredump(off) - + def _load_coredump(self, off): """Loads core dump from flash """ @@ -702,7 +738,8 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): return f def _read_core_dump_length(self, f): - print "Read core dump header from '%s'" % f.name + """Reads core dump length + """ data = f.read(4*4) mag1,tot_len,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_FLASH_HDR_FMT, data) if mag1 != self.ESP32_COREDUMP_FLASH_MAGIC_START: @@ -710,44 +747,50 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader): return tot_len def create_corefile(self, core_fname=None): - """ TBD + """Checks flash coredump data integrity and creates ELF file """ data = self.read_data(0, self.ESP32_COREDUMP_FLASH_MAGIC_SZ) mag1, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_MAGIC_FMT, data) if mag1 != self.ESP32_COREDUMP_FLASH_MAGIC_START: - print "Invalid start marker %x" % mag1 - return None + raise ESPCoreDumpLoaderError("Invalid start marker %x" % mag1) data = self.read_data(self.dump_sz-self.ESP32_COREDUMP_FLASH_MAGIC_SZ, self.ESP32_COREDUMP_FLASH_MAGIC_SZ) mag2, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_MAGIC_FMT, data) if mag2 != self.ESP32_COREDUMP_FLASH_MAGIC_END: - print "Invalid end marker %x" % mag2 - return None + raise ESPCoreDumpLoaderError("Invalid end marker %x" % mag2) return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname, off=self.ESP32_COREDUMP_FLASH_MAGIC_SZ) class GDBMIOutRecordHandler(object): - """ TBD + """GDB/MI output record handler base class """ TAG = '' def __init__(self, f, verbose=False): + """Base constructor for GDB/MI output record handler + """ self.verbose = verbose def execute(self, ln): + """Base method to execute GDB/MI output record handler function + """ if self.verbose: print "%s.execute '%s'" % (self.__class__.__name__, ln) class GDBMIOutStreamHandler(GDBMIOutRecordHandler): - """ TBD + """GDB/MI output stream handler class """ def __init__(self, f, verbose=False): + """Constructor for GDB/MI output stream handler + """ super(GDBMIOutStreamHandler, self).__init__(None, verbose) self.func = f def execute(self, ln): + """Executes GDB/MI output stream handler function + """ GDBMIOutRecordHandler.execute(self, ln) if self.func: # remove TAG / quotes and replace c-string \n with actual NL @@ -755,7 +798,7 @@ class GDBMIOutStreamHandler(GDBMIOutRecordHandler): class GDBMIResultHandler(GDBMIOutRecordHandler): - """ TBD + """GDB/MI result handler class """ TAG = '^' RC_DONE = 'done' @@ -765,11 +808,15 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): RC_EXIT = 'exit' def __init__(self, verbose=False): + """Constructor for GDB/MI result handler + """ super(GDBMIResultHandler, self).__init__(None, verbose) self.result_class = None self.result_str = None def _parse_rc(self, ln, rc): + """Parses result code + """ rc_str = "{0}{1}".format(self.TAG, rc) if ln.startswith(rc_str): self.result_class = rc @@ -786,6 +833,8 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): return False def execute(self, ln): + """Executes GDB/MI result handler function + """ GDBMIOutRecordHandler.execute(self, ln) if self._parse_rc(ln, self.RC_DONE): return @@ -797,17 +846,17 @@ class GDBMIResultHandler(GDBMIOutRecordHandler): return if self._parse_rc(ln, self.RC_EXIT): return - print "Unknown result: '%s'" % ln + print "Unknown GDB/MI result: '%s'" % ln class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler): - """ TBD + """GDB/MI console stream handler class """ TAG = '~' def dbg_corefile(args): - """ TBD + """ Command to load core dump from file or flash and run GDB debug session with it """ global CLOSE_FDS loader = None @@ -847,19 +896,14 @@ def dbg_corefile(args): def info_corefile(args): -# def info_corefile(args): - """ TBD + """ Command to load core dump from file or flash and print it's data in user friendly form """ global CLOSE_FDS def gdbmi_console_stream_handler(ln): - # print ln sys.stdout.write(ln) sys.stdout.flush() - def gdbmi_read2prompt(f, out_handlers=None): - """ TBD - """ while True: ln = f.readline().rstrip(' \r\n') if ln == '(gdb)': @@ -908,10 +952,11 @@ def info_corefile(args): gdbmi_read2prompt(p.stdout, handlers) exe_elf = ESPCoreDumpElfFile(args.prog) core_elf = ESPCoreDumpElfFile(core_fname) - merged_segs = []#[(s, 0) for s in exe_elf.sections if s.flags & (esptool.ELFSection.SHF_ALLOC | esptool.ELFSection.SHF_WRITE)] + merged_segs = [] + core_segs = core_elf.program_segments for s in exe_elf.sections: merged = False - for ps in core_elf.program_segments: + for ps in core_segs: if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr: # sec: |XXXXXXXXXX| # seg: |...XXX.............| @@ -927,6 +972,7 @@ def info_corefile(args): # merged: |XXXXXXXXXXXXXXXXX| seg_len = len(ps.data) merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + core_segs.remove(ps) merged = True elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data): # sec: |XXXXXXXXXX| @@ -943,11 +989,11 @@ def info_corefile(args): # merged: |XXXXXXXXXX| seg_len = len(s.data) merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True)) + core_segs.remove(ps) merged = True if not merged: merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False)) -# merged_segs.append(('None', ps.addr, len(ps.data), 'None')) print "===============================================================" print "==================== ESP32 CORE DUMP START ====================" @@ -969,7 +1015,7 @@ def info_corefile(args): gdbmi_read2prompt(p.stdout, handlers) if handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE: print "GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str) - print "\n======================= MEMORY REGIONS ========================" + print "\n======================= ALL MEMORY REGIONS ========================" print "Name Address Size Attrs" for ms in merged_segs: print "%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]) @@ -1018,11 +1064,6 @@ def main(): type=int, default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD)) -# parser.add_argument( -# '--no-stub', -# help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", -# action='store_true') - subparsers = parser.add_subparsers( dest='operation', help='Run coredumper {command} -h for additional help') @@ -1054,10 +1095,7 @@ def main(): args = parser.parse_args() - print 'coredumper.py v%s' % __version__ - - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPLoader class. + print 'espcoredump.py v%s' % __version__ operation_func = globals()[args.operation] operation_func(args) diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index c6896e53863..590b07a2a48 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -2185,7 +2185,15 @@ eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; */ void *pvTaskIncrementMutexHeldCount( void ); -/* Used by core dump facility to get list of task handles. */ +/* + * This function fills array with TaskSnapshot_t structures for every task in the system. + * Used by core dump facility to get snapshots of all tasks in the system. + * Only available when configENABLE_TASK_SNAPSHOT is set to 1. + * @param pxTaskSnapshotArray Pointer to array of TaskSnapshot_t structures to store tasks snapshot data. + * @param uxArraySize Size of tasks snapshots array. + * @param pxTcbSz Pointer to store size of TCB. + * @return Number of elements stored in array. + */ UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ); #ifdef __cplusplus diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index 4ec1f3203a7..a5442d1af7a 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -41,13 +41,13 @@ void spi_flash_disable_interrupts_caches_and_other_cpu(); void spi_flash_enable_interrupts_caches_and_other_cpu(); // Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs. -// This function is implied to be called from panic handler or when no OS is present -// when non-current CPU is halted and can not execute code from flash. +// This function is implied to be called from panic handler +// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. void spi_flash_disable_interrupts_caches_and_other_cpu_no_os(); -// Enable cache, enable interrupts (to be added in future) on current CPU. -// This function is implied to be called from panic handler or when no OS is present -// when non-current CPU is halted and can not execute code from flash. +// Enable cache, enable interrupts on current CPU. +// This function is implied to be called from panic handler +// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. void spi_flash_enable_interrupts_caches_no_os(); #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 402cd6e6f79..bf897e8995c 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -184,6 +184,9 @@ typedef void (*spi_flash_guard_end_func_t)(void); /** * Structure holding SPI flash access critical section management functions + * + * @note Structure and corresponding guard functions should not reside in flash. + * For example structure can be placed in DRAM and functions in IRAM sections. */ typedef struct { spi_flash_guard_start_func_t start; /**< critical section start func */ @@ -191,25 +194,25 @@ typedef struct { } spi_flash_guard_funcs_t; /** - * @brief Erase a range of flash sectors. + * @brief Sets guard functions to access flash. * + * @note Pointed structure and corresponding guard functions should not reside in flash. + * For example structure can be placed in DRAM and functions in IRAM sections. * - * @param start_address Address where erase operation has to start. - * Must be 4kB-aligned - * @param size Size of erased range, in bytes. Must be divisible by 4kB. - * - * @return esp_err_t + * @param funcs pointer to structure holding flash access guard functions. */ void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs); -/** Default OS-aware flash access critical section functions */ +/** + * @brief Default OS-aware flash access guard functions + */ extern const spi_flash_guard_funcs_t g_flash_guard_default_ops; -/** Non-OS flash access critical section functions +/** + * @brief Non-OS flash access guard functions * - * @note This version of functions is to be used when no OS is present or from panic handler. - * It does not use any OS primitives and IPC and implies that - * only calling CPU is active. + * @note This version of flash guard functions is to be used when no OS is present or from panic handler. + * It does not use any OS primitives and IPC and implies that only calling CPU is active. */ extern const spi_flash_guard_funcs_t g_flash_guard_no_os_ops; diff --git a/docs/Doxyfile b/docs/Doxyfile index 7dbd1cc8d1c..a3fc5faa124 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -35,7 +35,8 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_deep_sleep.h \ ../components/sdmmc/include/sdmmc_cmd.h \ ../components/fatfs/src/esp_vfs_fat.h \ - ../components/fatfs/src/diskio.h + ../components/fatfs/src/diskio.h \ + ../components/esp32/include/esp_core_dump.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/core_dump.rst b/docs/core_dump.rst new file mode 100644 index 00000000000..0d0624cc899 --- /dev/null +++ b/docs/core_dump.rst @@ -0,0 +1,127 @@ +ESP32 Core Dump +================ + +Overview +-------- + +ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analisys of software state at the moment of failure. +Upon the crash system enters panic state, prints some information and halts or reboots depending configuration. User can choose to generate core dump in order to analyse +the reason of failure on PC later on. Core dump contains snapshots of all tasks in the system at the moment of failure. Snapshots include tasks control blocks (TCB) and stacks. +So it is possible to find out what task, at what instruction (line of code) and what callstack of that task lead to the crash. +ESP-IDF provides special script `espcoredump.py` to help users to retrieve and analyse core dumps. This tool provides two commands for core dumps analysis: + +* info_corefile - prints crashed task's registers, callstack, list of available tasks in the system, memory regions and contents of memory stored in core dump (TCBs and stacks) +* dbg_corefile - creates core dump ELF file and runs GDB debug session with this file. User can examine memory, variables and tasks states manually. Note that since not all memory is saved in core dump only values of variables allocated on stack will be meaningfull + +Configuration +------------- + +Currently there are three options related to core dump generation which user can choose in configuration menu of the application (`make menuconfig`): + +* Disable core dump generation +* Save core dump to flash +* Print core dump to UART + +These options can be choosen in Components -> ESP32-specific config -> Core dump destination menu item. + +Save core dump to flash +----------------------- + +When this option is selected core dumps are saved to special partition on flash. When using default partition table files which are provided with ESP-IDF it automatically +allocates necessary space on flash, But if user wants to use its own layout file together with core dump feature it should define separate partition for core dump +as it is shown below:: + + # Name, Type, SubType, Offset, Size + # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild + nvs, data, nvs, 0x9000, 0x6000 + phy_init, data, phy, 0xf000, 0x1000 + factory, app, factory, 0x10000, 1M + coredump, data, 3, , 64K + +There are no special requrements for partition name. It can be choosen according to the user application needs, but partition type should be 'data' and +sub-type should be 3. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. +This overhead does not include size of TCB and stack for every task. So partirion size should be at least 20 + max tasks number x (12 + TCB size + max task stack size) bytes. + +The example of generic command to analyze core dump from flash is: `espcoredump.py -p info_corefile ` +or `espcoredump.py -p dbg_corefile ` + +Print core dump to UART +----------------------- + +When this option is selected base64-encoded core dumps are printed on UART upon system panic. In this case user should save core dump text body to some file manually and +then run the following command: `espcoredump.py -p info_corefile -t b64 -c ` +or `espcoredump.py -p dbg_corefile -t b64 -c ` + +Base64-encoded body of core dump will be between the following header and footer:: + + ================= CORE DUMP START ================= + + ================= CORE DUMP END =================== + +Command Options For 'espcoredump.py' +-------------------------------------------- + +usage: coredumper [-h] [--chip {auto,esp32}] [--port PORT] [--baud BAUD] + {dbg_corefile,info_corefile} ... + +espcoredump.py v0.1-dev - ESP32 Core Dump Utility + +positional arguments: + {dbg_corefile,info_corefile} + Run coredumper {command} -h for additional help + dbg_corefile Starts GDB debugging session with specified corefile + info_corefile Print core dump info from file + +optional arguments: + -h, --help show this help message and exit + --chip {auto,esp32}, -c {auto,esp32} + Target chip type + --port PORT, -p PORT Serial port device + --baud BAUD, -b BAUD Serial port baud rate used when flashing/reading + + +usage: coredumper info_corefile [-h] [--gdb GDB] [--core CORE] + [--core-format CORE_FORMAT] [--off OFF] + [--save-core SAVE_CORE] [--print-mem] + prog + +positional arguments: + prog Path to program's ELF binary + +optional arguments: + -h, --help show this help message and exit + --gdb GDB, -g GDB Path to gdb + --core CORE, -c CORE Path to core dump file (if skipped core dump will be + read from flash) + --core-format CORE_FORMAT, -t CORE_FORMAT + (elf, raw or b64). File specified with "-c" is an ELF + ("elf"), raw (raw) or base64-encoded (b64) binary + --off OFF, -o OFF Ofsset of coredump partition in flash (type "make + partition_table" to see). + --save-core SAVE_CORE, -s SAVE_CORE + Save core to file. Othwerwise temporary core file will + be deleted. Does not work with "-c" + --print-mem, -m Print memory dump + + +usage: coredumper dbg_corefile [-h] [--gdb GDB] [--core CORE] + [--core-format CORE_FORMAT] [--off OFF] + [--save-core SAVE_CORE] + prog + +positional arguments: + prog Path to program's ELF binary + +optional arguments: + -h, --help show this help message and exit + --gdb GDB, -g GDB Path to gdb + --core CORE, -c CORE Path to core dump file (if skipped core dump will be + read from flash) + --core-format CORE_FORMAT, -t CORE_FORMAT + (elf, raw or b64). File specified with "-c" is an ELF + ("elf"), raw (raw) or base64-encoded (b64) binary + --off OFF, -o OFF Ofsset of coredump partition in flash (type "make + partition_table" to see). + --save-core SAVE_CORE, -s SAVE_CORE + Save core to file. Othwerwise temporary core file will + be deleted. Ignored with "-c" From 9f151d745ea6b4c5204f3e1e6b66d2d048214d4d Mon Sep 17 00:00:00 2001 From: Island Date: Thu, 12 Jan 2017 00:48:48 +0800 Subject: [PATCH 142/167] component/bt : Optimize Gatt attr table and fix some bugs 1. Optimize GATT attribute table structure 2. fix read/write bugs 3. add docs --- .../bt/bluedroid/api/include/esp_gatt_defs.h | 32 ++-- .../bt/bluedroid/api/include/esp_gatts_api.h | 4 +- .../bt/bluedroid/bta/gatt/bta_gatts_act.c | 18 ++- .../bt/bluedroid/bta/gatt/bta_gatts_api.c | 51 ++++--- .../btc/profile/std/gatt/btc_gatts.c | 56 +++++-- components/bt/bluedroid/stack/gatt/gatt_db.c | 52 ++++--- components/bt/bluedroid/stack/gatt/gatt_sr.c | 103 +++++++------ .../bt/bluedroid/stack/include/gatt_api.h | 1 + docs/api/esp_gatt_defs.rst | 31 +++- docs/api/esp_gatts.rst | 6 +- .../main/gatts_table_creat_demo.c | 144 ++++++++---------- .../main/gatts_table_creat_demo.h | 9 -- 12 files changed, 284 insertions(+), 223 deletions(-) diff --git a/components/bt/bluedroid/api/include/esp_gatt_defs.h b/components/bt/bluedroid/api/include/esp_gatt_defs.h index f8b2302111d..153a702735d 100644 --- a/components/bt/bluedroid/api/include/esp_gatt_defs.h +++ b/components/bt/bluedroid/api/include/esp_gatt_defs.h @@ -286,13 +286,14 @@ typedef enum { * @brief Attribute description (used to create database) */ typedef struct - { - esp_bt_uuid_t uuid; /*!< Element UUID */ + { + uint16_t uuid_length; /*!< UUID length */ + uint8_t *uuid_p; /*!< UUID value */ uint16_t perm; /*!< Attribute permission */ uint16_t max_length; /*!< Maximum length of the element*/ uint16_t length; /*!< Current length of the element*/ - uint8_t* value; /*!< Element value array*/ - }esp_attr_desc_t; + uint8_t *value; /*!< Element value array*/ + } esp_attr_desc_t; /** @@ -303,7 +304,7 @@ typedef struct #define ESP_GATT_RSP_BY_APP 0 #define ESP_GATT_AUTO_RSP 1 uint8_t auto_rsp; /*!< need the app response to the client if need_rsp set to 1*/ -}esp_attr_control_t; +} esp_attr_control_t; /** @@ -312,8 +313,8 @@ typedef struct typedef struct { esp_attr_control_t attr_control; /*!< The attribue control type*/ - esp_attr_desc_t att_desc; /*!< The attribue type*/ -}esp_gatts_attr_db_t; + esp_attr_desc_t att_desc; /*!< The attribue type*/ +} esp_gatts_attr_db_t; /** @@ -323,8 +324,8 @@ typedef struct { uint16_t attr_max_len; /*!< attribute max value length */ uint16_t attr_len; /*!< attribute current value length */ - uint8_t *attr_value; /*!< the pointer to attribute value */ -}esp_attr_value_t; + uint8_t *attr_value; /*!< the pointer to attribute value */ +} esp_attr_value_t; /** @@ -335,7 +336,7 @@ typedef struct uint16_t start_hdl; /*!< Gatt start handle value of included service */ uint16_t end_hdl; /*!< Gatt end handle value of included service */ uint16_t uuid; /*!< Gatt attribute value UUID of included service */ -}esp_gatts_incl_svc_desc_t; /*!< Gatt include service entry element */ +} esp_gatts_incl_svc_desc_t; /*!< Gatt include service entry element */ /** * @brief Gatt include 128 bit service entry element @@ -344,18 +345,9 @@ typedef struct { uint16_t start_hdl; /*!< Gatt start handle value of included 128 bit service */ uint16_t end_hdl; /*!< Gatt end handle value of included 128 bit service */ -}esp_gatts_incl128_svc_desc_t; /*!< Gatt include 128 bit service entry element */ +} esp_gatts_incl128_svc_desc_t; /*!< Gatt include 128 bit service entry element */ -/** - * @brief Gatt characteristic entry element - */ -typedef struct -{ - uint8_t prop; /*!< Gatt attribute properties */ - uint16_t attr_hdl; /*!< Gatt attribute handle */ - esp_bt_uuid_t attr_uuid; /*!< Gatt attribute uuid typedle */ -}esp_gatts_char_desc_t; /*!< Gatt characteristic value descriptor */ /// Gatt attribute value diff --git a/components/bt/bluedroid/api/include/esp_gatts_api.h b/components/bt/bluedroid/api/include/esp_gatts_api.h index 86a539597ac..8d51ac7375f 100644 --- a/components/bt/bluedroid/api/include/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/esp_gatts_api.h @@ -74,8 +74,10 @@ typedef union { uint16_t handle; /*!< The attribute handle */ uint16_t offset; /*!< Offset of the value, if the value is too long */ bool is_long; /*!< The value is too long or not */ + bool need_rsp; /*!< The read operation need to do response */ } read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */ + /** * @brief ESP_GATTS_WRITE_EVT */ @@ -227,7 +229,7 @@ typedef union { * @brief ESP_GATTS_RESPONSE_EVT */ struct gatts_rsp_evt_param { - esp_gatt_status_t status; /*!< Operation status */ + esp_gatt_status_t status; /*!< Operation status */ uint16_t handle; /*!< Attribute handle which send response */ } rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */ diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index 9dd2ea4039e..83d5e4d7d71 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -403,16 +403,18 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) UINT16 attr_id = 0; tBTA_GATTS cb_data; - tGATT_ATTR_VAL *p_attr_val = NULL; + tGATT_ATTR_VAL *p_attr_val = NULL; tGATTS_ATTR_CONTROL *p_control = NULL; - if (p_msg->api_add_char_descr.attr_val.attr_max_len != 0) { - p_attr_val = &p_msg->api_add_char_descr.attr_val; + if(p_msg->api_add_char.attr_val.attr_max_len != 0){ + p_attr_val = &p_msg->api_add_char.attr_val; } - if (p_msg->api_add_char_descr.control.auto_rsp != 0) { - p_control = &p_msg->api_add_char_descr.control; + if(p_msg->api_add_char.control.auto_rsp != 0){ + p_control = &p_msg->api_add_char.control; } + + attr_id = GATTS_AddCharacteristic(p_msg->api_add_char.hdr.layer_specific, &p_msg->api_add_char.char_uuid, p_msg->api_add_char.perm, @@ -429,6 +431,9 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) } else { cb_data.add_result.status = BTA_GATT_ERROR; } + if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ + GKI_freebuf(p_attr_val->attr_val); + } if (p_rcb->p_cback) { (*p_rcb->p_cback)(BTA_GATTS_ADD_CHAR_EVT, &cb_data); @@ -476,6 +481,9 @@ void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ } else { cb_data.add_result.status = BTA_GATT_ERROR; } + if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ + GKI_freebuf(p_attr_val->attr_val); + } if (p_rcb->p_cback) { (*p_rcb->p_cback)(BTA_GATTS_ADD_CHAR_DESCR_EVT, &cb_data); diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c index 58e226c839e..94f1d407e80 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c @@ -274,47 +274,54 @@ void BTA_GATTS_AddCharDescriptor (UINT16 service_id, tBTA_GATTS_ATTR_CONTROL *control) { tBTA_GATTS_API_ADD_DESCR *p_buf; - UINT16 len = 0; - if(attr_val != NULL) { - len = sizeof(tBTA_GATTS_API_ADD_DESCR) + attr_val->attr_len; - } else { - len = sizeof(tBTA_GATTS_API_ADD_DESCR); - } + UINT16 value_len = 0; - if ((p_buf = (tBTA_GATTS_API_ADD_DESCR *) GKI_getbuf(len)) != NULL) { - memset(p_buf, 0, len); + if ((p_buf = (tBTA_GATTS_API_ADD_DESCR *) GKI_getbuf(sizeof(tBTA_GATTS_API_ADD_DESCR))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTS_API_ADD_DESCR)); p_buf->hdr.event = BTA_GATTS_API_ADD_DESCR_EVT; p_buf->hdr.layer_specific = service_id; p_buf->perm = perm; + if(control != NULL){ + p_buf->control.auto_rsp = control->auto_rsp; + } + if (p_descr_uuid) { memcpy(&p_buf->descr_uuid, p_descr_uuid, sizeof(tBT_UUID)); } - if(attr_val->attr_len != 0) { + if(attr_val != NULL){ p_buf->attr_val.attr_len = attr_val->attr_len; - p_buf->attr_val.attr_max_len= attr_val->attr_max_len; - memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + p_buf->attr_val.attr_max_len = attr_val->attr_max_len; + value_len = attr_val->attr_len; + if (value_len != 0){ + p_buf->attr_val.attr_val = (uint8_t*)GKI_getbuf(value_len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, value_len); + } + else{ + APPL_TRACE_ERROR("Allocate fail for %s\n", __func__); + + } + } } - if(control != NULL) { - p_buf->control.auto_rsp = control->auto_rsp; - } bta_sys_sendmsg(p_buf); } return; + } /******************************************************************************* -** -** Function BTA_GATTS_DeleteService -** -** Description This function is called to delete a service. When this is done, -** a callback event BTA_GATTS_DELETE_EVT is report with the status. -** -** Parameters service_id: service_id to be deleted. -** + ** + ** Function BTA_GATTS_DeleteService + ** + ** Description This function is called to delete a service. When this is done, + ** a callback event BTA_GATTS_DELETE_EVT is report with the status. + ** + ** Parameters service_id: service_id to be deleted. + ** ** Returns returns none. ** *******************************************************************************/ 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 255484fbc4b..a5631f5b7a2 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -47,6 +47,25 @@ static inline void btc_gatts_cb_to_app(esp_gatts_cb_event_t event, esp_gatt_if_t } } +static inline void btc_gatts_uuid_format_convert(esp_bt_uuid_t* dest_uuid, uint16_t src_uuid_len, uint8_t* src_uuid_p) +{ + dest_uuid->len = src_uuid_len; + if(src_uuid_len == ESP_UUID_LEN_16){ + dest_uuid->uuid.uuid16 = src_uuid_p[0] + (src_uuid_p[1]<<8); + } + else if(src_uuid_len == ESP_UUID_LEN_32){ + dest_uuid->uuid.uuid32 = src_uuid_p[0] + (src_uuid_p[1]<<8) + (src_uuid_p[2]<<16) + (src_uuid_p[3]<<24); + } + else if(src_uuid_len == ESP_UUID_LEN_128){ + memcpy(dest_uuid->uuid.uuid128, src_uuid_p, src_uuid_len); + } + else{ + LOG_ERROR("%s wrong uuid length %d\n", __func__, src_uuid_len); + } + +} + + void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { btc_ble_gatts_args_t *dst = (btc_ble_gatts_args_t *) p_dest; @@ -167,8 +186,8 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, btc_creat_tab_env.is_tab_creat_svc = true; btc_creat_tab_env.num_handle = max_nb_attr; for(int i = 0; i < max_nb_attr; i++){ - if(gatts_attr_db[i].att_desc.uuid.len == ESP_UUID_LEN_16){ - uuid = gatts_attr_db[i].att_desc.uuid.uuid.uuid16; + if(gatts_attr_db[i].att_desc.uuid_length== ESP_UUID_LEN_16){ + uuid = (gatts_attr_db[i].att_desc.uuid_p[1] << 8) + (gatts_attr_db[i].att_desc.uuid_p[0]); } future_p = future_new(); if (future_p == NULL) { @@ -184,8 +203,10 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, esp_gatt_srvc_id_t esp_srvc_id; esp_srvc_id.id.inst_id = srvc_inst_id; - memcpy(&esp_srvc_id.id.uuid, &gatts_attr_db[i].att_desc.uuid, sizeof(esp_bt_uuid_t)); - btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.uuid_length, + gatts_attr_db[i].att_desc.uuid_p); + + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, srvc_inst_id, max_nb_attr, true); @@ -200,8 +221,9 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, esp_gatt_srvc_id_t esp_srvc_id; esp_srvc_id.id.inst_id = srvc_inst_id; - memcpy(&esp_srvc_id.id.uuid, &gatts_attr_db[i].att_desc.uuid, sizeof(esp_bt_uuid_t)); - btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); + btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.uuid_length, + gatts_attr_db[i].att_desc.uuid_p); + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, srvc_inst_id, max_nb_attr, false); if (future_await(future_p) == FUTURE_FAIL) { @@ -230,27 +252,33 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, uint16_t svc_hal = 0; tBT_UUID bta_char_uuid; tGATT_ATTR_VAL attr_val; + esp_bt_uuid_t uuid_temp; tBTA_GATT_PERM perm; tBTA_GATTS_ATTR_CONTROL control; + uint8_t char_property; if(btc_creat_tab_env.svc_start_hdl != 0){ svc_hal = btc_creat_tab_env.svc_start_hdl; - esp_gatts_char_desc_t *char_desc = (esp_gatts_char_desc_t *)gatts_attr_db[i].att_desc.value; - if(char_desc != NULL){ + if((gatts_attr_db[i].att_desc.value) == NULL){ + LOG_ERROR("%s Characteristic declaration should not be NULL\n", __func__); + } + else{ + char_property = (uint8_t)(*(uint8_t*)(gatts_attr_db[i].att_desc.value)); perm = gatts_attr_db[i+1].att_desc.perm; attr_val.attr_len = gatts_attr_db[i+1].att_desc.length; attr_val.attr_max_len = gatts_attr_db[i+1].att_desc.max_length; - btc_to_bta_uuid(&bta_char_uuid, &char_desc->attr_uuid); + btc_gatts_uuid_format_convert(&uuid_temp, gatts_attr_db[i+1].att_desc.uuid_length,gatts_attr_db[i+1].att_desc.uuid_p); + btc_to_bta_uuid(&bta_char_uuid, &uuid_temp); attr_val.attr_val = gatts_attr_db[i+1].att_desc.value; control.auto_rsp = gatts_attr_db[i+1].attr_control.auto_rsp; BTA_GATTS_AddCharacteristic (svc_hal, &bta_char_uuid, - perm, char_desc->prop, &attr_val, &control); + perm, char_property, &attr_val, &control); if (future_await(future_p) == FUTURE_FAIL) { LOG_ERROR("%s failed\n", __func__); return; } - } + } } break; @@ -266,6 +294,7 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, case ESP_GATT_UUID_RPT_REF_DESCR:{ uint16_t svc_hal = btc_creat_tab_env.svc_start_hdl; tBT_UUID bta_char_uuid; + esp_bt_uuid_t uuid_temp; tGATT_ATTR_VAL attr_val; tBTA_GATT_PERM perm = gatts_attr_db[i].att_desc.perm; tBTA_GATTS_ATTR_CONTROL control; @@ -274,7 +303,9 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, attr_val.attr_len = gatts_attr_db[i].att_desc.length; attr_val.attr_max_len = gatts_attr_db[i].att_desc.max_length; attr_val.attr_val = gatts_attr_db[i].att_desc.value; - btc_to_bta_uuid(&bta_char_uuid, &gatts_attr_db[i].att_desc.uuid); + btc_gatts_uuid_format_convert(&uuid_temp, gatts_attr_db[i].att_desc.uuid_length, + gatts_attr_db[i].att_desc.uuid_p); + btc_to_bta_uuid(&bta_char_uuid, &uuid_temp); control.auto_rsp = gatts_attr_db[i].attr_control.auto_rsp; BTA_GATTS_AddCharDescriptor(svc_hal, perm, &bta_char_uuid, &attr_val, &control); @@ -573,6 +604,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.read.offset = p_data->req_data.p_data->read_req.offset; param.read.is_long = p_data->req_data.p_data->read_req.is_long; + param.read.need_rsp = p_data->req_data.p_data->read_req.need_rsp; btc_gatts_cb_to_app(ESP_GATTS_READ_EVT, gatts_if, ¶m); break; } diff --git a/components/bt/bluedroid/stack/gatt/gatt_db.c b/components/bt/bluedroid/stack/gatt/gatt_db.c index b95934a8fa5..fd27959d965 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_db.c +++ b/components/bt/bluedroid/stack/gatt/gatt_db.c @@ -45,7 +45,7 @@ static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 le static BOOLEAN gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri); static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, - UINT16 handle, UINT16 offset, UINT32 trans_id); + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp); /******************************************************************************* ** @@ -268,16 +268,12 @@ static tGATT_STATUS read_attr_value (void *p_attr, status = GATT_SUCCESS; } } else { /* characteristic description or characteristic value */ + if (p_attr16->control.auto_rsp == GATT_RSP_BY_STACK) { - GATT_TRACE_DEBUG("before characteristic description or characteristic value\n"); if (p_attr16->p_value != NULL && p_attr16->p_value->attr_val.attr_val != NULL) { uint8_t *value = p_attr16->p_value->attr_val.attr_val + offset; - GATT_TRACE_DEBUG("after characteristic description or characteristic value\n"); - if (mtu >= p_attr16->p_value->attr_val.attr_len) { - ARRAY_TO_STREAM(p, value, p_attr16->p_value->attr_val.attr_len); - } else { - ARRAY_TO_STREAM(p, value, mtu); - } + len = (mtu >= p_attr16->p_value->attr_val.attr_len) ? (p_attr16->p_value->attr_val.attr_len) : mtu; + ARRAY_TO_STREAM(p, value, len); } status = GATT_STACK_RSP; @@ -357,7 +353,8 @@ tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, status = read_attr_value ((void *)p_attr, 0, &p, FALSE, (UINT16)(*p_len - 2), &len, sec_flag, key_size); if (status == GATT_PENDING || status == GATT_STACK_RSP) { - status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id); + BOOLEAN need_rsp = (status != GATT_STACK_RSP); + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id, need_rsp); /* one callback at a time */ break; @@ -630,7 +627,7 @@ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, UINT16 length, UINT8 *value) { - tGATT_ATTR16 *p_cur, *p_next; + tGATT_ATTR16 *p_cur; if (p_db == NULL) { GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db is NULL.\n"); @@ -642,19 +639,21 @@ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, } p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; - p_next = (tGATT_ATTR16 *) p_cur->p_next; - - for (; p_cur != NULL; p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + while (p_cur != NULL) { if (p_cur->handle == attr_handle) { + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { switch (p_cur->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: case GATT_UUID_CHAR_DECLARE: case GATT_UUID_INCLUDE_SERVICE: return GATT_NOT_FOUND; default: if (p_cur->p_value->attr_val.attr_max_len < length) { GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); + return GATT_INVALID_ATTR_LEN; } else { memcpy(p_cur->p_value->attr_val.attr_val, value, length); p_cur->p_value->attr_val.attr_len = length; @@ -668,9 +667,14 @@ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, memcpy(p_cur->p_value->attr_val.attr_val, value, length); p_cur->p_value->attr_val.attr_len = length; } + } + break; + } + + p_cur = p_cur->p_next; } return GATT_SUCCESS; @@ -694,7 +698,7 @@ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, UINT16 *length, UINT8 **value) { - tGATT_ATTR16 *p_cur, *p_next; + tGATT_ATTR16 *p_cur; GATT_TRACE_DEBUG("***********%s*************\n", __func__); GATT_TRACE_DEBUG("attr_handle = %x\n", attr_handle); if (p_db == NULL) { @@ -707,10 +711,8 @@ tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, } p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; - p_next = (tGATT_ATTR16 *) p_cur->p_next; - - for (; p_cur != NULL; p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + while (p_cur != NULL) { LOG_ERROR("p_ur->handle = %x\n", p_cur->handle); if (p_cur->handle == attr_handle) { @@ -736,7 +738,7 @@ tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, *value = p_cur->p_value->attr_val.attr_val; return GATT_SUCCESS; } else { - GATT_TRACE_ERROR("gatts_get_attribute_vaule failt:the value length is 0"); + GATT_TRACE_ERROR("gatts_get_attribute_vaule failed:the value length is 0"); return GATT_INVALID_ATTR_LEN; } @@ -747,9 +749,10 @@ tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, } + p_cur = p_cur->p_next; } - return GATT_SUCCESS; + return GATT_NOT_FOUND; } BOOLEAN gatts_is_auto_response(UINT16 attr_handle) @@ -839,8 +842,9 @@ tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, (BOOLEAN)(op_code == GATT_REQ_READ_BLOB), mtu, p_len, sec_flag, key_size); - if (status == GATT_PENDING) { - status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id); + if ((status == GATT_PENDING) || (status == GATT_STACK_RSP)) { + BOOLEAN need_rsp = (status != GATT_STACK_RSP); + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id, need_rsp); } break; } @@ -848,6 +852,7 @@ tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, } } + return status; } @@ -867,7 +872,7 @@ tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, return GATT_APP_RSP; } - if (p_attr->p_value != NULL && (p_attr->p_value->attr_val.attr_max_len > + if (p_attr->p_value != NULL && (p_attr->p_value->attr_val.attr_max_len >= offset + len)) { memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); p_attr->p_value->attr_val.attr_len = len + offset; @@ -1301,7 +1306,7 @@ static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db) ** *******************************************************************************/ static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, - UINT16 handle, UINT16 offset, UINT32 trans_id) + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp) { tGATTS_DATA sr_data; UINT8 i_rcb; @@ -1323,6 +1328,7 @@ static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, sr_data.read_req.handle = handle; sr_data.read_req.is_long = (BOOLEAN)(op_code == GATT_REQ_READ_BLOB); sr_data.read_req.offset = offset; + sr_data.read_req.need_rsp = need_rsp; gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_READ, &sr_data); diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index 6f21e8c45d1..a817ad1478c 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -987,14 +987,14 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, UINT8 op_code, UINT16 len, UINT8 *p_data) { - UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); - tGATTS_DATA sr_data; - UINT32 trans_id; - tGATT_STATUS status; - UINT8 sec_flag, key_size, *p = p_data, *p_m; - tGATT_SR_REG *p_sreg; - UINT16 conn_id, offset = 0; - BT_HDR *p_msg = NULL; + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + tGATTS_DATA sr_data; + UINT32 trans_id; + tGATT_STATUS status; + UINT8 sec_flag, key_size, *p = p_data, *p_m; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + BT_HDR *p_msg = NULL; memset(&sr_data, 0, sizeof(tGATTS_DATA)); if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { @@ -1011,10 +1011,10 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, case GATT_REQ_PREPARE_WRITE: sr_data.write_req.is_prep = TRUE; STREAM_TO_UINT16(sr_data.write_req.offset, p); - UINT16_TO_STREAM(p_m, sr_data.write_req.is_prep); - offset = sr_data.write_req.offset; + UINT16_TO_STREAM(p_m, sr_data.write_req.is_prep); + offset = sr_data.write_req.offset; len -= 2; - /* fall through */ + /* fall through */ case GATT_SIGN_CMD_WRITE: if (op_code == GATT_SIGN_CMD_WRITE) { GATT_TRACE_DEBUG("Write CMD with data sigining" ); @@ -1025,16 +1025,16 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, case GATT_REQ_WRITE: if (op_code == GATT_REQ_WRITE || op_code == GATT_REQ_PREPARE_WRITE) { sr_data.write_req.need_rsp = TRUE; - if(op_code == GATT_REQ_PREPARE_WRITE){ - memcpy(p_m, p, len); - p_msg->len += len; - } + if(op_code == GATT_REQ_PREPARE_WRITE){ + memcpy(p_m, p, len); + p_msg->len += len; + } } sr_data.write_req.handle = handle; sr_data.write_req.len = len; if (len != 0 && p != NULL) { memcpy (sr_data.write_req.value, p, len); - + } break; } @@ -1053,24 +1053,31 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, sec_flag, key_size); - p_msg->len += len; if (status == GATT_SUCCESS) { if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { p_sreg = &gatt_cb.sr_reg[i_rcb]; conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); - status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, - handle, offset, p, len); + status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, + handle, offset, p, len); + if((sr_data.write_req.need_rsp == TRUE) && (status == GATT_APP_RSP)){ + sr_data.write_req.need_rsp = TRUE; + status = GATT_PENDING; + } + + else{ + sr_data.write_req.need_rsp = FALSE; + } + gatt_sr_send_req_callback(conn_id, - trans_id, - GATTS_REQ_TYPE_WRITE, - &sr_data); - - if(status == GATT_SUCCESS){ - attp_send_sr_msg(p_tcb, p_msg); - }else if(status == GATT_NOT_LONG){ - gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); - } - status = GATT_PENDING; + trans_id, + GATTS_REQ_TYPE_WRITE, + &sr_data); + + if(status == GATT_SUCCESS){ + attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); + } + } else { GATT_TRACE_ERROR("max pending command, send error\n"); status = GATT_BUSY; /* max pending command, application error */ @@ -1078,23 +1085,24 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, } /* in theroy BUSY is not possible(should already been checked), protected check */ - if (status != GATT_PENDING && status != GATT_BUSY && + if (status != GATT_PENDING && status != GATT_BUSY && status != GATT_SUCCESS && (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_REQ_WRITE)) { gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); + gatt_dequeue_sr_cmd(p_tcb); } return; } /******************************************************************************* -** -** Function gatts_process_read_req -** -** Description This function is called to process the read request -** from client. -** -** Returns void -** -*******************************************************************************/ + ** + ** Function gatts_process_read_req + ** + ** Description This function is called to process the read request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 op_code, UINT16 handle, UINT16 len, UINT8 *p_data) { @@ -1140,7 +1148,8 @@ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 p_msg->len += value_len; } - if (reason != GATT_SUCCESS && reason != GATT_PENDING) { + + if (reason != GATT_SUCCESS && reason != GATT_PENDING && reason != GATT_STACK_RSP) { if (p_msg) { GKI_freebuf(p_msg); } @@ -1148,9 +1157,11 @@ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 /* in theroy BUSY is not possible(should already been checked), protected check */ if (reason != GATT_BUSY) { gatt_send_error_rsp (p_tcb, reason, op_code, handle, FALSE); + gatt_dequeue_sr_cmd(p_tcb); } } else { attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); } } @@ -1376,24 +1387,24 @@ void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, /* otherwise, ignore the pkt */ } else { switch (op_code) { - case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ - case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ + case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ + case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ gatts_process_primary_service_req (p_tcb, op_code, len, p_data); break; - case GATT_REQ_FIND_INFO: /* discover char descrptor */ + case GATT_REQ_FIND_INFO: /* discover char descrptor */ gatts_process_find_info(p_tcb, op_code, len, p_data); break; - case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ + case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ /* discover characteristic, discover char by UUID */ gatts_process_read_by_type_req(p_tcb, op_code, len, p_data); break; - case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ: /* read char/char descriptor value */ case GATT_REQ_READ_BLOB: - case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_REQ_WRITE: /* write char/char descriptor value */ case GATT_CMD_WRITE: case GATT_SIGN_CMD_WRITE: case GATT_REQ_PREPARE_WRITE: diff --git a/components/bt/bluedroid/stack/include/gatt_api.h b/components/bt/bluedroid/stack/include/gatt_api.h index c460a655387..9360d4fbf35 100644 --- a/components/bt/bluedroid/stack/include/gatt_api.h +++ b/components/bt/bluedroid/stack/include/gatt_api.h @@ -349,6 +349,7 @@ typedef struct { UINT16 handle; UINT16 offset; BOOLEAN is_long; + BOOLEAN need_rsp; } tGATT_READ_REQ; /* write request data */ diff --git a/docs/api/esp_gatt_defs.rst b/docs/api/esp_gatt_defs.rst index 9eeac6193ca..70e808a5d77 100644 --- a/docs/api/esp_gatt_defs.rst +++ b/docs/api/esp_gatt_defs.rst @@ -25,6 +25,29 @@ Header Files Macros ^^^^^^ + +.. doxygendefine:: ESP_GATT_UUID_IMMEDIATE_ALERT_SVC +.. doxygendefine:: ESP_GATT_UUID_LINK_LOSS_SVC +.. doxygendefine:: ESP_GATT_UUID_TX_POWER_SVC +.. doxygendefine:: ESP_GATT_UUID_CURRENT_TIME_SVC +.. doxygendefine:: ESP_GATT_UUID_REF_TIME_UPDATE_SVC +.. doxygendefine:: ESP_GATT_UUID_NEXT_DST_CHANGE_SVC +.. doxygendefine:: ESP_GATT_UUID_GLUCOSE_SVC +.. doxygendefine:: ESP_GATT_UUID_HEALTH_THERMOM_SVC +.. doxygendefine:: ESP_GATT_UUID_DEVICE_INFO_SVC +.. doxygendefine:: ESP_GATT_UUID_HEART_RATE_SVC +.. doxygendefine:: ESP_GATT_UUID_PHONE_ALERT_STATUS_SVC +.. doxygendefine:: ESP_GATT_UUID_BATTERY_SERVICE_SVC +.. doxygendefine:: ESP_GATT_UUID_BLOOD_PRESSURE_SVC +.. doxygendefine:: ESP_GATT_UUID_ALERT_NTF_SVC +.. doxygendefine:: ESP_GATT_UUID_HID_SVC +.. doxygendefine:: ESP_GATT_UUID_SCAN_PARAMETERS_SVC +.. doxygendefine:: ESP_GATT_UUID_RUNNING_SPEED_CADENCE_SVC +.. doxygendefine:: ESP_GATT_UUID_CYCLING_SPEED_CADENCE_SVC +.. doxygendefine:: ESP_GATT_UUID_CYCLING_POWER_SVC +.. doxygendefine:: ESP_GATT_UUID_LOCATION_AND_NAVIGATION_SVC +.. doxygendefine:: ESP_GATT_UUID_USER_DATA_SVC +.. doxygendefine:: ESP_GATT_UUID_WEIGHT_SCALE_SVC .. doxygendefine:: ESP_GATT_UUID_PRI_SERVICE .. doxygendefine:: ESP_GATT_UUID_SEC_SERVICE .. doxygendefine:: ESP_GATT_UUID_INCLUDE_SERVICE @@ -74,6 +97,9 @@ Macros .. doxygendefine:: ESP_GATT_UUID_HID_BT_KB_INPUT .. doxygendefine:: ESP_GATT_UUID_HID_BT_KB_OUTPUT .. doxygendefine:: ESP_GATT_UUID_HID_BT_MOUSE_INPUT +.. doxygendefine:: ESP_GATT_HEART_RATE_MEAS +.. doxygendefine:: ESP_GATT_BODY_SENSOR_LOCATION +.. doxygendefine:: ESP_GATT_HEART_RATE_CNTL_POINT .. doxygendefine:: ESP_GATT_UUID_BATTERY_LEVEL .. doxygendefine:: ESP_GATT_UUID_SC_CONTROL_POINT .. doxygendefine:: ESP_GATT_UUID_SENSOR_LOCATION @@ -87,6 +113,8 @@ Macros .. doxygendefine:: ESP_GATT_ILLEGAL_HANDLE .. doxygendefine:: ESP_GATT_ATTR_HANDLE_MAX .. doxygendefine:: ESP_GATT_MAX_ATTR_LEN +.. doxygendefine:: ESP_GATT_RSP_BY_APP +.. doxygendefine:: ESP_GATT_AUTO_RSP .. doxygendefine:: ESP_GATT_IF_NONE Type Definitions @@ -126,9 +154,6 @@ Structures .. doxygenstruct:: esp_gatts_incl128_svc_desc_t :members: -.. doxygenstruct:: esp_gatts_char_desc_t - :members: - .. doxygenstruct:: esp_gatt_value_t :members: diff --git a/docs/api/esp_gatts.rst b/docs/api/esp_gatts.rst index 9e3749df0ac..4278e6b33ea 100644 --- a/docs/api/esp_gatts.rst +++ b/docs/api/esp_gatts.rst @@ -80,9 +80,6 @@ Structures .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_add_char_descr_evt_param :members: -.. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_add_attr_tab_evt_param - :members: - .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_delete_evt_param :members: @@ -104,6 +101,9 @@ Structures .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_rsp_evt_param :members: +.. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_add_attr_tab_evt_param + :members: + .. doxygenstruct:: esp_ble_gatts_cb_param_t::gatts_set_attr_val_evt_param :members: diff --git a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c index f4511f68a59..024362c5b7a 100644 --- a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c +++ b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c @@ -117,64 +117,6 @@ static struct gatts_profile_inst heart_rate_profile_tab[HEART_PROFILE_NUM] = { **************************************************************************************** */ -/// Full HRS Database Description - Used to add attributes into the database -const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = -{ - // Heart Rate Service Declaration - [HRS_IDX_SVC] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_UUID_PRI_SERVICE}}, - ESP_GATT_PERM_READ, - sizeof(heart_rate_svc), - sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}}, - - // Heart Rate Measurement Characteristic Declaration - [HRS_IDX_HR_MEAS_CHAR] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_DECLARE}}, - ESP_GATT_PERM_READ, - sizeof(heart_rate_meas_char), - sizeof(heart_rate_meas_char), (uint8_t *)&heart_rate_meas_char}}, - // Heart Rate Measurement Characteristic Value - [HRS_IDX_HR_MEAS_VAL] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_MEAS}}, - ESP_GATT_PERM_READ, - HRPS_HT_MEAS_MAX_LEN, - 0, NULL}}, - - // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor - [HRS_IDX_HR_MEAS_NTF_CFG] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_SRVR_CONFIG}}, - ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, - sizeof(uint16_t), - 0, NULL}}, - - // Body Sensor Location Characteristic Declaration - [HRS_IDX_BOBY_SENSOR_LOC_CHAR] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_DECLARE}}, - ESP_GATT_PERM_READ, - sizeof(heart_rate_body_sensor_loc_char), - sizeof(heart_rate_body_sensor_loc_char), (uint8_t *)&heart_rate_body_sensor_loc_char}}, - - // Body Sensor Location Characteristic Value - [HRS_IDX_BOBY_SENSOR_LOC_VAL] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_BODY_SENSOR_LOCATION}}, - ESP_GATT_PERM_READ, - sizeof(uint8_t), - 0, NULL}}, - - // Heart Rate Control Point Characteristic Declaration - [HRS_IDX_HR_CTNL_PT_CHAR] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_UUID_CHAR_DECLARE}}, - ESP_GATT_PERM_READ, - sizeof(heart_rate_cntl_point_char), - sizeof(heart_rate_cntl_point_char), (uint8_t *)&heart_rate_cntl_point_char}}, - - // Heart Rate Control Point Characteristic Value - [HRS_IDX_HR_CTNL_PT_VAL] = {{ESP_GATT_AUTO_RSP}, { - {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_CNTL_POINT}}, - ESP_GATT_PERM_WRITE, - sizeof(uint8_t), - 0, NULL}}, -}; /* * Heart Rate PROFILE ATTRIBUTES @@ -182,31 +124,75 @@ const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = */ /// Heart Rate Sensor Service -const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC; +static const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC; -/// Heart Rate Sensor Service - Heart Rate Measurement Characteristic -const esp_gatts_char_desc_t heart_rate_meas_char = +#define CHAR_DECLARATION_SIZE (sizeof(uint8_t)) +static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; +static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; +static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; +static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY; +static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ; +static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_READ; + +/// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify +static const uint16_t heart_rate_meas_uuid = ESP_GATT_HEART_RATE_MEAS; +static const uint8_t heart_measurement_ccc[2] ={ 0x00, 0x00}; + + +/// Heart Rate Sensor Service -Body Sensor Location characteristic, read +static const uint16_t body_sensor_location_uuid = ESP_GATT_BODY_SENSOR_LOCATION; +static const uint8_t body_sensor_loc_val[1] = {0x00}; + + +/// Heart Rate Sensor Service - Heart Rate Control Point characteristic, write&read +static const uint16_t heart_rate_ctrl_point = ESP_GATT_HEART_RATE_CNTL_POINT; +static const uint8_t heart_ctrl_point[1] = {0x00}; + +/// Full HRS Database Description - Used to add attributes into the database +static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = { - .prop = ESP_GATT_CHAR_PROP_BIT_NOTIFY, - .attr_hdl = 0, - .attr_uuid = {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_MEAS}}, + // Heart Rate Service Declaration + [HRS_IDX_SVC] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, + sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}}, + + // Heart Rate Measurement Characteristic Declaration + [HRS_IDX_HR_MEAS_CHAR] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, + CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}}, + + // Heart Rate Measurement Characteristic Value + [HRS_IDX_HR_MEAS_VAL] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ, + HRPS_HT_MEAS_MAX_LEN,0, NULL}}, + + // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor + [HRS_IDX_HR_MEAS_NTF_CFG] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, + sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}}, + + // Body Sensor Location Characteristic Declaration + [HRS_IDX_BOBY_SENSOR_LOC_CHAR] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, + CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read}}, + + // Body Sensor Location Characteristic Value + [HRS_IDX_BOBY_SENSOR_LOC_VAL] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&body_sensor_location_uuid, ESP_GATT_PERM_READ, + sizeof(uint8_t), sizeof(body_sensor_loc_val), (uint8_t *)body_sensor_loc_val}}, + + // Heart Rate Control Point Characteristic Declaration + [HRS_IDX_HR_CTNL_PT_CHAR] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, + CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}}, + + // Heart Rate Control Point Characteristic Value + [HRS_IDX_HR_CTNL_PT_VAL] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_ctrl_point, ESP_GATT_PERM_WRITE|ESP_GATT_PERM_READ, + sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}}, }; -/// Heart Rate Sensor Service -Body Sensor Location characteristic -const esp_gatts_char_desc_t heart_rate_body_sensor_loc_char = -{ - .prop = ESP_GATT_CHAR_PROP_BIT_READ, - .attr_hdl = 0, - .attr_uuid = {ESP_UUID_LEN_16, {ESP_GATT_BODY_SENSOR_LOCATION}}, -}; -/// Heart Rate Sensor Service - Heart Rate Control Point characteristic -const esp_gatts_char_desc_t heart_rate_cntl_point_char = -{ - .prop = ESP_GATT_CHAR_PROP_BIT_WRITE, - .attr_hdl = 0, - .attr_uuid = {ESP_UUID_LEN_16, {ESP_GATT_HEART_RATE_CNTL_POINT}}, -}; static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { @@ -223,7 +209,7 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) -{ +{ LOG_ERROR("event = %x\n",event); switch (event) { case ESP_GATTS_REG_EVT: diff --git a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h index ecce340df7c..c12d1aa59b7 100644 --- a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h +++ b/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h @@ -46,12 +46,3 @@ enum HRS_IDX_NB, }; - - -extern const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB]; -/// Heart Rate Sensor Service - only one instance for now -extern const uint16_t heart_rate_svc; - -extern const esp_gatts_char_desc_t heart_rate_meas_char; -extern const esp_gatts_char_desc_t heart_rate_body_sensor_loc_char; -extern const esp_gatts_char_desc_t heart_rate_cntl_point_char; From e4811216ffdd19e2a72ff60cf791245caf087091 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 17:17:13 +0800 Subject: [PATCH 143/167] deep sleep: fix regression due to moving ets_update_cpu_frequency into IRAM Deep sleep stub may call ets_update_cpu_frequency, which has been moved from ROM to IRAM. Restore the ROM version in the linker script, call it ets_update_cpu_frequency_rom, use it in the deep sleep stub. --- components/esp32/deep_sleep.c | 2 +- components/esp32/include/rom/ets_sys.h | 12 ++++++++++++ components/esp32/ld/esp32.rom.ld | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 7f59c216350..774fd4ce7af 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -91,7 +91,7 @@ void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void) { #if CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY > 0 // ROM code has not started yet, so we need to set delay factor // used by ets_delay_us first. - ets_update_cpu_frequency(ets_get_detected_xtal_freq() / 1000000); + ets_update_cpu_frequency_rom(ets_get_detected_xtal_freq() / 1000000); // This delay is configured in menuconfig, it can be used to give // the flash chip some time to become ready. ets_delay_us(CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY); diff --git a/components/esp32/include/rom/ets_sys.h b/components/esp32/include/rom/ets_sys.h index 690691675af..0f972f2c338 100644 --- a/components/esp32/include/rom/ets_sys.h +++ b/components/esp32/include/rom/ets_sys.h @@ -383,6 +383,18 @@ void ets_delay_us(uint32_t us); */ void ets_update_cpu_frequency(uint32_t ticks_per_us); +/** + * @brief Set the real CPU ticks per us to the ets, so that ets_delay_us will be accurate. + * + * @note This function only sets the tick rate for the current CPU. It is located in ROM, + * so the deep sleep stub can use it even if IRAM is not initialized yet. + * + * @param uint32_t ticks_per_us : CPU ticks per us. + * + * @return None + */ +void ets_update_cpu_frequency_rom(uint32_t ticks_per_us); + /** * @brief Get the real CPU ticks per us to the ets. * This function do not return real CPU ticks per us, just the record in ets. It can be used to check with the real CPU frequency. diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 33309894dc2..7543fa42aa7 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -202,7 +202,7 @@ PROVIDE ( ets_timer_init = 0x400084e8 ); PROVIDE ( ets_timer_setfn = 0x40008350 ); PROVIDE ( ets_unpack_flash_code = 0x40007018 ); PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); -/* PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); */ /* Updates g_ticks_per_us on the current CPU only; not on the other core */ +PROVIDE ( ets_update_cpu_frequency_rom = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ PROVIDE ( ets_waiti0 = 0x400067d8 ); PROVIDE ( exc_cause_table = 0x3ff991d0 ); PROVIDE ( _exit_r = 0x4000bd28 ); From 9aa0e290796c2ee2142dac2c92dcbb1dc8a2d9f3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 17:23:23 +0800 Subject: [PATCH 144/167] deep sleep: keep RTC_SLOW_MEM powered on if data is placed into RTC slow memory --- components/esp32/deep_sleep.c | 18 ++++++++++++------ components/esp32/include/esp_attr.h | 2 +- components/esp32/ld/esp32.common.ld | 2 ++ docs/api/deep_sleep.rst | 2 ++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 774fd4ce7af..3eef7ca29d0 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -302,12 +302,18 @@ static uint32_t get_power_down_flags() { // Where needed, convert AUTO options to ON. Later interpret AUTO as OFF. - // RTC_SLOW_MEM is needed only for the ULP. - // If RTC_SLOW_MEM is Auto, and ULP wakeup isn't enabled, power down RTC_SLOW_MEM. - if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO) { - if (s_config.wakeup_triggers & RTC_SAR_TRIG_EN) { - s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON; - } + // RTC_SLOW_MEM is needed for the ULP, so keep RTC_SLOW_MEM powered up if ULP + // is used and RTC_SLOW_MEM is Auto. + // If there is any data placed into .rtc.data or .rtc.bss segments, and + // RTC_SLOW_MEM is Auto, keep it powered up as well. + + // These labels are defined in the linker script: + extern int _rtc_data_start, _rtc_data_end, _rtc_bss_start, _rtc_bss_end; + + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO || + &_rtc_data_end > &_rtc_data_start || + &_rtc_bss_end > &_rtc_bss_start) { + s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON; } // RTC_FAST_MEM is needed for deep sleep stub. diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index 7ef2920d989..911201aace4 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -26,7 +26,7 @@ // Forces data into DRAM instead of flash #define DRAM_ATTR __attribute__((section(".dram1"))) -// Forces a string into DRAM instrad of flash +// Forces a string into DRAM instead of flash // Use as ets_printf(DRAM_STR("Hello world!\n")); #define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index 833eb909692..8cc3b5b22e4 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -19,9 +19,11 @@ SECTIONS */ .rtc.data : { + _rtc_data_start = ABSOLUTE(.); *(.rtc.data) *(.rtc.rodata) *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); } > rtc_slow_seg /* RTC bss, from any source file named rtc_wake_stub*.c */ diff --git a/docs/api/deep_sleep.rst b/docs/api/deep_sleep.rst index 3a458fb4a82..9e6642fd907 100644 --- a/docs/api/deep_sleep.rst +++ b/docs/api/deep_sleep.rst @@ -73,6 +73,8 @@ By default, ``esp_deep_sleep_start`` function will power down all RTC power doma Note: on the first revision of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep. +If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_deep_sleep_pd_config`` function, if desired. + .. doxygenfunction:: esp_deep_sleep_pd_config .. doxygenenum:: esp_deep_sleep_pd_domain_t .. doxygenenum:: esp_deep_sleep_pd_option_t From b24ac487cb49148ec17f2ab077fe72439de0f8e1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 17:28:09 +0800 Subject: [PATCH 145/167] newlib: use RTC_STORE registers to keep boot time instead of RTC_SLOW_MEM This allows RTC_SLOW_MEM to be powered down in deep sleep if no other variables are placed into RTC_SLOW_MEM. --- components/esp32/Kconfig | 2 ++ components/esp32/include/rom/rtc.h | 14 ++++++---- components/newlib/time.c | 44 +++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index bc35ca0e8f6..f0f74e98954 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -381,6 +381,8 @@ choice ESP32_TIME_SYSCALL longer to run. - If no timers are used, gettimeofday and time functions return -1 and set errno to ENOSYS. + - When RTC is used for timekeeping, two RTC_STORE registers are + used to keep time in deep sleep mode. config ESP32_TIME_SYSCALL_USE_RTC bool "RTC" diff --git a/components/esp32/include/rom/rtc.h b/components/esp32/include/rom/rtc.h index 1ff7f033b96..e3c031775d7 100644 --- a/components/esp32/include/rom/rtc.h +++ b/components/esp32/include/rom/rtc.h @@ -53,16 +53,18 @@ extern "C" { * Rtc store registers usage * RTC_CNTL_STORE0_REG * RTC_CNTL_STORE1_REG - * RTC_CNTL_STORE2_REG - * RTC_CNTL_STORE3_REG - * RTC_CNTL_STORE4_REG Reserved - * RTC_CNTL_STORE5_REG External Xtal Frequency + * RTC_CNTL_STORE2_REG Boot time, low word + * RTC_CNTL_STORE3_REG Boot time, high word + * RTC_CNTL_STORE4_REG External XTAL frequency + * RTC_CNTL_STORE5_REG APB bus frequency * RTC_CNTL_STORE6_REG FAST_RTC_MEMORY_ENTRY * RTC_CNTL_STORE7_REG FAST_RTC_MEMORY_CRC ************************************************************************************* */ -#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG -#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG +#define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG +#define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG +#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG +#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG typedef enum { diff --git a/components/newlib/time.c b/components/newlib/time.c index 363e17b3eb8..c9fa72eeee8 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "esp_attr.h" #include "esp_intr_alloc.h" #include "soc/soc.h" @@ -58,9 +59,9 @@ static uint64_t get_rtc_time_us() // s_boot_time: time from Epoch to the first boot time #ifdef WITH_RTC -static RTC_DATA_ATTR struct timeval s_boot_time; +// when RTC is used to persist time, two RTC_STORE registers are used to store boot time #elif defined(WITH_FRC1) -static struct timeval s_boot_time; +static uint64_t s_boot_time; #endif #if defined(WITH_RTC) || defined(WITH_FRC1) @@ -88,6 +89,31 @@ static void IRAM_ATTR frc_timer_isr() #endif // WITH_FRC1 +static void set_boot_time(uint64_t time_us) +{ + _lock_acquire(&s_boot_time_lock); +#ifdef WITH_RTC + REG_WRITE(RTC_BOOT_TIME_LOW_REG, (uint32_t) (time_us & 0xffffffff)); + REG_WRITE(RTC_BOOT_TIME_HIGH_REG, (uint32_t) (time_us >> 32)); +#else + s_boot_time = time_us; +#endif + _lock_release(&s_boot_time_lock); +} + +static uint64_t get_boot_time() +{ + uint64_t result; + _lock_acquire(&s_boot_time_lock); +#ifdef WITH_RTC + result = ((uint64_t) REG_READ(RTC_BOOT_TIME_LOW_REG)) + (((uint64_t) REG_READ(RTC_BOOT_TIME_HIGH_REG)) << 32); +#else + result = s_boot_time; +#endif + _lock_release(&s_boot_time_lock); + return result; +} + void esp_setup_time_syscalls() { #if defined( WITH_FRC1 ) @@ -148,13 +174,10 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { (void) tz; #if defined( WITH_FRC1 ) || defined( WITH_RTC ) - uint64_t microseconds = get_time_since_boot(); if (tv) { - _lock_acquire(&s_boot_time_lock); - microseconds += s_boot_time.tv_usec; - tv->tv_sec = s_boot_time.tv_sec + microseconds / 1000000; + uint64_t microseconds = get_boot_time() + get_time_since_boot(); + tv->tv_sec = microseconds / 1000000; tv->tv_usec = microseconds % 1000000; - _lock_release(&s_boot_time_lock); } return 0; #else @@ -168,14 +191,9 @@ int settimeofday(const struct timeval *tv, const struct timezone *tz) (void) tz; #if defined( WITH_FRC1 ) || defined( WITH_RTC ) if (tv) { - _lock_acquire(&s_boot_time_lock); uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec; uint64_t since_boot = get_time_since_boot(); - uint64_t boot_time = now - since_boot; - - s_boot_time.tv_sec = boot_time / 1000000; - s_boot_time.tv_usec = boot_time % 1000000; - _lock_release(&s_boot_time_lock); + set_boot_time(now - since_boot); } return 0; #else From 142756615b5eb4d985b731eb89b7d2b097f9c609 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 12 Jan 2017 14:44:26 +0800 Subject: [PATCH 146/167] component/bt : mv demo name 1. mv demo name 2. fix a docs --- components/bt/bluedroid/api/esp_blufi_api.c | 8 ++++---- components/bt/bluedroid/api/include/esp_gatts_api.h | 3 ++- .../Makefile | 2 +- .../README.rst | 0 .../main/component.mk | 0 .../main/gatts_table_creat_demo.c | 0 .../main/gatts_table_creat_demo.h | 0 .../sdkconfig.defaults | 0 8 files changed, 7 insertions(+), 6 deletions(-) rename examples/{30_gatt_server_table_create => 33_gatt_server_service_table}/Makefile (81%) rename examples/{30_gatt_server_table_create => 33_gatt_server_service_table}/README.rst (100%) rename examples/{30_gatt_server_table_create => 33_gatt_server_service_table}/main/component.mk (100%) rename examples/{30_gatt_server_table_create => 33_gatt_server_service_table}/main/gatts_table_creat_demo.c (100%) rename examples/{30_gatt_server_table_create => 33_gatt_server_service_table}/main/gatts_table_creat_demo.h (100%) rename examples/{30_gatt_server_table_create => 33_gatt_server_service_table}/sdkconfig.defaults (100%) diff --git a/components/bt/bluedroid/api/esp_blufi_api.c b/components/bt/bluedroid/api/esp_blufi_api.c index 00fbeffbc98..2fcbbab7948 100644 --- a/components/bt/bluedroid/api/esp_blufi_api.c +++ b/components/bt/bluedroid/api/esp_blufi_api.c @@ -27,7 +27,7 @@ esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) { return ESP_ERR_INVALID_STATE; } - + if (callbacks == NULL) { return ESP_FAIL; } @@ -44,7 +44,7 @@ esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_SEND_CFG_REPORT; @@ -64,7 +64,7 @@ esp_err_t esp_blufi_profile_init(void) if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_INIT; @@ -79,7 +79,7 @@ esp_err_t esp_blufi_profile_deinit(void) if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_BLUFI; msg.act = BTC_BLUFI_ACT_DEINIT; diff --git a/components/bt/bluedroid/api/include/esp_gatts_api.h b/components/bt/bluedroid/api/include/esp_gatts_api.h index 8d51ac7375f..b18039ce790 100644 --- a/components/bt/bluedroid/api/include/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/esp_gatts_api.h @@ -441,7 +441,8 @@ esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle); /** - * @brief This function is called to read a characteristics descriptor. + * @brief Send indicate or notify to GATT client. + * Set param need_confirm as false will send notification, otherwise indication. * * @param[in] gatts_if: GATT server access interface * @param[in] conn_id - connection id to indicate. diff --git a/examples/30_gatt_server_table_create/Makefile b/examples/33_gatt_server_service_table/Makefile similarity index 81% rename from examples/30_gatt_server_table_create/Makefile rename to examples/33_gatt_server_service_table/Makefile index a6e41ff33ac..2ff9c6eaeea 100644 --- a/examples/30_gatt_server_table_create/Makefile +++ b/examples/33_gatt_server_service_table/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -PROJECT_NAME := gatt_server_table_creat_demo +PROJECT_NAME := gatt_server_service_table_demo COMPONENT_ADD_INCLUDEDIRS := components/include diff --git a/examples/30_gatt_server_table_create/README.rst b/examples/33_gatt_server_service_table/README.rst similarity index 100% rename from examples/30_gatt_server_table_create/README.rst rename to examples/33_gatt_server_service_table/README.rst diff --git a/examples/30_gatt_server_table_create/main/component.mk b/examples/33_gatt_server_service_table/main/component.mk similarity index 100% rename from examples/30_gatt_server_table_create/main/component.mk rename to examples/33_gatt_server_service_table/main/component.mk diff --git a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c b/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.c similarity index 100% rename from examples/30_gatt_server_table_create/main/gatts_table_creat_demo.c rename to examples/33_gatt_server_service_table/main/gatts_table_creat_demo.c diff --git a/examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h b/examples/33_gatt_server_service_table/main/gatts_table_creat_demo.h similarity index 100% rename from examples/30_gatt_server_table_create/main/gatts_table_creat_demo.h rename to examples/33_gatt_server_service_table/main/gatts_table_creat_demo.h diff --git a/examples/30_gatt_server_table_create/sdkconfig.defaults b/examples/33_gatt_server_service_table/sdkconfig.defaults similarity index 100% rename from examples/30_gatt_server_table_create/sdkconfig.defaults rename to examples/33_gatt_server_service_table/sdkconfig.defaults From fec2f534d5e2ebe9f7472e125215f6841b1f61c7 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 12 Jan 2017 16:25:09 +0800 Subject: [PATCH 147/167] component/bt : gatt server table fix uuid discovery bug --- components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a5631f5b7a2..2ba3ec0f143 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -203,8 +203,8 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, esp_gatt_srvc_id_t esp_srvc_id; esp_srvc_id.id.inst_id = srvc_inst_id; - btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.uuid_length, - gatts_attr_db[i].att_desc.uuid_p); + btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.length, + gatts_attr_db[i].att_desc.value); btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, From 1895460406163bf7570992d975525ba03a1f6535 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 12 Jan 2017 16:29:07 +0800 Subject: [PATCH 148/167] component/bt : fix adv stop bug --- components/bt/bluedroid/stack/btm/btm_ble_gap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index 4ded30de830..98a8a23689f 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -1089,7 +1089,6 @@ tBTM_STATUS BTM_BleSetAdvParamsStartAdv(UINT16 adv_int_min, UINT16 adv_int_max, p_cb->adv_chnl_map = chnl_map; p_addr_cb->own_addr_type = own_bda_type; p_cb->evt_type = adv_type; - p_cb->adv_mode = BTM_BLE_ADV_ENABLE; p_cb->afp = afp; if (p_dir_bda) { From 6eedbfa9beb290d36a4c53ea698c66a6443d0710 Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Thu, 12 Jan 2017 20:39:36 +0800 Subject: [PATCH 149/167] tw9503: fix ets timer crash bug 1. tw9503: call ets_timer_disarm before calling ets_timer_setfn 2. change CHECK_AP_CONN to CHECK_STA_CONN --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index c01bfe9038e..b26cd217641 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit c01bfe9038e59fc0dc15947c1bf4616de006e103 +Subproject commit b26cd217641acf08da932f4de8e858083ea83113 From ad66fbe5ada66eb2ce8c7f6dee2421560fbf8d89 Mon Sep 17 00:00:00 2001 From: Alexey Gerenkov Date: Tue, 10 Jan 2017 14:48:47 +0300 Subject: [PATCH 150/167] esp32: Fixes issues discussed during code review of MR!341 The following issues mentioned during MR!341 review were fixed: 1) Core dump test application description 2) Usage of CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH and CONFIG_ESP32_ENABLE_COREDUMP_TO_UART 3) FLASH_GUARD_START macro usage is fixed in flash API 4) Core dump module logging facility 5) cache util functions doc updated 6) interactive delay before print core dump to uart 7) core dump partion support in build system --- components/esp32/Kconfig | 23 +++ components/esp32/core_dump.c | 189 +++++++++++------- components/esp32/cpu_start.c | 2 +- components/esp32/panic.c | 32 +-- components/espcoredump/test/component.mk | 0 components/espcoredump/test/test_core_dump.c | 105 ++++++++++ .../include/freertos/FreeRTOSConfig.h | 2 +- components/log/include/esp_log.h | 10 + components/partition_table/gen_esp32part.py | 1 + .../partitions_singleapp_coredump.csv | 2 +- .../partitions_two_ota_coredump.csv | 2 +- components/spi_flash/cache_utils.h | 6 +- components/spi_flash/flash_ops.c | 39 ++-- docs/core_dump.rst | 96 +++------ docs/index.rst | 1 + 15 files changed, 324 insertions(+), 186 deletions(-) create mode 100644 components/espcoredump/test/component.mk create mode 100644 components/espcoredump/test/test_core_dump.c diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 98115851406..3bdfd230cbe 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -66,12 +66,35 @@ choice ESP32_COREDUMP_TO_FLASH_OR_UART config ESP32_ENABLE_COREDUMP_TO_FLASH bool "Flash" + select ESP32_ENABLE_COREDUMP config ESP32_ENABLE_COREDUMP_TO_UART bool "UART" + select ESP32_ENABLE_COREDUMP config ESP32_ENABLE_COREDUMP_TO_NONE bool "None" endchoice +config ESP32_ENABLE_COREDUMP + bool + default F + help + Enables/disable core dump module. + +config ESP32_CORE_DUMP_UART_DELAY + int "Core dump print to UART delay" + depends on ESP32_ENABLE_COREDUMP_TO_UART + default 0 + help + Config delay (in ms) before printing core dump to UART. + Delay can be interrupted by pressing Enter key. + +config ESP32_CORE_DUMP_LOG_LEVEL + int "Core dump module logging level" + depends on ESP32_ENABLE_COREDUMP + default 1 + help + Config core dump module logging level (0-5). + # Not implemented and/or needs new silicon rev to work config MEMMAP_SPISRAM bool "Use external SPI SRAM chip as main memory" diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c index 13519badb79..85301f4dd64 100644 --- a/components/esp32/core_dump.c +++ b/components/esp32/core_dump.c @@ -14,21 +14,40 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "soc/uart_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "driver/gpio.h" #include "esp_panic.h" #include "esp_partition.h" -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if CONFIG_ESP32_ENABLE_COREDUMP +#define LOG_LOCAL_LEVEL CONFIG_ESP32_CORE_DUMP_LOG_LEVEL #include "esp_log.h" const static char *TAG = "esp_core_dump"; +#define ESP_COREDUMP_LOGE( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) { ets_printf(LOG_FORMAT(E, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGW( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { ets_printf(LOG_FORMAT(W, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGI( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) { ets_printf(LOG_FORMAT(I, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGD( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGV( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { ets_printf(LOG_FORMAT(V, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); } +#else +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0) +#endif + + // TODO: allow user to set this in menuconfig or get tasks iteratively #define COREDUMP_MAX_TASKS_NUM 32 -typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len, int verb); -typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv, int verb); -typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv, int verb); -typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len, int verb); +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); typedef struct _core_dump_write_config_t { @@ -40,19 +59,17 @@ typedef struct _core_dump_write_config_t } core_dump_write_config_t; -static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg, int verb) +static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg) { union { uint8_t data8[12]; uint32_t data32[3]; } rom_data; - //const esp_partition_t *core_part; esp_err_t err; TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM]; UBaseType_t tcb_sz, task_num; uint32_t data_len = 0, i, len; - //size_t off; task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz); // take TCB padding into account, actual TCB size will be stored in header @@ -63,22 +80,21 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri // header + tasknum*(tcb + stack start/end + tcb addr) data_len = 3*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *)); for (i = 0; i < task_num; i++) { - if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) { + if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { // set correct stack top for current task tasks[i].pxTopOfStack = (StackType_t *)frame; - if (verb) - ets_printf("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); + ESP_COREDUMP_LOG_PROCESS("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); } else { - if (verb) { - XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; - if (task_frame->exit == 0) { - ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); - } - else { - XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; - ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); - } + XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; + if (task_frame->exit == 0) { + ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); + } + else { +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; +#endif + ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); } } #if( portSTACK_GROWTH < 0 ) @@ -86,9 +102,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri #else len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack; #endif - if (verb) { - ets_printf("Stack len = %lu (%x %x)\r\n", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); - } + ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); // take stack padding into account if (len % sizeof(uint32_t)) len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t); @@ -97,22 +111,20 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri // prepare write if (write_cfg->prepare) { - err = write_cfg->prepare(write_cfg->priv, &data_len, verb); + err = write_cfg->prepare(write_cfg->priv, &data_len); if (err != ESP_OK) { - ets_printf("ERROR: Failed to prepare core dump (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err); return; } } - if (verb) { - ets_printf("Core dump len = %lu\r\n", data_len); - } + ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu", data_len); // write start if (write_cfg->start) { - err = write_cfg->start(write_cfg->priv, verb); + err = write_cfg->start(write_cfg->priv); if (err != ESP_OK) { - ets_printf("ERROR: Failed to start core dump (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err); return; } } @@ -121,30 +133,28 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri rom_data.data32[0] = data_len; rom_data.data32[1] = task_num; rom_data.data32[2] = tcb_sz; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write core dump header (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err); return; } // write tasks for (i = 0; i < task_num; i++) { - if (verb) { - ets_printf("Dump task %x\r\n", tasks[i].pxTCB); - } + ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].pxTCB); // save TCB address, stack base and stack top addr rom_data.data32[0] = (uint32_t)tasks[i].pxTCB; rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack; rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack; - err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb); + err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write task header (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err); return; } // save TCB - err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz, verb); + err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write TCB (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err); return; } // save task stack @@ -156,18 +166,18 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri tasks[i].pxEndOfStack, (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack #endif - , verb); + ); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write task stack (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err); return; } } // write end if (write_cfg->end) { - err = write_cfg->end(write_cfg->priv, verb); + err = write_cfg->end(write_cfg->priv); if (err != ESP_OK) { - ets_printf("ERROR: Failed to end core dump (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err); return; } } @@ -202,7 +212,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); err = spi_flash_write(off, data, data_len); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write data to flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err); return 0; } @@ -214,7 +224,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint rom_data.data8[k] = *(data + data_len + k); err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to finish write data to flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err); return 0; } data_len += sizeof(uint32_t); @@ -223,7 +233,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint return data_len; } -static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len, int verb) +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) { esp_err_t err; uint32_t sec_num; @@ -231,7 +241,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le // add space for 2 magics. TODO: change to CRC if ((*data_len + 2*sizeof(uint32_t)) > s_core_part_size) { - ets_printf("ERROR: Not enough space to save core dump!\r\n"); + ESP_COREDUMP_LOGE("Not enough space to save core dump!"); return ESP_ERR_NO_MEM; } *data_len += 2*sizeof(uint32_t); @@ -243,7 +253,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le sec_num++; err = spi_flash_erase_range(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE); if (err != ESP_OK) { - ets_printf("ERROR: Failed to erase flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err); return err; } @@ -257,7 +267,7 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr err = spi_flash_write(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t)); if (err != ESP_OK) { - ets_printf("ERROR: Failed to write to flash (%d)!\r\n", err); + ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err); return err; } wr_data->off += sizeof(uint32_t); @@ -265,16 +275,17 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr return err; } -static esp_err_t esp_core_dump_flash_write_start(void *priv, int verb) +static esp_err_t esp_core_dump_flash_write_start(void *priv) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; // save magic 1 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START); } -static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb) +static esp_err_t esp_core_dump_flash_write_end(void *priv) { core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; +#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG uint32_t i; union { @@ -282,27 +293,24 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb) uint32_t data32[4]; } rom_data; - if (verb) { - // TEST READ START - esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - ets_printf("ERROR: Failed to read flash (%d)!\r\n", err); - return err; - } - else { - ets_printf("Data from flash:\r\n"); - for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { - ets_printf("%x\r\n", rom_data.data32[i]); - } - } - // TEST READ END + esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err); + return err; } + else { + ESP_COREDUMP_LOG_PROCESS("Data from flash:"); + for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]); + } + } +#endif // save magic 2 return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END); } -static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len, int verb) +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) { esp_err_t err = ESP_OK; core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; @@ -330,9 +338,9 @@ void esp_core_dump_to_flash(XtExcFrame *frame) wr_cfg.write = esp_core_dump_flash_write_data; wr_cfg.priv = &wr_data; - ets_printf("Save core dump to flash...\r\n"); - esp_core_dump_write(frame, &wr_cfg, 0); - ets_printf("Core dump has been saved to flash.\r\n"); + ESP_COREDUMP_LOGI("Save core dump to flash..."); + esp_core_dump_write(frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been saved to flash."); } #endif @@ -362,27 +370,26 @@ static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8 dst[j++] = '\0'; } -static esp_err_t esp_core_dump_uart_write_start(void *priv, int verb) +static esp_err_t esp_core_dump_uart_write_start(void *priv) { esp_err_t err = ESP_OK; ets_printf("================= CORE DUMP START =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_end(void *priv, int verb) +static esp_err_t esp_core_dump_uart_write_end(void *priv) { esp_err_t err = ESP_OK; ets_printf("================= CORE DUMP END =================\r\n"); return err; } -static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len, int verb) +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) { esp_err_t err = ESP_OK; char buf[64 + 4], *addr = data; char *end = addr + data_len; - while (addr < end) { size_t len = end - addr; if (len > 48) len = 48; @@ -397,9 +404,21 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t return err; } +static int esp_core_dump_uart_get_char() { + int i; + uint32_t reg = (READ_PERI_REG(UART_STATUS_REG(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT; + if (reg) + i = READ_PERI_REG(UART_FIFO_REG(0)); + else + i = -1; + return i; +} + void esp_core_dump_to_uart(XtExcFrame *frame) { core_dump_write_config_t wr_cfg; + uint32_t tm_end, tm_cur; + int ch; wr_cfg.prepare = NULL; wr_cfg.start = esp_core_dump_uart_write_start; @@ -407,9 +426,27 @@ void esp_core_dump_to_uart(XtExcFrame *frame) wr_cfg.write = esp_core_dump_uart_write_data; wr_cfg.priv = NULL; - ets_printf("Print core dump to uart...\r\n"); - esp_core_dump_write(frame, &wr_cfg, 0); - ets_printf("Core dump has been written to uart.\r\n"); + //Make sure txd/rxd are enabled + gpio_pullup_dis(1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + + ESP_COREDUMP_LOGI("Press Enter to print core dump to UART..."); + tm_end = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000) + CONFIG_ESP32_CORE_DUMP_UART_DELAY; + ch = esp_core_dump_uart_get_char(); + while (!(ch == '\n' || ch == '\r')) { + tm_cur = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000); + if (tm_cur >= tm_end) + break; + /* Feed the Cerberus. */ + TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed = 1; + TIMERG0.wdt_wprotect = 0; + ch = esp_core_dump_uart_get_char(); + } + ESP_COREDUMP_LOGI("Print core dump to uart..."); + esp_core_dump_write(frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been written to uart."); } #endif diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 2ae4260bd5b..5ae68fc6437 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -217,7 +217,7 @@ void start_cpu0_default(void) } #endif -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if CONFIG_ESP32_ENABLE_COREDUMP esp_core_dump_init(); #endif diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 09ce520dede..3db09370ac3 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -162,39 +162,39 @@ void panicHandler(XtExcFrame *frame) reason = reasons[regs[20]]; } haltOtherCore(); - esp_panicPutStr("Guru Meditation Error: Core "); - esp_panicPutDec(xPortGetCoreID()); - esp_panicPutStr(" panic'ed ("); + panicPutStr("Guru Meditation Error: Core "); + panicPutDec(xPortGetCoreID()); + panicPutStr(" panic'ed ("); if (!abort_called) { - esp_panicPutStr(reason); - esp_panicPutStr(")\r\n"); + panicPutStr(reason); + panicPutStr(")\r\n"); if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) { int debugRsn; asm("rsr.debugcause %0":"=r"(debugRsn)); - esp_panicPutStr("Debug exception reason: "); - if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) esp_panicPutStr("SingleStep "); - if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) esp_panicPutStr("HwBreakpoint "); + panicPutStr("Debug exception reason: "); + if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep "); + if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint "); if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) { //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0. if (debugRsn&(1<<8)) { #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK - esp_panicPutStr("Stack canary watchpoint triggered "); + panicPutStr("Stack canary watchpoint triggered "); #else - esp_panicPutStr("Watchpoint 1 triggered "); + panicPutStr("Watchpoint 1 triggered "); #endif } else { - esp_panicPutStr("Watchpoint 0 triggered "); + panicPutStr("Watchpoint 0 triggered "); } } - if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) esp_panicPutStr("BREAK instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) esp_panicPutStr("BREAKN instr "); - if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) esp_panicPutStr("DebugIntr "); - esp_panicPutStr("\r\n"); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr "); + if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr "); + panicPutStr("\r\n"); } } else { - esp_panicPutStr("abort)\r\n"); + panicPutStr("abort)\r\n"); } if (esp_cpu_in_ocd_debug_mode()) { diff --git a/components/espcoredump/test/component.mk b/components/espcoredump/test/component.mk new file mode 100644 index 00000000000..e69de29bb2d diff --git a/components/espcoredump/test/test_core_dump.c b/components/espcoredump/test/test_core_dump.c new file mode 100644 index 00000000000..41137f9302a --- /dev/null +++ b/components/espcoredump/test/test_core_dump.c @@ -0,0 +1,105 @@ +/* Application For Core Dumps Generation + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" + + +// task crash indicators +#define TCI_NULL_PTR 0x1 +#define TCI_UNALIGN_PTR 0x2 +#define TCI_FAIL_ASSERT 0x4 + +volatile unsigned long crash_flags = TCI_UNALIGN_PTR; + +void bad_ptr_func() +{ + unsigned long *ptr = (unsigned long *)0; + volatile int cnt; + int i = 0; + + for (i = 0; i < 1000; i++) { + cnt++; + } + + if(crash_flags & TCI_NULL_PTR) { + printf("Write to bad address 0x%lx.\n", (unsigned long)ptr); + *ptr = 0xDEADBEEF; + } +} + +void bad_ptr_task(void *pvParameter) +{ + printf("Task 'bad_ptr_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'bad_ptr_task' run.\n"); + bad_ptr_func(); + } + fflush(stdout); +} + +void recur_func() +{ + static int rec_cnt; + unsigned short *ptr = (unsigned short *)0x5; + volatile int cnt; + int i = 0; + + if (rec_cnt++ > 2) { + return; + } + for (i = 0; i < 4; i++) { + cnt++; + if(i == 2) { + recur_func(); + break; + } + } + + if(crash_flags & TCI_UNALIGN_PTR) { + printf("Write to unaligned address 0x%lx.\n", (unsigned long)ptr); + *ptr = 0xDEAD; + } +} + +void unaligned_ptr_task(void *pvParameter) +{ + printf("Task 'unaligned_ptr_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'unaligned_ptr_task' run.\n"); + recur_func(); + } + fflush(stdout); +} + +void failed_assert_task(void *pvParameter) +{ + printf("Task 'failed_assert_task' start.\n"); + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Task 'failed_assert_task' run.\n"); + if(crash_flags & TCI_FAIL_ASSERT) { + printf("Assert.\n"); + assert(0); + } + } + fflush(stdout); +} + +void app_main() +{ + nvs_flash_init(); + xTaskCreate(&bad_ptr_task, "bad_ptr_task", 2048, NULL, 5, NULL); + xTaskCreatePinnedToCore(&unaligned_ptr_task, "unaligned_ptr_task", 2048, NULL, 7, NULL, 1); + xTaskCreatePinnedToCore(&failed_assert_task, "failed_assert_task", 2048, NULL, 10, NULL, 0); +} diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 7c9d8fd972b..b2fc077bc30 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -268,7 +268,7 @@ #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH | CONFIG_ESP32_ENABLE_COREDUMP_TO_UART +#if CONFIG_ESP32_ENABLE_COREDUMP #define configENABLE_TASK_SNAPSHOT 1 #endif diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 33bc10b42af..6fda8d6054e 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -75,6 +75,16 @@ void esp_log_set_vprintf(vprintf_like_t func); */ uint32_t esp_log_timestamp(void); +/** + * @brief Function which returns timestamp to be used in log output + * + * This function uses HW cycle counter and does not depend on OS, + * so it can be safely used after application crash. + * + * @return timestamp, in milliseconds + */ +uint32_t esp_log_early_timestamp(void); + /** * @brief Write message into the log * diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index a4412ad45b8..04912048850 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -127,6 +127,7 @@ class PartitionDefinition(object): "ota" : 0x00, "phy" : 0x01, "nvs" : 0x02, + "coredump" : 0x03, "esphttpd" : 0x80, "fat" : 0x81, "spiffs" : 0x82, diff --git a/components/partition_table/partitions_singleapp_coredump.csv b/components/partition_table/partitions_singleapp_coredump.csv index 96719a425a0..a9f12c0fd3d 100644 --- a/components/partition_table/partitions_singleapp_coredump.csv +++ b/components/partition_table/partitions_singleapp_coredump.csv @@ -3,4 +3,4 @@ nvs, data, nvs, 0x9000, 0x6000 phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M -coredump, data, 3, , 64K +coredump, data, coredump,, 64K diff --git a/components/partition_table/partitions_two_ota_coredump.csv b/components/partition_table/partitions_two_ota_coredump.csv index 3d2b34ec9a7..64d70b0d8eb 100644 --- a/components/partition_table/partitions_two_ota_coredump.csv +++ b/components/partition_table/partitions_two_ota_coredump.csv @@ -4,6 +4,6 @@ nvs, data, nvs, 0x9000, 0x4000 otadata, data, ota, 0xd000, 0x2000 phy_init, data, phy, 0xf000, 0x1000 factory, 0, 0, 0x10000, 1M -coredump, data, 3, , 64K +coredump, data, coredump,, 64K ota_0, 0, ota_0, , 1M ota_1, 0, ota_1, , 1M diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index a5442d1af7a..598b8fba778 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -41,13 +41,11 @@ void spi_flash_disable_interrupts_caches_and_other_cpu(); void spi_flash_enable_interrupts_caches_and_other_cpu(); // Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs. -// This function is implied to be called from panic handler -// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. +// This function is implied to be called when other CPU is not running or running code from IRAM. void spi_flash_disable_interrupts_caches_and_other_cpu_no_os(); // Enable cache, enable interrupts on current CPU. -// This function is implied to be called from panic handler -// (when non-current CPU is halted and can not execute code from flash) or when no OS is present. +// This function is implied to be called when other CPU is not running or running code from IRAM. void spi_flash_enable_interrupts_caches_no_os(); #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 831679f437a..fffe487bd15 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -58,9 +58,6 @@ static spi_flash_counters_t s_flash_stats; #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS -#define FLASH_GUARD_START(_gp_) do{if((_gp_)) (_gp_)->start();}while(0) -#define FLASH_GUARD_END(_gp_) do{if((_gp_)) (_gp_)->end();}while(0) - static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = { @@ -106,6 +103,18 @@ SpiFlashOpResult IRAM_ATTR spi_flash_unlock() return SPI_FLASH_RESULT_OK; } +static inline void spi_flash_guard_start() +{ + if (s_flash_guard_ops) + s_flash_guard_ops->start(); +} + +static inline void spi_flash_guard_end() +{ + if (s_flash_guard_ops) + s_flash_guard_ops->end(); +} + esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) { return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); @@ -126,7 +135,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) size_t end = start + size / SPI_FLASH_SEC_SIZE; const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE; COUNTER_START(); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { @@ -142,7 +151,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) } } } - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } @@ -180,9 +189,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (left_size > 0) { uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(left_off, &t, 4); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -198,9 +207,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) bool in_dram = true; #endif if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) { - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -214,9 +223,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t[8]; uint32_t write_size = MIN(mid_size, sizeof(t)); memcpy(t, srcc + mid_off, write_size); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(dst + mid_off, t, write_size); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -229,9 +238,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) if (right_size > 0) { uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); rc = SPIWrite(dst + right_off, &t, 4); - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); if (rc != SPI_FLASH_RESULT_OK) { goto out; } @@ -291,7 +300,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; COUNTER_START(); - FLASH_GUARD_START(s_flash_guard_ops); + spi_flash_guard_start(); /* To simplify boundary checks below, we handle small reads separately. */ if (size < 16) { uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */ @@ -365,7 +374,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) memcpy(dstc + pad_right_off, t, pad_right_size); } out: - FLASH_GUARD_END(s_flash_guard_ops); + spi_flash_guard_end(); COUNTER_STOP(read); return spi_flash_translate_rc(rc); } diff --git a/docs/core_dump.rst b/docs/core_dump.rst index 0d0624cc899..a8e328996fd 100644 --- a/docs/core_dump.rst +++ b/docs/core_dump.rst @@ -1,10 +1,10 @@ ESP32 Core Dump -================ +=============== Overview -------- -ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analisys of software state at the moment of failure. +ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analysis of software state at the moment of failure. Upon the crash system enters panic state, prints some information and halts or reboots depending configuration. User can choose to generate core dump in order to analyse the reason of failure on PC later on. Core dump contains snapshots of all tasks in the system at the moment of failure. Snapshots include tasks control blocks (TCB) and stacks. So it is possible to find out what task, at what instruction (line of code) and what callstack of that task lead to the crash. @@ -30,16 +30,16 @@ Save core dump to flash When this option is selected core dumps are saved to special partition on flash. When using default partition table files which are provided with ESP-IDF it automatically allocates necessary space on flash, But if user wants to use its own layout file together with core dump feature it should define separate partition for core dump as it is shown below:: - + # Name, Type, SubType, Offset, Size # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x6000 phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M - coredump, data, 3, , 64K - + coredump, data, coredump,, 64K + There are no special requrements for partition name. It can be choosen according to the user application needs, but partition type should be 'data' and -sub-type should be 3. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. +sub-type should be 'coredump'. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. This overhead does not include size of TCB and stack for every task. So partirion size should be at least 20 + max tasks number x (12 + TCB size + max task stack size) bytes. The example of generic command to analyze core dump from flash is: `espcoredump.py -p info_corefile ` @@ -55,73 +55,27 @@ or `espcoredump.py -p dbg_corefile -t b64 -c + ================= CORE DUMP END =================== -Command Options For 'espcoredump.py' --------------------------------------------- +Running 'espcoredump.py' +------------------------------------ -usage: coredumper [-h] [--chip {auto,esp32}] [--port PORT] [--baud BAUD] - {dbg_corefile,info_corefile} ... +Generic command syntax: -espcoredump.py v0.1-dev - ESP32 Core Dump Utility +`espcoredump.py [options] command [args]` -positional arguments: - {dbg_corefile,info_corefile} - Run coredumper {command} -h for additional help - dbg_corefile Starts GDB debugging session with specified corefile - info_corefile Print core dump info from file - -optional arguments: - -h, --help show this help message and exit - --chip {auto,esp32}, -c {auto,esp32} - Target chip type - --port PORT, -p PORT Serial port device - --baud BAUD, -b BAUD Serial port baud rate used when flashing/reading - - -usage: coredumper info_corefile [-h] [--gdb GDB] [--core CORE] - [--core-format CORE_FORMAT] [--off OFF] - [--save-core SAVE_CORE] [--print-mem] - prog - -positional arguments: - prog Path to program's ELF binary - -optional arguments: - -h, --help show this help message and exit - --gdb GDB, -g GDB Path to gdb - --core CORE, -c CORE Path to core dump file (if skipped core dump will be - read from flash) - --core-format CORE_FORMAT, -t CORE_FORMAT - (elf, raw or b64). File specified with "-c" is an ELF - ("elf"), raw (raw) or base64-encoded (b64) binary - --off OFF, -o OFF Ofsset of coredump partition in flash (type "make - partition_table" to see). - --save-core SAVE_CORE, -s SAVE_CORE - Save core to file. Othwerwise temporary core file will - be deleted. Does not work with "-c" - --print-mem, -m Print memory dump - - -usage: coredumper dbg_corefile [-h] [--gdb GDB] [--core CORE] - [--core-format CORE_FORMAT] [--off OFF] - [--save-core SAVE_CORE] - prog - -positional arguments: - prog Path to program's ELF binary - -optional arguments: - -h, --help show this help message and exit - --gdb GDB, -g GDB Path to gdb - --core CORE, -c CORE Path to core dump file (if skipped core dump will be - read from flash) - --core-format CORE_FORMAT, -t CORE_FORMAT - (elf, raw or b64). File specified with "-c" is an ELF - ("elf"), raw (raw) or base64-encoded (b64) binary - --off OFF, -o OFF Ofsset of coredump partition in flash (type "make - partition_table" to see). - --save-core SAVE_CORE, -s SAVE_CORE - Save core to file. Othwerwise temporary core file will - be deleted. Ignored with "-c" +:Script Options: + * --chip,-c {auto,esp32}. Target chip type. Supported values are `auto` and `esp32`. + * --port,-p PORT. Serial port device. + * --baud,-b BAUD. Serial port baud rate used when flashing/reading. +:Commands: + * info_corefile. Retrieve core dump and print useful info. + * dbg_corefile. Retrieve core dump and start GDB session with it. +:Command Arguments: + * --gdb,-g GDB. Path to gdb to use for data retrieval. + * --core,-c CORE. Path to core dump file to use (if skipped core dump will be read from flash). + * --core-format,-t CORE_FORMAT. Specifies that file passed with "-c" is an ELF ("elf"), dumped raw binary ("raw") or base64-encoded ("b64") format. + * --off,-o OFF. Ofsset of coredump partition in flash (type "make partition_table" to see it). + * --save-core,-s SAVE_CORE. Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c". + * --print-mem,-m Print memory dump. Used only with "info_corefile". diff --git a/docs/index.rst b/docs/index.rst index 2d9a62f14b8..b994648848b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,7 @@ Contents: partition-tables build_system openocd + core_dump Flash encryption Secure Boot ULP coprocessor From 7d40f17d1dab9a7f721afb21451e522c6948dee1 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 13 Jan 2017 10:26:58 +1100 Subject: [PATCH 151/167] bootloader_random: Restore all SARADC/I2S registers to reset values Fix for issue with I2S0 not being usable after bootloader_random_enable() --- components/bootloader_support/src/bootloader_random.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/bootloader_support/src/bootloader_random.c b/components/bootloader_support/src/bootloader_random.c index c8b6c24b1fe..b58ebe941da 100644 --- a/components/bootloader_support/src/bootloader_random.c +++ b/components/bootloader_support/src/bootloader_random.c @@ -124,10 +124,13 @@ void bootloader_random_disable(void) /* Restore SYSCON mode registers */ CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DIG_FORCE); CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DIG_FORCE); - CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX | SYSCON_SARADC_SAR_SEL); /* Restore SAR ADC mode */ CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST); + CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX + | SYSCON_SARADC_SAR_SEL | SYSCON_SARADC_DATA_TO_I2S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_START_WAIT, 8, SYSCON_SARADC_START_WAIT_S); /* Reset i2s peripheral */ SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); From 233fde166b1d55fdcf0b0f42c715d1d965b98409 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 13 Jan 2017 13:48:33 +1100 Subject: [PATCH 152/167] Toolchain detection: Allow for Windows executable name and not-yet-configured toolchain path Windows executable name based on fix suggested by @krzysztof --- make/project.mk | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/make/project.mk b/make/project.mk index d30d007dc54..3de9361c821 100644 --- a/make/project.mk +++ b/make/project.mk @@ -429,22 +429,26 @@ $(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUB # The part in brackets is extracted into TOOLCHAIN_COMMIT_DESC variable, # the part after the brackets is extracted into TOOLCHAIN_GCC_VER. ifndef MAKE_RESTARTS -TOOLCHAIN_COMMIT_DESC := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc\ \(([^)]*).*|\1|gp') -TOOLCHAIN_GCC_VER := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc\ \(.*\)\ (.*)|\1|gp') +TOOLCHAIN_COMMIT_DESC := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc.*?\ \(([^)]*).*|\1|gp') +TOOLCHAIN_GCC_VER := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc.*?\ \(.*\)\ (.*)|\1|gp') # Officially supported version(s) SUPPORTED_TOOLCHAIN_COMMIT_DESC := crosstool-NG crosstool-ng-1.22.0-61-gab8375a SUPPORTED_TOOLCHAIN_GCC_VERSIONS := 5.2.0 +ifdef TOOLCHAIN_COMMIT_DESC ifneq ($(TOOLCHAIN_COMMIT_DESC), $(SUPPORTED_TOOLCHAIN_COMMIT_DESC)) $(info WARNING: Toolchain version is not supported: $(TOOLCHAIN_COMMIT_DESC)) $(info Expected to see version: $(SUPPORTED_TOOLCHAIN_COMMIT_DESC)) $(info Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.) endif ifeq (,$(findstring $(TOOLCHAIN_GCC_VER), $(SUPPORTED_TOOLCHAIN_GCC_VERSIONS))) -$(warning WARNING: Compiler version is not supported: $(TOOLCHAIN_GCC_VER)) +$(info WARNING: Compiler version is not supported: $(TOOLCHAIN_GCC_VER)) $(info Expected to see version(s): $(SUPPORTED_TOOLCHAIN_GCC_VERSIONS)) $(info Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.) endif -endif #MAKE_RESTARTS +else +$(info WARNING: Failed to find Xtensa toolchain, may need to alter PATH or set one in the configuration menu) +endif # TOOLCHAIN_COMMIT_DESC +endif #MAKE_RESTARTS From 5c9c08eabb88de10a220ef7458bfc7a4093367a9 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 13 Jan 2017 14:09:01 +1100 Subject: [PATCH 153/167] Toolchain detection: Fix issue when run in a clean project If the makefile config entry hasn't been generated yet, don't test the toolchain. Closes #226 https://github.com/espressif/esp-idf/issues/226 --- make/project.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/make/project.mk b/make/project.mk index 3de9361c821..c4c3022a6b4 100644 --- a/make/project.mk +++ b/make/project.mk @@ -428,6 +428,7 @@ $(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUB # xtensa-esp32-elf-gcc (crosstool-NG crosstool-ng-1.22.0-59-ga194053) 4.8.5 # The part in brackets is extracted into TOOLCHAIN_COMMIT_DESC variable, # the part after the brackets is extracted into TOOLCHAIN_GCC_VER. +ifdef CONFIG_TOOLPREFIX ifndef MAKE_RESTARTS TOOLCHAIN_COMMIT_DESC := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc.*?\ \(([^)]*).*|\1|gp') TOOLCHAIN_GCC_VER := $(shell $(CC) --version | sed -E -n 's|xtensa-esp32-elf-gcc.*?\ \(.*\)\ (.*)|\1|gp') @@ -450,5 +451,6 @@ endif else $(info WARNING: Failed to find Xtensa toolchain, may need to alter PATH or set one in the configuration menu) endif # TOOLCHAIN_COMMIT_DESC -endif #MAKE_RESTARTS +endif #MAKE_RESTARTS +endif #CONFIG_TOOLPREFIX From e53d307814a9cb5c980c4e6d77496e8635d27c38 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 13 Jan 2017 14:30:00 +1100 Subject: [PATCH 154/167] Panic handler: Use same reset path as esp_restart(), disabling hardware Closes #223 https://github.com/espressif/esp-idf/issues/223 --- components/esp32/include/rom/rtc.h | 6 ++++++ components/esp32/panic.c | 7 +++---- components/esp32/system_api.c | 13 +++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/components/esp32/include/rom/rtc.h b/components/esp32/include/rom/rtc.h index e3c031775d7..9bcd9bd7e00 100644 --- a/components/esp32/include/rom/rtc.h +++ b/components/esp32/include/rom/rtc.h @@ -181,6 +181,9 @@ void set_rtc_memory_crc(void); /** * @brief Software Reset digital core. * + * It is not recommended to use this function in esp-idf, use + * esp_restart() instead. + * * @param None * * @return None @@ -190,6 +193,9 @@ void software_reset(void); /** * @brief Software Reset digital core. * + * It is not recommended to use this function in esp-idf, use + * esp_restart() instead. + * * @param int cpu_no : The CPU to reset, 0 for PRO CPU, 1 for APP CPU. * * @return None diff --git a/components/esp32/panic.c b/components/esp32/panic.c index c5b18870a93..dcf7ba8d7e8 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -320,6 +320,8 @@ static void doBacktrace(XtExcFrame *frame) panicPutStr("\n\n"); } +void esp_restart_noos() __attribute__ ((noreturn)); + /* We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the serial port and either jump to the gdb stub, halt the CPU or reboot. @@ -365,10 +367,7 @@ static void commonErrorHandler(XtExcFrame *frame) esp_gdbstub_panic_handler(frame); #elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT panicPutStr("Rebooting...\r\n"); - for (x = 0; x < 100; x++) { - ets_delay_us(1000); - } - software_reset(); + esp_restart_noos(); #else disableAllWdts(); panicPutStr("CPU halted.\r\n"); diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index 60fa1796cdc..55fe026591e 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -72,12 +72,25 @@ esp_err_t esp_efuse_read_mac(uint8_t* mac) esp_err_t system_efuse_read_mac(uint8_t mac[6]) __attribute__((alias("esp_efuse_read_mac"))); +void esp_restart_noos() __attribute__ ((noreturn)); + void IRAM_ATTR esp_restart(void) { esp_wifi_stop(); // Disable scheduler on this core. vTaskSuspendAll(); + + esp_restart_noos(); +} + +/* "inner" restart function for after RTOS, interrupts & anything else on this + * core are already stopped. Stalls other core, resets hardware, + * triggers restart. +*/ +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); From dd3f18d2d88ee78909d4af2840dfdf0b9f715f28 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Wed, 4 Jan 2017 18:54:07 +0200 Subject: [PATCH 155/167] Initial mDNS component and example --- components/mdns/component.mk | 0 components/mdns/include/mdns.h | 235 +++ components/mdns/mdns.c | 1861 +++++++++++++++++ .../tcpip_adapter/include/tcpip_adapter.h | 11 + docs/Doxyfile | 3 +- docs/api/mdns.rst | 215 ++ docs/index.rst | 1 + examples/30_mdns_example/Makefile | 9 + examples/30_mdns_example/README.md | 5 + .../30_mdns_example/main/Kconfig.projbuild | 29 + examples/30_mdns_example/main/component.mk | 4 + .../30_mdns_example/main/mdns_example_main.c | 184 ++ 12 files changed, 2556 insertions(+), 1 deletion(-) create mode 100644 components/mdns/component.mk create mode 100644 components/mdns/include/mdns.h create mode 100644 components/mdns/mdns.c create mode 100644 docs/api/mdns.rst create mode 100644 examples/30_mdns_example/Makefile create mode 100644 examples/30_mdns_example/README.md create mode 100644 examples/30_mdns_example/main/Kconfig.projbuild create mode 100644 examples/30_mdns_example/main/component.mk create mode 100644 examples/30_mdns_example/main/mdns_example_main.c diff --git a/components/mdns/component.mk b/components/mdns/component.mk new file mode 100644 index 00000000000..e69de29bb2d diff --git a/components/mdns/include/mdns.h b/components/mdns/include/mdns.h new file mode 100644 index 00000000000..58e588e3e66 --- /dev/null +++ b/components/mdns/include/mdns.h @@ -0,0 +1,235 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef ESP_MDNS_H_ +#define ESP_MDNS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct mdns_server_s; +typedef struct mdns_server_s mdns_server_t; + +/** + * @brief mDNS query result structure + * + */ +typedef struct mdns_result_s { + const char * host; /*!< hostname */ + const char * instance; /*!< instance */ + const char * txt; /*!< txt data */ + uint16_t priority; /*!< service priority */ + uint16_t weight; /*!< service weight */ + uint16_t port; /*!< service port */ + struct ip4_addr addr; /*!< ip4 address */ + struct ip6_addr addrv6; /*!< ip6 address */ + const struct mdns_result_s * next; /*!< next result, or NULL for the last result in the list */ +} mdns_result_t; + +/** + * @brief Initialize mDNS on given interface + * + * @param tcpip_if Interface that the server will listen on + * @param server Server pointer to populate on success + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG when bad tcpip_if is given + * - ESP_ERR_INVALID_STATE when the network returned error + * - ESP_ERR_NO_MEM on memory error + * - ESP_ERR_WIFI_NOT_INIT when WiFi is not initialized by eps_wifi_init + */ +esp_err_t mdns_init(tcpip_adapter_if_t tcpip_if, mdns_server_t ** server); + +/** + * @brief Stop and free mDNS server + * + * @param server mDNS Server to free + * + */ +void mdns_free(mdns_server_t * server); + +/** + * @brief Set the hostname for mDNS server + * + * @param server mDNS Server + * @param hostname Hostname to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_set_hostname(mdns_server_t * server, const char * hostname); + +/** + * @brief Set the default instance name for mDNS server + * + * @param server mDNS Server + * @param instance Instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_set_instance(mdns_server_t * server, const char * instance); + +/** + * @brief Add service to mDNS server + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param port service port + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_add(mdns_server_t * server, const char * service, const char * proto, uint16_t port); + +/** + * @brief Remove service from mDNS server + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_FAIL unknown error + */ +esp_err_t mdns_service_remove(mdns_server_t * server, const char * service, const char * proto); + +/** + * @brief Set instance name for service + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param instance instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_instance_set(mdns_server_t * server, const char * service, const char * proto, const char * instance); + +/** + * @brief Set TXT data for service + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param num_items number of items in TXT data + * @param txt string array of TXT data (eg. {"var=val","other=2"}) + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_set(mdns_server_t * server, const char * service, const char * proto, uint8_t num_items, const char ** txt); + +/** + * @brief Set service port + * + * @param server mDNS Server + * @param service service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param port service port + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + */ +esp_err_t mdns_service_port_set(mdns_server_t * server, const char * service, const char * proto, uint16_t port); + +/** + * @brief Remove and free all services from mDNS server + * + * @param server mDNS Server + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t mdns_service_remove_all(mdns_server_t * server); + +/** + * @brief Query mDNS for host or service + * + * @param server mDNS Server + * @param service service type or host name + * @param proto service protocol or NULL if searching for host + * @param timeout time to wait for answers. If 0, mdns_query_end MUST be called to end the search + * + * @return the number of results found + */ +size_t mdns_query(mdns_server_t * server, const char * service, const char * proto, uint32_t timeout); + +/** + * @brief Stop mDNS Query started with timeout = 0 + * + * @param server mDNS Server + * + * @return the number of results found + */ +size_t mdns_query_end(mdns_server_t * server); + +/** + * @brief get the number of results currently in memoty + * + * @param server mDNS Server + * + * @return the number of results + */ +size_t mdns_result_get_count(mdns_server_t * server); + +/** + * @brief Get mDNS Search result with given index + * + * @param server mDNS Server + * @param num the index of the result + * + * @return the result or NULL if error + */ +const mdns_result_t * mdns_result_get(mdns_server_t * server, size_t num); + +/** + * @brief Remove and free all search results from mDNS server + * + * @param server mDNS Server + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t mdns_result_free(mdns_server_t * server); + +#ifdef __cplusplus +} +#endif + +#endif /* ESP_MDNS_H_ */ diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c new file mode 100644 index 00000000000..d636134c7da --- /dev/null +++ b/components/mdns/mdns.c @@ -0,0 +1,1861 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "mdns.h" + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" +#include "lwip/igmp.h" +#include "lwip/udp.h" +#include "esp_wifi.h" + +#define MDNS_FLAGS_AUTHORITATIVE 0x8400 + +#define MDNS_NAME_REF 0xC000 + +#define MDNS_TYPE_AAAA 0x001C +#define MDNS_TYPE_A 0x0001 +#define MDNS_TYPE_PTR 0x000C +#define MDNS_TYPE_SRV 0x0021 +#define MDNS_TYPE_TXT 0x0010 +#define MDNS_TYPE_NSEC 0x002F +#define MDNS_TYPE_ANY 0x00FF + +#define MDNS_CLASS_IN 0x0001 +#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001 + +#define MDNS_ANSWER_ALL 0x3F +#define MDNS_ANSWER_PTR 0x08 +#define MDNS_ANSWER_TXT 0x04 +#define MDNS_ANSWER_SRV 0x02 +#define MDNS_ANSWER_A 0x01 +#define MDNS_ANSWER_AAAA 0x10 +#define MDNS_ANSWER_NSEC 0x20 + +#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on +#define MDNS_SERVICE_STACK_DEPTH 4096 // Stack size for the service thread +#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing +#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record +#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto +#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto +#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet + +#define MDNS_ANSWER_PTR_TTL 4500 +#define MDNS_ANSWER_TXT_TTL 4500 +#define MDNS_ANSWER_SRV_TTL 120 +#define MDNS_ANSWER_A_TTL 120 +#define MDNS_ANSWER_AAAA_TTL 120 + +#define MDNS_HEAD_LEN 12 +#define MDNS_HEAD_ID_OFFSET 0 +#define MDNS_HEAD_FLAGS_OFFSET 2 +#define MDNS_HEAD_QUESTIONS_OFFSET 4 +#define MDNS_HEAD_ANSWERS_OFFSET 6 +#define MDNS_HEAD_SERVERS_OFFSET 8 +#define MDNS_HEAD_ADDITIONAL_OFFSET 10 + +#define MDNS_TYPE_OFFSET 0 +#define MDNS_CLASS_OFFSET 2 +#define MDNS_TTL_OFFSET 4 +#define MDNS_LEN_OFFSET 8 +#define MDNS_DATA_OFFSET 10 + +#define MDNS_SRV_PRIORITY_OFFSET 0 +#define MDNS_SRV_WEIGHT_OFFSET 2 +#define MDNS_SRV_PORT_OFFSET 4 +#define MDNS_SRV_FQDN_OFFSET 6 + +typedef struct { + char host[MDNS_NAME_BUF_LEN]; + char service[MDNS_NAME_BUF_LEN]; + char proto[MDNS_NAME_BUF_LEN]; + char domain[MDNS_NAME_BUF_LEN]; + uint8_t parts; + uint8_t sub; +} mdns_name_t; + +typedef struct { + char host[MDNS_NAME_BUF_LEN]; + char instance[MDNS_NAME_BUF_LEN]; + char txt[MDNS_TXT_MAX_LEN]; + uint16_t priority; + uint16_t weight; + uint16_t port; + uint32_t addr; + uint8_t addrv6[16]; + uint8_t ptr; +} mdns_result_temp_t; + +typedef struct { + const char * host; + const char * sub; + const char * service; + const char * proto; + const char * domain; + uint8_t parts; + uint8_t done; +} mdns_string_t; + +typedef struct mdns_service_s { + const char * instance; + const char * service; + const char * proto; + uint16_t priority; + uint16_t weight; + uint16_t port; + uint8_t txt_num_items; + const char ** txt; +} mdns_service_t; + +typedef struct mdns_srv_item_s { + mdns_service_t * service; + struct mdns_srv_item_s * next; +} mdns_srv_item_t; + +typedef struct mdns_answer_item_s { + mdns_service_t * service; + uint8_t answer; + struct mdns_answer_item_s * next; +} mdns_answer_item_t; + +struct mdns_server_s { + tcpip_adapter_if_t tcpip_if; + struct udp_pcb * pcb; + const char * hostname; + const char * instance; + mdns_srv_item_t * services; + xSemaphoreHandle lock; + xQueueHandle queue; + struct { + char host[MDNS_NAME_BUF_LEN]; + char service[MDNS_NAME_BUF_LEN]; + char proto[MDNS_NAME_BUF_LEN]; + bool running; + xSemaphoreHandle lock; + mdns_result_t * results; + } search; +}; + +#define MDNS_MUTEX_LOCK() xSemaphoreTake(server->lock, portMAX_DELAY) +#define MDNS_MUTEX_UNLOCK() xSemaphoreGive(server->lock) + +#define MDNS_SEARCH_LOCK() xSemaphoreTake(server->search.lock, portMAX_DELAY) +#define MDNS_SEARCH_UNLOCK() xSemaphoreGive(server->search.lock) + +static const char * MDNS_DEFAULT_DOMAIN = "local"; +static const char * MDNS_SUB_STR = "_sub"; + +static mdns_server_t * _mdns_servers[TCPIP_ADAPTER_IF_MAX] = {0,0,0}; +static TaskHandle_t _mdns_service_task_handle = NULL; +static QueueSetHandle_t _mdns_queue_set = NULL; + +static xSemaphoreHandle _mdns_service_semaphore = NULL; +#define MDNS_SERVICE_LOCK() xSemaphoreTake(_mdns_service_semaphore, portMAX_DELAY) +#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(_mdns_service_semaphore) + +/* + * MDNS Server Networking + * */ + +/** + * @brief the receive callback of the raw udp api. Packets are received here + * + */ +static void _mdns_server_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *addr, uint16_t port) +{ + while(pb != NULL) { + struct pbuf * this_pb = pb; + pb = pb->next; + this_pb->next = NULL; + mdns_server_t * server = (mdns_server_t *)arg; + if (!server || !server->queue || xQueueSend(server->queue, &this_pb, (portTickType)0) != pdPASS) { + pbuf_free(this_pb); + } + } +} + +/** + * @brief init the network of MDNS server + * + * @param server The server + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE on igmp/bind error + * - ESP_ERR_NO_MEM on memory error + */ +esp_err_t _mdns_server_init(mdns_server_t * server) +{ + esp_err_t err = ESP_OK; + + tcpip_adapter_ip_info_t if_ip_info; + err = tcpip_adapter_get_ip_info(server->tcpip_if, &if_ip_info); + if (err) { + return err; + } + + ip_addr_t laddr; + IP_ADDR4(&laddr, 224, 0, 0, 251); + + ip_addr_t multicast_if_addr = IPADDR4_INIT(if_ip_info.ip.addr); + + if (igmp_joingroup((const struct ip4_addr *)&multicast_if_addr.u_addr.ip4, (const struct ip4_addr *)&laddr.u_addr.ip4)) { + return ESP_ERR_INVALID_STATE; + } + + struct udp_pcb * pcb = udp_new(); + if (!pcb) { + return ESP_ERR_NO_MEM; + } + + pcb->remote_port = MDNS_SERVICE_PORT; + + if (udp_bind(pcb, &multicast_if_addr, pcb->remote_port) != 0) { + udp_remove(pcb); + return ESP_ERR_INVALID_STATE; + } + + pcb->mcast_ttl = 1; + ip_addr_copy(pcb->multicast_ip, multicast_if_addr); + ip_addr_copy(pcb->remote_ip, laddr); + + server->pcb = pcb; + udp_recv(pcb, &_mdns_server_recv, server); + return err; +} + +/** + * @brief stop the network of MDNS server + * + * @param server The server + * + * @return ESP_OK + */ +esp_err_t _mdns_server_deinit(mdns_server_t * server) +{ + if (server->pcb) { + udp_recv(server->pcb, NULL, NULL); + udp_disconnect(server->pcb); + udp_remove(server->pcb); + server->pcb = NULL; + } + return ESP_OK; +} + +/** + * @brief send packet over UDP + * + * @param server The server + * @param data byte array containing the packet data + * @param len length of the packet data + * + * @return length of sent packet or 0 on error + */ +static size_t _mdns_server_write(mdns_server_t * server, uint8_t * data, size_t len) +{ + struct pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pbt != NULL) { + uint8_t* dst = (uint8_t *)pbt->payload; + memcpy(dst, data, len); + err_t err = udp_sendto(server->pcb, pbt, &(server->pcb->remote_ip), server->pcb->remote_port); + pbuf_free(pbt); + if (err) { + return 0; + } + return len; + } + return 0; +} + +/* + * MDNS Servers + * */ + +static void _mdns_parse_packet(mdns_server_t * server, const uint8_t * data, size_t len); + +/** + * @brief the main MDNS service task. Packets are received and parsed here + */ +static void _mdns_service_task(void *pvParameters) +{ + uint8_t i; + struct pbuf * pb; + QueueSetMemberHandle_t queue; + + for(;;) { + queue = xQueueSelectFromSet(_mdns_queue_set, portMAX_DELAY); + if (queue && xQueueReceive(queue, &pb, 0) == pdTRUE) { + for(i=0; iqueue == queue) { + MDNS_MUTEX_LOCK(); + _mdns_parse_packet(server, (uint8_t*)pb->payload, pb->len); + MDNS_MUTEX_UNLOCK(); + break; + } + } + pbuf_free(pb); + } + } +} + +/** + * @brief get the server assigned to particular interface + * + * @param tcpip_if The interface + * + * @return reference to the server from the server list or NULL if not found + */ +static mdns_server_t * _mdns_server_get(tcpip_adapter_if_t tcpip_if) +{ + if (tcpip_if < TCPIP_ADAPTER_IF_MAX) { + return _mdns_servers[tcpip_if]; + } + return NULL; +} + +/** + * @brief add server to the server list. Start the service thread if not running + * + * @param server The server to add + * + * @return + * - ESP_OK on success + * - ESP_FAIL on error + * - ESP_ERR_* on network error + */ +static esp_err_t _mdns_server_add(mdns_server_t * server) +{ + if (!_mdns_service_semaphore) { + _mdns_service_semaphore = xSemaphoreCreateMutex(); + if (!_mdns_service_semaphore) { + return ESP_FAIL; + } + } + MDNS_SERVICE_LOCK(); + if (!_mdns_service_task_handle) { + _mdns_queue_set = xQueueCreateSet(TCPIP_ADAPTER_IF_MAX * MDNS_PACKET_QUEUE_LEN); + if (!_mdns_queue_set) { + MDNS_SERVICE_UNLOCK(); + return ESP_FAIL; + } + xTaskCreatePinnedToCore(_mdns_service_task, "mdns", MDNS_SERVICE_STACK_DEPTH, NULL, 1, &_mdns_service_task_handle, 0); + if (!_mdns_service_task_handle) { + vQueueDelete(_mdns_queue_set); + _mdns_queue_set = NULL; + MDNS_SERVICE_UNLOCK(); + return ESP_FAIL; + } + } + MDNS_SERVICE_UNLOCK(); + + if (xQueueAddToSet(server->queue, _mdns_queue_set) != pdPASS) { + return ESP_FAIL; + } + + //start UDP + esp_err_t err = _mdns_server_init(server); + if (err) { + return err; + } + + _mdns_servers[server->tcpip_if] = server; + + return ESP_OK; +} + +/** + * @brief remove server from server list. Stop the service thread in no more servers are running + * + * @param server The server to remove + * + * @return + * - ESP_OK on success + * - ESP_FAIL on error + */ +static esp_err_t _mdns_server_remove(mdns_server_t * server) +{ + //stop UDP + _mdns_server_deinit(server); + + _mdns_servers[server->tcpip_if] = NULL; + + if (xQueueRemoveFromSet(server->queue, _mdns_queue_set) != pdPASS) { + return ESP_FAIL; + } + + uint8_t i; + for(i=0; iservice == service) { + //just add the new answer type to it + a->answer |= type; + return answers; + } + a = a->next; + } + //prepend the q with this new answer + a = (mdns_answer_item_t *)malloc(sizeof(mdns_answer_item_t)); + if (!a) { + return answers;//fail! + } + a->service = service; + a->answer = type; + a->next = answers; + answers = a; + return a; +} + +/** + * @brief reads MDNS FQDN into mdns_name_t structure + * FQDN is in format: [hostname.|[instance.]_service._proto.]local. + * + * @param packet MDNS packet + * @param start Starting point of FQDN + * @param name mdns_name_t structure to populate + * @param buf temporary char buffer + * + * @return the address after the parsed FQDN in the packet or NULL on error + */ +static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * start, mdns_name_t * name, char * buf) +{ + size_t index = 0; + while(start[index]) { + if (name->parts == 4) { + return NULL; + } + uint8_t len = start[index++]; + if ((len & 0xC0) == 0) { + if (len > 64) { + //length can not be more than 64 + return NULL; + } + uint8_t i; + for(i=0; iparts == 1 && buf[0] != '_' + && (strcmp(buf, MDNS_DEFAULT_DOMAIN) != 0) + && (strcmp(buf, "ip6") != 0) + && (strcmp(buf, "in-addr") != 0)) { + sprintf((char*)name, "%s.%s", name->host, buf); + } else if (strcmp(buf, MDNS_SUB_STR) == 0) { + name->sub = 1; + } else { + memcpy((uint8_t*)name + (name->parts++ * (MDNS_NAME_BUF_LEN)), buf, len+1); + } + } else { + size_t address = (((uint16_t)len & 0x3F) << 8) | start[index++]; + if ((packet + address) > start) { + //reference address can not be after where we are + return NULL; + } + if (_mdns_read_fqdn(packet, packet + address, name, buf)) { + return start + index; + } + return NULL; + } + } + return start + index + 1; +} + +/** + * @brief reads and formats MDNS FQDN into mdns_name_t structure + * + * @param packet MDNS packet + * @param start Starting point of FQDN + * @param name mdns_name_t structure to populate + * + * @return the address after the parsed FQDN in the packet or NULL on error + */ +static const uint8_t * _mdns_parse_fqdn(const uint8_t * packet, const uint8_t * start, mdns_name_t * name) +{ + name->parts = 0; + name->sub = 0; + name->host[0] = 0; + name->service[0] = 0; + name->proto[0] = 0; + name->domain[0] = 0; + + static char buf[MDNS_NAME_BUF_LEN]; + + const uint8_t * next_data = (uint8_t*)_mdns_read_fqdn(packet, start, name, buf); + if (!next_data || name->parts < 2) { + return 0; + } + if (name->parts == 3) { + memmove((uint8_t*)name + (MDNS_NAME_BUF_LEN), (uint8_t*)name, 3*(MDNS_NAME_BUF_LEN)); + name->host[0] = 0; + } else if (name->parts == 2) { + memmove((uint8_t*)(name->domain), (uint8_t*)(name->service), (MDNS_NAME_BUF_LEN)); + name->service[0] = 0; + name->proto[0] = 0; + } + if (strcmp(name->domain, MDNS_DEFAULT_DOMAIN) == 0 || strcmp(name->domain, "arpa") == 0) { + return next_data; + } + return 0; +} + +/* + * Packet construction + * */ + +/** + * @brief sets uint16_t value in a packet + * + * @param packet MDNS packet + * @param index offset of uint16_t value + * @param value the value to set + */ +static inline void _mdns_set_u16(uint8_t * packet, uint16_t index, uint16_t value) +{ + if ((index + 1) >= MDNS_MAX_PACKET_SIZE) { + return; + } + packet[index] = (value >> 8) & 0xFF; + packet[index+1] = value & 0xFF; +} + +/** + * @brief appends byte in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 1 on success + */ +static inline uint8_t _mdns_append_u8(uint8_t * packet, uint16_t * index, uint8_t value) +{ + if (*index >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + packet[*index] = value; + *index += 1; + return 1; +} + +/** + * @brief appends uint16_t in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 2 on success + */ +static inline uint8_t _mdns_append_u16(uint8_t * packet, uint16_t * index, uint16_t value) +{ + if ((*index + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, (value >> 8) & 0xFF); + _mdns_append_u8(packet, index, value & 0xFF); + return 2; +} + +/** + * @brief appends uint32_t in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 4 on success + */ +static inline uint8_t _mdns_append_u32(uint8_t * packet, uint16_t * index, uint32_t value) +{ + if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, (value >> 24) & 0xFF); + _mdns_append_u8(packet, index, (value >> 16) & 0xFF); + _mdns_append_u8(packet, index, (value >> 8) & 0xFF); + _mdns_append_u8(packet, index, value & 0xFF); + return 4; +} + +/** + * @brief appends answer type, class, ttl and data length to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param type answer type + * @param ttl answer ttl + * + * @return length of added data: 0 on error or 10 on success + */ +static inline uint8_t _mdns_append_type(uint8_t * packet, uint16_t * index, uint8_t type, uint32_t ttl) +{ + if ((*index + 10) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + if (type == MDNS_ANSWER_PTR) { + _mdns_append_u16(packet, index, MDNS_TYPE_PTR); + _mdns_append_u16(packet, index, MDNS_CLASS_IN); + } else if (type == MDNS_ANSWER_TXT) { + _mdns_append_u16(packet, index, MDNS_TYPE_TXT); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else if (type == MDNS_ANSWER_SRV) { + _mdns_append_u16(packet, index, MDNS_TYPE_SRV); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else if (type == MDNS_ANSWER_A) { + _mdns_append_u16(packet, index, MDNS_TYPE_A); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else if (type == MDNS_ANSWER_AAAA) { + _mdns_append_u16(packet, index, MDNS_TYPE_AAAA); + _mdns_append_u16(packet, index, MDNS_CLASS_IN_FLUSH_CACHE); + } else { + return 0; + } + _mdns_append_u32(packet, index, ttl); + _mdns_append_u16(packet, index, 0); + return 10; +} + +/** + * @brief appends single string to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param string the string to append + * + * @return length of added data: 0 on error or length of the string + 1 on success + */ +static inline uint8_t _mdns_append_string(uint8_t * packet, uint16_t * index, const char * string) +{ + uint8_t len = strlen(string); + if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, len); + memcpy(packet + *index, string, len); + *index += len; + return len + 1; +} + +/** + * @brief appends FQDN to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param strings string array containing the parts of the FQDN + * @param count number of strings in the array + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_fqdn(uint8_t * packet, uint16_t * index, const char * strings[], uint8_t count) +{ + if (!count) { + return _mdns_append_u8(packet, index, 0); + } + mdns_name_t name; + static char buf[MDNS_NAME_BUF_LEN]; + uint8_t len = strlen(strings[0]); + uint8_t * len_location = (uint8_t *)memchr(packet, (char)len, *index); + while(len_location) { + if (memcmp(len_location+1, strings[0], len)) { //not continuing with our string +search_next: + len_location = (uint8_t *)memchr(len_location+1, (char)len, *index - (len_location+1 - packet)); + continue; + } + //read string into name and compare + name.parts = 0; + name.sub = 0; + name.host[0] = 0; + name.service[0] = 0; + name.proto[0] = 0; + name.domain[0] = 0; + const uint8_t * content = _mdns_read_fqdn(packet, len_location, &name, buf); + if (!content) { + return 0; + } + if (name.parts == count) { + uint8_t i; + for(i=0; iinstance)?service->instance + :(server->instance)?server->instance + :server->hostname; + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str + 1, 3); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, MDNS_ANSWER_PTR_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + part_length = _mdns_append_fqdn(packet, index, str, 4); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} + +/** + * @brief appends TXT record for service to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server that is hosting the service + * @param service the service to add record for + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_txt_record(uint8_t * packet, uint16_t * index, mdns_server_t * server, mdns_service_t * service) +{ + const char * str[4]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = (service->instance)?service->instance + :(server->instance)?server->instance + :server->hostname; + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 4); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_TXT, MDNS_ANSWER_TXT_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + uint16_t data_len = 0; + if (service->txt_num_items) { + uint8_t len = service->txt_num_items; + const char ** txt = service->txt; + uint8_t i, l; + for(i=0; iinstance)?service->instance + :(server->instance)?server->instance + :server->hostname; + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 4); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_SRV, MDNS_ANSWER_SRV_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + part_length = 0; + part_length += _mdns_append_u16(packet, index, service->priority); + part_length += _mdns_append_u16(packet, index, service->weight); + part_length += _mdns_append_u16(packet, index, service->port); + if (part_length != 6) { + return 0; + } + + str[0] = server->hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + part_length = _mdns_append_fqdn(packet, index, str, 2); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length + 6); + + record_length += part_length + 6; + return record_length; +} + +/** + * @brief appends A record to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server + * @param ip the IP address to add + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_a_record(uint8_t * packet, uint16_t * index, mdns_server_t * server, uint32_t ip) +{ + const char * str[2]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = server->hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 2); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_A, MDNS_ANSWER_A_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, ip & 0xFF); + _mdns_append_u8(packet, index, (ip >> 8) & 0xFF); + _mdns_append_u8(packet, index, (ip >> 16) & 0xFF); + _mdns_append_u8(packet, index, (ip >> 24) & 0xFF); + _mdns_set_u16(packet, data_len_location, 4); + + record_length += 4; + return record_length; +} + +/** + * @brief appends AAAA record to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server + * @param ipv6 the IPv6 address to add + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_aaaa_record(uint8_t * packet, uint16_t * index, mdns_server_t * server, uint8_t * ipv6) +{ + const char * str[2]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = server->hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str, 2); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_AAAA, MDNS_ANSWER_AAAA_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + if ((*index + 15) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + + part_length = sizeof(ip6_addr_t); + memcpy(packet + *index, ipv6, part_length); + *index += part_length; + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} + +/** + * @brief sends all collected answers + * + * @param server the server + * @param answers linked list of answers + */ +static void _mdns_send_answers(mdns_server_t * server, mdns_answer_item_t * answers) +{ + bool send_ip = false; + static uint8_t packet[MDNS_MAX_PACKET_SIZE]; + uint16_t index = MDNS_HEAD_LEN; + uint8_t answer_count = 0; + + memset(packet, 0, MDNS_HEAD_LEN); + + _mdns_set_u16(packet, MDNS_HEAD_FLAGS_OFFSET, MDNS_FLAGS_AUTHORITATIVE); + + while(answers) { + if (answers->answer & MDNS_ANSWER_A) { + answers->answer &= ~MDNS_ANSWER_A; + send_ip = true; + } + if (answers->service) { + + if (answers->answer & MDNS_ANSWER_PTR) { + if (!_mdns_append_ptr_record(packet, &index, server, answers->service)) { + return; + } + answer_count += 1; + } + + if (answers->answer & MDNS_ANSWER_TXT) { + if (!_mdns_append_txt_record(packet, &index, server, answers->service)) { + return; + } + answer_count += 1; + } + + if (answers->answer & MDNS_ANSWER_SRV) { + if (!_mdns_append_srv_record(packet, &index, server, answers->service)) { + return; + } + answer_count += 1; + } + } + mdns_answer_item_t * a = answers; + answers = answers->next; + free(a); + } + if (send_ip) { + tcpip_adapter_ip_info_t if_ip_info; + tcpip_adapter_get_ip_info(server->tcpip_if, &if_ip_info); + + if (!_mdns_append_a_record(packet, &index, server, if_ip_info.ip.addr)) { + return; + } + answer_count += 1; + + //add ipv6 if available + struct ip6_addr if_ip6; + if (!tcpip_adapter_get_ip6_linklocal(server->tcpip_if, &if_ip6)) { + uint8_t * v6addr = (uint8_t*)if_ip6.addr; + //check if not 0 + int i; + for(i=0;ipriority = r->priority; + n->weight = r->weight; + n->port = r->port; + n->addr.addr = r->addr; + + size_t hlen = strlen(r->host); + if (hlen) { + n->host = strdup(r->host); + if (!n->host) { + free(n); + return; + } + } else { + n->host = NULL; + } + + size_t ilen = strlen(r->instance); + if (ilen) { + n->instance = strdup(r->instance); + if (!n->instance) { + free((char *)n->host); + free(n); + return; + } + } else { + n->instance = NULL; + } + + size_t tlen = strlen(r->txt); + if (tlen) { + n->txt = strdup(r->txt); + if (!n->txt) { + free((char *)n->host); + free((char *)n->instance); + free(n); + return; + } + } else { + n->txt = NULL; + } + + memcpy((uint8_t *)n->addrv6.addr, r->addrv6, sizeof(ip6_addr_t)); + + mdns_result_t * o = server->search.results; + server->search.results = n; + n->next = o; +} + +/** + * @brief finds service from given service type + * @param server the server + * @param service service type to match + * @param proto proto to match + * + * @return the service item if found or NULL on error + */ +static mdns_srv_item_t * _mdns_get_service_item(mdns_server_t * server, const char * service, const char * proto) +{ + mdns_srv_item_t * s = server->services; + while(s) { + if (!strcmp(s->service->service, service) && !strcmp(s->service->proto, proto)) { + return s; + } + s = s->next; + } + return NULL; +} + +/** + * @brief creates/allocates new service + * @param service service type + * @param proto service proto + * @param port service port + * + * @return pointer to the service or NULL on error + */ +static mdns_service_t * _mdns_create_service(const char * service, const char * proto, uint16_t port) +{ + mdns_service_t * s = (mdns_service_t *)malloc(sizeof(mdns_service_t)); + if (!s) { + return NULL; + } + + s->priority = 0; + s->weight = 0; + s->txt_num_items = 0; + s->instance = NULL; + s->txt = NULL; + s->port = port; + + s->service = strdup(service); + if (!s->service) { + free(s); + return NULL; + } + + s->proto = strdup(proto); + if (!s->proto) { + free((char *)s->service); + free(s); + return NULL; + } + + return s; +} + +/** + * @brief free service memory + * + * @param service the service + */ +static void _mdns_free_service(mdns_service_t * service) +{ + if (!service) { + return; + } + free((char *)service->instance); + free((char *)service->service); + free((char *)service->proto); + if (service->txt_num_items) { + uint8_t i; + for(i=0; itxt_num_items; i++) { + free((char *)service->txt[i]); + } + } + free(service->txt); + free(service); +} + +/** + * @brief read uint16_t from a packet + * @param packet the packet + * @param index index in the packet where the value starts + * + * @return the value + */ +static inline uint16_t _mdns_read_u16(const uint8_t * packet, uint16_t index) +{ + return (uint16_t)(packet[index]) << 8 | packet[index+1]; +} + +/** + * @brief main packet parser + * + * @param server the server + * @param data byte array holding the packet data + * @param len length of the byte array + */ +static void _mdns_parse_packet(mdns_server_t * server, const uint8_t * data, size_t len) +{ + static mdns_name_t n; + static mdns_result_temp_t a; + + const uint8_t * content = data + MDNS_HEAD_LEN; + mdns_name_t * name = &n; + memset(name, 0, sizeof(mdns_name_t)); + + uint16_t questions = _mdns_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET); + uint16_t answers = _mdns_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET); + uint16_t additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET); + + if (questions) { + uint8_t qs = questions; + mdns_answer_item_t * answers = NULL; + + while(qs--) { + content = _mdns_parse_fqdn(data, content, name); + if (!content) { + break;//error + } + + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + content = content + 4; + + if (!name->service[0] || !name->proto[0]) { + if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA || type == MDNS_TYPE_ANY) {//send A + AAAA + if (name->host[0] && server->hostname && server->hostname[0] && !strcmp(name->host, server->hostname)) { + answers = _mdns_add_answer(answers, NULL, MDNS_ANSWER_A); + } + } + continue; + } + + if (name->sub) { + continue; + } + + mdns_srv_item_t * si = _mdns_get_service_item(server, name->service, name->proto); + if (!si) { + //service not found + continue; + } + + if (type == MDNS_TYPE_PTR) { + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_ALL); + } else if (type == MDNS_TYPE_TXT) { + //match instance/host + const char * host = (si->service->instance)?si->service->instance + :(server->instance)?server->instance + :server->hostname; + if (!host || !host[0] || !name->host[0] || strcmp(name->host, host)) { + continue; + } + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_TXT); + } else if (type == MDNS_TYPE_SRV) { + //match instance/host + const char * host = (si->service->instance)?si->service->instance + :(server->instance)?server->instance + :server->hostname; + if (!host || !host[0] || !name->host[0] || strcmp(name->host, host)) { + continue; + } + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_SRV | MDNS_ANSWER_A); + } else if (type == MDNS_TYPE_ANY) {//send all + //match host + if (!name->host[0] || !server->hostname || !server->hostname[0] || strcmp(name->host, server->hostname)) { + answers = _mdns_add_answer(answers, si->service, MDNS_ANSWER_ALL); + } + } + } + if (answers) { + _mdns_send_answers(server, answers); + } + } + + if (server->search.running && (answers || additional)) { + mdns_result_temp_t * answer = &a; + memset(answer, 0, sizeof(mdns_result_temp_t)); + + while(content < (data + len)) { + content = _mdns_parse_fqdn(data, content, name); + if (!content) { + break;//error + } + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + uint16_t data_len = _mdns_read_u16(content, MDNS_LEN_OFFSET); + const uint8_t * data_ptr = content + MDNS_DATA_OFFSET; + + content = data_ptr + data_len; + + if (type == MDNS_TYPE_PTR) { + if (!_mdns_parse_fqdn(data, data_ptr, name)) { + continue;//error + } + if (server->search.host[0] || + (strcmp(name->service, server->search.service) != 0) || + (strcmp(name->proto, server->search.proto) != 0)) { + continue;//not searching for service or wrong service/proto + } + sprintf(answer->instance, "%s", name->host); + } else if (type == MDNS_TYPE_SRV) { + if (server->search.host[0] || + (strcmp(name->service, server->search.service) != 0) || + (strcmp(name->proto, server->search.proto) != 0)) { + continue;//not searching for service or wrong service/proto + } + if (answer->instance[0]) { + if (strcmp(answer->instance, name->host) != 0) { + continue;//instance name is not the same as the one in the PTR record + } + } else { + sprintf(answer->instance, "%s", name->host); + } + //parse record value + if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name)) { + continue;//error + } + + answer->ptr = 1; + answer->priority = _mdns_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET); + answer->weight = _mdns_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET); + answer->port = _mdns_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET); + if (answer->host[0]) { + if (strcmp(answer->host, name->host) != 0) { + answer->addr = 0; + sprintf(answer->host, "%s", name->host); + } + } else { + sprintf(answer->host, "%s", name->host); + } + } else if (type == MDNS_TYPE_TXT) { + uint16_t i=0,b=0, y; + while(i < data_len) { + uint8_t partLen = data_ptr[i++]; + for(y=0; ytxt[b++] = d; + } + if (itxt[b++] = '&'; + } + } + answer->txt[b] = 0; + } else if (type == MDNS_TYPE_AAAA) { + if (server->search.host[0]) { + if (strcmp(name->host, server->search.host) != 0) { + continue;//wrong host + } + } else if (!answer->ptr) { + sprintf(answer->host, "%s", name->host); + } else if (strcmp(answer->host, name->host) != 0) { + continue;//wrong host + } + memcpy(answer->addrv6, data_ptr, sizeof(ip6_addr_t)); + } else if (type == MDNS_TYPE_A) { + if (server->search.host[0]) { + if (strcmp(name->host, server->search.host) != 0) { + continue;//wrong host + } + } else if (!answer->ptr) { + sprintf(answer->host, "%s", name->host); + } else if (strcmp(answer->host, name->host) != 0) { + continue;//wrong host + } + if (server->search.running && answer->addr) { + _mdns_add_result(server, answer);//another IP for our host + } + IP4_ADDR(answer, data_ptr[0], data_ptr[1], data_ptr[2], data_ptr[3]); + } + } + if (server->search.running && (server->search.host[0] || answer->ptr) && answer->addr) { + _mdns_add_result(server, answer); + } + //end while + } +} + + + +/* + * Public Methods + * */ +esp_err_t mdns_init(tcpip_adapter_if_t tcpip_if, mdns_server_t ** mdns_server) +{ + esp_err_t err = ESP_OK; + + if (tcpip_if >= TCPIP_ADAPTER_IF_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (_mdns_server_get(tcpip_if)) { + return ESP_ERR_INVALID_STATE; + } + + uint8_t mode; + err = esp_wifi_get_mode((wifi_mode_t*)&mode); + if (err) { + return err; + } + + if ((tcpip_if == TCPIP_ADAPTER_IF_STA && !(mode & WIFI_MODE_STA)) + || (tcpip_if == TCPIP_ADAPTER_IF_AP && !(mode & WIFI_MODE_AP))) { + return ESP_ERR_INVALID_ARG; + } + + mdns_server_t * server = (mdns_server_t *)malloc(sizeof(mdns_server_t)); + if (!server) { + return ESP_ERR_NO_MEM; + } + + server->tcpip_if = tcpip_if; + server->hostname = NULL; + server->instance = NULL; + server->services = NULL; + server->search.host[0] = 0; + server->search.service[0] = 0; + server->search.proto[0] = 0; + server->search.running = false; + server->search.results = NULL; + server->pcb = NULL; + + server->lock = xSemaphoreCreateMutex(); + if (!server->lock) { + free(server); + return ESP_ERR_NO_MEM; + } + + server->search.lock = xSemaphoreCreateMutex(); + if (!server->search.lock) { + vSemaphoreDelete(server->lock); + free(server); + return ESP_ERR_NO_MEM; + } + + server->queue = xQueueCreate(MDNS_PACKET_QUEUE_LEN, sizeof(struct pbuf *)); + if (!server->queue) { + vSemaphoreDelete(server->lock); + vSemaphoreDelete(server->search.lock); + free(server); + return ESP_ERR_NO_MEM; + } + + if (_mdns_server_add(server)) { + //service start failed! + vSemaphoreDelete(server->lock); + vSemaphoreDelete(server->search.lock); + vQueueDelete(server->queue); + free(server); + return ESP_FAIL; + } + + const char * hostname = NULL; + tcpip_adapter_get_hostname(server->tcpip_if, &hostname); + mdns_set_hostname(server, hostname); + + *mdns_server = server; + + return ESP_OK; +} + +void mdns_free(mdns_server_t * server) +{ + if (!server) { + return; + } + _mdns_server_remove(server); + mdns_service_remove_all(server); + MDNS_MUTEX_LOCK(); + free((char*)server->hostname); + free((char*)server->instance); + if (server->queue) { + struct pbuf * c; + while(xQueueReceive(server->queue, &c, 0) == pdTRUE) { + pbuf_free(c); + } + vQueueDelete(server->queue); + } + if (server->search.running) { + mdns_query_end(server); + } + mdns_result_free(server); + vSemaphoreDelete(server->search.lock); + MDNS_MUTEX_UNLOCK(); + vSemaphoreDelete(server->lock); + free(server); +} + +esp_err_t mdns_set_hostname(mdns_server_t * server, const char * hostname) +{ + if (!server) { + return ESP_ERR_INVALID_ARG; + } + MDNS_MUTEX_LOCK(); + free((char*)server->hostname); + server->hostname = (char *)malloc(strlen(hostname)+1); + if (!server->hostname) { + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; + } + sprintf((char *)server->hostname, "%s", hostname); + MDNS_MUTEX_UNLOCK(); + return ERR_OK; +} + +esp_err_t mdns_set_instance(mdns_server_t * server, const char * instance) +{ + if (!server) { + return ESP_ERR_INVALID_ARG; + } + MDNS_MUTEX_LOCK(); + free((char*)server->instance); + server->instance = (char *)malloc(strlen(instance)+1); + if (!server->instance) { + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; + } + sprintf((char *)server->instance, "%s", instance); + MDNS_MUTEX_UNLOCK(); + return ERR_OK; +} + +/* + * MDNS SERVICES + * */ + +esp_err_t mdns_service_add(mdns_server_t * server, const char * service, const char * proto, uint16_t port) +{ + if (!server || !service || !proto || !port) { + //bad argument + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * item = _mdns_get_service_item(server, service, proto); + if (item) { + //we already have that service + return mdns_service_port_set(server, service, proto, port); + } + + mdns_service_t * s = _mdns_create_service(service, proto, port); + if (!s) { + return ESP_ERR_NO_MEM; + } + + item = (mdns_srv_item_t *)malloc(sizeof(mdns_srv_item_t)); + if (!item) { + return ESP_ERR_NO_MEM; + } + + item->service = s; + item->next = server->services; + server->services = item; + return ESP_OK; +} + +esp_err_t mdns_service_port_set(mdns_server_t * server, const char * service, const char * proto, uint16_t port) +{ + if (!server || !server->services || !service || !proto || !port) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + MDNS_MUTEX_LOCK(); + s->service->port = port; + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +} + +esp_err_t mdns_service_txt_set(mdns_server_t * server, const char * service, const char * proto, uint8_t num_items, const char ** txt) +{ + if (!server || !server->services || !service || !proto) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + MDNS_MUTEX_LOCK(); + if (s->service->txt_num_items) { + uint8_t i; + for(i=0; iservice->txt_num_items; i++) { + free((char *)s->service->txt[i]); + } + } + free(s->service->txt); + if (num_items) { + s->service->txt = (const char **)malloc(sizeof(char *) * num_items); + if (!s->service->txt) { + s->service->txt_num_items = 0; + goto fail; + } + uint8_t i; + s->service->txt_num_items = num_items; + for(i=0; iservice->txt[i] = strdup(txt[i]); + if (!s->service->txt[i]) { + s->service->txt_num_items = i; + goto fail; + } + } + } + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +fail: + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; +} + +esp_err_t mdns_service_instance_set(mdns_server_t * server, const char * service, const char * proto, const char * instance) +{ + if (!server || !server->services || !service || !proto) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + MDNS_MUTEX_LOCK(); + free((char*)s->service->instance); + s->service->instance = strdup(instance); + if (!s->service->instance) { + MDNS_MUTEX_UNLOCK(); + return ESP_ERR_NO_MEM; + } + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +} + +esp_err_t mdns_service_remove(mdns_server_t * server, const char * service, const char * proto) +{ + if (!server || !server->services || !service || !proto) { + return ESP_ERR_INVALID_ARG; + } + mdns_srv_item_t * s = _mdns_get_service_item(server, service, proto); + if (!s) { + return ESP_ERR_NOT_FOUND; + } + //first item + if (server->services == s) { + MDNS_MUTEX_LOCK(); + server->services = server->services->next; + MDNS_MUTEX_UNLOCK(); + _mdns_free_service(s->service); + free(s); + return ESP_OK; + } + //not first item + mdns_srv_item_t * a = server->services; + while(a->next && a->next != s) { + a = a->next; + } + //next item of the current item is our item + if (a->next == s) { + MDNS_MUTEX_LOCK(); + a->next = s->next; + MDNS_MUTEX_UNLOCK(); + _mdns_free_service(s->service); + free(s); + return ESP_OK; + } + //how did we end here? + return ESP_FAIL; +} + +esp_err_t mdns_service_remove_all(mdns_server_t * server) +{ + if (!server) { + return ESP_ERR_INVALID_ARG; + } + if (!server->services) { + return ESP_OK; + } + MDNS_MUTEX_LOCK(); + mdns_srv_item_t * a = server->services; + server->services = NULL; + while(a) { + mdns_srv_item_t * s = a; + a = a->next; + _mdns_free_service(s->service); + free(s); + } + MDNS_MUTEX_UNLOCK(); + return ESP_OK; +} + +/* + * MDNS QUERY + * */ + +uint32_t mdns_query(mdns_server_t * server, const char * service, const char * proto, uint32_t timeout) +{ + if (!server || !service) { + return 0; + } + MDNS_SEARCH_LOCK(); + uint16_t qtype = MDNS_TYPE_PTR; + mdns_result_free(server); + if (proto) { + server->search.host[0] = 0; + snprintf(server->search.service, MDNS_NAME_MAX_LEN, "%s", service); + snprintf(server->search.proto, MDNS_NAME_MAX_LEN, "%s", proto); + } else { + snprintf(server->search.host, MDNS_NAME_MAX_LEN, "%s", service); + server->search.service[0] = 0; + server->search.proto[0] = 0; + qtype = MDNS_TYPE_A; + } + + uint8_t hostname_len = strlen(server->search.host); + uint8_t service_type_len = strlen(server->search.service); + uint8_t protoname_len = strlen(server->search.proto); + + size_t len = 23; //head+type+class+(strlen(local)+1)+fqdn_end + if (hostname_len) { + len += hostname_len + 1; + } else if (service_type_len) { + len += service_type_len + protoname_len + 2; + } + + uint8_t * packet = (uint8_t *)malloc(len); + if (!packet) { + return 0; + } + memset(packet, 0, len); + _mdns_set_u16(packet, MDNS_HEAD_QUESTIONS_OFFSET, 1); + uint16_t index = MDNS_HEAD_LEN; + + if (hostname_len) { + _mdns_append_string(packet, &index, server->search.host); + } else if (service_type_len) { + _mdns_append_string(packet, &index, server->search.service); + _mdns_append_string(packet, &index, server->search.proto); + } + + _mdns_append_string(packet, &index, MDNS_DEFAULT_DOMAIN); + _mdns_append_u8(packet, &index, 0); //fqdn_end + + _mdns_append_u16(packet, &index, qtype); + _mdns_append_u16(packet, &index, MDNS_CLASS_IN); + + MDNS_MUTEX_LOCK(); + size_t written = _mdns_server_write(server, packet, index); + MDNS_MUTEX_UNLOCK(); + free(packet); + if (written != index) { + return 0; + } + + server->search.running = true; + if (timeout) { + uint32_t startAt = xTaskGetTickCount() * portTICK_PERIOD_MS; + while(server->search.running && ((xTaskGetTickCount() * portTICK_PERIOD_MS) - startAt) < timeout) { + vTaskDelay(1 / portTICK_PERIOD_MS); + } + server->search.running = false; + MDNS_SEARCH_UNLOCK(); + return mdns_result_get_count(server); + } + return 0; +} + +size_t mdns_query_end(mdns_server_t * server) +{ + if (!server || !server->search.running) { + return 0; + } + server->search.running = false; + MDNS_SEARCH_UNLOCK(); + return mdns_result_get_count(server); +} + +esp_err_t mdns_result_free(mdns_server_t * server) +{ + if (!server || server->search.running || !server->search.results) { + return ESP_ERR_INVALID_ARG; + } + while(server->search.results) { + const mdns_result_t * r = server->search.results; + server->search.results = (mdns_result_t *)r->next; + free((char *)r->host); + free((char *)r->instance); + free((char *)r->txt); + free((mdns_result_t *)r); + } + server->search.results = NULL; + return ESP_OK; +} + +size_t mdns_result_get_count(mdns_server_t * server) +{ + if (!server || !server->search.results) { + return 0; + } + size_t len = 0; + const mdns_result_t * r = server->search.results; + while(r) { + len++; + r = r->next; + } + return len; +} + +const mdns_result_t * mdns_result_get(mdns_server_t * server, size_t num) +{ + if (!server || !server->search.results) { + return NULL; + } + size_t len = 0; + const mdns_result_t * r = server->search.results; + while(r) { + if (len++ == num) { + return r; + } + r = r->next; + } + return NULL; +} diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 07bdc12f913..4f3b49ed270 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -58,6 +58,17 @@ extern "C" { #define IPSTR "%d.%d.%d.%d" +#define IPV62STR(ipaddr) IP6_ADDR_BLOCK1(&(ipaddr)), \ + IP6_ADDR_BLOCK2(&(ipaddr)), \ + IP6_ADDR_BLOCK3(&(ipaddr)), \ + IP6_ADDR_BLOCK4(&(ipaddr)), \ + IP6_ADDR_BLOCK5(&(ipaddr)), \ + IP6_ADDR_BLOCK6(&(ipaddr)), \ + IP6_ADDR_BLOCK7(&(ipaddr)), \ + IP6_ADDR_BLOCK8(&(ipaddr)) + +#define IPV6STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" + typedef struct { ip4_addr_t ip; ip4_addr_t netmask; diff --git a/docs/Doxyfile b/docs/Doxyfile index a3fc5faa124..aa6c87476e6 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -36,7 +36,8 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/sdmmc/include/sdmmc_cmd.h \ ../components/fatfs/src/esp_vfs_fat.h \ ../components/fatfs/src/diskio.h \ - ../components/esp32/include/esp_core_dump.h + ../components/esp32/include/esp_core_dump.h \ + ../components/mdns/include/mdns.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/api/mdns.rst b/docs/api/mdns.rst new file mode 100644 index 00000000000..95789444f58 --- /dev/null +++ b/docs/api/mdns.rst @@ -0,0 +1,215 @@ +mDNS Service +============ + +Overview +-------- + +mDNS is a multicast UDP service that is used to provide local network service and host discovery. + +mDNS is installed by default on most operating systems or is available as separate package. On ``Mac OS`` it is installed by default and is called ``Bonjour``. Apple releases an installer for ``Windows`` that can be found `on Apple's support page `_. On ``Linux``, mDNS is provided by `avahi `_ and is usually installed by default. + +mDNS Properties +^^^^^^^^^^^^^^^ + + * ``hostname``: the hostname that the device will respond to. If not set, the ``hostname`` will be read from the interface. Example: ``my-esp32`` will resolve to ``my-esp32.local`` + * ``default_instance``: friendly name for your device, like ``Jhon's ESP32 Thing``. If not set, ``hostname`` will be used. + +Example method to start mDNS for the STA interface and set ``hostname`` and ``default_instance``: + + :: + + mdns_server_t * mdns = NULL; + + void start_mdns_service() + { + //initialize mDNS service on STA interface + esp_err_t err = mdns_init(TCPIP_ADAPTER_IF_STA, &mdns); + if (err) { + printf("MDNS Init failed: %d\n", err); + return; + } + + //set hostname + mdns_set_hostname(mdns, "my-esp32"); + //set default instance + mdns_set_instance(mdns, "Jhon's ESP32 Thing"); + } + +mDNS Services +^^^^^^^^^^^^^ + +mDNS can advertise information about network services that your device offers. Each service is defined by a few properties. + + * ``service``: (required) service type, prepended with underscore. Some common types can be found `here `_. + * ``proto``: (required) protocol that the service runs on, prepended with underscore. Example: ``_tcp`` or ``_udp`` + * ``port``: (required) network port that the service runs on + * ``instance``: friendly name for your service, like ``Jhon's ESP32 Web Server``. If not defined, ``default_instance`` will be used. + * ``txt``: ``var=val`` array of strings, used to define properties for your service + +Example method to add a few services and different properties: + + :: + + void add_mdns_services() + { + //add our services + mdns_service_add(mdns, "_http", "_tcp", 80); + mdns_service_add(mdns, "_arduino", "_tcp", 3232); + mdns_service_add(mdns, "_myservice", "_udp", 1234); + + //NOTE: services must be added before their properties can be set + //use custom instance for the web server + mdns_service_instance_set(mdns, "_http", "_tcp", "Jhon's ESP32 Web Server"); + + const char * arduTxtData[4] = { + "board=esp32", + "tcp_check=no", + "ssh_upload=no", + "auth_upload=no" + }; + //set txt data for service (will free and replace current data) + mdns_service_txt_set(mdns, "_arduino", "_tcp", 4, arduTxtData); + + //change service port + mdns_service_port_set(mdns, "_myservice", "_udp", 4321); + } + +mDNS Query +^^^^^^^^^^ + +mDNS provides methods for browsing for services and resolving host's IP/IPv6 addresses. + Results are returned as a linked list of ``mdns_result_t`` objects. If the result is from host query, + it will contain only ``addr`` and ``addrv6`` if found. Service queries will populate all fields + in a result that were found. + +Example method to resolve host IPs: + + :: + + void resolve_mdns_host(const char * hostname) + { + printf("mDNS Host Lookup: %s.local\n", hostname); + //run search for 1000 ms + if (mdns_query(mdns, hostname, NULL, 1000)) { + //results were found + const mdns_result_t * results = mdns_result_get(mdns, 0); + //itterate through all results + size_t i = 1; + while(results) { + //print result information + printf(" %u: IP:" IPSTR ", IPv6:" IPV6STR "\n", i++ + IP2STR(&results->addr), IPV62STR(results->addrv6)); + //load next result. Will be NULL if this was the last one + results = results->next; + } + //free the results from memory + mdns_result_free(mdns); + } else { + //host was not found + printf(" Host Not Found\n"); + } + } + +Example method to resolve local services: + + :: + + void find_mdns_service(const char * service, const char * proto) + { + printf("mDNS Service Lookup: %s.%s\n", service, proto); + //run search for 1000 ms + if (mdns_query(mdns, service, proto, 1000)) { + //results were found + const mdns_result_t * results = mdns_result_get(mdns, 0); + //itterate through all results + size_t i = 1; + while(results) { + //print result information + printf(" %u: hostname:%s, instance:\"%s\", IP:" IPSTR ", IPv6:" IPV6STR ", port:%u, txt:%s\n", i++, + (results->host)?results->host:"NULL", (results->instance)?results->instance:"NULL", + IP2STR(&results->addr), IPV62STR(results->addrv6), + results->port, (results->txt)?results->txt:"\r"); + //load next result. Will be NULL if this was the last one + results = results->next; + } + //free the results from memory + mdns_result_free(mdns); + } else { + //service was not found + printf(" Service Not Found\n"); + } + } + +Example of using the methods above: + + :: + + void my_app_some_method(){ + //search for esp32-mdns.local + resolve_mdns_host("esp32-mdns"); + + //search for HTTP servers + find_mdns_service("_http", "_tcp"); + //or file servers + find_mdns_service("_smb", "_tcp"); //windows sharing + find_mdns_service("_afpovertcp", "_tcp"); //apple sharing + find_mdns_service("_nfs", "_tcp"); //NFS server + find_mdns_service("_ftp", "_tcp"); //FTP server + //or networked printer + find_mdns_service("_printer", "_tcp"); + find_mdns_service("_ipp", "_tcp"); + } + +Application Example +------------------- + +mDNS server/scanner example: `examples/30_mdns_example `_. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `components/mdns/include/mdns.h `_ + +Macros +^^^^^^ + + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: mdns_server_t +.. doxygentypedef:: mdns_result_t + +Enumerations +^^^^^^^^^^^^ + + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: mdns_result_s + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: mdns_init +.. doxygenfunction:: mdns_free +.. doxygenfunction:: mdns_set_hostname +.. doxygenfunction:: mdns_set_instance +.. doxygenfunction:: mdns_service_add +.. doxygenfunction:: mdns_service_remove +.. doxygenfunction:: mdns_service_instance_set +.. doxygenfunction:: mdns_service_txt_set +.. doxygenfunction:: mdns_service_port_set +.. doxygenfunction:: mdns_service_remove_all +.. doxygenfunction:: mdns_query +.. doxygenfunction:: mdns_query_end +.. doxygenfunction:: mdns_result_get_count +.. doxygenfunction:: mdns_result_get +.. doxygenfunction:: mdns_result_free + diff --git a/docs/index.rst b/docs/index.rst index b994648848b..bcc3c923a24 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -124,6 +124,7 @@ Contents: Deep Sleep deep-sleep-stub + mDNS Template .. toctree:: diff --git a/examples/30_mdns_example/Makefile b/examples/30_mdns_example/Makefile new file mode 100644 index 00000000000..0353c51c0eb --- /dev/null +++ b/examples/30_mdns_example/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := mdns-test + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/30_mdns_example/README.md b/examples/30_mdns_example/README.md new file mode 100644 index 00000000000..1c298604b88 --- /dev/null +++ b/examples/30_mdns_example/README.md @@ -0,0 +1,5 @@ +# 30_mdns example + +Shows how to use mDNS to advertise lookup services and hosts + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/30_mdns_example/main/Kconfig.projbuild b/examples/30_mdns_example/main/Kconfig.projbuild new file mode 100644 index 00000000000..3122e0309c1 --- /dev/null +++ b/examples/30_mdns_example/main/Kconfig.projbuild @@ -0,0 +1,29 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + + Can be left blank if the network has no security set. + +config MDNS_HOSTNAME + string "mDNS Hostname" + default "esp32-mdns" + help + mDNS Hostname for example to use + +config MDNS_INSTANCE + string "mDNS Instance Name" + default "ESP32 with mDNS" + help + mDNS Instance Name for example to use + +endmenu \ No newline at end of file diff --git a/examples/30_mdns_example/main/component.mk b/examples/30_mdns_example/main/component.mk new file mode 100644 index 00000000000..a98f634eae0 --- /dev/null +++ b/examples/30_mdns_example/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/30_mdns_example/main/mdns_example_main.c b/examples/30_mdns_example/main/mdns_example_main.c new file mode 100644 index 00000000000..d19fd1ae68a --- /dev/null +++ b/examples/30_mdns_example/main/mdns_example_main.c @@ -0,0 +1,184 @@ +/* MDNS-SD Query and advertise Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "mdns.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define EXAMPLE_MDNS_HOSTNAME CONFIG_MDNS_HOSTNAME +#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +static const char *TAG = "mdns-test"; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_CONNECTED: + /* enable ipv6 */ + tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static void query_mdns_service(mdns_server_t * mdns, const char * service, const char * proto) +{ + if(!mdns) { + return; + } + uint32_t res; + if (!proto) { + ESP_LOGI(TAG, "Host Lookup: %s", service); + res = mdns_query(mdns, service, 0, 1000); + if (res) { + size_t i; + for(i=0; iaddr), IPV62STR(r->addrv6)); + } + } + mdns_result_free(mdns); + } else { + ESP_LOGI(TAG, " Not Found"); + } + } else { + ESP_LOGI(TAG, "Service Lookup: %s.%s ", service, proto); + res = mdns_query(mdns, service, proto, 1000); + if (res) { + size_t i; + for(i=0; ihost)?r->host:"", (r->instance)?r->instance:"", + IP2STR(&r->addr), IPV62STR(r->addrv6), + r->port, (r->txt)?r->txt:""); + } + } + mdns_result_free(mdns); + } + } +} + +static void mdns_task(void *pvParameters) +{ + mdns_server_t * mdns = NULL; + while(1) { + /* Wait for the callback to set the CONNECTED_BIT in the + event group. + */ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connected to AP"); + + if (!mdns) { + esp_err_t err = mdns_init(TCPIP_ADAPTER_IF_STA, &mdns); + if (err) { + ESP_LOGE(TAG, "Failed starting MDNS: %u", err); + continue; + } + + ESP_ERROR_CHECK( mdns_set_hostname(mdns, EXAMPLE_MDNS_HOSTNAME) ); + ESP_ERROR_CHECK( mdns_set_instance(mdns, EXAMPLE_MDNS_INSTANCE) ); + + const char * arduTxtData[4] = { + "board=esp32", + "tcp_check=no", + "ssh_upload=no", + "auth_upload=no" + }; + + ESP_ERROR_CHECK( mdns_service_add(mdns, "_arduino", "_tcp", 3232) ); + ESP_ERROR_CHECK( mdns_service_txt_set(mdns, "_arduino", "_tcp", 4, arduTxtData) ); + ESP_ERROR_CHECK( mdns_service_add(mdns, "_http", "_tcp", 80) ); + ESP_ERROR_CHECK( mdns_service_instance_set(mdns, "_http", "_tcp", "ESP32 WebServer") ); + ESP_ERROR_CHECK( mdns_service_add(mdns, "_smb", "_tcp", 445) ); + } else { + query_mdns_service(mdns, "esp32", NULL); + query_mdns_service(mdns, "_arduino", "_tcp"); + query_mdns_service(mdns, "_http", "_tcp"); + query_mdns_service(mdns, "_printer", "_tcp"); + query_mdns_service(mdns, "_ipp", "_tcp"); + query_mdns_service(mdns, "_afpovertcp", "_tcp"); + query_mdns_service(mdns, "_smb", "_tcp"); + query_mdns_service(mdns, "_ftp", "_tcp"); + query_mdns_service(mdns, "_nfs", "_tcp"); + } + + ESP_LOGI(TAG, "Restarting in 10 seconds!"); + vTaskDelay(10000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "Starting again!"); + } +} + +void app_main() +{ + nvs_flash_init(); + initialise_wifi(); + xTaskCreate(&mdns_task, "mdns_task", 2048, NULL, 5, NULL); +} From 08f11e4c537191796166e07a882f9d9c577a4c84 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Sun, 15 Jan 2017 16:49:18 +0800 Subject: [PATCH 156/167] Fix panic register dump format --- components/esp32/panic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 3db09370ac3..22a515a3523 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -352,8 +352,8 @@ static void commonErrorHandler(XtExcFrame *frame) panicPutHex(regs[x + y + 1]); panicPutStr(" "); } - panicPutStr("\r\n"); } + panicPutStr("\r\n"); } } From a8b75ed974c0c2b4d8bd5b61bb3dcb4ba15e5642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lourens=20Naud=C3=A9?= Date: Sat, 31 Dec 2016 17:54:40 +0000 Subject: [PATCH 157/167] log: fix esp_log_level_set function name in docs Fix documentation and comments to reflect the new esp_log_level_set API (and not deprecated esp_log_set_level) --- components/log/README.rst | 12 ++++++------ components/log/log.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/log/README.rst b/components/log/README.rst index d378179c8f6..002dadf812a 100644 --- a/components/log/README.rst +++ b/components/log/README.rst @@ -4,11 +4,11 @@ Logging library Overview -------- -Log library has two ways of managing log verbosity: compile time, set via menuconfig; and runtime, using ``esp_log_set_level`` function. +Log library has two ways of managing log verbosity: compile time, set via menuconfig; and runtime, using ``esp_log_level_set`` function. At compile time, filtering is done using ``CONFIG_LOG_DEFAULT_LEVEL`` macro, set via menuconfig. All logging statments for levels higher than ``CONFIG_LOG_DEFAULT_LEVEL`` will be removed by the preprocessor. -At run time, all logs below ``CONFIG_LOG_DEFAULT_LEVEL`` are enabled by default. ``esp_log_set_level`` function may be used to set logging level per module. Modules are identified by their tags, which are human-readable ASCII zero-terminated strings. +At run time, all logs below ``CONFIG_LOG_DEFAULT_LEVEL`` are enabled by default. ``esp_log_level_set`` function may be used to set logging level per module. Modules are identified by their tags, which are human-readable ASCII zero-terminated strings. How to use this library ----------------------- @@ -51,11 +51,11 @@ At component scope, define it in component makefile: CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG -To configure logging output per module at runtime, add calls to ``esp_log_set_level`` function: +To configure logging output per module at runtime, add calls to ``esp_log_level_set`` function: .. code-block:: c - esp_log_set_level("*", ESP_LOG_ERROR); // set all components to ERROR level - esp_log_set_level("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack - esp_log_set_level("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client + esp_log_level_set("*", ESP_LOG_ERROR); // set all components to ERROR level + esp_log_level_set("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack + esp_log_level_set("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client diff --git a/components/log/log.c b/components/log/log.c index 9670b82dfde..3826e173ba2 100644 --- a/components/log/log.c +++ b/components/log/log.c @@ -15,7 +15,7 @@ /* * Log library — implementation notes. * - * Log library stores all tags provided to esp_log_set_level as a linked + * Log library stores all tags provided to esp_log_level_set as a linked * list. See uncached_tag_entry_t structure. * * To avoid looking up log level for given tag each time message is From d8fb30c930ba10ed7054d5f8669a230d95053245 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Sat, 7 Jan 2017 13:14:40 +0100 Subject: [PATCH 158/167] i2c: fix i2c_get_period name rename i2s_get_period to i2c_get_period --- components/driver/i2c.c | 2 +- components/driver/include/driver/i2c.h | 2 +- docs/api/i2c.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index 311f01516c9..39eb877349f 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -494,7 +494,7 @@ esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period) return ESP_OK; } -esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period) +esp_err_t i2c_get_period(i2c_port_t i2c_num, int* high_period, int* low_period) { I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); diff --git a/components/driver/include/driver/i2c.h b/components/driver/include/driver/i2c.h index 69b80b1e670..960ec61d276 100644 --- a/components/driver/include/driver/i2c.h +++ b/components/driver/include/driver/i2c.h @@ -401,7 +401,7 @@ esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period); * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period); +esp_err_t i2c_get_period(i2c_port_t i2c_num, int* high_period, int* low_period); /** * @brief set I2C master start signal timing diff --git a/docs/api/i2c.rst b/docs/api/i2c.rst index 1186e0583bf..2f89681f1d6 100644 --- a/docs/api/i2c.rst +++ b/docs/api/i2c.rst @@ -68,7 +68,7 @@ Functions .. doxygenfunction:: i2c_slave_write_buffer .. doxygenfunction:: i2c_slave_read .. doxygenfunction:: i2c_set_period -.. doxygenfunction:: i2s_get_period +.. doxygenfunction:: i2c_get_period .. doxygenfunction:: i2c_set_start_timing .. doxygenfunction:: i2c_get_start_timing .. doxygenfunction:: i2c_set_stop_timing From e5feeb195f1588ffc26a5235f21cfaaa14863385 Mon Sep 17 00:00:00 2001 From: "rudi ;-)" Date: Wed, 11 Jan 2017 21:47:38 +0100 Subject: [PATCH 159/167] sdmmc: change idx num of example --- docs/api/sdmmc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/sdmmc.rst b/docs/api/sdmmc.rst index 126be86576d..e751852f954 100644 --- a/docs/api/sdmmc.rst +++ b/docs/api/sdmmc.rst @@ -15,7 +15,7 @@ Protocol layer works with the host via ``sdmmc_host_t`` structure. This structur Application Example ------------------- -An example which combines SDMMC driver with FATFS library is provided in ``examples/24_sd_card`` directory. This example initializes the card, writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. +An example which combines SDMMC driver with FATFS library is provided in ``examples/27_sd_card`` directory. This example initializes the card, writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. Protocol layer APIs From a2ace698edc44271d8cdcb26e9aee8d957ecda4d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 16 Jan 2017 01:19:14 +0800 Subject: [PATCH 160/167] sdmmc: fix explanation of flash voltage regulator configuration efuses - low level on GPIO12 corresponds to 3.3V output, not 1.8V - add espefuse.py commands to burn efuses - add XPD_SDIO_REG to the list of efuses which must be set Reported on the forum: http://esp32.com/viewtopic.php?f=2&t=849&start=10#p4170 --- examples/27_sd_card/README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/examples/27_sd_card/README.md b/examples/27_sd_card/README.md index 4053ad26092..bfc6a39fe97 100644 --- a/examples/27_sd_card/README.md +++ b/examples/27_sd_card/README.md @@ -40,9 +40,20 @@ GPIO12 is used as a bootstrapping pin to select output voltage of an internal re - For boards which don't use the internal regulator, GPIO12 can be pulled high. - On boards which use the internal regulator and a 3.3V flash chip, GPIO12 should be pulled up high, which is compatible with SD card operation. -- For boards which use 1.8V flash chip, GPIO12 needs to be low at reset. +- For boards which use 3.3V flash chip, GPIO12 needs to be low at reset. * In this case, internal pullup can be enabled using a `gpio_pullup_en(GPIO_NUM_12);` call. Most SD cards work fine when an internal pullup on GPIO12 line is enabled. Note that if ESP32 experiences a power-on reset while the SD card is sending data, high level on GPIO12 can be latched into the bootstrapping register, and ESP32 will enter a boot loop until external reset with correct GPIO12 level is applied. - * Another option is to program flash voltage selection efuses: set `SDIO_TIEH=0` and `SDIO_FORCE=1`. This will permanently select 1.8v output voltage for the internal regulator, and GPIO12 will not be used as a bootstrapping pin anymore. Then it is safe to connect a pullup resistor to GPIO12. This option is suggested for production use. + * Another option is to program flash voltage selection efuses: set `XPD_SDIO_TIEH=1`, `XPD_SDIO_FORCE=1`, and `XPD_SDIO_REG = 1`. This will permanently select 3.3V output voltage for the internal regulator, and GPIO12 will not be used as a bootstrapping pin anymore. Then it is safe to connect a pullup resistor to GPIO12. This option is suggested for production use. + +The following commands can be used to program flash voltage selection efuses **to 3.3V**: + +```sh + # Override flash regulator configuration using efuses + components/esptool_py/esptool/espefuse.py burn_efuse XPD_SDIO_FORCE + # Select 3.3V output voltage + components/esptool_py/esptool/espefuse.py burn_efuse XPD_SDIO_TIEH + # Enable internal voltage regulator + components/esptool_py/esptool/espefuse.py burn_efuse XPD_SDIO_REG +``` ## 4-line and 1-line modes From 3efbbab2cfd4a6766c1d9ffe182d447ec58f247a Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 1 Jan 2017 23:51:48 -0600 Subject: [PATCH 161/167] wifi: fix typos, rename ESP_ERR_WIFI_NOT_START to ESP_ERR_WIFI_NOT_STARTED --- components/esp32/include/esp_wifi.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index c835c071c78..197a04fd3b9 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -76,8 +76,8 @@ extern "C" { #define ESP_ERR_WIFI_ARG ESP_ERR_INVALID_ARG /*!< Invalid argument */ #define ESP_ERR_WIFI_NOT_SUPPORT ESP_ERR_NOT_SUPPORTED /*!< Indicates that API is not supported yet */ -#define ESP_ERR_WIFI_NOT_INIT (ESP_ERR_WIFI_BASE + 1) /*!< WiFi driver is not installed by esp_wifi_init */ -#define ESP_ERR_WIFI_NOT_START (ESP_ERR_WIFI_BASE + 2) /*!< WiFi driver is not started by esp_wifi_start */ +#define ESP_ERR_WIFI_NOT_INIT (ESP_ERR_WIFI_BASE + 1) /*!< WiFi driver was not installed by esp_wifi_init */ +#define ESP_ERR_WIFI_NOT_STARTED (ESP_ERR_WIFI_BASE + 2) /*!< WiFi driver was not started by esp_wifi_start */ #define ESP_ERR_WIFI_IF (ESP_ERR_WIFI_BASE + 3) /*!< WiFi interface error */ #define ESP_ERR_WIFI_MODE (ESP_ERR_WIFI_BASE + 4) /*!< WiFi mode error */ #define ESP_ERR_WIFI_STATE (ESP_ERR_WIFI_BASE + 5) /*!< WiFi internal state error */ @@ -85,7 +85,7 @@ extern "C" { #define ESP_ERR_WIFI_NVS (ESP_ERR_WIFI_BASE + 7) /*!< WiFi internal NVS module error */ #define ESP_ERR_WIFI_MAC (ESP_ERR_WIFI_BASE + 8) /*!< MAC address is invalid */ #define ESP_ERR_WIFI_SSID (ESP_ERR_WIFI_BASE + 9) /*!< SSID is invalid */ -#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 10) /*!< Passord is invalid */ +#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 10) /*!< Password is invalid */ #define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 11) /*!< Timeout error */ #define ESP_ERR_WIFI_WAKE_FAIL (ESP_ERR_WIFI_BASE + 12) /*!< WiFi is in sleep state(RF closed) and wakeup fail */ @@ -222,8 +222,8 @@ esp_err_t esp_wifi_connect(void); * * @return * - ESP_OK: succeed - * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_INIT: WiFi was not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_STARTED: WiFi was not started by esp_wifi_start * - ESP_ERR_WIFI_FAIL: other WiFi internal errors */ esp_err_t esp_wifi_disconnect(void); @@ -246,7 +246,7 @@ esp_err_t esp_wifi_clear_fast_connect(void); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi was not started by esp_wifi_start * - ESP_ERR_WIFI_ARG: invalid argument * - ESP_ERR_WIFI_MODE: WiFi mode is wrong */ @@ -266,7 +266,7 @@ esp_err_t esp_wifi_deauth_sta(uint16_t aid); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi was not started by esp_wifi_start * - ESP_ERR_WIFI_TIMEOUT: blocking scan is timeout * - others: refer to error code in esp_err.h */ @@ -278,7 +278,7 @@ esp_err_t esp_wifi_scan_start(wifi_scan_config_t *config, bool block); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi is not started by esp_wifi_start */ esp_err_t esp_wifi_scan_stop(void); @@ -292,7 +292,7 @@ esp_err_t esp_wifi_scan_stop(void); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi is not started by esp_wifi_start * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); @@ -307,7 +307,7 @@ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_NOT_STARTED: WiFi is not started by esp_wifi_start * - ESP_ERR_WIFI_ARG: invalid argument * - ESP_ERR_WIFI_NO_MEM: out of memory */ From 4b71d9cb351666ae6405f85b48da1c5b0197b934 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 16 Jan 2017 01:48:47 +0800 Subject: [PATCH 162/167] docs: update index, improve structure of the readme --- README.md | 44 ++++++++++++++++++++++++-------------------- docs/index.rst | 10 +++------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 5290aa79811..64fe3ed3659 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,36 @@ -# Using Espressif IoT Development Framework with the ESP32 +# Espressif IoT Development Framework [![alt text](https://readthedocs.org/projects/docs/badge/?version=latest "Documentation Status")](http://esp-idf.readthedocs.io/en/latest/?badge=latest) -# Setting Up ESP-IDF +ESP-IDF is the official development framework for the `ESP32 `_ chip. -In the [docs](docs) directory you will find per-platform setup guides: +# Developing With the ESP-IDF -* [Windows Setup Guide](docs/windows-setup.rst) -* [Mac OS Setup Guide](docs/macos-setup.rst) -* [Linux Setup Guide](docs/linux-setup.rst) +## Setting Up ESP-IDF -# Finding A Project +See setup guides for detailed instructions to set up the ESP-IDF: -As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in the setup guide, esp-idf comes with some example projects in the [examples](examples) directory. +* [Windows Setup Guide](http://esp-idf.readthedocs.io/en/latest/windows-setup.html) +* [Mac OS Setup Guide](http://esp-idf.readthedocs.io/en/latest/macos-setup.html) +* [Linux Setup Guide](http://esp-idf.readthedocs.io/en/latest/linux-setup.html) -Once you've found the project you want to work with, change to its directory and you can configure and build it: +## Finding a Project -# Configuring your project +As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in the setup guide, ESP-IDF comes with some example projects in the [examples](examples) directory. + +Once you've found the project you want to work with, change to its directory and you can configure and build it. + +## Configuring the Project `make menuconfig` -# Compiling your project +## Compiling the Project `make all` ... will compile app, bootloader and generate a partition table based on the config. -# Flashing your project +## Flashing the Project When `make all` finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this from make by running: @@ -36,7 +40,7 @@ This will flash the entire project (app, bootloader and partition table) to a ne You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it. -# Viewing Serial Output +## Viewing Serial Output The `make monitor` target will use the already-installed [miniterm](http://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.miniterm) (a part of pyserial) to display serial output from the ESP32 on the terminal console. @@ -46,7 +50,7 @@ To flash and monitor output in one pass, you can run: `make flash monitor` -# Compiling & Flashing Just the App +## Compiling & Flashing Just the App After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table: @@ -57,9 +61,9 @@ After the initial flash, you may just want to build and flash just your app, not (There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.) -# Parallel Builds +## Parallel Builds -esp-idf supports compiling multiple files in parallel, so all of the above commands can be run as `make -jN` where `N` is the number of parallel make processes to run (generally N should be equal to or one more than the number of CPU cores in your system.) +ESP-IDF supports compiling multiple files in parallel, so all of the above commands can be run as `make -jN` where `N` is the number of parallel make processes to run (generally N should be equal to or one more than the number of CPU cores in your system.) Multiple make functions can be combined into one. For example: to build the app & bootloader using 5 jobs in parallel, then flash everything, and then display serial output from the ESP32 run: @@ -67,7 +71,7 @@ Multiple make functions can be combined into one. For example: to build the app make -j5 flash monitor ``` -# The Partition Table +## The Partition Table Once you've compiled your project, the "build" directory will contain a binary file with a name like "my_app.bin". This is an ESP32 image binary that can be loaded by the bootloader. @@ -84,7 +88,7 @@ In both cases the factory app is flashed at offset 0x10000. If you `make partiti For more details about partition tables and how to create custom variations, view the `docs/partition-tables.rst` file. -# Erasing Flash +## Erasing Flash The `make flash` target does not erase the entire flash contents. However it is sometimes useful to set the device back to a totally erased state, particularly when making partition table changes or OTA app updates. To erase the entire flash, run `make erase_flash`. @@ -92,11 +96,11 @@ This can be combined with other targets, ie `make erase_flash flash` will erase # Resources -* The [docs directory of the esp-idf repository](docs) contains source of [esp-idf](http://esp-idf.readthedocs.io/) documentation. +* Documentation for the latest version: http://esp-idf.readthedocs.io/. This documentation is built from the [docs directory](docs) of this repository. * The [esp32.com forum](http://esp32.com/) is a place to ask questions and find community resources. * [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one. -* If you're interested in contributing to esp-idf, please check the [Contributions Guide](http://esp-idf.readthedocs.io/en/latest/contributing.html>). +* If you're interested in contributing to ESP-IDF, please check the [Contributions Guide](http://esp-idf.readthedocs.io/en/latest/contributing.html>). diff --git a/docs/index.rst b/docs/index.rst index bcc3c923a24..eafe1024470 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,11 +1,7 @@ -ESP32 Programming Guide -======================= +ESP-IDF Programming Guide +========================= -.. caution:: - - Until ESP-IDF release 1.0, this documentation is a draft. It is incomplete and may have mistakes. Please mind your step! - -Documentation adressed to developers of applications for `ESP32 `_ by `Espressif `_ using `esp-idf `_. +This is the documentation for Espressif IoT Developement Framework (`esp-idf `_). ESP-IDF is the official development framework for the `ESP32 `_ chip. Contents: From 4c0f90bfaeeae4696bfac0cadd1380a90b873345 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 16 Jan 2017 02:07:46 +0800 Subject: [PATCH 163/167] docs: fix some of the Sphinx warnings --- docs/api/ledc.rst | 9 --------- docs/api/sigmadelta.rst | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst index 40855ddb470..12d7cd16d08 100644 --- a/docs/api/ledc.rst +++ b/docs/api/ledc.rst @@ -22,15 +22,6 @@ Header Files * `driver/include/driver/ledc.h `_ -Data Structures -^^^^^^^^^^^^^^^ - -.. doxygenstruct:: ledc_channel_config_t - :members: - -.. doxygenstruct:: ledc_timer_config_t - :members: - Macros ^^^^^^ diff --git a/docs/api/sigmadelta.rst b/docs/api/sigmadelta.rst index acfdaaa27da..b03f0494211 100644 --- a/docs/api/sigmadelta.rst +++ b/docs/api/sigmadelta.rst @@ -1,5 +1,5 @@ Sigma-delta Modulation -======== +====================== Overview -------- From 786573fb2d9b4d0beac8b12d792b65ba656ee648 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 16 Jan 2017 02:18:42 +0800 Subject: [PATCH 164/167] docs: fix macOS setup instructions Most of the packages mentioned are only needed for building the toolchain using crosstool-NG, not for the normal ESP-IDF environment. Mention that pyserial needs to be installed (fixes https://github.com/espressif/esp-idf/issues/229). --- docs/macos-setup.rst | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/macos-setup.rst b/docs/macos-setup.rst index ac772c52984..eeed9e4e262 100644 --- a/docs/macos-setup.rst +++ b/docs/macos-setup.rst @@ -4,19 +4,13 @@ Set up of Toolchain for Mac OS Step 0: Prerequisites ===================== -Getting MacPorts or homebrew ----------------------------- +- install pip:: -Whether you compile the toolchain from source or download binary toolchain, there are some dependencies which need to be installed on macOS first. These dependencies are installed with one of the package managers: homebrew or MacPorts. If you have these already, you can skip the following instructions. + sudo easy_install pip -- Install XCode from Mac App Store -- Open Terminal.app and run ``xcode-select --install`` -- Run ``sudo xcodebuild -license`` and agree to XCode license -- Install MacPorts_ or homebrew_ - -.. _homebrew: http://brew.sh/ -.. _MacPorts: https://www.macports.org/install.php +- install pyserial + sudo pip install pyserial Step 1: Download binary toolchain for the ESP32 ================================================== @@ -64,13 +58,18 @@ In any case, here are the steps to compile the toolchain yourself. - Install dependencies: + - Install either MacPorts_ or homebrew_ package manager. MacPorts needs a full XCode installation, while homebrew only needs XCode command line tools. + + .. _homebrew: http://brew.sh/ + .. _MacPorts: https://www.macports.org/install.php + - with MacPorts:: - sudo port install gsed gawk binutils gperf grep gettext ncurses + sudo port install gsed gawk binutils gperf grep gettext wget libtool autoconf automake - - with homebrew + - with homebrew:: - brew install gnu-sed gawk binutils gperf grep gettext ncurses + brew install gnu-sed gawk binutils gperftools gettext wget help2man libtool autoconf automake Create a case-sensitive filesystem image:: From f20135af5400f78ea09a69f178d72e66020cceac Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 16 Jan 2017 02:33:16 +0800 Subject: [PATCH 165/167] esp32: compile PHY-related code only when WiFi is enabled Fixes https://github.com/espressif/esp-idf/issues/230, https://github.com/espressif/esp-idf/issues/237 --- components/esp32/cpu_start.c | 7 ++++++- components/esp32/phy_init.c | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 5ae68fc6437..b5e896e573f 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -71,9 +71,11 @@ static bool app_cpu_started = false; #endif //!CONFIG_FREERTOS_UNICORE static void do_global_ctors(void); -static void do_phy_init(); static void main_task(void* args); extern void app_main(void); +#if CONFIG_ESP32_PHY_AUTO_INIT +static void do_phy_init(); +#endif extern int _bss_start; extern int _bss_end; @@ -264,6 +266,7 @@ static void main_task(void* args) vTaskDelete(NULL); } +#if CONFIG_ESP32_PHY_AUTO_INIT static void do_phy_init() { esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; @@ -297,3 +300,5 @@ static void do_phy_init() esp_phy_release_init_data(init_data); free(cal_data); // PHY maintains a copy of calibration data, so we can free this } +#endif //CONFIG_ESP32_PHY_AUTO_INIT + diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index bfc5a15f5ed..07a455d501f 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -23,10 +23,12 @@ #include "esp_err.h" #include "esp_phy_init.h" #include "esp_system.h" -#include "phy.h" #include "esp_log.h" #include "nvs.h" #include "sdkconfig.h" + +#ifdef CONFIG_WIFI_ENABLED +#include "phy.h" #include "phy_init_data.h" static const char* TAG = "phy_init"; @@ -219,6 +221,4 @@ static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle, return err; } -void register_chipv7_phy_stub() -{ -} +#endif // CONFIG_WIFI_ENABLED From ea19838d3a9e4dc100a90209fa4e4084577521d0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 16 Jan 2017 11:52:15 +1100 Subject: [PATCH 166/167] Build: Handle WiFi & BT enabled/disabled combos gracefully as possible If using WIFI_INIT_CONFIG_DEFAULT, error message will point out lack of WiFi. Otherwise linker errors on WiFi symbols should help give a clue as to what is broken. Piggy-backs on changes in !420, ref github #230 #237 --- components/esp32/component.mk | 9 ++++++++- components/esp32/include/esp_wifi.h | 6 +++++- components/esp32/system_api.c | 3 +++ examples/02_blink/sdkconfig.defaults | 2 ++ examples/14_gatt_server/sdkconfig.defaults | 7 ++----- examples/15_gatt_client/sdkconfig.defaults | 7 ++----- 6 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 examples/02_blink/sdkconfig.defaults diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 4ceb13e837d..e1cd2c6524a 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -3,8 +3,15 @@ # COMPONENT_SRCDIRS := . hwcrypto +LIBS := core rtc phy +ifdef CONFIG_BT_ENABLED +LIBS += coexist +endif +ifdef CONFIG_WIFI_ENABLED +LIBS += net80211 pp wpa smartconfig coexist wps wpa2 +endif -LIBS := core net80211 phy rtc pp wpa smartconfig coexist wps wpa2 +LIBS := $(sort $(LIBS)) # de-duplicate, we can handle different orders here LINKER_SCRIPTS += esp32.common.ld esp32.rom.ld esp32.peripherals.ld diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 197a04fd3b9..8d4fa17bb73 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -62,6 +62,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "rom/queue.h" +#include "sdkconfig.h" #include "esp_err.h" #include "esp_wifi_types.h" #include "esp_event.h" @@ -97,11 +98,14 @@ typedef struct { uint32_t rx_buf_num; /**< WiFi RX buffer number */ } wifi_init_config_t; - +#ifdef CONFIG_WIFI_ENABLED #define WIFI_INIT_CONFIG_DEFAULT() { \ .event_handler = &esp_event_send, \ .rx_buf_num = CONFIG_ESP32_WIFI_RX_BUFFER_NUM, \ }; +#else +#define WIFI_INIT_CONFIG_DEFAULT #error Wifi is disabled in config, WIFI_INIT_CONFIG_DEFAULT will not work +#endif /** * @brief Init WiFi diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index 60fa1796cdc..bfc0f075279 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -17,6 +17,7 @@ #include "esp_wifi.h" #include "esp_wifi_internal.h" #include "esp_log.h" +#include "sdkconfig.h" #include "rom/efuse.h" #include "rom/cache.h" #include "rom/uart.h" @@ -74,7 +75,9 @@ esp_err_t system_efuse_read_mac(uint8_t mac[6]) __attribute__((alias("esp_efuse_ void IRAM_ATTR esp_restart(void) { +#ifdef CONFIG_WIFI_ENABLED esp_wifi_stop(); +#endif // Disable scheduler on this core. vTaskSuspendAll(); diff --git a/examples/02_blink/sdkconfig.defaults b/examples/02_blink/sdkconfig.defaults new file mode 100644 index 00000000000..8c618fb9b40 --- /dev/null +++ b/examples/02_blink/sdkconfig.defaults @@ -0,0 +1,2 @@ +# Disable WiFi stack by default +CONFIG_WIFI_ENABLED=n diff --git a/examples/14_gatt_server/sdkconfig.defaults b/examples/14_gatt_server/sdkconfig.defaults index dcf4ad2c2d1..9d51df5ee57 100644 --- a/examples/14_gatt_server/sdkconfig.defaults +++ b/examples/14_gatt_server/sdkconfig.defaults @@ -1,7 +1,4 @@ # Override some defaults so BT stack is enabled -# in this example - -# -# BT config -# +# and WiFi disabled by default in this example CONFIG_BT_ENABLED=y +CONFIG_WIFI_ENABLED=n diff --git a/examples/15_gatt_client/sdkconfig.defaults b/examples/15_gatt_client/sdkconfig.defaults index dcf4ad2c2d1..9d51df5ee57 100644 --- a/examples/15_gatt_client/sdkconfig.defaults +++ b/examples/15_gatt_client/sdkconfig.defaults @@ -1,7 +1,4 @@ # Override some defaults so BT stack is enabled -# in this example - -# -# BT config -# +# and WiFi disabled by default in this example CONFIG_BT_ENABLED=y +CONFIG_WIFI_ENABLED=n From 0d00a1ba01fd5a774e4da65970a88c535585ebb2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 9 Jan 2017 22:50:42 +0800 Subject: [PATCH 167/167] vfs: implement reading from UART --- components/esp32/Kconfig | 5 ++ components/vfs/README.rst | 32 +++++-- components/vfs/test/component.mk | 1 + components/vfs/test/test_vfs_uart.c | 124 ++++++++++++++++++++++++++++ components/vfs/vfs_uart.c | 54 +++++++++++- 5 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 components/vfs/test/component.mk create mode 100644 components/vfs/test/test_vfs_uart.c diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 5a36b30a5db..0afe5ba0f87 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -134,6 +134,11 @@ config NEWLIB_STDOUT_ADDCR cursor one line down, not also move it to the beginning of the line. This is usually done by an added CR character. Enabling this will make the standard output code automatically add a CR character before a LF. + + With this option enabled, C standard library functions which read from UART + (like scanf) will convert "\r\n" character sequences back to "\n". + + This option doesn't affect behavior of the UART driver (drivers/uart.h). config NEWLIB_NANO_FORMAT bool "Enable 'nano' formatting options for printf/scanf family" diff --git a/components/vfs/README.rst b/components/vfs/README.rst index c58161c9002..2ad7aa7ec20 100644 --- a/components/vfs/README.rst +++ b/components/vfs/README.rst @@ -23,11 +23,6 @@ To register an FS driver, application needs to define in instance of esp_vfs_t s .fstat = &myfs_fstat, .close = &myfs_close, .read = &myfs_read, - .lseek = NULL, - .stat = NULL, - .link = NULL, - .unlink = NULL, - .rename = NULL }; ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); @@ -125,4 +120,31 @@ When VFS component receives a call from newlib which has a file descriptor, this +-------------+ +Standard IO streams (stdin, stdout, stderr) +------------------------------------------- + +If "UART for console output" menuconfig option is not set to "None", then ``stdin``, ``stdout``, and ``stderr`` are configured to read from, and write to, a UART. It is possible to use UART0 or UART1 for standard IO. By default, UART0 is used, with 115200 baud rate, TX pin is GPIO1 and RX pin is GPIO3. These parameters can be changed in menuconfig. + +Writing to ``stdout`` or ``stderr`` will send characters to the UART transmit FIFO. Reading from ``stdin`` will retrieve characters from the UART receive FIFO. + +Note that while writing to ``stdout`` or ``stderr`` will block until all characters are put into the FIFO, reading from ``stdin`` is non-blocking. The function which reads from UART will get all the characters present in the FIFO (if any), and return. I.e. doing ``fscanf("%d\n", &var);`` may not have desired results. This is a temporary limitation which will be removed once ``fcntl`` is added to the VFS interface. + +Standard streams and FreeRTOS tasks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``FILE`` objects for ``stdin``, ``stdout``, and ``stderr`` are shared between all FreeRTOS tasks, but the pointers to these objects are are stored in per-task ``struct _reent``. The following code:: + + fprintf(stderr, "42\n"); + +actually is translated to to this (by the preprocessor): + + fprintf(__getreent()->_stderr, "42\n"); + +where the ``__getreent()`` function returns a per-task pointer to ``struct _reent`` (`source `_). This structure is allocated on the TCB of each task. When a task is initialized, ``_stdin``, ``_stdout`` and ``_stderr`` members of ``struct _reent`` are set to the values of ``_stdin``, ``_stdout`` and ``_stderr`` of ``_GLOBAL_REENT`` (i.e. the structure which is used before FreeRTOS is started). + +Such a design has the following consequences: + +- It is possible to set ``stdin``, ``stdout``, and ``stderr`` for any given task without affecting other tasks, e.g. by doing ``stdin = fopen("/dev/uart/1", "r")``. +- Closing default ``stdin``, ``stdout``, or ``stderr`` using ``fclose`` will close the ``FILE`` stream object — this will affect all other tasks. +- To change the default ``stdin``, ``stdout``, ``stderr`` streams for new tasks, modify ``_GLOBAL_REENT->_stdin`` (``_stdout``, ``_stderr``) before creating the task. diff --git a/components/vfs/test/component.mk b/components/vfs/test/component.mk new file mode 100644 index 00000000000..ce464a212a6 --- /dev/null +++ b/components/vfs/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/vfs/test/test_vfs_uart.c b/components/vfs/test/test_vfs_uart.c new file mode 100644 index 00000000000..892ca527c65 --- /dev/null +++ b/components/vfs/test/test_vfs_uart.c @@ -0,0 +1,124 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "unity.h" +#include "rom/uart.h" +#include "soc/uart_struct.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +static void fwrite_str_loopback(const char* str, size_t size) +{ + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + UART0.conf0.loopback = 1; + fwrite(str, 1, size, stdout); + fflush(stdout); + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + vTaskDelay(2 / portTICK_PERIOD_MS); + UART0.conf0.loopback = 0; +} + +static void flush_stdin_stdout() +{ + vTaskDelay(10 / portTICK_PERIOD_MS); + char *bitbucket = (char*) 0x3f000000; + while (fread(bitbucket, 1, 128, stdin) > 0) { + ; + } + fflush(stdout); + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); +} + +TEST_CASE("can read from stdin", "[vfs]") +{ + flush_stdin_stdout(); + + const size_t count = 12; + srand(count); + char* data = (char*) calloc(1, count * 8 + 1); + char* buf = (char*) calloc(1, count * 8 + 1); + char* p = data; + for (size_t i = 0; i < count; ++i) { + p += sprintf(p, "%08x", rand()); + } + p += sprintf(p, "\n"); + size_t len = p - data; + fwrite_str_loopback(data, len); + size_t cb = fread(buf, 1, len, stdin); + TEST_ASSERT_EQUAL(len, cb); + TEST_ASSERT_EQUAL_UINT8_ARRAY(data, buf, len); +} + + +#if CONFIG_NEWLIB_STDOUT_ADDCR + +TEST_CASE("CRs are removed from the stdin correctly", "[vfs]") +{ + flush_stdin_stdout(); + const char* send_str = "1234567890\n\r123\r\n4\n"; + /* with CONFIG_NEWLIB_STDOUT_ADDCR, the following will be sent on the wire. + * (last character of each part is marked with a hat) + * + * 1234567890\r\n\r123\r\r\n4\r\n + * ^ ^^ ^ + */ + char buf[128]; + char* dst = buf; + + fwrite_str_loopback(send_str, 11); // send up to the first \n + size_t rb = fread(dst, 1, 5, stdin); // read first 5 + TEST_ASSERT_EQUAL(5, rb); + dst += rb; + + rb = fread(dst, 1, 6, stdin); // ask for 6 + TEST_ASSERT_EQUAL(6, rb); // get 6 + + TEST_ASSERT_EQUAL_UINT8_ARRAY("1234567890\n", buf, 11); + dst += rb; + + rb = fread(dst, 1, 2, stdin); // any more characters? + TEST_ASSERT_EQUAL(0, rb); // nothing + + fwrite_str_loopback(send_str + 11, 1); // send the '\r' + vTaskDelay(10 / portTICK_PERIOD_MS); + + rb = fread(dst, 1, 2, stdin); // try to get somthing + TEST_ASSERT_EQUAL(0, rb); // still nothing (\r is buffered) + + fwrite_str_loopback(send_str + 12, 1); // Now send the '1' + vTaskDelay(10 / portTICK_PERIOD_MS); + rb = fread(dst, 1, 2, stdin); // try again + TEST_ASSERT_EQUAL(2, rb); // get two characters + TEST_ASSERT_EQUAL_UINT8_ARRAY("\r1", dst, 2); + dst += rb; + + fwrite_str_loopback(send_str + 13, 6); // Send the rest + vTaskDelay(10 / portTICK_PERIOD_MS); + + rb = fread(dst, 1, 4, stdin); // consume "23\r\n" + TEST_ASSERT_EQUAL(4, rb); + TEST_ASSERT_EQUAL_UINT8_ARRAY("23\r\n", dst, 4); + dst += rb; + + rb = fread(dst, 1, 4, stdin); // ask for more than the remainder + TEST_ASSERT_EQUAL(2, rb); + TEST_ASSERT_EQUAL_UINT8_ARRAY("4\n", dst, 2); +} + +#endif //CONFIG_NEWLIB_STDOUT_ADDCR + diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index d9d755f9bef..545a5474fad 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -70,6 +70,58 @@ static size_t IRAM_ATTR uart_write(int fd, const void * data, size_t size) return size; } +static ssize_t IRAM_ATTR uart_read(int fd, void* data, size_t size) +{ + assert(fd >=0 && fd < 3); + uint8_t *data_c = (uint8_t *) data; + uart_dev_t* uart = s_uarts[fd]; + size_t received = 0; + _lock_acquire_recursive(&s_uart_locks[fd]); + while (uart->status.rxfifo_cnt > 0 && received < size) { + uint8_t c = uart->fifo.rw_byte; +#if CONFIG_NEWLIB_STDOUT_ADDCR + /* Convert \r\n sequences to \n. + * If \r is received, it is put into 'buffered_char' until the next + * character is received. Then depending on the character, we either + * drop \r (if the next one is \n) or output \r and then proceed to output + * the new character. + */ + const int NONE = -1; + static int buffered_char = NONE; + if (buffered_char != NONE) { + if (buffered_char == '\r' && c == '\n') { + buffered_char = NONE; + } else { + data_c[received] = buffered_char; + buffered_char = NONE; + ++received; + if (received == size) { + /* We have placed the buffered character into the output buffer + * but there won't be enough space for the newly received one. + * Keep the new character in buffered_char until read is called + * again. + */ + buffered_char = c; + break; + } + } + } + if (c == '\r') { + buffered_char = c; + continue; + } +#endif //CONFIG_NEWLIB_STDOUT_ADDCR + data_c[received] = c; + ++received; + } + _lock_release_recursive(&s_uart_locks[fd]); + if (received > 0) { + return received; + } + errno = EWOULDBLOCK; + return -1; +} + static int IRAM_ATTR uart_fstat(int fd, struct stat * st) { assert(fd >=0 && fd < 3); @@ -92,7 +144,7 @@ void esp_vfs_dev_uart_register() .open = &uart_open, .fstat = &uart_fstat, .close = &uart_close, - .read = NULL, // TODO: implement reading from UART + .read = &uart_read, .lseek = NULL, .stat = NULL, .link = NULL,

  • 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 a##GHQz2LqM=#naf9A-7fGt=`Kt;GRP+cwPr delta 1110267 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