fix(spi): several fixes about maros (flags) and GPIO0.
See merge request idf/esp-idf!1666
- components/idf_test/*/CIConfigs
- components/idf_test/*/TC.sqlite
- $EXAMPLE_CONFIG_OUTPUT_PATH
+ - tools/unit-test-app/output
expire_in: 1 mos
before_script: *add_gitlab_key_before
script:
# first move test bins together: test_bins/CHIP_SDK/TestApp/bin_files
- mkdir -p $OUTPUT_BIN_PATH
- # copy and rename folder name to "UT_config"
- - for CONFIG in $(ls $UT_BIN_PATH); do cp -r "$UT_BIN_PATH/$CONFIG" "$OUTPUT_BIN_PATH/UT_$CONFIG"; done
- cp -r SSC/ssc_bin/* $OUTPUT_BIN_PATH
# assign example tests
- python $TEST_FW_PATH/CIAssignExampleTest.py $IDF_PATH/examples $IDF_PATH/.gitlab-ci.yml $EXAMPLE_CONFIG_OUTPUT_PATH
+ # assign unit test cases
+ - python $TEST_FW_PATH/CIAssignUnitTest.py $IDF_PATH/components/idf_test/unit_test/TestCaseAll.yml $IDF_PATH/.gitlab-ci.yml $IDF_PATH/components/idf_test/unit_test/CIConfigs
# clone test script to assign tests
- git clone $TEST_SCRIPT_REPOSITORY
- cd auto_test_script
- python $CHECKOUT_REF_SCRIPT auto_test_script
- # assign unit test cases
- - python CIAssignTestCases.py -t $IDF_PATH/components/idf_test/unit_test -c $IDF_PATH/.gitlab-ci.yml -b $IDF_PATH/test_bins
# assgin integration test cases
- python CIAssignTestCases.py -t $IDF_PATH/components/idf_test/integration_test -c $IDF_PATH/.gitlab-ci.yml -b $IDF_PATH/test_bins
# run test
- python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE
+.unit_test_template: &unit_test_template
+ <<: *example_test_template
+ stage: unit_test
+ dependencies:
+ - assign_test
+ variables:
+ TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
+ TEST_CASE_PATH: "$CI_PROJECT_DIR/tools/unit-test-app"
+ CONFIG_FILE: "$CI_PROJECT_DIR/components/idf_test/unit_test/CIConfigs/$CI_JOB_NAME.yml"
+ LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
+
.test_template: &test_template
stage: test
when: on_success
# run test
- python CIRunner.py -l "$LOG_PATH/$CI_JOB_NAME" -c $CONFIG_FILE -e $LOCAL_ENV_CONFIG_PATH -t $TEST_CASE_FILE_PATH -m $MODULE_UPDATE_FILE
-# template for unit test jobs
-.unit_test_template: &unit_test_template
- <<: *test_template
- allow_failure: false
- stage: unit_test
- variables:
- LOCAL_ENV_CONFIG_PATH: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/ESP32_IDF"
- LOG_PATH: "$CI_PROJECT_DIR/$CI_COMMIT_SHA"
- TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test/unit_test"
- MODULE_UPDATE_FILE: "$CI_PROJECT_DIR/components/idf_test/ModuleDefinition.yml"
- CONFIG_FILE: "$CI_PROJECT_DIR/components/idf_test/unit_test/CIConfigs/$CI_JOB_NAME.yml"
-
nvs_compatible_test:
<<: *test_template
artifacts:
[submodule "components/spiffs/spiffs"]
path = components/spiffs/spiffs
url = https://github.com/pellepl/spiffs.git
+
+[submodule "components/json/cJSON"]
+ path = components/json/cJSON
+ url = https://github.com/DaveGamble/cJSON.git
Enables supporrt for SEGGER SystemView tracing functionality.
choice SYSVIEW_TS_SOURCE
- prompt "ESP32 timer to use as SystemView timestamp source"
+ prompt "Timer to use as timestamp source"
depends on SYSVIEW_ENABLE
- default SYSVIEW_TS_SOURCE_TIMER_00
+ default SYSVIEW_TS_SOURCE_CCOUNT if FREERTOS_UNICORE && !PM_ENABLE
+ default SYSVIEW_TS_SOURCE_TIMER_00 if !FREERTOS_UNICORE && !PM_ENABLE
+ default SYSVIEW_TS_SOURCE_ESP_TIMER if PM_ENABLE
help
SystemView needs to use a hardware timer as the source of timestamps
- when tracing
- This option selects HW timer for it.
+ when tracing. This option selects the timer for it.
+
+config SYSVIEW_TS_SOURCE_CCOUNT
+ bool "CPU cycle counter (CCOUNT)"
+ depends on FREERTOS_UNICORE && !PM_ENABLE
config SYSVIEW_TS_SOURCE_TIMER_00
bool "Timer 0, Group 0"
- help
- Select this to use timer 0 of group 0
+ depends on !PM_ENABLE
config SYSVIEW_TS_SOURCE_TIMER_01
bool "Timer 1, Group 0"
- help
- Select this to use timer 1 of group 0
+ depends on !PM_ENABLE
config SYSVIEW_TS_SOURCE_TIMER_10
bool "Timer 0, Group 1"
- help
- Select this to use timer 0 of group 1
+ depends on !PM_ENABLE
config SYSVIEW_TS_SOURCE_TIMER_11
bool "Timer 1, Group 1"
- help
- Select this to use timer 1 of group 1
+ depends on !PM_ENABLE
+
+config SYSVIEW_TS_SOURCE_ESP_TIMER
+ bool "esp_timer high resolution timer"
endchoice
#include "freertos/FreeRTOS.h"
#include "SEGGER_SYSVIEW.h"
#include "rom/ets_sys.h"
-#if CONFIG_FREERTOS_UNICORE == 0
-#include "driver/timer.h"
-#endif
#include "esp_app_trace.h"
#include "esp_app_trace_util.h"
#include "esp_intr_alloc.h"
// The target device name
#define SYSVIEW_DEVICE_NAME "ESP32"
+// Determine which timer to use as timestamp source
+#if CONFIG_SYSVIEW_TS_SOURCE_CCOUNT
+#define TS_USE_CCOUNT 1
+#elif CONFIG_SYSVIEW_TS_SOURCE_ESP_TIMER
+#define TS_USE_ESP_TIMER 1
+#else
+#define TS_USE_TIMERGROUP 1
+#endif
+
+#if TS_USE_TIMERGROUP
+#include "driver/timer.h"
+
// Timer group timer divisor
#define SYSVIEW_TIMER_DIV 2
+
// Frequency of the timestamp.
#define SYSVIEW_TIMESTAMP_FREQ (esp_clk_apb_freq() / SYSVIEW_TIMER_DIV)
+
+// Timer ID and group ID
+#if defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_00) || defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_01)
+#define TS_TIMER_ID 0
+#else
+#define TS_TIMER_ID 1
+#endif // TIMER_00 || TIMER_01
+
+#if defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_00) || defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_10)
+#define TS_TIMER_GROUP 0
+#else
+#define TS_TIMER_GROUP 1
+#endif // TIMER_00 || TIMER_10
+
+#endif // TS_USE_TIMERGROUP
+
+#if TS_USE_ESP_TIMER
+// esp_timer provides 1us resolution
+#define SYSVIEW_TIMESTAMP_FREQ (1000000)
+#endif // TS_USE_ESP_TIMER
+
+#if TS_USE_CCOUNT
+// CCOUNT is incremented at CPU frequency
+#define SYSVIEW_TIMESTAMP_FREQ (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ * 1000000)
+#endif // TS_USE_CCOUNT
+
// System Frequency.
#define SYSVIEW_CPU_FREQ (esp_clk_cpu_freq())
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
#endif
-static timer_idx_t s_ts_timer_idx;
-static timer_group_t s_ts_timer_group;
-
// SystemView is single core specific: it implies that SEGGER_SYSVIEW_LOCK()
-// disables IRQs (disables rescheduling globaly). So we can not use finite timeouts for locks and return error
+// disables IRQs (disables rescheduling globally). So we can not use finite timeouts for locks and return error
// in case of expiration, because error will not be handled and SEGGER's code will go further implying that
// everything is fine, so for multi-core env we have to wait on underlying lock forever
#define SEGGER_LOCK_WAIT_TMO ESP_APPTRACE_TMO_INFINITE
*/
static void SEGGER_SYSVIEW_TS_Init()
{
- timer_config_t config;
-
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_00
- s_ts_timer_group = TIMER_GROUP_0;
- s_ts_timer_idx = TIMER_0;
-#endif
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_01
- s_ts_timer_group = TIMER_GROUP_0;
- s_ts_timer_idx = TIMER_1;
-#endif
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_10
- s_ts_timer_group = TIMER_GROUP_1;
- s_ts_timer_idx = TIMER_0;
-#endif
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_11
- s_ts_timer_group = TIMER_GROUP_1;
- s_ts_timer_idx = TIMER_1;
-#endif
- config.alarm_en = 0;
- config.auto_reload = 0;
- config.counter_dir = TIMER_COUNT_UP;
- config.divider = SYSVIEW_TIMER_DIV;
- config.counter_en = 0;
- /*Configure timer*/
- timer_init(s_ts_timer_group, s_ts_timer_idx, &config);
- /*Load counter value */
- timer_set_counter_value(s_ts_timer_group, s_ts_timer_idx, 0x00000000ULL);
- /*Enable timer interrupt*/
- timer_start(s_ts_timer_group, s_ts_timer_idx);
+ /* We only need to initialize something if we use Timer Group.
+ * esp_timer and ccount can be used as is.
+ */
+#if TS_USE_TIMERGROUP
+ timer_config_t config = {
+ .alarm_en = 0,
+ .auto_reload = 0,
+ .counter_dir = TIMER_COUNT_UP,
+ .divider = SYSVIEW_TIMER_DIV,
+ .counter_en = 0
+ };
+ /* Configure timer */
+ timer_init(TS_TIMER_GROUP, TS_TIMER_ID, &config);
+ /* Load counter value */
+ timer_set_counter_value(TS_TIMER_GROUP, TS_TIMER_ID, 0x00000000ULL);
+ /* Start counting */
+ timer_start(TS_TIMER_GROUP, TS_TIMER_ID);
+#endif // TS_USE_TIMERGROUP
}
void SEGGER_SYSVIEW_Conf(void) {
U32 SEGGER_SYSVIEW_X_GetTimestamp()
{
-#if CONFIG_FREERTOS_UNICORE == 0
+#if TS_USE_TIMERGROUP
uint64_t ts = 0;
- timer_get_counter_value(s_ts_timer_group, s_ts_timer_idx, &ts);
- return (U32)ts; // return lower part of counter value
-#else
+ timer_get_counter_value(TS_TIMER_GROUP, TS_TIMER_ID, &ts);
+ return (U32) ts; // return lower part of counter value
+#elif TS_USE_CCOUNT
return portGET_RUN_TIME_COUNTER_VALUE();
+#elif TS_USE_ESP_TIMER
+ return (U32) esp_timer_get_time(); // return lower part of counter value
#endif
}
Maximum delay between reconnection attempts. If the exponentially increased delay
interval reaches this value, the client will stop automatically attempting to reconnect.
+menu "Thing Shadow"
+ depends on AWS_IOT_SDK
+
+config AWS_IOT_OVERRIDE_THING_SHADOW_RX_BUFFER
+ bool "Override Shadow RX buffer size"
+ depends on AWS_IOT_SDK
+ default n
+ help
+ Allows setting a different Thing Shadow RX buffer
+ size. This is the maximum size of a Thing Shadow
+ message in bytes, plus one.
+
+ If not overridden, the default value is the MQTT RX Buffer length plus one. If overriden, do not set higher than the default value.
+
+config AWS_IOT_SHADOW_MAX_SIZE_OF_RX_BUFFER
+ int "Maximum RX Buffer (bytes)"
+ depends on AWS_IOT_OVERRIDE_THING_SHADOW_RX_BUFFER
+ default 513
+ range 32 65536
+ help
+ Allows setting a different Thing Shadow RX buffer size.
+ This is the maximum size of a Thing Shadow message in bytes,
+ plus one.
+
+
+config AWS_IOT_SHADOW_MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES
+ int "Maximum unique client ID size (bytes)"
+ depends on AWS_IOT_SDK
+ default 80
+ range 4 1000
+ help
+ Maximum size of the Unique Client Id.
+
+config AWS_IOT_SHADOW_MAX_SIMULTANEOUS_ACKS
+ int "Maximum simultaneous responses"
+ depends on AWS_IOT_SDK
+ default 10
+ range 1 100
+ help
+ At any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
+
+config AWS_IOT_SHADOW_MAX_SIMULTANEOUS_THINGNAMES
+ int "Maximum simultaneous Thing Name operations"
+ depends on AWS_IOT_SDK
+ default 10
+ range 1 100
+ help
+ We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
+
+config AWS_IOT_SHADOW_MAX_JSON_TOKEN_EXPECTED
+ int "Maximum expected JSON tokens"
+ depends on AWS_IOT_SDK
+ default 120
+ help
+ These are the max tokens that is expected to be in the Shadow JSON document. Includes the metadata which is published
+
+config AWS_IOT_SHADOW_MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME
+ int "Maximum topic length (not including Thing Name)"
+ depends on AWS_IOT_SDK
+ default 60
+ range 10 1000
+ help
+ All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
+
+config AWS_IOT_SHADOW_MAX_SIZE_OF_THING_NAME
+ int "Maximum Thing Name length"
+ depends on AWS_IOT_SDK
+ default 20
+ range 4 1000
+ help
+ Maximum length of a Thing Name.
+
+endmenu # Thing Shadow
+
-Subproject commit b9f19d57a0d94e98469cde470003ec45d5819ba4
+Subproject commit 8bf852db77c360eebfa4b800754fdb90e29ea43e
#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS CONFIG_AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow
// Thing Shadow specific configs
-#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN + 1) ///< Maximum size of the SHADOW buffer to store the received Shadow message
+#ifdef CONFIG_AWS_IOT_OVERRIDE_THING_SHADOW_RX_BUFFER
+#define SHADOW_MAX_SIZE_OF_RX_BUFFER CONFIG AWS_IOT_SHADOW_MAX_SIZE_OF_RX_BUFFER ///< Maximum size of the SHADOW buffer to store the received Shadow message, including NULL termianting byte
+#else
+#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN + 1)
+#endif
+
#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments"
#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE (MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10) ///< This is size of the extra sequence number that will be appended to the Unique client Id
#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE (MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20) ///< This is size of the the total clientToken key and value pair in the JSON
-#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
-#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
-#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published
-#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the formablogt $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
-#define MAX_SIZE_OF_THING_NAME 20 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger
+#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME CONFIG_AWS_IOT_SHADOW_MAX_SIMULTANEOUS_ACKS ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
+#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME CONFIG_AWS_IOT_SHADOW_MAX_SIMULTANEOUS_THINGNAMES ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
+#define MAX_JSON_TOKEN_EXPECTED CONFIG_AWS_IOT_SHADOW_MAX_JSON_TOKEN_EXPECTED ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published
+#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME CONFIG_AWS_IOT_SHADOW_MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME ///< All shadow actions have to be published or subscribed to a topic which is of the formablogt $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
+#define MAX_SIZE_OF_THING_NAME CONFIG_AWS_IOT_SHADOW_MAX_SIZE_OF_THING_NAME ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger
#define MAX_SHADOW_TOPIC_LENGTH_BYTES (MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME) ///< This size includes the length of topic with Thing Name
// Auto Reconnect specific config
// 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 <string.h>
#include "esp_flash_partitions.h"
#include "esp_log.h"
#include "rom/spi_flash.h"
+#include "rom/md5_hash.h"
+#include "esp_flash_data_types.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) {
- /* TODO: check md5 */
- ESP_LOGD(TAG, "partition table verified, %d entries", num_parts);
- *num_partitions = num_parts;
- return ESP_OK;
- }
+ int md5_found = 0;
+ int num_parts;
+ uint32_t chip_size = g_rom_flashchip.chip_size;
+ *num_partitions = 0;
- 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;
- }
+ 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 == ESP_PARTITION_MAGIC) {
+ 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;
+ }
+ } else if (part->magic == ESP_PARTITION_MAGIC_MD5) {
+ if (md5_found) {
+ if (log_errors) {
+ ESP_LOGE(TAG, "Only one MD5 checksum is allowed");
+ }
+ 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);
+ struct MD5Context context;
+ unsigned char digest[16];
+ MD5Init(&context);
+ MD5Update(&context, (unsigned char *) partition_table, num_parts * sizeof(esp_partition_info_t));
+ MD5Final(digest, &context);
+
+ unsigned char *md5sum = ((unsigned char *) part) + 16; // skip the 2B magic number and the 14B fillup bytes
+
+ if (memcmp(md5sum, digest, sizeof(digest)) != 0) {
+ if (log_errors) {
+ ESP_LOGE(TAG, "Incorrect MD5 checksum");
+ }
+ return ESP_ERR_INVALID_STATE;
+ }
+ //MD5 checksum matches and we continue with the next interation in
+ //order to detect the end of the partition table
+ md5_found = 1;
+ } else if (part->magic == 0xFFFF
+ && part->type == PART_TYPE_END
+ && part->subtype == PART_SUBTYPE_END) {
+ ESP_LOGD(TAG, "partition table verified, %d entries", num_parts);
+ *num_partitions = num_parts - md5_found; //do not count the partition where the MD5 checksum is held
+ return ESP_OK;
+ } else {
+ if (log_errors) {
+ ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic);
+ }
+ return ESP_ERR_INVALID_STATE;
}
- return ESP_ERR_INVALID_SIZE;
}
- }
- if (log_errors) {
- ESP_LOGE(TAG, "partition table has no terminating entry, not valid");
- }
- return ESP_ERR_INVALID_STATE;
+ if (log_errors) {
+ ESP_LOGE(TAG, "partition table has no terminating entry, not valid");
+ }
+ return ESP_ERR_INVALID_STATE;
}
bool "SOURCE"
endchoice
+config A2DP_SINK_TASK_STACK_SIZE
+ int "A2DP sink (audio stream decoding) task stack size"
+ depends on A2DP_ENABLE && A2DP_SINK_ENABLE
+ default 2048
+
+config A2DP_SOURCE_TASK_STACK_SIZE
+ int "A2DP source (audio stream encoding) task stack size"
+ depends on A2DP_ENABLE && A2DP_SRC_ENABLE
+ default 2048
+
config BT_SPP_ENABLED
bool "SPP"
depends on CLASSIC_BT_ENABLED
#include "esp_gatt_common_api.h"
#include "esp_bt_main.h"
#include "esp_gatt_defs.h"
-#include "btc_main.h"
+#include "btc_gatt_common.h"
/**
* @brief This function is called to set local MTU,
esp_err_t esp_ble_gatt_set_local_mtu (uint16_t mtu)
{
btc_msg_t msg;
- btc_ble_main_args_t arg;
+ btc_ble_gatt_com_args_t arg;
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
}
msg.sig = BTC_SIG_API_CALL;
- msg.pid = BTC_PID_MAIN_INIT;
+ msg.pid = BTC_PID_GATT_COMMON;
msg.act = BTC_GATT_ACT_SET_LOCAL_MTU;
arg.set_mtu.mtu = mtu;
- return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_main_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+ return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatt_com_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
}
\ No newline at end of file
/**
* @brief Register A2DP sink data output function; For now the output is PCM data stream decoded
* from SBC format. This function should be called only after esp_bluedroid_enable()
- * completes successfully, used only by A2DP sink.
+ * completes successfully, used only by A2DP sink. The callback is invoked in the context
+ * of A2DP sink task whose stack size is configurable through menuconfig
*
* @param[in] callback: A2DP sink data callback function
*
/**
* @brief Register A2DP source data input function; For now the input is PCM data stream.
* This function should be called only after esp_bluedroid_enable() completes
- * successfully
+ * successfully. The callback is invoked in the context of A2DP source task whose
+ * stack size is configurable through menuconfig
*
* @param[in] callback: A2DP source data callback function
*
hdr.layer_specific = xx + 1;
bta_av_api_deregister((tBTA_AV_DATA *)&hdr);
}
+
+ bta_sys_free_timer(&p_cb->sig_tmr);
+ memset(&p_cb->sig_tmr, 0, sizeof(TIMER_LIST_ENT));
+ bta_sys_free_timer(&p_cb->acp_sig_tmr);
+ memset(&p_cb->acp_sig_tmr, 0, sizeof(TIMER_LIST_ENT));
}
/*******************************************************************************
}
}
+/*******************************************************************************
+ *
+ * Function bta_dm_init_cb
+ *
+ * Description Initializes the bta_dm_cb control block
+ *
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void bta_dm_init_cb(void)
+{
+ memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+}
+
+/*******************************************************************************
+ *
+ * Function bta_dm_deinit_cb
+ *
+ * Description De-initializes the bta_dm_cb control block
+ *
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void bta_dm_deinit_cb(void)
+{
+ bta_sys_free_timer(&bta_dm_cb.disable_timer);
+#if ( BTA_EIR_CANNED_UUID_LIST != TRUE )
+ bta_sys_free_timer(&bta_dm_cb.app_ready_timer);
+#endif
+#if BTM_SSR_INCLUDED == TRUE
+ for (size_t i = 0; i < BTA_DM_NUM_PM_TIMER; i++) {
+ for (size_t j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) {
+ bta_sys_free_timer(&bta_dm_cb.pm_timer[i].timer[j]);
+ }
+ }
+#endif
+ memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+}
+
/*******************************************************************************
**
** Function bta_dm_sys_hw_cback
}
/* reinitialize the control block */
- memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+ bta_dm_deinit_cb();
+
+ bta_sys_free_timer(&bta_dm_search_cb.search_timer);
+#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
+#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE)
+ bta_sys_free_timer(&bta_dm_search_cb.gatt_close_timer);
+#endif
+#endif
+ memset(&bta_dm_search_cb, 0x00, sizeof(bta_dm_search_cb));
/* unregister from SYS */
bta_sys_hw_unregister( BTA_SYS_HW_BLUETOOTH );
/* save security callback */
temp_cback = bta_dm_cb.p_sec_cback;
/* make sure the control block is properly initialized */
- memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+ bta_dm_init_cb();
+
/* and retrieve the callback */
bta_dm_cb.p_sec_cback = temp_cback;
bta_dm_cb.is_bta_dm_active = TRUE;
+ bta_sys_free_timer(&bta_dm_search_cb.search_timer);
+#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
+#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE)
+ bta_sys_free_timer(&bta_dm_search_cb.gatt_close_timer);
+#endif
+#endif
/* hw is ready, go on with BTA DM initialization */
memset(&bta_dm_search_cb, 0x00, sizeof(bta_dm_search_cb));
#if (BTM_SSR_INCLUDED == TRUE)
return BTM_NOT_AUTHORIZED;
}
}
-
+
result.inq_res.flag = p_inq->flag;
result.inq_res.adv_data_len = p_inq->adv_data_len;
result.inq_res.scan_rsp_len = p_inq->scan_rsp_len;
+ memcpy(result.inq_res.dev_class, p_inq->dev_class, sizeof(DEV_CLASS));
+ result.inq_res.ble_evt_type = p_inq->ble_evt_type;
/* application will parse EIR to find out remote device name */
result.inq_res.p_eir = p_eir;
} else {
sec_event.auth_cmpl.success = TRUE;
if (!p_data->complt.smp_over_br) {
-
+
}
}
if (BTA_SUCCESS == st) {
ctrl_state = bta_dm_pm_obtain_controller_state();
}
-#endif
+#endif
if (bta_dm_cb.p_energy_info_cback) {
bta_dm_cb.p_energy_info_cback(tx_time, rx_time, idle_time, energy_used, ctrl_state, st);
}
#if GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE
-static osi_mutex_t write_ccc_mutex;
-
/*****************************************************************************
** Constants
*****************************************************************************/
memset(&bta_gattc_cb, 0, sizeof(tBTA_GATTC_CB));
p_cb->state = BTA_GATTC_STATE_ENABLED;
// Create a write ccc mutex when the gatt client enable
- osi_mutex_new(&write_ccc_mutex);
+ osi_mutex_new(&bta_gattc_cb.write_ccc_mutex);
} else {
- APPL_TRACE_DEBUG("GATTC is arelady enabled");
+ APPL_TRACE_DEBUG("GATTC is already enabled");
}
}
return;
}
// Free the write ccc mutex when the gatt client disable
- osi_mutex_free(&write_ccc_mutex);
+ osi_mutex_free(&bta_gattc_cb.write_ccc_mutex);
for (i = 0; i < BTA_GATTC_CL_MAX; i ++) {
if (p_cb->cl_rcb[i].in_use) {
else if ((transport == BT_TRANSPORT_LE) && (connected == FALSE) && (p_conn != NULL)){
p_conn->service_change_ccc_written = FALSE;
if (p_conn->ccc_timer_used == TRUE){
- assert(write_ccc_mutex != NULL);
- osi_mutex_lock(&write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ assert(bta_gattc_cb.write_ccc_mutex != NULL);
+ osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
if (p_conn->service_change_ccc_timer.param != 0) {
osi_free((void *)p_conn->service_change_ccc_timer.param);
}
bta_sys_stop_timer(&(p_conn->service_change_ccc_timer));
p_conn->ccc_timer_used = FALSE;
- osi_mutex_unlock(&write_ccc_mutex);
+ osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
}
}
BOOLEAN start_ccc_timer = FALSE;
UINT32 new_timeout;
- assert(write_ccc_mutex != NULL);
- osi_mutex_lock(&write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ assert(bta_gattc_cb.write_ccc_mutex != NULL);
+ osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
tBTA_GATTC_WAIT_CCC_TIMER *p_timer_param = (tBTA_GATTC_WAIT_CCC_TIMER*) p_tle->param;
p_tle->param = (TIMER_PARAM_TYPE)0;
if (p_timer_param == NULL){
APPL_TRACE_ERROR("p_timer_param is NULL in %s\n", __func__);
- osi_mutex_unlock(&write_ccc_mutex);
+ osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
return;
}
if (p_conn == NULL){
APPL_TRACE_ERROR("p_conn is NULL in %s\n", __func__);
osi_free(p_timer_param);
- osi_mutex_unlock(&write_ccc_mutex);
+ osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
return;
}
}
osi_free(p_timer_param);
- osi_mutex_unlock(&write_ccc_mutex);
+ osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
}
#endif
if (p_conn != NULL) {
p_conn->in_use = FALSE;
memset(p_conn->remote_bda, 0, BD_ADDR_LEN);
+ osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ bta_sys_free_timer(&p_conn->service_change_ccc_timer);
+ p_conn->ccc_timer_used = FALSE;
+ osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
return TRUE;
}
return FALSE;
#include "bta_gattc_ci.h"
#include "bta_gattc_co.h"
#include "fixed_queue.h"
+#include "mutex.h"
/*****************************************************************************
** Constants and data types
};
typedef struct {
- UINT8 state;
-
+ UINT8 state;
+ osi_mutex_t write_ccc_mutex;
tBTA_GATTC_CONN conn_track[BTA_GATTC_CONN_MAX];
tBTA_GATTC_BG_TCK bg_track[BTA_GATTC_KNOWN_SR_MAX];
tBTA_GATTC_RCB cl_rcb[BTA_GATTC_CL_MAX];
extern void bta_sys_sendmsg(void *p_msg);
extern void bta_sys_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, INT32 timeout_ms);
extern void bta_sys_stop_timer(TIMER_LIST_ENT *p_tle);
+extern void bta_sys_free_timer(TIMER_LIST_ENT *p_tle);
extern void bta_sys_disable(tBTA_SYS_HW_MODULE module);
extern UINT32 bta_sys_get_remaining_ticks(TIMER_LIST_ENT *p_target_tle);
osi_alarm_cancel(alarm);
}
+/*******************************************************************************
+**
+** Function bta_sys_free_timer
+**
+** Description Stop and free a BTA timer.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_sys_free_timer(TIMER_LIST_ENT *p_tle)
+{
+ assert(p_tle != NULL);
+
+ osi_alarm_t *alarm = hash_map_get(bta_alarm_hash_map, p_tle);
+ if (alarm == NULL) {
+ LOG_DEBUG("%s expected alarm was not in bta alarm hash map.", __func__);
+ return;
+ }
+ osi_alarm_cancel(alarm);
+ hash_map_erase(bta_alarm_hash_map, p_tle);
+}
+
/*******************************************************************************
**
** Function bta_sys_disable
#include "btc_config.h"
#include "alarm.h"
#include "btc_ble_storage.h"
-#include "bta_gatt_common.h"
#include "btc_gap_ble.h"
#include "bta_gattc_int.h"
#include "bta_gatts_int.h"
future_ready(*btc_main_get_future_p(BTC_MAIN_DEINIT_FUTURE), FUTURE_SUCCESS);
}
-static void btc_set_local_mtu(uint16_t mtu)
-{
- BTA_GATT_SetLocalMTU(mtu);
-}
-
void btc_main_call_handler(btc_msg_t *msg)
{
LOG_DEBUG("%s act %d\n", __func__, msg->act);
case BTC_MAIN_ACT_DISABLE:
btc_disable_bluetooth();
break;
- case BTC_GATT_ACT_SET_LOCAL_MTU:
- {
- btc_ble_main_args_t *arg = (btc_ble_main_args_t *)(msg->arg);
- btc_set_local_mtu(arg->set_mtu.mtu);
- break;
- }
default:
LOG_ERROR("%s UNKNOWN ACT %d\n", __func__, msg->act);
break;
#include "btc_dev.h"
#include "btc_gatts.h"
#include "btc_gattc.h"
+#include "btc_gatt_common.h"
#include "btc_gap_ble.h"
#include "btc_blufi_prf.h"
#include "btc_dm.h"
static xQueueHandle xBtcQueue = 0;
static btc_func_t profile_tab[BTC_PID_NUM] = {
- [BTC_PID_MAIN_INIT] = {btc_main_call_handler, NULL },
- [BTC_PID_DEV] = {btc_dev_call_handler, NULL },
+ [BTC_PID_MAIN_INIT] = {btc_main_call_handler, NULL },
+ [BTC_PID_DEV] = {btc_dev_call_handler, NULL },
#if (GATTS_INCLUDED == TRUE)
- [BTC_PID_GATTS] = {btc_gatts_call_handler, btc_gatts_cb_handler },
+ [BTC_PID_GATTS] = {btc_gatts_call_handler, btc_gatts_cb_handler },
#endif ///GATTS_INCLUDED == TRUE
#if (GATTC_INCLUDED == TRUE)
- [BTC_PID_GATTC] = {btc_gattc_call_handler, btc_gattc_cb_handler },
+ [BTC_PID_GATTC] = {btc_gattc_call_handler, btc_gattc_cb_handler },
#endif ///GATTC_INCLUDED == TRUE
- [BTC_PID_GAP_BLE] = {btc_gap_ble_call_handler, btc_gap_ble_cb_handler },
- [BTC_PID_BLE_HID] = {NULL, NULL},
- [BTC_PID_SPPLIKE] = {NULL, NULL},
+#if (GATTS_INCLUDED == TRUE || GATTC_INCLUDED == TRUE)
+ [BTC_PID_GATT_COMMON] = {btc_gatt_com_call_handler, NULL },
+#endif //GATTC_INCLUDED == TRUE || GATTS_INCLUDED == TRUE
+ [BTC_PID_GAP_BLE] = {btc_gap_ble_call_handler, btc_gap_ble_cb_handler },
+ [BTC_PID_BLE_HID] = {NULL, NULL},
+ [BTC_PID_SPPLIKE] = {NULL, NULL},
#if (GATTS_INCLUDED == TRUE)
- [BTC_PID_BLUFI] = {btc_blufi_call_handler, btc_blufi_cb_handler },
+ [BTC_PID_BLUFI] = {btc_blufi_call_handler, btc_blufi_cb_handler },
#endif ///GATTS_INCLUDED == TRUE
- [BTC_PID_DM_SEC] = {NULL, btc_dm_sec_cb_handler },
- [BTC_PID_ALARM] = {btc_alarm_handler, NULL },
+ [BTC_PID_DM_SEC] = {NULL, btc_dm_sec_cb_handler },
+ [BTC_PID_ALARM] = {btc_alarm_handler, NULL },
#if CONFIG_CLASSIC_BT_ENABLED
#if (BTC_GAP_BT_INCLUDED == TRUE)
- [BTC_PID_GAP_BT] = {btc_gap_bt_call_handler, NULL },
+ [BTC_PID_GAP_BT] = {btc_gap_bt_call_handler, NULL },
#endif /* (BTC_GAP_BT_INCLUDED == TRUE) */
- [BTC_PID_PRF_QUE] = {btc_profile_queue_handler, NULL },
+ [BTC_PID_PRF_QUE] = {btc_profile_queue_handler, NULL },
#if BTC_AV_INCLUDED
- [BTC_PID_A2DP] = {btc_a2dp_call_handler, btc_a2dp_cb_handler },
- [BTC_PID_AVRC] = {btc_avrc_call_handler, NULL },
+ [BTC_PID_A2DP] = {btc_a2dp_call_handler, btc_a2dp_cb_handler },
+ [BTC_PID_AVRC] = {btc_avrc_call_handler, NULL },
#endif /* #if BTC_AV_INCLUDED */
#if CONFIG_BT_SPP_ENABLED
- [BTC_PID_SPP] = {btc_spp_call_handler, btc_spp_cb_handler },
+ [BTC_PID_SPP] = {btc_spp_call_handler, btc_spp_cb_handler },
#endif /* #if CONFIG_BT_SPP_ENABLED */
#endif /* #if CONFIG_CLASSIC_BT_ENABLED */
};
BTC_MAIN_ACT_DEINIT,
BTC_MAIN_ACT_ENABLE,
BTC_MAIN_ACT_DISABLE,
- BTC_GATT_ACT_SET_LOCAL_MTU,
} btc_main_act_t;
typedef enum {
void btc_deinit_bluetooth(future_t *future);
#endif
-/* btc_ble_gattc_args_t */
-typedef union {
- //BTC_GATT_ACT_SET_LOCAL_MTU,
- struct set_mtu_arg {
- uint16_t mtu;
- } set_mtu;
-} btc_ble_main_args_t;
-
void btc_main_call_handler(btc_msg_t *msg);
#endif /* __BTC_BT_MAIN_H__ */
#if (GATTC_INCLUDED == TRUE)
BTC_PID_GATTC,
#endif ///GATTC_INCLUDED == TRUE
+ BTC_PID_GATT_COMMON,
BTC_PID_GAP_BLE,
BTC_PID_BLE_HID,
BTC_PID_SPPLIKE,
APPL_TRACE_EVENT("## A2DP SINK START MEDIA THREAD ##");
- btc_aa_snk_queue_set = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN);
+ btc_aa_snk_queue_set = xQueueCreateSet(BTC_A2DP_SINK_TASK_QUEUE_SET_LEN);
configASSERT(btc_aa_snk_queue_set);
- btc_aa_snk_data_queue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(int32_t));
+ btc_aa_snk_data_queue = xQueueCreate(BTC_A2DP_SINK_DATA_QUEUE_LEN, sizeof(int32_t));
configASSERT(btc_aa_snk_data_queue);
xQueueAddToSet(btc_aa_snk_data_queue, btc_aa_snk_queue_set);
- btc_aa_snk_ctrl_queue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *));
+ btc_aa_snk_ctrl_queue = xQueueCreate(BTC_A2DP_SINK_CTRL_QUEUE_LEN, sizeof(void *));
configASSERT(btc_aa_snk_ctrl_queue);
xQueueAddToSet(btc_aa_snk_ctrl_queue, btc_aa_snk_queue_set);
goto error_exit;
}
- xTaskCreatePinnedToCore(btc_a2dp_sink_task_handler, BTC_MEDIA_TASK_NAME, BTC_MEDIA_TASK_STACK_SIZE, NULL, BTC_MEDIA_TASK_PRIO, &btc_aa_snk_task_hdl, BTC_MEDIA_TASK_PINNED_TO_CORE);
+ xTaskCreatePinnedToCore(btc_a2dp_sink_task_handler, BTC_A2DP_SINK_TASK_NAME, BTC_A2DP_SINK_TASK_STACK_SIZE, NULL, BTC_A2DP_SINK_TASK_PRIO, &btc_aa_snk_task_hdl, BTC_A2DP_SINK_TASK_PINNED_TO_CORE);
if (btc_aa_snk_task_hdl == NULL) {
goto error_exit;
}
APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##");
- btc_aa_src_queue_set = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN);
+ btc_aa_src_queue_set = xQueueCreateSet(BTC_A2DP_SOURCE_TASK_QUEUE_SET_LEN);
configASSERT(btc_aa_src_queue_set);
- btc_aa_src_data_queue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(void *));
+ btc_aa_src_data_queue = xQueueCreate(BTC_A2DP_SOURCE_DATA_QUEUE_LEN, sizeof(void *));
configASSERT(btc_aa_src_data_queue);
xQueueAddToSet(btc_aa_src_data_queue, btc_aa_src_queue_set);
- btc_aa_src_ctrl_queue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *));
+ btc_aa_src_ctrl_queue = xQueueCreate(BTC_A2DP_SOURCE_CTRL_QUEUE_LEN, sizeof(void *));
configASSERT(btc_aa_src_ctrl_queue);
xQueueAddToSet(btc_aa_src_ctrl_queue, btc_aa_src_queue_set);
goto error_exit;
}
- xTaskCreatePinnedToCore(btc_a2dp_source_task_handler, BTC_MEDIA_TASK_NAME, BTC_MEDIA_TASK_STACK_SIZE, NULL, BTC_MEDIA_TASK_PRIO, &btc_aa_src_task_hdl, BTC_MEDIA_TASK_PINNED_TO_CORE);
+ xTaskCreatePinnedToCore(btc_a2dp_source_task_handler, BTC_A2DP_SOURCE_TASK_NAME, BTC_A2DP_SOURCE_TASK_STACK_SIZE, NULL, BTC_A2DP_SOURCE_TASK_PRIO, &btc_aa_src_task_hdl, BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE);
if (btc_aa_src_task_hdl == NULL) {
goto error_exit;
}
--- /dev/null
+// 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 "btc_task.h"
+#include "btc_main.h"
+#include "btc_dm.h"
+#include "future.h"
+#include "esp_err.h"
+#include "btc_config.h"
+#include "alarm.h"
+#include "btc_ble_storage.h"
+#include "btc_gatt_common.h"
+#include "bta_gatt_common.h"
+
+
+static void btc_set_local_mtu(uint16_t mtu)
+{
+ BTA_GATT_SetLocalMTU(mtu);
+}
+
+void btc_gatt_com_call_handler(btc_msg_t *msg)
+{
+ LOG_DEBUG("%s act %d\n", __func__, msg->act);
+ switch (msg->act) {
+ case BTC_GATT_ACT_SET_LOCAL_MTU:
+ {
+ btc_ble_gatt_com_args_t *arg = (btc_ble_gatt_com_args_t *)(msg->arg);
+ btc_set_local_mtu(arg->set_mtu.mtu);
+ break;
+ }
+ default:
+ LOG_ERROR("%s UNKNOWN ACT %d\n", __func__, msg->act);
+ break;
+ }
+}
+
--- /dev/null
+// 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 __BTC_GATT_COMMON_H__
+#define __BTC_GATT_COMMON_H__
+
+#include "future.h"
+#include "bt_types.h"
+#include "bta_api.h"
+#include "btc_main.h"
+#include "btc_task.h"
+
+typedef enum {
+ BTC_GATT_ACT_SET_LOCAL_MTU = 0,
+} btc_gatt_com_act_t;
+
+/* btc_ble_gattc_args_t */
+typedef union {
+ //BTC_GATT_ACT_SET_LOCAL_MTU,
+ struct set_mtu_arg {
+ uint16_t mtu;
+ } set_mtu;
+} btc_ble_gatt_com_args_t;
+
+void btc_gatt_com_call_handler(btc_msg_t *msg);
+#endif /* __BTC_GATT_COMMON_H__ */
#define BTC_TASK_PRIO (configMAX_PRIORITIES - 6)
#define BTC_TASK_QUEUE_LEN 60
-#define BTC_MEDIA_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
-#define BTC_MEDIA_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE)
-#define BTC_MEDIA_TASK_NAME "BtcMediaT"
-#define BTC_MEDIA_TASK_PRIO (configMAX_PRIORITIES - 3)
-#define BTC_MEDIA_DATA_QUEUE_LEN (3)
-#define BTC_MEDIA_CTRL_QUEUE_LEN (5)
-#define BTC_MEDIA_TASK_QUEUE_SET_LEN (BTC_MEDIA_DATA_QUEUE_LEN + BTC_MEDIA_CTRL_QUEUE_LEN)
+#define BTC_A2DP_SINK_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
+#define BTC_A2DP_SINK_TASK_STACK_SIZE (CONFIG_A2DP_SINK_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig
+#define BTC_A2DP_SINK_TASK_NAME "BtA2dSinkT"
+#define BTC_A2DP_SINK_TASK_PRIO (configMAX_PRIORITIES - 3)
+#define BTC_A2DP_SINK_DATA_QUEUE_LEN (3)
+#define BTC_A2DP_SINK_CTRL_QUEUE_LEN (5)
+#define BTC_A2DP_SINK_TASK_QUEUE_SET_LEN (BTC_A2DP_SINK_DATA_QUEUE_LEN + BTC_A2DP_SINK_CTRL_QUEUE_LEN)
+
+#define BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
+#define BTC_A2DP_SOURCE_TASK_STACK_SIZE (CONFIG_A2DP_SOURCE_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig
+#define BTC_A2DP_SOURCE_TASK_NAME "BtA2dSourceT"
+#define BTC_A2DP_SOURCE_TASK_PRIO (configMAX_PRIORITIES - 3)
+#define BTC_A2DP_SOURCE_DATA_QUEUE_LEN (3)
+#define BTC_A2DP_SOURCE_CTRL_QUEUE_LEN (5)
+#define BTC_A2DP_SOURCE_TASK_QUEUE_SET_LEN (BTC_A2DP_SOURCE_DATA_QUEUE_LEN + BTC_A2DP_SOURCE_CTRL_QUEUE_LEN)
#define TASK_POST_NON_BLOCKING (0)
#define TASK_POST_BLOCKING (portMAX_DELAY)
UNUSED(p_data);
AVDT_TRACE_DEBUG("avdt_ccb_dealloc %d\n", avdt_ccb_to_idx(p_ccb));
- btu_stop_timer(&p_ccb->timer_entry);
+ btu_free_timer(&p_ccb->timer_entry);
fixed_queue_free(p_ccb->cmd_q, NULL);
fixed_queue_free(p_ccb->rsp_q, NULL);
memset(p_ccb, 0, sizeof(tAVDT_CCB));
UNUSED(p_data);
AVDT_TRACE_DEBUG("avdt_scb_dealloc hdl=%d\n", avdt_scb_to_hdl(p_scb));
- btu_stop_timer(&p_scb->timer_entry);
+ btu_free_timer(&p_scb->timer_entry);
#if AVDT_MULTIPLEXING == TRUE
/* free fragments we're holding, if any; it shouldn't happen */
} else if (p_cb->p_stop_adv_cb && p_cb->adv_mode == BTM_BLE_ADV_DISABLE) {
p_cb->state = BTM_BLE_STOP_ADV;
(*p_cb->p_stop_adv_cb)(status);
+ }else {
+ // p_cb->p_adv_cb is NULL or p_cb->p_stop_adv_cb is NULL
+ if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) {
+ p_cb->state = BTM_BLE_ADVERTISING;
+ }else {
+ p_cb->state = BTM_BLE_STOP_ADV;
+ }
+ p_cb->adv_callback_twice = FALSE;
}
/* if write adv enable/disbale not succeed */
if (*p != HCI_SUCCESS) {
BTM_TRACE_DEBUG("%s", __func__);
+ btu_free_timer(&p_cb->obs_timer_ent);
+ btu_free_timer(&p_cb->scan_timer_ent);
+ btu_free_timer(&p_cb->inq_var.fast_adv_timer);
memset(p_cb, 0, sizeof(tBTM_BLE_CB));
memset(&(btm_cb.cmn_ble_vsc_cb), 0 , sizeof(tBTM_BLE_VSC_CB));
btm_cb.cmn_ble_vsc_cb.values_read = FALSE;
UINT8 status;
UNUSED(evt_len);
- btu_stop_timer (&btm_cb.devcb.rln_timer);
+ btu_free_timer (&btm_cb.devcb.rln_timer);
/* If there was a callback address for read local name, call it */
btm_cb.devcb.p_rln_cmpl_cb = NULL;
#if 0 /* cleared in btm_init; put back in if called from anywhere else! */
memset (&btm_cb.btm_inq_vars, 0, sizeof (tBTM_INQUIRY_VAR_ST));
#endif
+
+ btu_free_timer(&btm_cb.btm_inq_vars.rmt_name_timer_ent);
+ memset(&btm_cb.btm_inq_vars.rmt_name_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+ btu_free_timer(&btm_cb.btm_inq_vars.inq_timer_ent);
+ memset(&btm_cb.btm_inq_vars.inq_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+
btm_cb.btm_inq_vars.no_inc_ssp = BTM_NO_SSP_ON_INQUIRY;
}
{
assert(p_tle != NULL);
- if (p_tle->in_use == FALSE) {
- return;
- }
p_tle->in_use = FALSE;
// Get the alarm for the timer list entry.
osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle);
if (alarm == NULL) {
- LOG_WARN("%s Unable to find expected alarm in hashmap", __func__);
+ LOG_DEBUG("%s Unable to find expected alarm in hashmap", __func__);
return;
}
osi_alarm_cancel(alarm);
}
osi_alarm_cancel(alarm);
}
+
+void btu_free_quick_timer(TIMER_LIST_ENT *p_tle)
+{
+ assert(p_tle != NULL);
+
+ p_tle->in_use = FALSE;
+
+ // Get the alarm for the timer list entry.
+ osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle);
+ if (alarm == NULL) {
+ LOG_DEBUG("%s Unable to find expected alarm in hashmap", __func__);
+ return;
+ }
+ osi_alarm_cancel(alarm);
+ hash_map_erase(btu_l2cap_alarm_hash_map, p_tle);
+}
+
#endif /* defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) */
void btu_oneshot_alarm_cb(void *data)
fixed_queue_free(gatt_cb.tcb[i].pending_ind_q, NULL);
gatt_cb.tcb[i].pending_ind_q = NULL;
+
+ btu_free_timer(&gatt_cb.tcb[i].conf_timer_ent);
+ memset(&gatt_cb.tcb[i].conf_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+
+ btu_free_timer(&gatt_cb.tcb[i].ind_ack_timer_ent);
+ memset(&gatt_cb.tcb[i].ind_ack_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+
#if (GATTS_INCLUDED == TRUE)
fixed_queue_free(gatt_cb.tcb[i].sr_cmd.multi_rsp_q, NULL);
gatt_cb.tcb[i].sr_cmd.multi_rsp_q = NULL;
void gatt_clcb_dealloc (tGATT_CLCB *p_clcb)
{
if (p_clcb && p_clcb->in_use) {
+ btu_free_timer(&p_clcb->rsp_timer_ent);
memset(p_clcb, 0, sizeof(tGATT_CLCB));
}
}
}
}
- btu_stop_timer (&p_tcb->ind_ack_timer_ent);
- btu_stop_timer (&p_tcb->conf_timer_ent);
+ btu_free_timer (&p_tcb->ind_ack_timer_ent);
+ btu_free_timer (&p_tcb->conf_timer_ent);
gatt_free_pending_ind(p_tcb);
gatt_free_pending_enc_queue(p_tcb);
gatt_free_pending_prepare_write_queue(p_tcb);
#if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0)
void btu_start_quick_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout);
void btu_stop_quick_timer (TIMER_LIST_ENT *p_tle);
+void btu_free_quick_timer (TIMER_LIST_ENT *p_tle);
void btu_process_quick_timer_evt (void);
#endif
extern UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
extern void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb);
extern void l2c_fcr_stop_timer (tL2C_CCB *p_ccb);
-
+extern void l2c_fcr_free_timer (tL2C_CCB *p_ccb);
/* Functions provided by l2c_ble.c
************************************
*/
}
}
+/*******************************************************************************
+**
+** Function l2c_fcr_free_timer
+**
+** Description This function releases the (monitor or transmission) timer.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_free_timer (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+}
+
/*******************************************************************************
**
** Function l2c_fcr_cleanup
fixed_queue_free(p_fcrb->retrans_q, osi_free_func);
p_fcrb->retrans_q = NULL;
- btu_stop_quick_timer (&p_fcrb->ack_timer);
- btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
-
+ btu_free_quick_timer (&p_fcrb->ack_timer);
+ memset(&p_fcrb->ack_timer, 0, sizeof(TIMER_LIST_ENT));
+
+ btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+ memset(&p_fcrb->mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT));
+
#if (L2CAP_ERTM_STATS == TRUE)
if ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) ) {
UINT32 dur = osi_time_get_os_boottime_ms() - p_ccb->fcrb.connect_tick_count;
for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) {
if (!p_lcb->in_use) {
+ btu_free_timer(&p_lcb->timer_entry);
+ btu_free_timer(&p_lcb->info_timer_entry);
+ btu_free_timer(&p_lcb->upda_con_timer);
+
memset (p_lcb, 0, sizeof (tL2C_LCB));
-
memcpy (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN);
p_lcb->in_use = TRUE;
p_lcb->in_use = FALSE;
p_lcb->is_bonding = FALSE;
- /* Stop timers */
- btu_stop_timer (&p_lcb->timer_entry);
- btu_stop_timer (&p_lcb->info_timer_entry);
-
+ /* Stop and release timers */
+ btu_free_timer (&p_lcb->timer_entry);
+ memset(&p_lcb->timer_entry, 0, sizeof(TIMER_LIST_ENT));
+ btu_free_timer (&p_lcb->info_timer_entry);
+ memset(&p_lcb->info_timer_entry, 0, sizeof(TIMER_LIST_ENT));
+ btu_free_timer(&p_lcb->upda_con_timer);
+ memset(&p_lcb->upda_con_timer, 0, sizeof(TIMER_LIST_ENT));
+
/* Release any unfinished L2CAP packet on this link */
if (p_lcb->p_hcit_rcv_acl) {
osi_free(p_lcb->p_hcit_rcv_acl);
memset (&p_ccb->ertm_info, 0, sizeof(tL2CAP_ERTM_INFO));
p_ccb->peer_cfg_already_rejected = FALSE;
p_ccb->fcr_cfg_tries = L2CAP_MAX_FCR_CFG_TRIES;
- p_ccb->fcrb.ack_timer.param = (TIMER_PARAM_TYPE)p_ccb;
-
- /* if timer is running, remove it from timer list */
- if (p_ccb->fcrb.ack_timer.in_use) {
- btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
- }
+ /* stop and release timers */
+ btu_free_quick_timer(&p_ccb->fcrb.ack_timer);
+ memset(&p_ccb->fcrb.ack_timer, 0, sizeof(TIMER_LIST_ENT));
+ p_ccb->fcrb.ack_timer.param = (TIMER_PARAM_TYPE)p_ccb;
+
+ btu_free_quick_timer(&p_ccb->fcrb.mon_retrans_timer);
+ memset(&p_ccb->fcrb.mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT));
p_ccb->fcrb.mon_retrans_timer.param = (TIMER_PARAM_TYPE)p_ccb;
// btla-specific ++
/* CSP408639 Fix: When L2CAP send amp move channel request or receive
* L2CEVT_AMP_MOVE_REQ do following sequence. Send channel move
* request -> Stop retrans/monitor timer -> Change channel state to CST_AMP_MOVING. */
- if (p_ccb->fcrb.mon_retrans_timer.in_use) {
- btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
- }
// btla-specific --
+
#if (CLASSIC_BT_INCLUDED == TRUE)
- l2c_fcr_stop_timer (p_ccb);
+ l2c_fcr_free_timer (p_ccb);
#endif ///CLASSIC_BT_INCLUDED == TRUE
p_ccb->ertm_info.preferred_mode = L2CAP_FCR_BASIC_MODE; /* Default mode for channel is basic mode */
p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_BASIC; /* Default mode for channel is basic mode */
p_ccb->is_flushable = FALSE;
#endif
+ btu_free_timer(&p_ccb->timer_entry);
+ memset(&p_ccb->timer_entry, 0, sizeof(TIMER_LIST_ENT));
p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb;
p_ccb->timer_entry.in_use = 0;
btm_sec_clr_temp_auth_service (p_lcb->remote_bd_addr);
}
- /* Stop the timer */
- btu_stop_timer (&p_ccb->timer_entry);
-
+ /* Stop and free the timer */
+ btu_free_timer (&p_ccb->timer_entry);
fixed_queue_free(p_ccb->xmit_hold_q, osi_free_func);
p_ccb->xmit_hold_q = NULL;
extern void rfc_release_multiplexer_channel (tRFC_MCB *p_rfc_mcb);
extern void rfc_timer_start (tRFC_MCB *p_rfc_mcb, UINT16 timeout);
extern void rfc_timer_stop (tRFC_MCB *p_rfc_mcb);
+extern void rfc_timer_free (tRFC_MCB *p_rfc_mcb);
extern void rfc_port_timer_start (tPORT *p_port, UINT16 tout);
extern void rfc_port_timer_stop (tPORT *p_port);
+extern void rfc_port_timer_free (tPORT *p_port);
BOOLEAN rfc_check_uih_fcs (UINT8 dlci, UINT8 received_fcs);
BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs);
memcpy (p_port->bd_addr, BT_BD_ANY, BD_ADDR_LEN);
} else {
RFCOMM_TRACE_DEBUG ("port_release_port:Clean-up handle:%d", p_port->inx);
+ rfc_port_timer_free (p_port);
memset (p_port, 0, sizeof (tPORT));
}
}
}
-#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
\ No newline at end of file
+#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
if (rfc_cb.port.rfc_mcb[j].state == RFC_MX_STATE_IDLE) {
/* New multiplexer control block */
fixed_queue_free(p_mcb->cmd_q, NULL);
+ rfc_timer_free(p_mcb);
memset (p_mcb, 0, sizeof (tRFC_MCB));
memcpy (p_mcb->bd_addr, bd_addr, BD_ADDR_LEN);
RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, create new p_mcb:%p, index:%d",
void rfc_release_multiplexer_channel (tRFC_MCB *p_mcb)
{
- rfc_timer_stop (p_mcb);
+ rfc_timer_free (p_mcb);
fixed_queue_free(p_mcb->cmd_q, osi_free_fun);
btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_MFC, timeout);
}
-
/*******************************************************************************
**
** Function rfc_timer_stop
btu_stop_timer (&p_mcb->tle);
}
+/*******************************************************************************
+**
+** Function rfc_timer_free
+**
+** Description Stop and free RFC Timer
+**
+*******************************************************************************/
+void rfc_timer_free (tRFC_MCB *p_mcb)
+{
+ RFCOMM_TRACE_EVENT ("rfc_timer_free");
+
+ btu_free_timer (&p_mcb->tle);
+ memset(&p_mcb->tle, 0, sizeof(TIMER_LIST_ENT));
+}
/*******************************************************************************
**
btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_PORT, timeout);
}
-
/*******************************************************************************
**
** Function rfc_port_timer_stop
{
RFCOMM_TRACE_EVENT ("rfc_port_timer_stop");
- btu_free_timer (&p_port->rfc.tle);
+ btu_stop_timer (&p_port->rfc.tle);
}
+/*******************************************************************************
+**
+** Function rfc_port_timer_free
+**
+** Description Stop and free RFC Timer
+**
+*******************************************************************************/
+void rfc_port_timer_free (tPORT *p_port)
+{
+ RFCOMM_TRACE_EVENT ("rfc_port_timer_stop");
+
+ btu_free_timer (&p_port->rfc.tle);
+ memset(&p_port->rfc.tle, 0, sizeof(TIMER_LIST_ENT));
+}
/*******************************************************************************
**
}
-#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
\ No newline at end of file
+#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
/* Look through each connection control block for a free one */
for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) {
if (p_ccb->con_state == SDP_STATE_IDLE) {
+ btu_free_timer(&p_ccb->timer_entry);
memset (p_ccb, 0, sizeof (tCONN_CB));
p_ccb->timer_entry.param = (UINT32) p_ccb;
*******************************************************************************/
void sdpu_release_ccb (tCONN_CB *p_ccb)
{
- /* Ensure timer is stopped */
- btu_stop_timer (&p_ccb->timer_entry);
+ /* Ensure timer is stopped and released */
+ btu_free_timer(&p_ccb->timer_entry);
/* Drop any response pointer we may be holding */
p_ccb->con_state = SDP_STATE_IDLE;
-Subproject commit 429496d568d4dd052d9659545dd3f0936d5529ca
+Subproject commit 0575731e349ca54b46cfc084a752790c1ddcdbba
template<> int SlowInit<2>::mInitCount = 0;
template<int obj>
-static void start_slow_init_task(int id, int affinity)
+static int start_slow_init_task(int id, int affinity)
{
- xTaskCreatePinnedToCore(&SlowInit<obj>::task, "slow_init", 2048,
- reinterpret_cast<void*>(id), 3, NULL, affinity);
+ return xTaskCreatePinnedToCore(&SlowInit<obj>::task, "slow_init", 2048,
+ reinterpret_cast<void*>(id), 3, NULL, affinity) ? 1 : 0;
}
TEST_CASE("static initialization guards work as expected", "[cxx]")
{
s_slow_init_sem = xSemaphoreCreateCounting(10, 0);
TEST_ASSERT_NOT_NULL(s_slow_init_sem);
+ int task_count = 0;
// 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);
+ task_count += start_slow_init_task<1>(0, PRO_CPU_NUM);
+#if portNUM_PROCESSORS == 2
+ task_count += start_slow_init_task<1>(1, APP_CPU_NUM);
+#endif
+ task_count += start_slow_init_task<1>(2, PRO_CPU_NUM);
+ task_count += 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);
+ task_count += start_slow_init_task<2>(0, PRO_CPU_NUM);
+#if portNUM_PROCESSORS == 2
+ task_count += start_slow_init_task<2>(1, APP_CPU_NUM);
+#endif
+ task_count += start_slow_init_task<2>(2, PRO_CPU_NUM);
+ task_count += start_slow_init_task<2>(3, tskNO_AFFINITY);
// All tasks should
- for (int i = 0; i < 8; ++i) {
+ for (int i = 0; i < task_count; ++i) {
TEST_ASSERT_TRUE(xSemaphoreTake(s_slow_init_sem, 500/portTICK_PERIOD_MS));
}
vSemaphoreDelete(s_slow_init_sem);
}
const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = {
- GPIO_PIN_REG_0,
- GPIO_PIN_REG_1,
- GPIO_PIN_REG_2,
- GPIO_PIN_REG_3,
- GPIO_PIN_REG_4,
- GPIO_PIN_REG_5,
- GPIO_PIN_REG_6,
- GPIO_PIN_REG_7,
- GPIO_PIN_REG_8,
- GPIO_PIN_REG_9,
- GPIO_PIN_REG_10,
- GPIO_PIN_REG_11,
- GPIO_PIN_REG_12,
- GPIO_PIN_REG_13,
- GPIO_PIN_REG_14,
- GPIO_PIN_REG_15,
- GPIO_PIN_REG_16,
- GPIO_PIN_REG_17,
- GPIO_PIN_REG_18,
- GPIO_PIN_REG_19,
+ IO_MUX_GPIO0_REG,
+ IO_MUX_GPIO1_REG,
+ IO_MUX_GPIO2_REG,
+ IO_MUX_GPIO3_REG,
+ IO_MUX_GPIO4_REG,
+ IO_MUX_GPIO5_REG,
+ IO_MUX_GPIO6_REG,
+ IO_MUX_GPIO7_REG,
+ IO_MUX_GPIO8_REG,
+ IO_MUX_GPIO9_REG,
+ IO_MUX_GPIO10_REG,
+ IO_MUX_GPIO11_REG,
+ IO_MUX_GPIO12_REG,
+ IO_MUX_GPIO13_REG,
+ IO_MUX_GPIO14_REG,
+ IO_MUX_GPIO15_REG,
+ IO_MUX_GPIO16_REG,
+ IO_MUX_GPIO17_REG,
+ IO_MUX_GPIO18_REG,
+ IO_MUX_GPIO19_REG,
0,
- GPIO_PIN_REG_21,
- GPIO_PIN_REG_22,
- GPIO_PIN_REG_23,
+ IO_MUX_GPIO21_REG,
+ IO_MUX_GPIO22_REG,
+ IO_MUX_GPIO23_REG,
0,
- GPIO_PIN_REG_25,
- GPIO_PIN_REG_26,
- GPIO_PIN_REG_27,
+ IO_MUX_GPIO25_REG,
+ IO_MUX_GPIO26_REG,
+ IO_MUX_GPIO27_REG,
0,
0,
0,
0,
- GPIO_PIN_REG_32,
- GPIO_PIN_REG_33,
- GPIO_PIN_REG_34,
- GPIO_PIN_REG_35,
- GPIO_PIN_REG_36,
- GPIO_PIN_REG_37,
- GPIO_PIN_REG_38,
- GPIO_PIN_REG_39
+ IO_MUX_GPIO32_REG,
+ IO_MUX_GPIO33_REG,
+ IO_MUX_GPIO34_REG,
+ IO_MUX_GPIO35_REG,
+ IO_MUX_GPIO36_REG,
+ IO_MUX_GPIO37_REG,
+ IO_MUX_GPIO38_REG,
+ IO_MUX_GPIO39_REG,
};
typedef struct {
#define GPIO_SEL_38 ((uint64_t)(((uint64_t)1)<<38)) /*!< Pin 38 selected */
#define GPIO_SEL_39 ((uint64_t)(((uint64_t)1)<<39)) /*!< Pin 39 selected */
-#define GPIO_PIN_REG_0 PERIPHS_IO_MUX_GPIO0_U
-#define GPIO_PIN_REG_1 PERIPHS_IO_MUX_U0TXD_U
-#define GPIO_PIN_REG_2 PERIPHS_IO_MUX_GPIO2_U
-#define GPIO_PIN_REG_3 PERIPHS_IO_MUX_U0RXD_U
-#define GPIO_PIN_REG_4 PERIPHS_IO_MUX_GPIO4_U
-#define GPIO_PIN_REG_5 PERIPHS_IO_MUX_GPIO5_U
-#define GPIO_PIN_REG_6 PERIPHS_IO_MUX_SD_CLK_U
-#define GPIO_PIN_REG_7 PERIPHS_IO_MUX_SD_DATA0_U
-#define GPIO_PIN_REG_8 PERIPHS_IO_MUX_SD_DATA1_U
-#define GPIO_PIN_REG_9 PERIPHS_IO_MUX_SD_DATA2_U
-#define GPIO_PIN_REG_10 PERIPHS_IO_MUX_SD_DATA3_U
-#define GPIO_PIN_REG_11 PERIPHS_IO_MUX_SD_CMD_U
-#define GPIO_PIN_REG_12 PERIPHS_IO_MUX_MTDI_U
-#define GPIO_PIN_REG_13 PERIPHS_IO_MUX_MTCK_U
-#define GPIO_PIN_REG_14 PERIPHS_IO_MUX_MTMS_U
-#define GPIO_PIN_REG_15 PERIPHS_IO_MUX_MTDO_U
-#define GPIO_PIN_REG_16 PERIPHS_IO_MUX_GPIO16_U
-#define GPIO_PIN_REG_17 PERIPHS_IO_MUX_GPIO17_U
-#define GPIO_PIN_REG_18 PERIPHS_IO_MUX_GPIO18_U
-#define GPIO_PIN_REG_19 PERIPHS_IO_MUX_GPIO19_U
-#define GPIO_PIN_REG_20 PERIPHS_IO_MUX_GPIO20_U
-#define GPIO_PIN_REG_21 PERIPHS_IO_MUX_GPIO21_U
-#define GPIO_PIN_REG_22 PERIPHS_IO_MUX_GPIO22_U
-#define GPIO_PIN_REG_23 PERIPHS_IO_MUX_GPIO23_U
-#define GPIO_PIN_REG_25 PERIPHS_IO_MUX_GPIO25_U
-#define GPIO_PIN_REG_26 PERIPHS_IO_MUX_GPIO26_U
-#define GPIO_PIN_REG_27 PERIPHS_IO_MUX_GPIO27_U
-#define GPIO_PIN_REG_32 PERIPHS_IO_MUX_GPIO32_U
-#define GPIO_PIN_REG_33 PERIPHS_IO_MUX_GPIO33_U
-#define GPIO_PIN_REG_34 PERIPHS_IO_MUX_GPIO34_U
-#define GPIO_PIN_REG_35 PERIPHS_IO_MUX_GPIO35_U
-#define GPIO_PIN_REG_36 PERIPHS_IO_MUX_GPIO36_U
-#define GPIO_PIN_REG_37 PERIPHS_IO_MUX_GPIO37_U
-#define GPIO_PIN_REG_38 PERIPHS_IO_MUX_GPIO38_U
-#define GPIO_PIN_REG_39 PERIPHS_IO_MUX_GPIO39_U
+#define GPIO_PIN_REG_0 IO_MUX_GPIO0_REG
+#define GPIO_PIN_REG_1 IO_MUX_GPIO1_REG
+#define GPIO_PIN_REG_2 IO_MUX_GPIO2_REG
+#define GPIO_PIN_REG_3 IO_MUX_GPIO3_REG
+#define GPIO_PIN_REG_4 IO_MUX_GPIO4_REG
+#define GPIO_PIN_REG_5 IO_MUX_GPIO5_REG
+#define GPIO_PIN_REG_6 IO_MUX_GPIO6_REG
+#define GPIO_PIN_REG_7 IO_MUX_GPIO7_REG
+#define GPIO_PIN_REG_8 IO_MUX_GPIO8_REG
+#define GPIO_PIN_REG_9 IO_MUX_GPIO9_REG
+#define GPIO_PIN_REG_10 IO_MUX_GPIO10_REG
+#define GPIO_PIN_REG_11 IO_MUX_GPIO11_REG
+#define GPIO_PIN_REG_12 IO_MUX_GPIO12_REG
+#define GPIO_PIN_REG_13 IO_MUX_GPIO13_REG
+#define GPIO_PIN_REG_14 IO_MUX_GPIO14_REG
+#define GPIO_PIN_REG_15 IO_MUX_GPIO15_REG
+#define GPIO_PIN_REG_16 IO_MUX_GPIO16_REG
+#define GPIO_PIN_REG_17 IO_MUX_GPIO17_REG
+#define GPIO_PIN_REG_18 IO_MUX_GPIO18_REG
+#define GPIO_PIN_REG_19 IO_MUX_GPIO19_REG
+#define GPIO_PIN_REG_20 IO_MUX_GPIO20_REG
+#define GPIO_PIN_REG_21 IO_MUX_GPIO21_REG
+#define GPIO_PIN_REG_22 IO_MUX_GPIO22_REG
+#define GPIO_PIN_REG_23 IO_MUX_GPIO23_REG
+#define GPIO_PIN_REG_25 IO_MUX_GPIO25_REG
+#define GPIO_PIN_REG_26 IO_MUX_GPIO26_REG
+#define GPIO_PIN_REG_27 IO_MUX_GPIO27_REG
+#define GPIO_PIN_REG_32 IO_MUX_GPIO32_REG
+#define GPIO_PIN_REG_33 IO_MUX_GPIO33_REG
+#define GPIO_PIN_REG_34 IO_MUX_GPIO34_REG
+#define GPIO_PIN_REG_35 IO_MUX_GPIO35_REG
+#define GPIO_PIN_REG_36 IO_MUX_GPIO36_REG
+#define GPIO_PIN_REG_37 IO_MUX_GPIO37_REG
+#define GPIO_PIN_REG_38 IO_MUX_GPIO38_REG
+#define GPIO_PIN_REG_39 IO_MUX_GPIO39_REG
#define GPIO_APP_CPU_INTR_ENA (BIT(0))
#define GPIO_APP_CPU_NMI_INTR_ENA (BIT(1))
.io_voltage = 3.3f, \
.init = &sdmmc_host_init, \
.set_bus_width = &sdmmc_host_set_bus_width, \
+ .get_bus_width = &sdmmc_host_get_slot_width, \
.set_card_clk = &sdmmc_host_set_card_clk, \
.do_transaction = &sdmmc_host_do_transaction, \
.deinit = &sdmmc_host_deinit, \
*/
esp_err_t sdmmc_host_set_bus_width(int slot, size_t width);
+/**
+ * @brief Get bus width configured in ``sdmmc_host_init_slot`` to be used for data transfer
+ *
+ * @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
+ * @return configured bus width of the specified slot.
+ */
+size_t sdmmc_host_get_slot_width(int slot);
+
/**
* @brief Set card clock frequency
*
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 */
+ size_t (*get_bus_width)(int slot); /*!< host function to get 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 */
uint32_t d5;
uint32_t d6;
uint32_t d7;
+ uint8_t d3_gpio;
uint8_t card_detect;
uint8_t write_protect;
uint8_t width;
.d1 = PERIPHS_IO_MUX_SD_DATA1_U,
.d2 = PERIPHS_IO_MUX_SD_DATA2_U,
.d3 = PERIPHS_IO_MUX_SD_DATA3_U,
+ .d3_gpio = 10,
.d4 = PERIPHS_IO_MUX_GPIO16_U,
.d5 = PERIPHS_IO_MUX_GPIO17_U,
.d6 = PERIPHS_IO_MUX_GPIO5_U,
.d1 = PERIPHS_IO_MUX_GPIO4_U,
.d2 = PERIPHS_IO_MUX_MTDI_U,
.d3 = PERIPHS_IO_MUX_MTCK_U,
+ .d3_gpio = 13,
.card_detect = HOST_CARD_DETECT_N_2_IDX,
.write_protect = HOST_CARD_WRITE_PRT_2_IDX,
.width = 4
static intr_handle_t s_intr_handle;
static QueueHandle_t s_event_queue;
+size_t s_slot_width[2] = {1,1};
void sdmmc_host_reset()
{
else if (slot_width > pslot->width) {
return ESP_ERR_INVALID_ARG;
}
+ s_slot_width[slot] = slot_width;
configure_pin(pslot->clk);
configure_pin(pslot->cmd);
configure_pin(pslot->d0);
+
if (slot_width >= 4) {
configure_pin(pslot->d1);
configure_pin(pslot->d2);
- configure_pin(pslot->d3);
+ //force pull-up D3 to make slave detect SD mode. connect to peripheral after width configuration.
+ gpio_config_t gpio_conf = {
+ .pin_bit_mask = BIT(pslot->d3_gpio),
+ .mode = GPIO_MODE_OUTPUT ,
+ .pull_up_en = 0,
+ .pull_down_en = 0,
+ .intr_type = GPIO_INTR_DISABLE,
+ };
+ gpio_config( &gpio_conf );
+ gpio_set_level( pslot->d3_gpio, 1 );
if (slot_width == 8) {
configure_pin(pslot->d4);
configure_pin(pslot->d5);
} else if (width == 4) {
SDMMC.ctype.card_width_8 &= ~mask;
SDMMC.ctype.card_width |= mask;
+ configure_pin(s_slot_info[slot].d3); // D3 was set to GPIO high to force slave into SD 1-bit mode, until 4-bit mode is set
} else if (width == 8){
SDMMC.ctype.card_width_8 |= mask;
+ configure_pin(s_slot_info[slot].d3); // D3 was set to GPIO high to force slave into SD 1-bit mode, until 4-bit mode is set
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
+size_t sdmmc_host_get_slot_width(int slot)
+{
+ assert( slot == 0 || slot == 1 );
+ return s_slot_width[slot];
+}
+
static void sdmmc_host_dma_init()
{
SDMMC.ctrl.dma_enable = 1;
return ESP_OK;
}
-TEST_CASE("adc2 work with wifi","[adc]")
+TEST_CASE("adc2 work with wifi","[adc][ignore]")
{
int read_raw;
int target_value;
#define ESP_PARTITION_TABLE_ADDR 0x8000
#define ESP_PARTITION_MAGIC 0x50AA
+#define ESP_PARTITION_MAGIC_MD5 0xEBEB
/* OTA selection structure (two copies in the OTA data partition.)
Size of 32 bytes is friendly to flash encryption */
*libesp32.a:core_dump.o(.literal .text .literal.* .text.*)
*libapp_trace.a:(.literal .text .literal.* .text.*)
*libxtensa-debug-module.a:eri.o(.literal .text .literal.* .text.*)
- *libphy.a:(.literal .text .literal.* .text.*)
*librtc.a:(.literal .text .literal.* .text.*)
*libsoc.a:(.literal .text .literal.* .text.*)
*libhal.a:(.literal .text .literal.* .text.*)
-Subproject commit b35fc86bbc5f96e8d52cc32527dbfbabaed0d6fc
+Subproject commit 32402930b3b2d28bf894cba918d08446b04a38e6
/* declare the performance here */
-#define IDF_PERFORMANCE_MAX_HTTPS_REQUEST_BIN_SIZE 610
+#define IDF_PERFORMANCE_MAX_HTTPS_REQUEST_BIN_SIZE 800
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP 200
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_UNICORE 130
#define IDF_PERFORMANCE_MAX_ESP_TIMER_GET_TIME_PER_CALL 1000
+++ /dev/null
- Copyright (c) 2009 Dave Gamble
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-
--- /dev/null
+Subproject commit 7cc52f60356909b3dd260304c7c50c0693699353
#
# Component Makefile
#
-COMPONENT_ADD_INCLUDEDIRS := include port/include
-
-COMPONENT_SRCDIRS := library port
-
+COMPONENT_ADD_INCLUDEDIRS := cJSON
+COMPONENT_SRCDIRS := cJSON
+COMPONENT_SUBMODULES := cJSON
+++ /dev/null
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-*/
-
-#ifndef cJSON__h
-#define cJSON__h
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/* project version */
-#define CJSON_VERSION_MAJOR 1
-#define CJSON_VERSION_MINOR 6
-#define CJSON_VERSION_PATCH 0
-
-#include <stddef.h>
-
-/* cJSON Types: */
-#define cJSON_Invalid (0)
-#define cJSON_False (1 << 0)
-#define cJSON_True (1 << 1)
-#define cJSON_NULL (1 << 2)
-#define cJSON_Number (1 << 3)
-#define cJSON_String (1 << 4)
-#define cJSON_Array (1 << 5)
-#define cJSON_Object (1 << 6)
-#define cJSON_Raw (1 << 7) /* raw json */
-
-#define cJSON_IsReference 256
-#define cJSON_StringIsConst 512
-
-/* The cJSON structure: */
-typedef struct cJSON
-{
- /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
- struct cJSON *next;
- struct cJSON *prev;
- /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
- struct cJSON *child;
-
- /* The type of the item, as above. */
- int type;
-
- /* The item's string, if type==cJSON_String and type == cJSON_Raw */
- char *valuestring;
- /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
- int valueint;
- /* The item's number, if type==cJSON_Number */
- double valuedouble;
-
- /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
- char *string;
-} cJSON;
-
-typedef struct cJSON_Hooks
-{
- void *(*malloc_fn)(size_t sz);
- void (*free_fn)(void *ptr);
-} cJSON_Hooks;
-
-typedef int cJSON_bool;
-
-#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
-#define __WINDOWS__
-#endif
-#ifdef __WINDOWS__
-
-/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options:
-
-CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
-CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
-CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
-
-For *nix builds that support visibility attribute, you can define similar behavior by
-
-setting default visibility to hidden by adding
--fvisibility=hidden (for gcc)
-or
--xldscope=hidden (for sun cc)
-to CFLAGS
-
-then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
-
-*/
-
-/* export symbols by default, this is necessary for copy pasting the C and header file */
-#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
-#define CJSON_EXPORT_SYMBOLS
-#endif
-
-#if defined(CJSON_HIDE_SYMBOLS)
-#define CJSON_PUBLIC(type) type __stdcall
-#elif defined(CJSON_EXPORT_SYMBOLS)
-#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
-#elif defined(CJSON_IMPORT_SYMBOLS)
-#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall
-#endif
-#else /* !WIN32 */
-#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
-#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
-#else
-#define CJSON_PUBLIC(type) type
-#endif
-#endif
-
-/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
- * This is to prevent stack overflows. */
-#ifndef CJSON_NESTING_LIMIT
-#define CJSON_NESTING_LIMIT 1000
-#endif
-
-/* returns the version of cJSON as a string */
-CJSON_PUBLIC(const char*) cJSON_Version(void);
-
-/* Supply malloc, realloc and free functions to cJSON */
-CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
-
-/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
-/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
-CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
-/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
-/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
-
-/* Render a cJSON entity to text for transfer/storage. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
-/* Render a cJSON entity to text for transfer/storage without any formatting. */
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
-/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
-/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
-/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
-/* Delete a cJSON entity and all subentities. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
-
-/* Returns the number of items in an array (or object). */
-CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
-/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
-CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
-/* Get item "string" from object. Case insensitive. */
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
-/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
-CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
-
-/* These functions check the type of an item */
-CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
-
-/* These calls create a cJSON item of the appropriate type. */
-CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
-CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
-CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
-/* raw json */
-CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
-CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
-
-/* These utilities create an Array of count items. */
-CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
-
-/* Append item to the specified array/object. */
-CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
-CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
-/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
- * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
- * writing to `item->string` */
-CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
-/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
-
-/* Remove/Detatch items from Arrays/Objects. */
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
-
-/* Update array items. */
-CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
-CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
-
-/* Duplicate a cJSON item */
-CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
-/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
-need to be released. With recurse!=0, it will duplicate any children connected to the item.
-The item->next and ->prev pointers are always zero on return from Duplicate. */
-/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
- * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
-CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
-
-
-CJSON_PUBLIC(void) cJSON_Minify(char *json);
-
-/* Macros for creating things quickly. */
-#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
-#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
-#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
-#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
-#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
-#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
-#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s))
-
-/* When assigning an integer value, it needs to be propagated to valuedouble too. */
-#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
-/* helper for the cJSON_SetNumberValue macro */
-CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
-#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
-
-/* Macro for iterating over an array or object */
-#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
-
-/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
-CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
-CJSON_PUBLIC(void) cJSON_free(void *object);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-*/
-
-/* cJSON */
-/* JSON parser in C. */
-
-/* disable warnings about old C89 functions in MSVC */
-#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
-#define _CRT_SECURE_NO_DEPRECATE
-#endif
-
-#ifdef __GNUC__
-#pragma GCC visibility push(default)
-#endif
-#if defined(_MSC_VER)
-#pragma warning (push)
-/* disable warning about single line comments in system headers */
-#pragma warning (disable : 4001)
-#endif
-
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdlib.h>
-#include <float.h>
-#include <limits.h>
-#include <ctype.h>
-
-#ifdef ENABLE_LOCALES
-#include <locale.h>
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-#ifdef __GNUC__
-#pragma GCC visibility pop
-#endif
-
-#include "cJSON.h"
-
-/* define our own boolean type */
-#define true ((cJSON_bool)1)
-#define false ((cJSON_bool)0)
-
-typedef struct {
- const unsigned char *json;
- size_t position;
-} error;
-static error global_error = { NULL, 0 };
-
-CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
-{
- return (const char*) (global_error.json + global_error.position);
-}
-
-/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
-#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 6) || (CJSON_VERSION_PATCH != 0)
- #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
-#endif
-
-CJSON_PUBLIC(const char*) cJSON_Version(void)
-{
- static char version[15];
- sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
-
- return version;
-}
-
-/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
-static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
-{
- if ((string1 == NULL) || (string2 == NULL))
- {
- return 1;
- }
-
- if (string1 == string2)
- {
- return 0;
- }
-
- for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
- {
- if (*string1 == '\0')
- {
- return 0;
- }
- }
-
- return tolower(*string1) - tolower(*string2);
-}
-
-typedef struct internal_hooks
-{
- void *(*allocate)(size_t size);
- void (*deallocate)(void *pointer);
- void *(*reallocate)(void *pointer, size_t size);
-} internal_hooks;
-
-#if defined(_MSC_VER)
-/* work around MSVC error C2322: '...' address of dillimport '...' is not static */
-static void *internal_malloc(size_t size)
-{
- return malloc(size);
-}
-static void internal_free(void *pointer)
-{
- free(pointer);
-}
-static void *internal_realloc(void *pointer, size_t size)
-{
- return realloc(pointer, size);
-}
-#else
-#define internal_malloc malloc
-#define internal_free free
-#define internal_realloc realloc
-#endif
-
-static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
-
-static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
-{
- size_t length = 0;
- unsigned char *copy = NULL;
-
- if (string == NULL)
- {
- return NULL;
- }
-
- length = strlen((const char*)string) + sizeof("");
- copy = (unsigned char*)hooks->allocate(length);
- if (copy == NULL)
- {
- return NULL;
- }
- memcpy(copy, string, length);
-
- return copy;
-}
-
-CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
-{
- if (hooks == NULL)
- {
- /* Reset hooks */
- global_hooks.allocate = malloc;
- global_hooks.deallocate = free;
- global_hooks.reallocate = realloc;
- return;
- }
-
- global_hooks.allocate = malloc;
- if (hooks->malloc_fn != NULL)
- {
- global_hooks.allocate = hooks->malloc_fn;
- }
-
- global_hooks.deallocate = free;
- if (hooks->free_fn != NULL)
- {
- global_hooks.deallocate = hooks->free_fn;
- }
-
- /* use realloc only if both free and malloc are used */
- global_hooks.reallocate = NULL;
- if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
- {
- global_hooks.reallocate = realloc;
- }
-}
-
-/* Internal constructor. */
-static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
-{
- cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
- if (node)
- {
- memset(node, '\0', sizeof(cJSON));
- }
-
- return node;
-}
-
-/* Delete a cJSON structure. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
-{
- cJSON *next = NULL;
- while (item != NULL)
- {
- next = item->next;
- if (!(item->type & cJSON_IsReference) && (item->child != NULL))
- {
- cJSON_Delete(item->child);
- }
- if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
- {
- global_hooks.deallocate(item->valuestring);
- }
- if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
- {
- global_hooks.deallocate(item->string);
- }
- global_hooks.deallocate(item);
- item = next;
- }
-}
-
-/* get the decimal point character of the current locale */
-static unsigned char get_decimal_point(void)
-{
-#ifdef ENABLE_LOCALES
- struct lconv *lconv = localeconv();
- return (unsigned char) lconv->decimal_point[0];
-#else
- return '.';
-#endif
-}
-
-typedef struct
-{
- const unsigned char *content;
- size_t length;
- size_t offset;
- size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
- internal_hooks hooks;
-} parse_buffer;
-
-/* check if the given size is left to read in a given parse buffer (starting with 1) */
-#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
-/* check if the buffer can be accessed at the given index (starting with 0) */
-#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
-#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
-/* get a pointer to the buffer at the position */
-#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
-
-/* Parse the input text to generate a number, and populate the result into item. */
-static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
-{
- double number = 0;
- unsigned char *after_end = NULL;
- unsigned char number_c_string[64];
- unsigned char decimal_point = get_decimal_point();
- size_t i = 0;
-
- if ((input_buffer == NULL) || (input_buffer->content == NULL))
- {
- return false;
- }
-
- /* copy the number into a temporary buffer and replace '.' with the decimal point
- * of the current locale (for strtod)
- * This also takes care of '\0' not necessarily being available for marking the end of the input */
- for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
- {
- switch (buffer_at_offset(input_buffer)[i])
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '+':
- case '-':
- case 'e':
- case 'E':
- number_c_string[i] = buffer_at_offset(input_buffer)[i];
- break;
-
- case '.':
- number_c_string[i] = decimal_point;
- break;
-
- default:
- goto loop_end;
- }
- }
-loop_end:
- number_c_string[i] = '\0';
-
- number = strtod((const char*)number_c_string, (char**)&after_end);
- if (number_c_string == after_end)
- {
- return false; /* parse_error */
- }
-
- item->valuedouble = number;
-
- /* use saturation in case of overflow */
- if (number >= INT_MAX)
- {
- item->valueint = INT_MAX;
- }
- else if (number <= INT_MIN)
- {
- item->valueint = INT_MIN;
- }
- else
- {
- item->valueint = (int)number;
- }
-
- item->type = cJSON_Number;
-
- input_buffer->offset += (size_t)(after_end - number_c_string);
- return true;
-}
-
-/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
-CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
-{
- if (number >= INT_MAX)
- {
- object->valueint = INT_MAX;
- }
- else if (number <= INT_MIN)
- {
- object->valueint = INT_MIN;
- }
- else
- {
- object->valueint = (int)number;
- }
-
- return object->valuedouble = number;
-}
-
-typedef struct
-{
- unsigned char *buffer;
- size_t length;
- size_t offset;
- size_t depth; /* current nesting depth (for formatted printing) */
- cJSON_bool noalloc;
- cJSON_bool format; /* is this print a formatted print */
- internal_hooks hooks;
-} printbuffer;
-
-/* realloc printbuffer if necessary to have at least "needed" bytes more */
-static unsigned char* ensure(printbuffer * const p, size_t needed)
-{
- unsigned char *newbuffer = NULL;
- size_t newsize = 0;
-
- if ((p == NULL) || (p->buffer == NULL))
- {
- return NULL;
- }
-
- if ((p->length > 0) && (p->offset >= p->length))
- {
- /* make sure that offset is valid */
- return NULL;
- }
-
- if (needed > INT_MAX)
- {
- /* sizes bigger than INT_MAX are currently not supported */
- return NULL;
- }
-
- needed += p->offset + 1;
- if (needed <= p->length)
- {
- return p->buffer + p->offset;
- }
-
- if (p->noalloc) {
- return NULL;
- }
-
- /* calculate new buffer size */
- if (needed > (INT_MAX / 2))
- {
- /* overflow of int, use INT_MAX if possible */
- if (needed <= INT_MAX)
- {
- newsize = INT_MAX;
- }
- else
- {
- return NULL;
- }
- }
- else
- {
- newsize = needed * 2;
- }
-
- if (p->hooks.reallocate != NULL)
- {
- /* reallocate with realloc if available */
- newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
- if (newbuffer == NULL)
- {
- p->hooks.deallocate(p->buffer);
- p->length = 0;
- p->buffer = NULL;
-
- return NULL;
- }
- }
- else
- {
- /* otherwise reallocate manually */
- newbuffer = (unsigned char*)p->hooks.allocate(newsize);
- if (!newbuffer)
- {
- p->hooks.deallocate(p->buffer);
- p->length = 0;
- p->buffer = NULL;
-
- return NULL;
- }
- if (newbuffer)
- {
- memcpy(newbuffer, p->buffer, p->offset + 1);
- }
- p->hooks.deallocate(p->buffer);
- }
- p->length = newsize;
- p->buffer = newbuffer;
-
- return newbuffer + p->offset;
-}
-
-/* calculate the new length of the string in a printbuffer and update the offset */
-static void update_offset(printbuffer * const buffer)
-{
- const unsigned char *buffer_pointer = NULL;
- if ((buffer == NULL) || (buffer->buffer == NULL))
- {
- return;
- }
- buffer_pointer = buffer->buffer + buffer->offset;
-
- buffer->offset += strlen((const char*)buffer_pointer);
-}
-
-/* Render the number nicely from the given item into a string. */
-static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- double d = item->valuedouble;
- int length = 0;
- size_t i = 0;
- unsigned char number_buffer[26]; /* temporary buffer to print the number into */
- unsigned char decimal_point = get_decimal_point();
- double test;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* This checks for NaN and Infinity */
- if ((d * 0) != 0)
- {
- length = sprintf((char*)number_buffer, "null");
- }
- else
- {
- /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
- length = sprintf((char*)number_buffer, "%1.15g", d);
-
- /* Check whether the original double can be recovered */
- if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d))
- {
- /* If not, print with 17 decimal places of precision */
- length = sprintf((char*)number_buffer, "%1.17g", d);
- }
- }
-
- /* sprintf failed or buffer overrun occured */
- if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
- {
- return false;
- }
-
- /* reserve appropriate space in the output */
- output_pointer = ensure(output_buffer, (size_t)length);
- if (output_pointer == NULL)
- {
- return false;
- }
-
- /* copy the printed number to the output and replace locale
- * dependent decimal point with '.' */
- for (i = 0; i < ((size_t)length); i++)
- {
- if (number_buffer[i] == decimal_point)
- {
- output_pointer[i] = '.';
- continue;
- }
-
- output_pointer[i] = number_buffer[i];
- }
- output_pointer[i] = '\0';
-
- output_buffer->offset += (size_t)length;
-
- return true;
-}
-
-/* parse 4 digit hexadecimal number */
-static unsigned parse_hex4(const unsigned char * const input)
-{
- unsigned int h = 0;
- size_t i = 0;
-
- for (i = 0; i < 4; i++)
- {
- /* parse digit */
- if ((input[i] >= '0') && (input[i] <= '9'))
- {
- h += (unsigned int) input[i] - '0';
- }
- else if ((input[i] >= 'A') && (input[i] <= 'F'))
- {
- h += (unsigned int) 10 + input[i] - 'A';
- }
- else if ((input[i] >= 'a') && (input[i] <= 'f'))
- {
- h += (unsigned int) 10 + input[i] - 'a';
- }
- else /* invalid */
- {
- return 0;
- }
-
- if (i < 3)
- {
- /* shift left to make place for the next nibble */
- h = h << 4;
- }
- }
-
- return h;
-}
-
-/* converts a UTF-16 literal to UTF-8
- * A literal can be one or two sequences of the form \uXXXX */
-static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
-{
- long unsigned int codepoint = 0;
- unsigned int first_code = 0;
- const unsigned char *first_sequence = input_pointer;
- unsigned char utf8_length = 0;
- unsigned char utf8_position = 0;
- unsigned char sequence_length = 0;
- unsigned char first_byte_mark = 0;
-
- if ((input_end - first_sequence) < 6)
- {
- /* input ends unexpectedly */
- goto fail;
- }
-
- /* get the first utf16 sequence */
- first_code = parse_hex4(first_sequence + 2);
-
- /* check that the code is valid */
- if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
- {
- goto fail;
- }
-
- /* UTF16 surrogate pair */
- if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
- {
- const unsigned char *second_sequence = first_sequence + 6;
- unsigned int second_code = 0;
- sequence_length = 12; /* \uXXXX\uXXXX */
-
- if ((input_end - second_sequence) < 6)
- {
- /* input ends unexpectedly */
- goto fail;
- }
-
- if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
- {
- /* missing second half of the surrogate pair */
- goto fail;
- }
-
- /* get the second utf16 sequence */
- second_code = parse_hex4(second_sequence + 2);
- /* check that the code is valid */
- if ((second_code < 0xDC00) || (second_code > 0xDFFF))
- {
- /* invalid second half of the surrogate pair */
- goto fail;
- }
-
-
- /* calculate the unicode codepoint from the surrogate pair */
- codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
- }
- else
- {
- sequence_length = 6; /* \uXXXX */
- codepoint = first_code;
- }
-
- /* encode as UTF-8
- * takes at maximum 4 bytes to encode:
- * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
- if (codepoint < 0x80)
- {
- /* normal ascii, encoding 0xxxxxxx */
- utf8_length = 1;
- }
- else if (codepoint < 0x800)
- {
- /* two bytes, encoding 110xxxxx 10xxxxxx */
- utf8_length = 2;
- first_byte_mark = 0xC0; /* 11000000 */
- }
- else if (codepoint < 0x10000)
- {
- /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
- utf8_length = 3;
- first_byte_mark = 0xE0; /* 11100000 */
- }
- else if (codepoint <= 0x10FFFF)
- {
- /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
- utf8_length = 4;
- first_byte_mark = 0xF0; /* 11110000 */
- }
- else
- {
- /* invalid unicode codepoint */
- goto fail;
- }
-
- /* encode as utf8 */
- for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
- {
- /* 10xxxxxx */
- (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
- codepoint >>= 6;
- }
- /* encode first byte */
- if (utf8_length > 1)
- {
- (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
- }
- else
- {
- (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
- }
-
- *output_pointer += utf8_length;
-
- return sequence_length;
-
-fail:
- return 0;
-}
-
-/* Parse the input text into an unescaped cinput, and populate item. */
-static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
-{
- const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
- const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
- unsigned char *output_pointer = NULL;
- unsigned char *output = NULL;
-
- /* not a string */
- if (buffer_at_offset(input_buffer)[0] != '\"')
- {
- goto fail;
- }
-
- {
- /* calculate approximate size of the output (overestimate) */
- size_t allocation_length = 0;
- size_t skipped_bytes = 0;
- while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
- {
- /* is escape sequence */
- if (input_end[0] == '\\')
- {
- if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
- {
- /* prevent buffer overflow when last input character is a backslash */
- goto fail;
- }
- skipped_bytes++;
- input_end++;
- }
- input_end++;
- }
- if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
- {
- goto fail; /* string ended unexpectedly */
- }
-
- /* This is at most how much we need for the output */
- allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
- output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
- if (output == NULL)
- {
- goto fail; /* allocation failure */
- }
- }
-
- output_pointer = output;
- /* loop through the string literal */
- while (input_pointer < input_end)
- {
- if (*input_pointer != '\\')
- {
- *output_pointer++ = *input_pointer++;
- }
- /* escape sequence */
- else
- {
- unsigned char sequence_length = 2;
- if ((input_end - input_pointer) < 1)
- {
- goto fail;
- }
-
- switch (input_pointer[1])
- {
- case 'b':
- *output_pointer++ = '\b';
- break;
- case 'f':
- *output_pointer++ = '\f';
- break;
- case 'n':
- *output_pointer++ = '\n';
- break;
- case 'r':
- *output_pointer++ = '\r';
- break;
- case 't':
- *output_pointer++ = '\t';
- break;
- case '\"':
- case '\\':
- case '/':
- *output_pointer++ = input_pointer[1];
- break;
-
- /* UTF-16 literal */
- case 'u':
- sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
- if (sequence_length == 0)
- {
- /* failed to convert UTF16-literal to UTF-8 */
- goto fail;
- }
- break;
-
- default:
- goto fail;
- }
- input_pointer += sequence_length;
- }
- }
-
- /* zero terminate the output */
- *output_pointer = '\0';
-
- item->type = cJSON_String;
- item->valuestring = (char*)output;
-
- input_buffer->offset = (size_t) (input_end - input_buffer->content);
- input_buffer->offset++;
-
- return true;
-
-fail:
- if (output != NULL)
- {
- input_buffer->hooks.deallocate(output);
- }
-
- if (input_pointer != NULL)
- {
- input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
- }
-
- return false;
-}
-
-/* Render the cstring provided to an escaped version that can be printed. */
-static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
-{
- const unsigned char *input_pointer = NULL;
- unsigned char *output = NULL;
- unsigned char *output_pointer = NULL;
- size_t output_length = 0;
- /* numbers of additional characters needed for escaping */
- size_t escape_characters = 0;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* empty string */
- if (input == NULL)
- {
- output = ensure(output_buffer, sizeof("\"\""));
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "\"\"");
-
- return true;
- }
-
- /* set "flag" to 1 if something needs to be escaped */
- for (input_pointer = input; *input_pointer; input_pointer++)
- {
- switch (*input_pointer)
- {
- case '\"':
- case '\\':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- /* one character escape sequence */
- escape_characters++;
- break;
- default:
- if (*input_pointer < 32)
- {
- /* UTF-16 escape sequence uXXXX */
- escape_characters += 5;
- }
- break;
- }
- }
- output_length = (size_t)(input_pointer - input) + escape_characters;
-
- output = ensure(output_buffer, output_length + sizeof("\"\""));
- if (output == NULL)
- {
- return false;
- }
-
- /* no characters have to be escaped */
- if (escape_characters == 0)
- {
- output[0] = '\"';
- memcpy(output + 1, input, output_length);
- output[output_length + 1] = '\"';
- output[output_length + 2] = '\0';
-
- return true;
- }
-
- output[0] = '\"';
- output_pointer = output + 1;
- /* copy the string */
- for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
- {
- if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
- {
- /* normal character, copy */
- *output_pointer = *input_pointer;
- }
- else
- {
- /* character needs to be escaped */
- *output_pointer++ = '\\';
- switch (*input_pointer)
- {
- case '\\':
- *output_pointer = '\\';
- break;
- case '\"':
- *output_pointer = '\"';
- break;
- case '\b':
- *output_pointer = 'b';
- break;
- case '\f':
- *output_pointer = 'f';
- break;
- case '\n':
- *output_pointer = 'n';
- break;
- case '\r':
- *output_pointer = 'r';
- break;
- case '\t':
- *output_pointer = 't';
- break;
- default:
- /* escape and print as unicode codepoint */
- sprintf((char*)output_pointer, "u%04x", *input_pointer);
- output_pointer += 4;
- break;
- }
- }
- }
- output[output_length + 1] = '\"';
- output[output_length + 2] = '\0';
-
- return true;
-}
-
-/* Invoke print_string_ptr (which is useful) on an item. */
-static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
-{
- return print_string_ptr((unsigned char*)item->valuestring, p);
-}
-
-/* Predeclare these prototypes. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
-
-/* Utility to jump whitespace and cr/lf */
-static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
-{
- if ((buffer == NULL) || (buffer->content == NULL))
- {
- return NULL;
- }
-
- while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
- {
- buffer->offset++;
- }
-
- if (buffer->offset == buffer->length)
- {
- buffer->offset--;
- }
-
- return buffer;
-}
-
-/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
-static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
-{
- if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
- {
- return NULL;
- }
-
- if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
- {
- buffer->offset += 3;
- }
-
- return buffer;
-}
-
-/* Parse an object - create a new root, and populate. */
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
-{
- parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
- cJSON *item = NULL;
-
- /* reset error position */
- global_error.json = NULL;
- global_error.position = 0;
-
- if (value == NULL)
- {
- goto fail;
- }
-
- buffer.content = (const unsigned char*)value;
- buffer.length = strlen((const char*)value) + sizeof("");
- buffer.offset = 0;
- buffer.hooks = global_hooks;
-
- item = cJSON_New_Item(&global_hooks);
- if (item == NULL) /* memory fail */
- {
- goto fail;
- }
-
- if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
- {
- /* parse failure. ep is set. */
- goto fail;
- }
-
- /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
- if (require_null_terminated)
- {
- buffer_skip_whitespace(&buffer);
- if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
- {
- goto fail;
- }
- }
- if (return_parse_end)
- {
- *return_parse_end = (const char*)buffer_at_offset(&buffer);
- }
-
- return item;
-
-fail:
- if (item != NULL)
- {
- cJSON_Delete(item);
- }
-
- if (value != NULL)
- {
- error local_error;
- local_error.json = (const unsigned char*)value;
- local_error.position = 0;
-
- if (buffer.offset < buffer.length)
- {
- local_error.position = buffer.offset;
- }
- else if (buffer.length > 0)
- {
- local_error.position = buffer.length - 1;
- }
-
- if (return_parse_end != NULL)
- {
- *return_parse_end = (const char*)local_error.json + local_error.position;
- }
-
- global_error = local_error;
- }
-
- return NULL;
-}
-
-/* Default options for cJSON_Parse */
-CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
-{
- return cJSON_ParseWithOpts(value, 0, 0);
-}
-
-#define cjson_min(a, b) ((a < b) ? a : b)
-
-static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
-{
- printbuffer buffer[1];
- unsigned char *printed = NULL;
-
- memset(buffer, 0, sizeof(buffer));
-
- /* create buffer */
- buffer->buffer = (unsigned char*) hooks->allocate(256);
- buffer->format = format;
- buffer->hooks = *hooks;
- if (buffer->buffer == NULL)
- {
- goto fail;
- }
-
- /* print the value */
- if (!print_value(item, buffer))
- {
- goto fail;
- }
- update_offset(buffer);
-
- /* check if reallocate is available */
- if (hooks->reallocate != NULL)
- {
- printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length);
- buffer->buffer = NULL;
- if (printed == NULL) {
- goto fail;
- }
- }
- else /* otherwise copy the JSON over to a new buffer */
- {
- printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
- if (printed == NULL)
- {
- goto fail;
- }
- memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
- printed[buffer->offset] = '\0'; /* just to be sure */
-
- /* free the buffer */
- hooks->deallocate(buffer->buffer);
- }
-
- return printed;
-
-fail:
- if (buffer->buffer != NULL)
- {
- hooks->deallocate(buffer->buffer);
- }
-
- if (printed != NULL)
- {
- hooks->deallocate(printed);
- }
-
- return NULL;
-}
-
-/* Render a cJSON item/entity/structure to text. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
-{
- return (char*)print(item, true, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
-{
- return (char*)print(item, false, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
-{
- printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
-
- if (prebuffer < 0)
- {
- return NULL;
- }
-
- p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
- if (!p.buffer)
- {
- return NULL;
- }
-
- p.length = (size_t)prebuffer;
- p.offset = 0;
- p.noalloc = false;
- p.format = fmt;
- p.hooks = global_hooks;
-
- if (!print_value(item, &p))
- {
- global_hooks.deallocate(p.buffer);
- return NULL;
- }
-
- return (char*)p.buffer;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)
-{
- printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
-
- if ((len < 0) || (buf == NULL))
- {
- return false;
- }
-
- p.buffer = (unsigned char*)buf;
- p.length = (size_t)len;
- p.offset = 0;
- p.noalloc = true;
- p.format = fmt;
- p.hooks = global_hooks;
-
- return print_value(item, &p);
-}
-
-/* Parser core - when encountering text, process appropriately. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
-{
- if ((input_buffer == NULL) || (input_buffer->content == NULL))
- {
- return false; /* no input */
- }
-
- /* parse the different types of values */
- /* null */
- if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
- {
- item->type = cJSON_NULL;
- input_buffer->offset += 4;
- return true;
- }
- /* false */
- if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
- {
- item->type = cJSON_False;
- input_buffer->offset += 5;
- return true;
- }
- /* true */
- if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
- {
- item->type = cJSON_True;
- item->valueint = 1;
- input_buffer->offset += 4;
- return true;
- }
- /* string */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
- {
- return parse_string(item, input_buffer);
- }
- /* number */
- if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
- {
- return parse_number(item, input_buffer);
- }
- /* array */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
- {
- return parse_array(item, input_buffer);
- }
- /* object */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
- {
- return parse_object(item, input_buffer);
- }
-
- return false;
-}
-
-/* Render a value to text. */
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output = NULL;
-
- if ((item == NULL) || (output_buffer == NULL))
- {
- return false;
- }
-
- switch ((item->type) & 0xFF)
- {
- case cJSON_NULL:
- output = ensure(output_buffer, 5);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "null");
- return true;
-
- case cJSON_False:
- output = ensure(output_buffer, 6);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "false");
- return true;
-
- case cJSON_True:
- output = ensure(output_buffer, 5);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "true");
- return true;
-
- case cJSON_Number:
- return print_number(item, output_buffer);
-
- case cJSON_Raw:
- {
- size_t raw_length = 0;
- if (item->valuestring == NULL)
- {
- if (!output_buffer->noalloc)
- {
- output_buffer->hooks.deallocate(output_buffer->buffer);
- }
- return false;
- }
-
- raw_length = strlen(item->valuestring) + sizeof("");
- output = ensure(output_buffer, raw_length);
- if (output == NULL)
- {
- return false;
- }
- memcpy(output, item->valuestring, raw_length);
- return true;
- }
-
- case cJSON_String:
- return print_string(item, output_buffer);
-
- case cJSON_Array:
- return print_array(item, output_buffer);
-
- case cJSON_Object:
- return print_object(item, output_buffer);
-
- default:
- return false;
- }
-}
-
-/* Build an array from input text. */
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
-{
- cJSON *head = NULL; /* head of the linked list */
- cJSON *current_item = NULL;
-
- if (input_buffer->depth >= CJSON_NESTING_LIMIT)
- {
- return false; /* to deeply nested */
- }
- input_buffer->depth++;
-
- if (buffer_at_offset(input_buffer)[0] != '[')
- {
- /* not an array */
- goto fail;
- }
-
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
- {
- /* empty array */
- goto success;
- }
-
- /* check if we skipped to the end of the buffer */
- if (cannot_access_at_index(input_buffer, 0))
- {
- input_buffer->offset--;
- goto fail;
- }
-
- /* step back to character in front of the first element */
- input_buffer->offset--;
- /* loop through the comma separated array elements */
- do
- {
- /* allocate next item */
- cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
- if (new_item == NULL)
- {
- goto fail; /* allocation failure */
- }
-
- /* attach next item to list */
- if (head == NULL)
- {
- /* start the linked list */
- current_item = head = new_item;
- }
- else
- {
- /* add to the end and advance */
- current_item->next = new_item;
- new_item->prev = current_item;
- current_item = new_item;
- }
-
- /* parse next value */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_value(current_item, input_buffer))
- {
- goto fail; /* failed to parse value */
- }
- buffer_skip_whitespace(input_buffer);
- }
- while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
-
- if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
- {
- goto fail; /* expected end of array */
- }
-
-success:
- input_buffer->depth--;
-
- item->type = cJSON_Array;
- item->child = head;
-
- input_buffer->offset++;
-
- return true;
-
-fail:
- if (head != NULL)
- {
- cJSON_Delete(head);
- }
-
- return false;
-}
-
-/* Render an array to text */
-static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- size_t length = 0;
- cJSON *current_element = item->child;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* Compose the output array. */
- /* opening square bracket */
- output_pointer = ensure(output_buffer, 1);
- if (output_pointer == NULL)
- {
- return false;
- }
-
- *output_pointer = '[';
- output_buffer->offset++;
- output_buffer->depth++;
-
- while (current_element != NULL)
- {
- if (!print_value(current_element, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
- if (current_element->next)
- {
- length = (size_t) (output_buffer->format ? 2 : 1);
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ',';
- if(output_buffer->format)
- {
- *output_pointer++ = ' ';
- }
- *output_pointer = '\0';
- output_buffer->offset += length;
- }
- current_element = current_element->next;
- }
-
- output_pointer = ensure(output_buffer, 2);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ']';
- *output_pointer = '\0';
- output_buffer->depth--;
-
- return true;
-}
-
-/* Build an object from the text. */
-static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
-{
- cJSON *head = NULL; /* linked list head */
- cJSON *current_item = NULL;
-
- if (input_buffer->depth >= CJSON_NESTING_LIMIT)
- {
- return false; /* to deeply nested */
- }
- input_buffer->depth++;
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
- {
- goto fail; /* not an object */
- }
-
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
- {
- goto success; /* empty object */
- }
-
- /* check if we skipped to the end of the buffer */
- if (cannot_access_at_index(input_buffer, 0))
- {
- input_buffer->offset--;
- goto fail;
- }
-
- /* step back to character in front of the first element */
- input_buffer->offset--;
- /* loop through the comma separated array elements */
- do
- {
- /* allocate next item */
- cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
- if (new_item == NULL)
- {
- goto fail; /* allocation failure */
- }
-
- /* attach next item to list */
- if (head == NULL)
- {
- /* start the linked list */
- current_item = head = new_item;
- }
- else
- {
- /* add to the end and advance */
- current_item->next = new_item;
- new_item->prev = current_item;
- current_item = new_item;
- }
-
- /* parse the name of the child */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_string(current_item, input_buffer))
- {
- goto fail; /* faile to parse name */
- }
- buffer_skip_whitespace(input_buffer);
-
- /* swap valuestring and string, because we parsed the name */
- current_item->string = current_item->valuestring;
- current_item->valuestring = NULL;
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
- {
- goto fail; /* invalid object */
- }
-
- /* parse the value */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_value(current_item, input_buffer))
- {
- goto fail; /* failed to parse value */
- }
- buffer_skip_whitespace(input_buffer);
- }
- while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
- {
- goto fail; /* expected end of object */
- }
-
-success:
- input_buffer->depth--;
-
- item->type = cJSON_Object;
- item->child = head;
-
- input_buffer->offset++;
- return true;
-
-fail:
- if (head != NULL)
- {
- cJSON_Delete(head);
- }
-
- return false;
-}
-
-/* Render an object to text. */
-static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- size_t length = 0;
- cJSON *current_item = item->child;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* Compose the output: */
- length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
-
- *output_pointer++ = '{';
- output_buffer->depth++;
- if (output_buffer->format)
- {
- *output_pointer++ = '\n';
- }
- output_buffer->offset += length;
-
- while (current_item)
- {
- if (output_buffer->format)
- {
- size_t i;
- output_pointer = ensure(output_buffer, output_buffer->depth);
- if (output_pointer == NULL)
- {
- return false;
- }
- for (i = 0; i < output_buffer->depth; i++)
- {
- *output_pointer++ = '\t';
- }
- output_buffer->offset += output_buffer->depth;
- }
-
- /* print key */
- if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
-
- length = (size_t) (output_buffer->format ? 2 : 1);
- output_pointer = ensure(output_buffer, length);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ':';
- if (output_buffer->format)
- {
- *output_pointer++ = '\t';
- }
- output_buffer->offset += length;
-
- /* print value */
- if (!print_value(current_item, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
-
- /* print comma if not last */
- length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0));
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
- if (current_item->next)
- {
- *output_pointer++ = ',';
- }
-
- if (output_buffer->format)
- {
- *output_pointer++ = '\n';
- }
- *output_pointer = '\0';
- output_buffer->offset += length;
-
- current_item = current_item->next;
- }
-
- output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
- if (output_pointer == NULL)
- {
- return false;
- }
- if (output_buffer->format)
- {
- size_t i;
- for (i = 0; i < (output_buffer->depth - 1); i++)
- {
- *output_pointer++ = '\t';
- }
- }
- *output_pointer++ = '}';
- *output_pointer = '\0';
- output_buffer->depth--;
-
- return true;
-}
-
-/* Get Array size/item / object item. */
-CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
-{
- cJSON *child = NULL;
- size_t size = 0;
-
- if (array == NULL)
- {
- return 0;
- }
-
- child = array->child;
-
- while(child != NULL)
- {
- size++;
- child = child->next;
- }
-
- /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
-
- return (int)size;
-}
-
-static cJSON* get_array_item(const cJSON *array, size_t index)
-{
- cJSON *current_child = NULL;
-
- if (array == NULL)
- {
- return NULL;
- }
-
- current_child = array->child;
- while ((current_child != NULL) && (index > 0))
- {
- index--;
- current_child = current_child->next;
- }
-
- return current_child;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
-{
- if (index < 0)
- {
- return NULL;
- }
-
- return get_array_item(array, (size_t)index);
-}
-
-static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
-{
- cJSON *current_element = NULL;
-
- if ((object == NULL) || (name == NULL))
- {
- return NULL;
- }
-
- current_element = object->child;
- if (case_sensitive)
- {
- while ((current_element != NULL) && (strcmp(name, current_element->string) != 0))
- {
- current_element = current_element->next;
- }
- }
- else
- {
- while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
- {
- current_element = current_element->next;
- }
- }
-
- return current_element;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
-{
- return get_object_item(object, string, false);
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
-{
- return get_object_item(object, string, true);
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
-{
- return cJSON_GetObjectItem(object, string) ? 1 : 0;
-}
-
-/* Utility for array list handling. */
-static void suffix_object(cJSON *prev, cJSON *item)
-{
- prev->next = item;
- item->prev = prev;
-}
-
-/* Utility for handling references. */
-static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
-{
- cJSON *reference = NULL;
- if (item == NULL)
- {
- return NULL;
- }
-
- reference = cJSON_New_Item(hooks);
- if (reference == NULL)
- {
- return NULL;
- }
-
- memcpy(reference, item, sizeof(cJSON));
- reference->string = NULL;
- reference->type |= cJSON_IsReference;
- reference->next = reference->prev = NULL;
- return reference;
-}
-
-/* Add item to array/object. */
-CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item)
-{
- cJSON *child = NULL;
-
- if ((item == NULL) || (array == NULL))
- {
- return;
- }
-
- child = array->child;
-
- if (child == NULL)
- {
- /* list is empty, start new one */
- array->child = item;
- }
- else
- {
- /* append to the end */
- while (child->next)
- {
- child = child->next;
- }
- suffix_object(child, item);
- }
-}
-
-CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
-{
- if (item == NULL)
- {
- return;
- }
-
- /* call cJSON_AddItemToObjectCS for code reuse */
- cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item);
- /* remove cJSON_StringIsConst flag */
- item->type &= ~cJSON_StringIsConst;
-}
-
-#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
- #pragma GCC diagnostic push
-#endif
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Wcast-qual"
-#endif
-
-/* Add an item to an object with constant string as key */
-CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
-{
- if ((item == NULL) || (string == NULL))
- {
- return;
- }
- if (!(item->type & cJSON_StringIsConst) && item->string)
- {
- global_hooks.deallocate(item->string);
- }
- item->string = (char*)string;
- item->type |= cJSON_StringIsConst;
- cJSON_AddItemToArray(object, item);
-}
-#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
- #pragma GCC diagnostic pop
-#endif
-
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
-{
- if (array == NULL)
- {
- return;
- }
-
- cJSON_AddItemToArray(array, create_reference(item, &global_hooks));
-}
-
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
-{
- if ((object == NULL) || (string == NULL))
- {
- return;
- }
-
- cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks));
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
-{
- if ((parent == NULL) || (item == NULL))
- {
- return NULL;
- }
-
- if (item->prev != NULL)
- {
- /* not the first element */
- item->prev->next = item->next;
- }
- if (item->next != NULL)
- {
- /* not the last element */
- item->next->prev = item->prev;
- }
-
- if (item == parent->child)
- {
- /* first element */
- parent->child = item->next;
- }
- /* make sure the detached item doesn't point anywhere anymore */
- item->prev = NULL;
- item->next = NULL;
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
-{
- if (which < 0)
- {
- return NULL;
- }
-
- return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
-{
- cJSON_Delete(cJSON_DetachItemFromArray(array, which));
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
-{
- cJSON *to_detach = cJSON_GetObjectItem(object, string);
-
- return cJSON_DetachItemViaPointer(object, to_detach);
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
-{
- cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
-
- return cJSON_DetachItemViaPointer(object, to_detach);
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
-{
- cJSON_Delete(cJSON_DetachItemFromObject(object, string));
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
-{
- cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
-}
-
-/* Replace array/object items with new ones. */
-CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
-{
- cJSON *after_inserted = NULL;
-
- if (which < 0)
- {
- return;
- }
-
- after_inserted = get_array_item(array, (size_t)which);
- if (after_inserted == NULL)
- {
- cJSON_AddItemToArray(array, newitem);
- return;
- }
-
- newitem->next = after_inserted;
- newitem->prev = after_inserted->prev;
- after_inserted->prev = newitem;
- if (after_inserted == array->child)
- {
- array->child = newitem;
- }
- else
- {
- newitem->prev->next = newitem;
- }
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
-{
- if ((parent == NULL) || (replacement == NULL) || (item == NULL))
- {
- return false;
- }
-
- if (replacement == item)
- {
- return true;
- }
-
- replacement->next = item->next;
- replacement->prev = item->prev;
-
- if (replacement->next != NULL)
- {
- replacement->next->prev = replacement;
- }
- if (replacement->prev != NULL)
- {
- replacement->prev->next = replacement;
- }
- if (parent->child == item)
- {
- parent->child = replacement;
- }
-
- item->next = NULL;
- item->prev = NULL;
- cJSON_Delete(item);
-
- return true;
-}
-
-CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
-{
- if (which < 0)
- {
- return;
- }
-
- cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
-}
-
-static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
-{
- if ((replacement == NULL) || (string == NULL))
- {
- return false;
- }
-
- /* replace the name in the replacement */
- if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
- {
- cJSON_free(replacement->string);
- }
- replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
- replacement->type &= ~cJSON_StringIsConst;
-
- cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
-
- return true;
-}
-
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
-{
- replace_item_in_object(object, string, newitem, false);
-}
-
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
-{
- replace_item_in_object(object, string, newitem, true);
-}
-
-/* Create basic types: */
-CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_NULL;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_True;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_False;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = b ? cJSON_True : cJSON_False;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_Number;
- item->valuedouble = num;
-
- /* use saturation in case of overflow */
- if (num >= INT_MAX)
- {
- item->valueint = INT_MAX;
- }
- else if (num <= INT_MIN)
- {
- item->valueint = INT_MIN;
- }
- else
- {
- item->valueint = (int)num;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_String;
- item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
- if(!item->valuestring)
- {
- cJSON_Delete(item);
- return NULL;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_Raw;
- item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
- if(!item->valuestring)
- {
- cJSON_Delete(item);
- return NULL;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type=cJSON_Array;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item)
- {
- item->type = cJSON_Object;
- }
-
- return item;
-}
-
-/* Create Arrays: */
-CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
- for(i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber(numbers[i]);
- if (!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for(i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber((double)numbers[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for(i = 0;a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber(numbers[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (strings == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for (i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateString(strings[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p,n);
- }
- p = n;
- }
-
- return a;
-}
-
-/* Duplication */
-CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
-{
- cJSON *newitem = NULL;
- cJSON *child = NULL;
- cJSON *next = NULL;
- cJSON *newchild = NULL;
-
- /* Bail on bad ptr */
- if (!item)
- {
- goto fail;
- }
- /* Create new item */
- newitem = cJSON_New_Item(&global_hooks);
- if (!newitem)
- {
- goto fail;
- }
- /* Copy over all vars */
- newitem->type = item->type & (~cJSON_IsReference);
- newitem->valueint = item->valueint;
- newitem->valuedouble = item->valuedouble;
- if (item->valuestring)
- {
- newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
- if (!newitem->valuestring)
- {
- goto fail;
- }
- }
- if (item->string)
- {
- newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
- if (!newitem->string)
- {
- goto fail;
- }
- }
- /* If non-recursive, then we're done! */
- if (!recurse)
- {
- return newitem;
- }
- /* Walk the ->next chain for the child. */
- child = item->child;
- while (child != NULL)
- {
- newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
- if (!newchild)
- {
- goto fail;
- }
- if (next != NULL)
- {
- /* If newitem->child already set, then crosswire ->prev and ->next and move on */
- next->next = newchild;
- newchild->prev = next;
- next = newchild;
- }
- else
- {
- /* Set newitem->child and move to it */
- newitem->child = newchild;
- next = newchild;
- }
- child = child->next;
- }
-
- return newitem;
-
-fail:
- if (newitem != NULL)
- {
- cJSON_Delete(newitem);
- }
-
- return NULL;
-}
-
-CJSON_PUBLIC(void) cJSON_Minify(char *json)
-{
- unsigned char *into = (unsigned char*)json;
-
- if (json == NULL)
- {
- return;
- }
-
- while (*json)
- {
- if (*json == ' ')
- {
- json++;
- }
- else if (*json == '\t')
- {
- /* Whitespace characters. */
- json++;
- }
- else if (*json == '\r')
- {
- json++;
- }
- else if (*json=='\n')
- {
- json++;
- }
- else if ((*json == '/') && (json[1] == '/'))
- {
- /* double-slash comments, to end of line. */
- while (*json && (*json != '\n'))
- {
- json++;
- }
- }
- else if ((*json == '/') && (json[1] == '*'))
- {
- /* multiline comments. */
- while (*json && !((*json == '*') && (json[1] == '/')))
- {
- json++;
- }
- json += 2;
- }
- else if (*json == '\"')
- {
- /* string literals, which are \" sensitive. */
- *into++ = (unsigned char)*json++;
- while (*json && (*json != '\"'))
- {
- if (*json == '\\')
- {
- *into++ = (unsigned char)*json++;
- }
- *into++ = (unsigned char)*json++;
- }
- *into++ = (unsigned char)*json++;
- }
- else
- {
- /* All other characters. */
- *into++ = (unsigned char)*json++;
- }
- }
-
- /* and null-terminate. */
- *into = '\0';
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Invalid;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_False;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xff) == cJSON_True;
-}
-
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & (cJSON_True | cJSON_False)) != 0;
-}
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_NULL;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Number;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_String;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Array;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Object;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Raw;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
-{
- if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
- {
- return false;
- }
-
- /* check if type is valid */
- switch (a->type & 0xFF)
- {
- case cJSON_False:
- case cJSON_True:
- case cJSON_NULL:
- case cJSON_Number:
- case cJSON_String:
- case cJSON_Raw:
- case cJSON_Array:
- case cJSON_Object:
- break;
-
- default:
- return false;
- }
-
- /* identical objects are equal */
- if (a == b)
- {
- return true;
- }
-
- switch (a->type & 0xFF)
- {
- /* in these cases and equal type is enough */
- case cJSON_False:
- case cJSON_True:
- case cJSON_NULL:
- return true;
-
- case cJSON_Number:
- if (a->valuedouble == b->valuedouble)
- {
- return true;
- }
- return false;
-
- case cJSON_String:
- case cJSON_Raw:
- if ((a->valuestring == NULL) || (b->valuestring == NULL))
- {
- return false;
- }
- if (strcmp(a->valuestring, b->valuestring) == 0)
- {
- return true;
- }
-
- return false;
-
- case cJSON_Array:
- {
- cJSON *a_element = a->child;
- cJSON *b_element = b->child;
-
- for (; (a_element != NULL) && (b_element != NULL);)
- {
- if (!cJSON_Compare(a_element, b_element, case_sensitive))
- {
- return false;
- }
-
- a_element = a_element->next;
- b_element = b_element->next;
- }
-
- /* one of the arrays is longer than the other */
- if (a_element != b_element) {
- return false;
- }
-
- return true;
- }
-
- case cJSON_Object:
- {
- cJSON *a_element = NULL;
- cJSON *b_element = NULL;
- cJSON_ArrayForEach(a_element, a)
- {
- /* TODO This has O(n^2) runtime, which is horrible! */
- b_element = get_object_item(b, a_element->string, case_sensitive);
- if (b_element == NULL)
- {
- return false;
- }
-
- if (!cJSON_Compare(a_element, b_element, case_sensitive))
- {
- return false;
- }
- }
-
- /* doing this twice, once on a and b to prevent true comparison if a subset of b
- * TODO: Do this the proper way, this is just a fix for now */
- cJSON_ArrayForEach(b_element, b)
- {
- a_element = get_object_item(a, b_element->string, case_sensitive);
- if (a_element == NULL)
- {
- return false;
- }
-
- if (!cJSON_Compare(b_element, a_element, case_sensitive))
- {
- return false;
- }
- }
-
- return true;
- }
-
- default:
- return false;
- }
-}
-
-CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
-{
- return global_hooks.allocate(size);
-}
-
-CJSON_PUBLIC(void) cJSON_free(void *object)
-{
- global_hooks.deallocate(object);
-}
+++ /dev/null
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-*/
-
-/* disable warnings about old C89 functions in MSVC */
-#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
-#define _CRT_SECURE_NO_DEPRECATE
-#endif
-
-#ifdef __GNUCC__
-#pragma GCC visibility push(default)
-#endif
-#if defined(_MSC_VER)
-#pragma warning (push)
-/* disable warning about single line comments in system headers */
-#pragma warning (disable : 4001)
-#endif
-
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <limits.h>
-
-#if defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-#ifdef __GNUCC__
-#pragma GCC visibility pop
-#endif
-
-#include "cJSON_Utils.h"
-
-/* define our own boolean type */
-#define true ((cJSON_bool)1)
-#define false ((cJSON_bool)0)
-
-static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
-{
- size_t length = 0;
- unsigned char *copy = NULL;
-
- length = strlen((const char*)string) + sizeof("");
- copy = (unsigned char*) cJSON_malloc(length);
- if (copy == NULL)
- {
- return NULL;
- }
- memcpy(copy, string, length);
-
- return copy;
-}
-
-/* string comparison which doesn't consider NULL pointers equal */
-static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive)
-{
- if ((string1 == NULL) || (string2 == NULL))
- {
- return 1;
- }
-
- if (string1 == string2)
- {
- return 0;
- }
-
- if (case_sensitive)
- {
- return strcmp((const char*)string1, (const char*)string2);
- }
-
- for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
- {
- if (*string1 == '\0')
- {
- return 0;
- }
- }
-
- return tolower(*string1) - tolower(*string2);
-}
-
-/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
-static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive)
-{
- if ((name == NULL) || (pointer == NULL))
- {
- return false;
- }
-
- for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
- {
- if (*pointer == '~')
- {
- /* check for escaped '~' (~0) and '/' (~1) */
- if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
- {
- /* invalid escape sequence or wrong character in *name */
- return false;
- }
- else
- {
- pointer++;
- }
- }
- else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer)))
- {
- return false;
- }
- }
- if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
- {
- /* one string has ended, the other not */
- return false;;
- }
-
- return true;
-}
-
-/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
-static size_t pointer_encoded_length(const unsigned char *string)
-{
- size_t length;
- for (length = 0; *string != '\0'; (void)string++, length++)
- {
- /* character needs to be escaped? */
- if ((*string == '~') || (*string == '/'))
- {
- length++;
- }
- }
-
- return length;
-}
-
-/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
-static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source)
-{
- for (; source[0] != '\0'; (void)source++, destination++)
- {
- if (source[0] == '/')
- {
- destination[1] = '1';
- destination++;
- }
- else if (source[0] == '~')
- {
- destination[0] = '~';
- destination[1] = '1';
- destination++;
- }
- else
- {
- destination[0] = source[0];
- }
- }
-
- destination[0] = '\0';
-}
-
-CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
-{
- size_t child_index = 0;
- cJSON *current_child = 0;
-
- if ((object == NULL) || (target == NULL))
- {
- return NULL;
- }
-
- if (object == target)
- {
- /* found */
- return (char*)cJSONUtils_strdup((const unsigned char*)"");
- }
-
- /* recursively search all children of the object or array */
- for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++)
- {
- unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
- /* found the target? */
- if (target_pointer != NULL)
- {
- if (cJSON_IsArray(object))
- {
- /* reserve enough memory for a 64 bit integer + '/' and '\0' */
- unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
- /* check if conversion to unsigned long is valid
- * This should be eliminated at compile time by dead code elimination
- * if size_t is an alias of unsigned long, or if it is bigger */
- if (child_index > ULONG_MAX)
- {
- cJSON_free(target_pointer);
- return NULL;
- }
- sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */
- cJSON_free(target_pointer);
-
- return (char*)full_pointer;
- }
-
- if (cJSON_IsObject(object))
- {
- unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
- full_pointer[0] = '/';
- encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
- strcat((char*)full_pointer, (char*)target_pointer);
- cJSON_free(target_pointer);
-
- return (char*)full_pointer;
- }
-
- /* reached leaf of the tree, found nothing */
- cJSON_free(target_pointer);
- return NULL;
- }
- }
-
- /* not found */
- return NULL;
-}
-
-/* non broken version of cJSON_GetArrayItem */
-static cJSON *get_array_item(const cJSON *array, size_t item)
-{
- cJSON *child = array ? array->child : NULL;
- while ((child != NULL) && (item > 0))
- {
- item--;
- child = child->next;
- }
-
- return child;
-}
-
-static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
-{
- size_t parsed_index = 0;
- size_t position = 0;
-
- if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
- {
- /* leading zeroes are not permitted */
- return 0;
- }
-
- for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
- {
- parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');
-
- }
-
- if ((pointer[position] != '\0') && (pointer[position] != '/'))
- {
- return 0;
- }
-
- *index = parsed_index;
-
- return 1;
-}
-
-static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
-{
- cJSON *current_element = object;
-
- if (pointer == NULL)
- {
- return NULL;
- }
-
- /* follow path of the pointer */
- while ((pointer[0] == '/') && (current_element != NULL))
- {
- pointer++;
- if (cJSON_IsArray(current_element))
- {
- size_t index = 0;
- if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
- {
- return NULL;
- }
-
- current_element = get_array_item(current_element, index);
- }
- else if (cJSON_IsObject(current_element))
- {
- current_element = current_element->child;
- /* GetObjectItem. */
- while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive))
- {
- current_element = current_element->next;
- }
- }
- else
- {
- return NULL;
- }
-
- /* skip to the next path token or end of string */
- while ((pointer[0] != '\0') && (pointer[0] != '/'))
- {
- pointer++;
- }
- }
-
- return current_element;
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
-{
- return get_item_from_pointer(object, pointer, false);
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer)
-{
- return get_item_from_pointer(object, pointer, true);
-}
-
-/* JSON Patch implementation. */
-static void decode_pointer_inplace(unsigned char *string)
-{
- unsigned char *decoded_string = string;
-
- if (string == NULL) {
- return;
- }
-
- for (; *string; (void)decoded_string++, string++)
- {
- if (string[0] == '~')
- {
- if (string[1] == '0')
- {
- decoded_string[0] = '~';
- }
- else if (string[1] == '1')
- {
- decoded_string[1] = '/';
- }
- else
- {
- /* invalid escape sequence */
- return;
- }
-
- string++;
- }
- }
-
- decoded_string[0] = '\0';
-}
-
-/* non-broken cJSON_DetachItemFromArray */
-static cJSON *detach_item_from_array(cJSON *array, size_t which)
-{
- cJSON *c = array->child;
- while (c && (which > 0))
- {
- c = c->next;
- which--;
- }
- if (!c)
- {
- /* item doesn't exist */
- return NULL;
- }
- if (c->prev)
- {
- /* not the first element */
- c->prev->next = c->next;
- }
- if (c->next)
- {
- c->next->prev = c->prev;
- }
- if (c==array->child)
- {
- array->child = c->next;
- }
- /* make sure the detached item doesn't point anywhere anymore */
- c->prev = c->next = NULL;
-
- return c;
-}
-
-/* detach an item at the given path */
-static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive)
-{
- unsigned char *parent_pointer = NULL;
- unsigned char *child_pointer = NULL;
- cJSON *parent = NULL;
- cJSON *detached_item = NULL;
-
- /* copy path and split it in parent and child */
- parent_pointer = cJSONUtils_strdup(path);
- if (parent_pointer == NULL) {
- goto cleanup;
- }
-
- child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
- if (child_pointer == NULL)
- {
- goto cleanup;
- }
- /* split strings */
- child_pointer[0] = '\0';
- child_pointer++;
-
- parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
- decode_pointer_inplace(child_pointer);
-
- if (cJSON_IsArray(parent))
- {
- size_t index = 0;
- if (!decode_array_index_from_pointer(child_pointer, &index))
- {
- goto cleanup;
- }
- detached_item = detach_item_from_array(parent, index);
- }
- else if (cJSON_IsObject(parent))
- {
- detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer);
- }
- else
- {
- /* Couldn't find object to remove child from. */
- goto cleanup;
- }
-
-cleanup:
- if (parent_pointer != NULL)
- {
- cJSON_free(parent_pointer);
- }
-
- return detached_item;
-}
-
-/* sort lists using mergesort */
-static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive)
-{
- cJSON *first = list;
- cJSON *second = list;
- cJSON *current_item = list;
- cJSON *result = list;
- cJSON *result_tail = NULL;
-
- if ((list == NULL) || (list->next == NULL))
- {
- /* One entry is sorted already. */
- return result;
- }
-
- while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0))
- {
- /* Test for list sorted. */
- current_item = current_item->next;
- }
- if ((current_item == NULL) || (current_item->next == NULL))
- {
- /* Leave sorted lists unmodified. */
- return result;
- }
-
- /* reset pointer to the beginning */
- current_item = list;
- while (current_item != NULL)
- {
- /* Walk two pointers to find the middle. */
- second = second->next;
- current_item = current_item->next;
- /* advances current_item two steps at a time */
- if (current_item != NULL)
- {
- current_item = current_item->next;
- }
- }
- if ((second != NULL) && (second->prev != NULL))
- {
- /* Split the lists */
- second->prev->next = NULL;
- }
-
- /* Recursively sort the sub-lists. */
- first = sort_list(first, case_sensitive);
- second = sort_list(second, case_sensitive);
- result = NULL;
-
- /* Merge the sub-lists */
- while ((first != NULL) && (second != NULL))
- {
- cJSON *smaller = NULL;
- if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0)
- {
- smaller = first;
- }
- else
- {
- smaller = second;
- }
-
- if (result == NULL)
- {
- /* start merged list with the smaller element */
- result_tail = smaller;
- result = smaller;
- }
- else
- {
- /* add smaller element to the list */
- result_tail->next = smaller;
- smaller->prev = result_tail;
- result_tail = smaller;
- }
-
- if (first == smaller)
- {
- first = first->next;
- }
- else
- {
- second = second->next;
- }
- }
-
- if (first != NULL)
- {
- /* Append rest of first list. */
- if (result == NULL)
- {
- return first;
- }
- result_tail->next = first;
- first->prev = result_tail;
- }
- if (second != NULL)
- {
- /* Append rest of second list */
- if (result == NULL)
- {
- return second;
- }
- result_tail->next = second;
- second->prev = result_tail;
- }
-
- return result;
-}
-
-static void sort_object(cJSON * const object, const cJSON_bool case_sensitive)
-{
- if (object == NULL)
- {
- return;
- }
- object->child = sort_list(object->child, case_sensitive);
-}
-
-static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive)
-{
- if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
- {
- /* mismatched type. */
- return false;
- }
- switch (a->type & 0xFF)
- {
- case cJSON_Number:
- /* numeric mismatch. */
- if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble))
- {
- return false;
- }
- else
- {
- return true;
- }
-
- case cJSON_String:
- /* string mismatch. */
- if (strcmp(a->valuestring, b->valuestring) != 0)
- {
- return false;
- }
- else
- {
- return true;
- }
-
- case cJSON_Array:
- for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
- {
- cJSON_bool identical = compare_json(a, b, case_sensitive);
- if (!identical)
- {
- return false;
- }
- }
-
- /* array size mismatch? (one of both children is not NULL) */
- if ((a != NULL) || (b != NULL))
- {
- return false;
- }
- else
- {
- return true;
- }
-
- case cJSON_Object:
- sort_object(a, case_sensitive);
- sort_object(b, case_sensitive);
- for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
- {
- cJSON_bool identical = false;
- /* compare object keys */
- if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive))
- {
- /* missing member */
- return false;
- }
- identical = compare_json(a, b, case_sensitive);
- if (!identical)
- {
- return false;
- }
- }
-
- /* object length mismatch (one of both children is not null) */
- if ((a != NULL) || (b != NULL))
- {
- return false;
- }
- else
- {
- return true;
- }
-
- default:
- break;
- }
-
- /* null, true or false */
- return true;
-}
-
-/* non broken version of cJSON_InsertItemInArray */
-static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
-{
- cJSON *child = array->child;
- while (child && (which > 0))
- {
- child = child->next;
- which--;
- }
- if (which > 0)
- {
- /* item is after the end of the array */
- return 0;
- }
- if (child == NULL)
- {
- cJSON_AddItemToArray(array, newitem);
- return 1;
- }
-
- /* insert into the linked list */
- newitem->next = child;
- newitem->prev = child->prev;
- child->prev = newitem;
-
- /* was it at the beginning */
- if (child == array->child)
- {
- array->child = newitem;
- }
- else
- {
- newitem->prev->next = newitem;
- }
-
- return 1;
-}
-
-static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)
-{
- if (case_sensitive)
- {
- return cJSON_GetObjectItemCaseSensitive(object, name);
- }
-
- return cJSON_GetObjectItem(object, name);
-}
-
-enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };
-
-static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)
-{
- cJSON *operation = get_object_item(patch, "op", case_sensitive);
- if (!cJSON_IsString(operation))
- {
- return INVALID;
- }
-
- if (strcmp(operation->valuestring, "add") == 0)
- {
- return ADD;
- }
-
- if (strcmp(operation->valuestring, "remove") == 0)
- {
- return REMOVE;
- }
-
- if (strcmp(operation->valuestring, "replace") == 0)
- {
- return REPLACE;
- }
-
- if (strcmp(operation->valuestring, "move") == 0)
- {
- return MOVE;
- }
-
- if (strcmp(operation->valuestring, "copy") == 0)
- {
- return COPY;
- }
-
- if (strcmp(operation->valuestring, "test") == 0)
- {
- return TEST;
- }
-
- return INVALID;
-}
-
-/* overwrite and existing item with another one and free resources on the way */
-static void overwrite_item(cJSON * const root, const cJSON replacement)
-{
- if (root == NULL)
- {
- return;
- }
-
- if (root->string != NULL)
- {
- cJSON_free(root->string);
- }
- if (root->valuestring != NULL)
- {
- cJSON_free(root->valuestring);
- }
- if (root->child != NULL)
- {
- cJSON_Delete(root->child);
- }
-
- memcpy(root, &replacement, sizeof(cJSON));
-}
-
-static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)
-{
- cJSON *path = NULL;
- cJSON *value = NULL;
- cJSON *parent = NULL;
- enum patch_operation opcode = INVALID;
- unsigned char *parent_pointer = NULL;
- unsigned char *child_pointer = NULL;
- int status = 0;
-
- path = get_object_item(patch, "path", case_sensitive);
- if (!cJSON_IsString(path))
- {
- /* malformed patch. */
- status = 2;
- goto cleanup;
- }
-
- opcode = decode_patch_operation(patch, case_sensitive);
- if (opcode == INVALID)
- {
- status = 3;
- goto cleanup;
- }
- else if (opcode == TEST)
- {
- /* compare value: {...} with the given path */
- status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive);
- goto cleanup;
- }
-
- /* special case for replacing the root */
- if (path->valuestring[0] == '\0')
- {
- if (opcode == REMOVE)
- {
- static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
-
- overwrite_item(object, invalid);
-
- status = 0;
- goto cleanup;
- }
-
- if ((opcode == REPLACE) || (opcode == ADD))
- {
- value = get_object_item(patch, "value", case_sensitive);
- if (value == NULL)
- {
- /* missing "value" for add/replace. */
- status = 7;
- goto cleanup;
- }
-
- value = cJSON_Duplicate(value, 1);
- if (value == NULL)
- {
- /* out of memory for add/replace. */
- status = 8;
- goto cleanup;
- }
-
- overwrite_item(object, *value);
-
- /* delete the duplicated value */
- cJSON_free(value);
- value = NULL;
-
- /* the string "value" isn't needed */
- if (object->string != NULL)
- {
- cJSON_free(object->string);
- object->string = NULL;
- }
-
- status = 0;
- goto cleanup;
- }
- }
-
- if ((opcode == REMOVE) || (opcode == REPLACE))
- {
- /* Get rid of old. */
- cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive);
- if (old_item == NULL)
- {
- status = 13;
- goto cleanup;
- }
- cJSON_Delete(old_item);
- if (opcode == REMOVE)
- {
- /* For Remove, this job is done. */
- status = 0;
- goto cleanup;
- }
- }
-
- /* Copy/Move uses "from". */
- if ((opcode == MOVE) || (opcode == COPY))
- {
- cJSON *from = get_object_item(patch, "from", case_sensitive);
- if (from == NULL)
- {
- /* missing "from" for copy/move. */
- status = 4;
- goto cleanup;
- }
-
- if (opcode == MOVE)
- {
- value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive);
- }
- if (opcode == COPY)
- {
- value = get_item_from_pointer(object, from->valuestring, case_sensitive);
- }
- if (value == NULL)
- {
- /* missing "from" for copy/move. */
- status = 5;
- goto cleanup;
- }
- if (opcode == COPY)
- {
- value = cJSON_Duplicate(value, 1);
- }
- if (value == NULL)
- {
- /* out of memory for copy/move. */
- status = 6;
- goto cleanup;
- }
- }
- else /* Add/Replace uses "value". */
- {
- value = get_object_item(patch, "value", case_sensitive);
- if (value == NULL)
- {
- /* missing "value" for add/replace. */
- status = 7;
- goto cleanup;
- }
- value = cJSON_Duplicate(value, 1);
- if (value == NULL)
- {
- /* out of memory for add/replace. */
- status = 8;
- goto cleanup;
- }
- }
-
- /* Now, just add "value" to "path". */
-
- /* split pointer in parent and child */
- parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
- child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
- if (child_pointer != NULL)
- {
- child_pointer[0] = '\0';
- child_pointer++;
- }
- parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
- decode_pointer_inplace(child_pointer);
-
- /* add, remove, replace, move, copy, test. */
- if ((parent == NULL) || (child_pointer == NULL))
- {
- /* Couldn't find object to add to. */
- status = 9;
- goto cleanup;
- }
- else if (cJSON_IsArray(parent))
- {
- if (strcmp((char*)child_pointer, "-") == 0)
- {
- cJSON_AddItemToArray(parent, value);
- value = NULL;
- }
- else
- {
- size_t index = 0;
- if (!decode_array_index_from_pointer(child_pointer, &index))
- {
- status = 11;
- goto cleanup;
- }
-
- if (!insert_item_in_array(parent, index, value))
- {
- status = 10;
- goto cleanup;
- }
- value = NULL;
- }
- }
- else if (cJSON_IsObject(parent))
- {
- if (case_sensitive)
- {
- cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
- }
- else
- {
- cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
- }
- cJSON_AddItemToObject(parent, (char*)child_pointer, value);
- value = NULL;
- }
-
-cleanup:
- if (value != NULL)
- {
- cJSON_Delete(value);
- }
- if (parent_pointer != NULL)
- {
- cJSON_free(parent_pointer);
- }
-
- return status;
-}
-
-CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
-{
- const cJSON *current_patch = NULL;
- int status = 0;
-
- if (!cJSON_IsArray(patches))
- {
- /* malformed patches. */
- return 1;
- }
-
- if (patches != NULL)
- {
- current_patch = patches->child;
- }
-
- while (current_patch != NULL)
- {
- status = apply_patch(object, current_patch, false);
- if (status != 0)
- {
- return status;
- }
- current_patch = current_patch->next;
- }
-
- return 0;
-}
-
-CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
-{
- const cJSON *current_patch = NULL;
- int status = 0;
-
- if (!cJSON_IsArray(patches))
- {
- /* malformed patches. */
- return 1;
- }
-
- if (patches != NULL)
- {
- current_patch = patches->child;
- }
-
- while (current_patch != NULL)
- {
- status = apply_patch(object, current_patch, true);
- if (status != 0)
- {
- return status;
- }
- current_patch = current_patch->next;
- }
-
- return 0;
-}
-
-static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
-{
- cJSON *patch = NULL;
-
- if ((patches == NULL) || (operation == NULL) || (path == NULL))
- {
- return;
- }
-
- patch = cJSON_CreateObject();
- if (patch == NULL)
- {
- return;
- }
- cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));
-
- if (suffix == NULL)
- {
- cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
- }
- else
- {
- size_t suffix_length = pointer_encoded_length(suffix);
- size_t path_length = strlen((const char*)path);
- unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));
-
- sprintf((char*)full_path, "%s/", (const char*)path);
- encode_string_as_pointer(full_path + path_length + 1, suffix);
-
- cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
- cJSON_free(full_path);
- }
-
- if (value != NULL)
- {
- cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
- }
- cJSON_AddItemToArray(patches, patch);
-}
-
-CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
-{
- compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
-}
-
-static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
-{
- if ((from == NULL) || (to == NULL))
- {
- return;
- }
-
- if ((from->type & 0xFF) != (to->type & 0xFF))
- {
- compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
- return;
- }
-
- switch (from->type & 0xFF)
- {
- case cJSON_Number:
- if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
- {
- compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
- }
- return;
-
- case cJSON_String:
- if (strcmp(from->valuestring, to->valuestring) != 0)
- {
- compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
- }
- return;
-
- case cJSON_Array:
- {
- size_t index = 0;
- cJSON *from_child = from->child;
- cJSON *to_child = to->child;
- unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */
-
- /* generate patches for all array elements that exist in both "from" and "to" */
- for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
- {
- /* check if conversion to unsigned long is valid
- * This should be eliminated at compile time by dead code elimination
- * if size_t is an alias of unsigned long, or if it is bigger */
- if (index > ULONG_MAX)
- {
- cJSON_free(new_path);
- return;
- }
- sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
- create_patches(patches, new_path, from_child, to_child, case_sensitive);
- }
-
- /* remove leftover elements from 'from' that are not in 'to' */
- for (; (from_child != NULL); (void)(from_child = from_child->next))
- {
- /* check if conversion to unsigned long is valid
- * This should be eliminated at compile time by dead code elimination
- * if size_t is an alias of unsigned long, or if it is bigger */
- if (index > ULONG_MAX)
- {
- cJSON_free(new_path);
- return;
- }
- sprintf((char*)new_path, "%lu", (unsigned long)index);
- compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
- }
- /* add new elements in 'to' that were not in 'from' */
- for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
- {
- compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
- }
- cJSON_free(new_path);
- return;
- }
-
- case cJSON_Object:
- {
- cJSON *from_child = NULL;
- cJSON *to_child = NULL;
- sort_object(from, case_sensitive);
- sort_object(to, case_sensitive);
-
- from_child = from->child;
- to_child = to->child;
- /* for all object values in the object with more of them */
- while ((from_child != NULL) || (to_child != NULL))
- {
- int diff;
- if (from_child == NULL)
- {
- diff = 1;
- }
- else if (to_child == NULL)
- {
- diff = -1;
- }
- else
- {
- diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
- }
-
- if (diff == 0)
- {
- /* both object keys are the same */
- size_t path_length = strlen((const char*)path);
- size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
- unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));
-
- sprintf((char*)new_path, "%s/", path);
- encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
-
- /* create a patch for the element */
- create_patches(patches, new_path, from_child, to_child, case_sensitive);
- cJSON_free(new_path);
-
- from_child = from_child->next;
- to_child = to_child->next;
- }
- else if (diff < 0)
- {
- /* object element doesn't exist in 'to' --> remove it */
- compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
-
- from_child = from_child->next;
- }
- else
- {
- /* object element doesn't exist in 'from' --> add it */
- compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
-
- to_child = to_child->next;
- }
- }
- return;
- }
-
- default:
- break;
- }
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
-{
- cJSON *patches = NULL;
-
- if ((from == NULL) || (to == NULL))
- {
- return NULL;
- }
-
- patches = cJSON_CreateArray();
- create_patches(patches, (const unsigned char*)"", from, to, false);
-
- return patches;
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
-{
- cJSON *patches = NULL;
-
- if ((from == NULL) || (to == NULL))
- {
- return NULL;
- }
-
- patches = cJSON_CreateArray();
- create_patches(patches, (const unsigned char*)"", from, to, true);
-
- return patches;
-}
-
-CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
-{
- sort_object(object, false);
-}
-
-CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
-{
- sort_object(object, true);
-}
-
-static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
-{
- cJSON *patch_child = NULL;
-
- if (!cJSON_IsObject(patch))
- {
- /* scalar value, array or NULL, just duplicate */
- cJSON_Delete(target);
- return cJSON_Duplicate(patch, 1);
- }
-
- if (!cJSON_IsObject(target))
- {
- cJSON_Delete(target);
- target = cJSON_CreateObject();
- }
-
- patch_child = patch->child;
- while (patch_child != NULL)
- {
- if (cJSON_IsNull(patch_child))
- {
- /* NULL is the indicator to remove a value, see RFC7396 */
- if (case_sensitive)
- {
- cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
- }
- else
- {
- cJSON_DeleteItemFromObject(target, patch_child->string);
- }
- }
- else
- {
- cJSON *replace_me = NULL;
- cJSON *replacement = NULL;
-
- if (case_sensitive)
- {
- replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
- }
- else
- {
- replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
- }
-
- replacement = merge_patch(replace_me, patch_child, case_sensitive);
- if (replacement == NULL)
- {
- return NULL;
- }
-
- cJSON_AddItemToObject(target, patch_child->string, replacement);
- }
- patch_child = patch_child->next;
- }
- return target;
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
-{
- return merge_patch(target, patch, false);
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
-{
- return merge_patch(target, patch, true);
-}
-
-static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
-{
- cJSON *from_child = NULL;
- cJSON *to_child = NULL;
- cJSON *patch = NULL;
- if (to == NULL)
- {
- /* patch to delete everything */
- return cJSON_CreateNull();
- }
- if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
- {
- return cJSON_Duplicate(to, 1);
- }
-
- sort_object(from, case_sensitive);
- sort_object(to, case_sensitive);
-
- from_child = from->child;
- to_child = to->child;
- patch = cJSON_CreateObject();
- while (from_child || to_child)
- {
- int diff;
- if (from_child != NULL)
- {
- if (to_child != NULL)
- {
- diff = strcmp(from_child->string, to_child->string);
- }
- else
- {
- diff = -1;
- }
- }
- else
- {
- diff = 1;
- }
-
- if (diff < 0)
- {
- /* from has a value that to doesn't have -> remove */
- cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());
-
- from_child = from_child->next;
- }
- else if (diff > 0)
- {
- /* to has a value that from doesn't have -> add to patch */
- cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));
-
- to_child = to_child->next;
- }
- else
- {
- /* object key exists in both objects */
- if (!compare_json(from_child, to_child, case_sensitive))
- {
- /* not identical --> generate a patch */
- cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
- }
-
- /* next key in the object */
- from_child = from_child->next;
- to_child = to_child->next;
- }
- }
- if (patch->child == NULL)
- {
- /* no patch generated */
- cJSON_Delete(patch);
- return NULL;
- }
-
- return patch;
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
-{
- return generate_merge_patch(from, to, false);
-}
-
-CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
-{
- return generate_merge_patch(from, to, true);
-}
+++ /dev/null
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-*/
-
-#include "cJSON.h"
-
-/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
-CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
-CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
-
-/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
-/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
-CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
-CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
-/* Utility for generating patch array entries. */
-CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
-/* Returns 0 for success. */
-CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
-CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
-
-/*
-// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
-//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
-//{
-// cJSON *modme = cJSON_Duplicate(*object, 1);
-// int error = cJSONUtils_ApplyPatches(modme, patches);
-// if (!error)
-// {
-// cJSON_Delete(*object);
-// *object = modme;
-// }
-// else
-// {
-// cJSON_Delete(modme);
-// }
-//
-// return error;
-//}
-// Code not added to library since this strategy is a LOT slower.
-*/
-
-/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
-/* target will be modified by patch. return value is new ptr for target. */
-CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
-CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
-/* generates a patch to move from -> to */
-/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
-CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
-CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
-
-/* Given a root object and a target object, construct a pointer from one to the other. */
-CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
-
-/* Sorts the members of the object into alphabetical order. */
-CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
-CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
* @param level level of the log
*
*/
-#define ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, level ) do {\
- if ( LOG_LOCAL_LEVEL >= level ) esp_log_buffer_hex_internal( tag, buffer, buff_len, level ); } while(0)
+#define ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, level ) \
+ do {\
+ if ( LOG_LOCAL_LEVEL >= (level) ) { \
+ esp_log_buffer_hex_internal( tag, buffer, buff_len, level ); \
+ } \
+ } while(0)
/**
* @brief Log a buffer of characters at specified level, separated into 16 bytes each line. Buffer should contain only printable characters.
* @param level level of the log
*
*/
-#define ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, level ) do {\
- if ( LOG_LOCAL_LEVEL >= level ) esp_log_buffer_char_internal( tag, buffer, buff_len, level ); } while(0)
+#define ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, level ) \
+ do {\
+ if ( LOG_LOCAL_LEVEL >= (level) ) { \
+ esp_log_buffer_char_internal( tag, buffer, buff_len, level ); \
+ } \
+ } while(0)
/**
* @brief Dump a buffer to the log at specified level.
*
* @param level level of the log
*/
-#define ESP_LOG_BUFFER_HEXDUMP( tag, buffer, buff_len, level ) do {\
- if ( LOG_LOCAL_LEVEL >= level ) esp_log_buffer_hexdump_internal( tag, buffer, buff_len, level); } while(0)
+#define ESP_LOG_BUFFER_HEXDUMP( tag, buffer, buff_len, level ) \
+ do { \
+ if ( LOG_LOCAL_LEVEL >= (level) ) { \
+ esp_log_buffer_hexdump_internal( tag, buffer, buff_len, level); \
+ } \
+ } while(0)
-
-#if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO)
/**
* @brief Log a buffer of hex bytes at Info level
*
* @see ``esp_log_buffer_hex_level``
*
*/
-#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len) ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO )
+#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len) \
+ do { \
+ if (LOG_LOCAL_LEVEL > ESP_LOG_INFO) { \
+ ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO ); \
+ }\
+ } while(0)
/**
* @brief Log a buffer of characters at Info level. Buffer should contain only printable characters.
* @see ``esp_log_buffer_char_level``
*
*/
-#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len) ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO )
+#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len) \
+ do { \
+ if (LOG_LOCAL_LEVEL > ESP_LOG_INFO) { \
+ ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO ); \
+ }\
+ } while(0)
-#else
-#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len) {}
-#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len) {}
-#endif
//to be back compatible
#define esp_log_buffer_hex ESP_LOG_BUFFER_HEX
#ifdef LOG_BUILTIN_CHECKS
assert(i == 0 || s_log_cache[(i - 1) / 2].generation < s_log_cache[i].generation);
#endif
- if (s_log_cache[i].tag == tag) {
+ if (strcmp(s_log_cache[i].tag,tag) == 0) {
s_log_cache[i].level = level;
break;
}
Please make sure you fully understand the impact of this feature before
enabling it.
+config LWIP_IRAM_OPTIMIZATION
+ bool "Enable LWIP IRAM optimization"
+ default n
+ help
+ If this feature is enabled, some functions relating to RX/TX in LWIP will be
+ put into IRAM, it can improve UDP/TCP throughput by >10% for single core mode,
+ it doesn't help too much for dual core mode. On the other hand, it needs about
+ 10KB IRAM for these optimizations.
+
+ If this feature is disabled, all lwip functions will be put into FLASH.
+
config LWIP_MAX_SOCKETS
int "Max number of open sockets"
range 1 32
* @param apimsg a struct containing the function to call and its parameters
* @return ERR_OK if the function was called, another err_t if not
*/
-static err_t
+static err_t ESP_IRAM_ATTR
tcpip_apimsg(struct api_msg *apimsg)
{
#if LWIP_DEBUG
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
*/
-static err_t
+static err_t ESP_IRAM_ATTR
netconn_recv_data(struct netconn *conn, void **new_buf)
{
void *buf = NULL;
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
*/
-err_t
+err_t ESP_IRAM_ATTR
netconn_recv(struct netconn *conn, struct netbuf **new_buf)
{
#if LWIP_TCP
* @param buf a netbuf containing the data to send
* @return ERR_OK if data was sent, any other err_t on error
*/
-err_t
+err_t ESP_IRAM_ATTR
netconn_send(struct netconn *conn, struct netbuf *buf)
{
API_MSG_VAR_DECLARE(msg);
*
* @param msg the api_msg_msg pointing to the connection
*/
-void
+void ESP_IRAM_ATTR
lwip_netconn_do_send(void *m)
{
struct api_msg_msg *msg = (struct api_msg_msg*)m;
* @return pointer to the allocated memory
* NULL if no memory could be allocated
*/
-void *
+void * ESP_IRAM_ATTR
netbuf_alloc(struct netbuf *buf, u16_t size)
{
LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
*
* @param buf pointer to the netbuf which contains the packet buffer to free
*/
-void
+void ESP_IRAM_ATTR
netbuf_free(struct netbuf *buf)
{
LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
* @param s externally used socket index
* @return struct lwip_sock for the socket or NULL if not found
*/
-static struct lwip_sock *
+static struct lwip_sock * ESP_IRAM_ATTR
get_socket(int s)
{
struct lwip_sock *sock;
return 0;
}
-int
+int ESP_IRAM_ATTR
lwip_recvfrom(int s, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
#endif /* LWIP_UDP || LWIP_RAW */
}
-int
+int ESP_IRAM_ATTR
lwip_sendto(int s, const void *data, size_t size, int flags,
const struct sockaddr *to, socklen_t tolen)
{
* Callback registered in the netconn layer for each socket-netconn.
* Processes recvevent (data available) and wakes up tasks waiting for select.
*/
-static void
+static void ESP_IRAM_ATTR
event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
{
int s;
#if ESP_THREAD_SAFE
-int
+int ESP_IRAM_ATTR
lwip_sendto_r(int s, const void *data, size_t size, int flags,
const struct sockaddr *to, socklen_t tolen)
{
LWIP_API_UNLOCK();
}
-int
+int ESP_IRAM_ATTR
lwip_recvfrom_r(int s, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
*
* @param arg unused argument
*/
-static void
+static void ESP_IRAM_ATTR
tcpip_thread(void *arg)
{
* @param inp the network interface on which the packet was received
* @param input_fn input function to call
*/
-err_t
+err_t ESP_IRAM_ATTR
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
{
#if LWIP_TCPIP_CORE_LOCKING_INPUT
* NETIF_FLAG_ETHERNET flags)
* @param inp the network interface on which the packet was received
*/
-err_t
+err_t ESP_IRAM_ATTR
tcpip_input(struct pbuf *p, struct netif *inp)
{
#if LWIP_ETHERNET
* @param sem semaphore to wait on
* @return ERR_OK if the function was called, another err_t if not
*/
-err_t
+err_t ESP_IRAM_ATTR
tcpip_send_api_msg(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
{
LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
extern "C" {
#endif
-#define ESP_ERR_PING_BASE 0x5000
+#define ESP_ERR_PING_BASE 0x6000
#define ESP_ERR_PING_INVALID_PARAMS ESP_ERR_PING_BASE + 0x00
#define ESP_ERR_PING_NO_MEM ESP_ERR_PING_BASE + 0x01
#endif
/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
-static u16_t
+static u16_t ESP_IRAM_ATTR
inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
{
struct pbuf *q;
* @param proto_len length of the ip data part (used for checksum of pseudo header)
* @return checksum (as u16_t) to be saved directly in the protocol header
*/
-u16_t
+u16_t ESP_IRAM_ATTR
inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
const ip4_addr_t *src, const ip4_addr_t *dest)
{
* @param proto_len length of the ip data part (used for checksum of pseudo header)
* @return checksum (as u16_t) to be saved directly in the protocol header
*/
-u16_t
+u16_t ESP_IRAM_ATTR
ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
const ip_addr_t *src, const ip_addr_t *dest)
{
* Source based IPv4 routing hook function. This function works only
* when destination IP is broadcast IP.
*/
-struct netif *
+struct netif * ESP_IRAM_ATTR
ip4_route_src_hook(const ip4_addr_t *dest, const ip4_addr_t *src)
{
struct netif *netif = NULL;
* Source based IPv4 routing must be fully implemented in
* LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides the parameters.
*/
-struct netif *
+struct netif * ESP_IRAM_ATTR
ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
{
if (src != NULL) {
* @param dest the destination IP address for which to find the route
* @return the netif on which to send to reach dest
*/
-struct netif *
+struct netif * ESP_IRAM_ATTR
ip4_route(const ip4_addr_t *dest)
{
struct netif *netif;
* @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
* processed, but currently always returns ERR_OK)
*/
-err_t
+err_t ESP_IRAM_ATTR
ip4_input(struct pbuf *p, struct netif *inp)
{
struct ip_hdr *iphdr;
* Same as ip_output_if() but 'src' address is not replaced by netif address
* when it is 'any'.
*/
-err_t
+err_t ESP_IRAM_ATTR
ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
* Same as ip_output_if_opt() but 'src' address is not replaced by netif address
* when it is 'any'.
*/
-err_t
+err_t ESP_IRAM_ATTR
ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
u16_t optlen)
* @param netif the network interface against which the address is checked
* @return returns non-zero if the address is a broadcast address
*/
-u8_t
+u8_t ESP_IRAM_ATTR
ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
{
ip4_addr_t ipaddr;
* @return non-zero on failure, zero on success.
*
*/
-static u8_t
+static u8_t ESP_IRAM_ATTR
pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
{
u16_t type;
* @return non-zero on failure, zero on success.
*
*/
-u8_t
+u8_t ESP_IRAM_ATTR
pbuf_header(struct pbuf *p, s16_t header_size_increment)
{
return pbuf_header_impl(p, header_size_increment, 0);
* @param mbox the mbox to fetch the message from
* @param msg the place to store the message
*/
-void
+void ESP_IRAM_ATTR
sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
{
u32_t time_needed;
* @param broadcast 1 if his is an IPv4 broadcast (global or subnet-only), 0 otherwise (only used for IPv4)
* @return 1 on match, 0 otherwise
*/
-static u8_t
+static u8_t ESP_IRAM_ATTR
udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
{
LWIP_UNUSED_ARG(inp); /* in IPv6 only case */
* @param inp network interface on which the datagram was received.
*
*/
-void
+void ESP_IRAM_ATTR
udp_input(struct pbuf *p, struct netif *inp)
{
struct udp_hdr *udphdr;
*
* @see udp_disconnect() udp_sendto()
*/
-err_t
+err_t ESP_IRAM_ATTR
udp_send(struct udp_pcb *pcb, struct pbuf *p)
{
if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
*
* @see udp_disconnect() udp_send()
*/
-err_t
+err_t ESP_IRAM_ATTR
udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *dst_ip, u16_t dst_port)
{
*
* @see udp_disconnect() udp_send()
*/
-err_t
+err_t ESP_IRAM_ATTR
udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
{
}
/** Same as udp_sendto_if(), but with source address */
-err_t
+err_t ESP_IRAM_ATTR
udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip)
{
#define ESP_LWIP_LOGI(...) ESP_LOGI("lwip", __VA_ARGS__)
#define ESP_PING 1
+#if CONFIG_LWIP_IRAM_OPTIMIZATION
+#define ESP_IRAM_ATTR IRAM_ATTR
+#else
+#define ESP_IRAM_ATTR
+#endif
+
#define TCP_WND_DEFAULT CONFIG_TCP_WND_DEFAULT
#define TCP_SND_BUF_DEFAULT CONFIG_TCP_SND_BUF_DEFAULT
* @params dst the destination MAC address to be copied into the ethernet header
* @return ERR_OK if the packet was sent, any other err_t on failure
*/
-static err_t
+static err_t ESP_IRAM_ATTR
etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, const struct eth_addr *dst)
{
struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
/** Just a small helper function that sends a pbuf to an ethernet address
* in the arp_table specified by the index 'arp_idx'.
*/
-static err_t
+static err_t ESP_IRAM_ATTR
etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
{
LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
* - ERR_RTE No route to destination (no gateway to external networks),
* or the return type of either etharp_query() or etharp_send_ip().
*/
-err_t
+err_t ESP_IRAM_ATTR
etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
{
const struct eth_addr *dest;
* @param p the received packet, p->payload pointing to the ethernet header
* @param netif the network interface on which the packet was received
*/
-err_t
+err_t ESP_IRAM_ATTR
ethernet_input(struct pbuf *p, struct netif *netif)
{
struct eth_hdr* ethhdr;
/** Lock a mutex
* @param mutex the mutex to lock */
-void
+void ESP_IRAM_ATTR
sys_mutex_lock(sys_mutex_t *pxMutex)
{
while (xSemaphoreTake(*pxMutex, portMAX_DELAY) != pdPASS);
/** Unlock a mutex
* @param mutex the mutex to unlock */
-void
+void ESP_IRAM_ATTR
sys_mutex_unlock(sys_mutex_t *pxMutex)
{
xSemaphoreGive(*pxMutex);
/*-----------------------------------------------------------------------------------*/
// Signals a semaphore
-void
+void ESP_IRAM_ATTR
sys_sem_signal(sys_sem_t *sem)
{
xSemaphoreGive(*sem);
Notice that lwIP implements a function with a similar name,
sys_sem_wait(), that uses the sys_arch_sem_wait() function.
*/
-u32_t
+u32_t ESP_IRAM_ATTR
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
portTickType StartTime, EndTime, Elapsed;
/*-----------------------------------------------------------------------------------*/
// Posts the "msg" to the mailbox.
-void
+void ESP_IRAM_ATTR
sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
while (xQueueSendToBack((*mbox)->os_mbox, &msg, portMAX_DELAY) != pdTRUE);
}
/*-----------------------------------------------------------------------------------*/
-err_t
+err_t ESP_IRAM_ATTR
sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
err_t xReturn;
Note that a function with a similar name, sys_mbox_fetch(), is
implemented by lwIP.
*/
-u32_t
+u32_t ESP_IRAM_ATTR
sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
void *dummyptr;
* to become availale since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
-static err_t
+static err_t ESP_IRAM_ATTR
low_level_output(struct netif *netif, struct pbuf *p)
{
wifi_interface_t wifi_if = tcpip_adapter_get_esp_if(netif);
*
* @param netif the lwip network interface structure for this ethernetif
*/
-void
+void ESP_IRAM_ATTR
wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb)
{
struct pbuf *p;
import re
import struct
import sys
+import hashlib
+import binascii
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
+MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum
__version__ = '1.0'
@classmethod
def from_binary(cls, b):
+ md5 = hashlib.md5();
result = cls()
for o in range(0,len(b),32):
data = b[o:o+32]
raise InputError("Partition table length must be a multiple of 32 bytes")
if data == b'\xFF'*32:
return result # got end marker
+ if data[:2] == MD5_PARTITION_BEGIN[:2]: #check only the magic number part
+ if data[16:] == md5.digest():
+ continue # the next iteration will check for the end marker
+ else:
+ raise InputError("MD5 checksums don't match! (computed: 0x%s, parsed: 0x%s)" % (md5.hexdigest(), binascii.hexlify(data[16:])))
+ else:
+ md5.update(data)
result.append(PartitionDefinition.from_binary(data))
raise InputError("Partition table is missing an end-of-table marker")
def to_binary(self):
result = b"".join(e.to_binary() for e in self)
+ result += MD5_PARTITION_BEGIN + hashlib.md5(result).digest()
if len(result )>= MAX_PARTITION_LENGTH:
raise InputError("Binary partition table length (%d) longer than max" % len(result))
result += b"\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
b"\x00\x10\x00\x00" + \
b"second" + (b"\0"*10) + \
b"\x00\x00\x00\x00"
+# MD5 checksum
+LONGER_BINARY_TABLE += b"\xEB\xEB" + b"\xFF" * 14
+LONGER_BINARY_TABLE += b'\xf9\xbd\x06\x1b\x45\x68\x6f\x86\x57\x1a\x2c\xd5\x2a\x1d\xa6\x5b'
+# empty partition
LONGER_BINARY_TABLE += b"\xFF" * 32
"""
t = PartitionTable.from_csv(csv)
tb = _strip_trailing_ffs(t.to_binary())
- self.assertEqual(len(tb), 64)
+ self.assertEqual(len(tb), 64+32)
self.assertEqual(b'\xAA\x50', tb[0:2]) # magic
self.assertEqual(b'\x30\xee', tb[2:4]) # type, subtype
eo, es = struct.unpack("<LL", tb[4:12])
self.assertEqual(eo, 0x100400) # offset
self.assertEqual(es, 0x300000) # size
+ self.assertEqual(b"\xEB\xEB" + b"\xFF" * 14, tb[32:48])
+ self.assertEqual(b'\x43\x03\x3f\x33\x40\x87\x57\x51\x69\x83\x9b\x40\x61\xb1\x27\x26', tb[48:64])
def test_multiple_entries(self):
csv = """
"""
t = PartitionTable.from_csv(csv)
tb = _strip_trailing_ffs(t.to_binary())
- self.assertEqual(len(tb), 96)
+ self.assertEqual(len(tb), 96+32)
self.assertEqual(b'\xAA\x50', tb[0:2])
self.assertEqual(b'\xAA\x50', tb[32:34])
memcpy(&card->host, config, sizeof(*config));
const bool is_spi = host_is_spi(card);
+ if ( !is_spi ) {
+ //check HOST flags compatible with slot configuration.
+ int slot_bit_width = config->get_bus_width(config->slot);
+ if ( slot_bit_width == 1 && (config->flags & (SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT))) {
+ ESP_LOGW(TAG, "HOST slot only enables 1-bit.");
+ card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_1BIT);
+ } else if ( slot_bit_width == 4 && (config->flags & SDMMC_HOST_FLAG_8BIT)){
+ ESP_LOGW(TAG, "HOST slot only enables 4-bit.");
+ card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_1BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_4BIT);
+ }
+ }
/* GO_IDLE_STATE (CMD0) command resets the card */
esp_err_t err = sdmmc_send_cmd_go_idle_state(card);
if (err != ESP_OK) {
/* If the host has been initialized with 4-bit bus support, and the card
* supports 4-bit bus, switch to 4-bit bus now.
*/
- if ((config->flags & SDMMC_HOST_FLAG_4BIT) &&
+ if ((card->host.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);
TEST_ASSERT_EQUAL_HEX32(0x11, MMC_RSP_BITS(data, 59, 5));
}
-TEST_CASE("can probe SD", "[sd][test_env=UT_T1_SDMODE][ignore]")
+TEST_CASE("can probe SD (4-bit)", "[sd][test_env=UT_T1_SDMODE]")
{
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
free(card);
}
+TEST_CASE("can probe SD (1-bit)", "[sd][test_env=UT_T1_SDMODE]")
+{
+ //the card DAT3 should be connected to high in SD 1-bit mode
+ //do it by our own GPIO.
+ gpio_config_t conf = {
+ .pin_bit_mask = GPIO_SEL_13,
+ .mode = GPIO_MODE_OUTPUT,
+ .pull_up_en = 1,
+ .pull_down_en = 0,
+ .intr_type = GPIO_INTR_DISABLE,
+ };
+ gpio_config(&conf);
+ gpio_set_level(GPIO_NUM_13, 1);
+
+ sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+ config.flags = SDMMC_HOST_FLAG_1BIT;
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+ slot_config.width=1;
+ TEST_ESP_OK(sdmmc_host_init());
+ TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
+ sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+ TEST_ASSERT_NOT_NULL(card);
+ TEST_ESP_OK(sdmmc_card_init(&config, card));
+ sdmmc_card_print_info(stdout, card);
+ TEST_ESP_OK(sdmmc_host_deinit());
+ free(card);
+}
+
+
-TEST_CASE("can probe SD(using SPI)", "[sdspi][test_env=UT_T1_SPIMODE][ignore]")
+TEST_CASE("can probe SD(using SPI)", "[sdspi][test_env=UT_T1_SPIMODE]")
{
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
#define CLK_OUT1_S 0
#define PERIPHS_IO_MUX_GPIO0_U (DR_REG_IO_MUX_BASE +0x44)
+#define IO_MUX_GPIO0_REG PERIPHS_IO_MUX_GPIO0_U
#define FUNC_GPIO0_EMAC_TX_CLK 5
#define FUNC_GPIO0_GPIO0 2
#define FUNC_GPIO0_CLK_OUT1 1
#define FUNC_GPIO0_GPIO0_0 0
#define PERIPHS_IO_MUX_U0TXD_U (DR_REG_IO_MUX_BASE +0x88)
+#define IO_MUX_GPIO1_REG PERIPHS_IO_MUX_U0TXD_U
#define FUNC_U0TXD_EMAC_RXD2 5
#define FUNC_U0TXD_GPIO1 2
#define FUNC_U0TXD_CLK_OUT3 1
#define FUNC_U0TXD_U0TXD 0
#define PERIPHS_IO_MUX_GPIO2_U (DR_REG_IO_MUX_BASE +0x40)
+#define IO_MUX_GPIO2_REG PERIPHS_IO_MUX_GPIO2_U
#define FUNC_GPIO2_SD_DATA0 4
#define FUNC_GPIO2_HS2_DATA0 3
#define FUNC_GPIO2_GPIO2 2
#define FUNC_GPIO2_GPIO2_0 0
#define PERIPHS_IO_MUX_U0RXD_U (DR_REG_IO_MUX_BASE +0x84)
+#define IO_MUX_GPIO3_REG PERIPHS_IO_MUX_U0RXD_U
#define FUNC_U0RXD_GPIO3 2
#define FUNC_U0RXD_CLK_OUT2 1
#define FUNC_U0RXD_U0RXD 0
#define PERIPHS_IO_MUX_GPIO4_U (DR_REG_IO_MUX_BASE +0x48)
+#define IO_MUX_GPIO4_REG PERIPHS_IO_MUX_GPIO4_U
#define FUNC_GPIO4_EMAC_TX_ER 5
#define FUNC_GPIO4_SD_DATA1 4
#define FUNC_GPIO4_HS2_DATA1 3
#define FUNC_GPIO4_GPIO4_0 0
#define PERIPHS_IO_MUX_GPIO5_U (DR_REG_IO_MUX_BASE +0x6c)
+#define IO_MUX_GPIO5_REG PERIPHS_IO_MUX_GPIO5_U
#define FUNC_GPIO5_EMAC_RX_CLK 5
#define FUNC_GPIO5_HS1_DATA6 3
#define FUNC_GPIO5_GPIO5 2
#define FUNC_GPIO5_GPIO5_0 0
#define PERIPHS_IO_MUX_SD_CLK_U (DR_REG_IO_MUX_BASE +0x60)
+#define IO_MUX_GPIO6_REG PERIPHS_IO_MUX_SD_CLK_U
#define FUNC_SD_CLK_U1CTS 4
#define FUNC_SD_CLK_HS1_CLK 3
#define FUNC_SD_CLK_GPIO6 2
#define FUNC_SD_CLK_SD_CLK 0
#define PERIPHS_IO_MUX_SD_DATA0_U (DR_REG_IO_MUX_BASE +0x64)
+#define IO_MUX_GPIO7_REG PERIPHS_IO_MUX_SD_DATA0_U
#define FUNC_SD_DATA0_U2RTS 4
#define FUNC_SD_DATA0_HS1_DATA0 3
#define FUNC_SD_DATA0_GPIO7 2
#define FUNC_SD_DATA0_SD_DATA0 0
#define PERIPHS_IO_MUX_SD_DATA1_U (DR_REG_IO_MUX_BASE +0x68)
+#define IO_MUX_GPIO8_REG PERIPHS_IO_MUX_SD_DATA1_U
#define FUNC_SD_DATA1_U2CTS 4
#define FUNC_SD_DATA1_HS1_DATA1 3
#define FUNC_SD_DATA1_GPIO8 2
#define FUNC_SD_DATA1_SD_DATA1 0
#define PERIPHS_IO_MUX_SD_DATA2_U (DR_REG_IO_MUX_BASE +0x54)
+#define IO_MUX_GPIO9_REG PERIPHS_IO_MUX_SD_DATA2_U
#define FUNC_SD_DATA2_U1RXD 4
#define FUNC_SD_DATA2_HS1_DATA2 3
#define FUNC_SD_DATA2_GPIO9 2
#define FUNC_SD_DATA2_SD_DATA2 0
#define PERIPHS_IO_MUX_SD_DATA3_U (DR_REG_IO_MUX_BASE +0x58)
+#define IO_MUX_GPIO10_REG PERIPHS_IO_MUX_SD_DATA3_U
#define FUNC_SD_DATA3_U1TXD 4
#define FUNC_SD_DATA3_HS1_DATA3 3
#define FUNC_SD_DATA3_GPIO10 2
#define FUNC_SD_DATA3_SD_DATA3 0
#define PERIPHS_IO_MUX_SD_CMD_U (DR_REG_IO_MUX_BASE +0x5c)
+#define IO_MUX_GPIO11_REG PERIPHS_IO_MUX_SD_CMD_U
#define FUNC_SD_CMD_U1RTS 4
#define FUNC_SD_CMD_HS1_CMD 3
#define FUNC_SD_CMD_GPIO11 2
#define FUNC_SD_CMD_SD_CMD 0
#define PERIPHS_IO_MUX_MTDI_U (DR_REG_IO_MUX_BASE +0x34)
+#define IO_MUX_GPIO12_REG PERIPHS_IO_MUX_MTDI_U
#define FUNC_MTDI_EMAC_TXD3 5
#define FUNC_MTDI_SD_DATA2 4
#define FUNC_MTDI_HS2_DATA2 3
#define FUNC_MTDI_MTDI 0
#define PERIPHS_IO_MUX_MTCK_U (DR_REG_IO_MUX_BASE +0x38)
+#define IO_MUX_GPIO13_REG PERIPHS_IO_MUX_MTCK_U
#define FUNC_MTCK_EMAC_RX_ER 5
#define FUNC_MTCK_SD_DATA3 4
#define FUNC_MTCK_HS2_DATA3 3
#define FUNC_MTCK_MTCK 0
#define PERIPHS_IO_MUX_MTMS_U (DR_REG_IO_MUX_BASE +0x30)
+#define IO_MUX_GPIO14_REG PERIPHS_IO_MUX_MTMS_U
#define FUNC_MTMS_EMAC_TXD2 5
#define FUNC_MTMS_SD_CLK 4
#define FUNC_MTMS_HS2_CLK 3
#define FUNC_MTMS_MTMS 0
#define PERIPHS_IO_MUX_MTDO_U (DR_REG_IO_MUX_BASE +0x3c)
+#define IO_MUX_GPIO15_REG PERIPHS_IO_MUX_MTDO_U
#define FUNC_MTDO_EMAC_RXD3 5
#define FUNC_MTDO_SD_CMD 4
#define FUNC_MTDO_HS2_CMD 3
#define FUNC_MTDO_MTDO 0
#define PERIPHS_IO_MUX_GPIO16_U (DR_REG_IO_MUX_BASE +0x4c)
+#define IO_MUX_GPIO16_REG PERIPHS_IO_MUX_GPIO16_U
#define FUNC_GPIO16_EMAC_CLK_OUT 5
#define FUNC_GPIO16_U2RXD 4
#define FUNC_GPIO16_HS1_DATA4 3
#define FUNC_GPIO16_GPIO16_0 0
#define PERIPHS_IO_MUX_GPIO17_U (DR_REG_IO_MUX_BASE +0x50)
+#define IO_MUX_GPIO17_REG PERIPHS_IO_MUX_GPIO17_U
#define FUNC_GPIO17_EMAC_CLK_OUT_180 5
#define FUNC_GPIO17_U2TXD 4
#define FUNC_GPIO17_HS1_DATA5 3
#define FUNC_GPIO17_GPIO17_0 0
#define PERIPHS_IO_MUX_GPIO18_U (DR_REG_IO_MUX_BASE +0x70)
+#define IO_MUX_GPIO18_REG PERIPHS_IO_MUX_GPIO18_U
#define FUNC_GPIO18_HS1_DATA7 3
#define FUNC_GPIO18_GPIO18 2
#define FUNC_GPIO18_VSPICLK 1
#define FUNC_GPIO18_GPIO18_0 0
#define PERIPHS_IO_MUX_GPIO19_U (DR_REG_IO_MUX_BASE +0x74)
+#define IO_MUX_GPIO19_REG PERIPHS_IO_MUX_GPIO19_U
#define FUNC_GPIO19_EMAC_TXD0 5
#define FUNC_GPIO19_U0CTS 3
#define FUNC_GPIO19_GPIO19 2
#define FUNC_GPIO19_GPIO19_0 0
#define PERIPHS_IO_MUX_GPIO20_U (DR_REG_IO_MUX_BASE +0x78)
+#define IO_MUX_GPIO20_REG PERIPHS_IO_MUX_GPIO20_U
#define FUNC_GPIO20_GPIO20 2
#define FUNC_GPIO20_GPIO20_0 0
#define PERIPHS_IO_MUX_GPIO21_U (DR_REG_IO_MUX_BASE +0x7c)
+#define IO_MUX_GPIO21_REG PERIPHS_IO_MUX_GPIO21_U
#define FUNC_GPIO21_EMAC_TX_EN 5
#define FUNC_GPIO21_GPIO21 2
#define FUNC_GPIO21_VSPIHD 1
#define FUNC_GPIO21_GPIO21_0 0
#define PERIPHS_IO_MUX_GPIO22_U (DR_REG_IO_MUX_BASE +0x80)
+#define IO_MUX_GPIO22_REG PERIPHS_IO_MUX_GPIO22_U
#define FUNC_GPIO22_EMAC_TXD1 5
#define FUNC_GPIO22_U0RTS 3
#define FUNC_GPIO22_GPIO22 2
#define FUNC_GPIO22_GPIO22_0 0
#define PERIPHS_IO_MUX_GPIO23_U (DR_REG_IO_MUX_BASE +0x8c)
+#define IO_MUX_GPIO23_REG PERIPHS_IO_MUX_GPIO23_U
#define FUNC_GPIO23_HS1_STROBE 3
#define FUNC_GPIO23_GPIO23 2
#define FUNC_GPIO23_VSPID 1
#define FUNC_GPIO23_GPIO23_0 0
#define PERIPHS_IO_MUX_GPIO24_U (DR_REG_IO_MUX_BASE +0x90)
+#define IO_MUX_GPIO24_REG PERIPHS_IO_MUX_GPIO24_U
#define FUNC_GPIO24_GPIO24 2
#define FUNC_GPIO24_GPIO24_0 0
#define PERIPHS_IO_MUX_GPIO25_U (DR_REG_IO_MUX_BASE +0x24)
+#define IO_MUX_GPIO25_REG PERIPHS_IO_MUX_GPIO25_U
#define FUNC_GPIO25_EMAC_RXD0 5
#define FUNC_GPIO25_GPIO25 2
#define FUNC_GPIO25_GPIO25_0 0
#define PERIPHS_IO_MUX_GPIO26_U (DR_REG_IO_MUX_BASE +0x28)
+#define IO_MUX_GPIO26_REG PERIPHS_IO_MUX_GPIO26_U
#define FUNC_GPIO26_EMAC_RXD1 5
#define FUNC_GPIO26_GPIO26 2
#define FUNC_GPIO26_GPIO26_0 0
#define PERIPHS_IO_MUX_GPIO27_U (DR_REG_IO_MUX_BASE +0x2c)
+#define IO_MUX_GPIO27_REG PERIPHS_IO_MUX_GPIO27_U
#define FUNC_GPIO27_EMAC_RX_DV 5
#define FUNC_GPIO27_GPIO27 2
#define FUNC_GPIO27_GPIO27_0 0
#define PERIPHS_IO_MUX_GPIO32_U (DR_REG_IO_MUX_BASE +0x1c)
+#define IO_MUX_GPIO32_REG PERIPHS_IO_MUX_GPIO32_U
#define FUNC_GPIO32_GPIO32 2
#define FUNC_GPIO32_GPIO32_0 0
#define PERIPHS_IO_MUX_GPIO33_U (DR_REG_IO_MUX_BASE +0x20)
+#define IO_MUX_GPIO33_REG PERIPHS_IO_MUX_GPIO33_U
#define FUNC_GPIO33_GPIO33 2
#define FUNC_GPIO33_GPIO33_0 0
#define PERIPHS_IO_MUX_GPIO34_U (DR_REG_IO_MUX_BASE +0x14)
+#define IO_MUX_GPIO34_REG PERIPHS_IO_MUX_GPIO34_U
#define FUNC_GPIO34_GPIO34 2
#define FUNC_GPIO34_GPIO34_0 0
#define PERIPHS_IO_MUX_GPIO35_U (DR_REG_IO_MUX_BASE +0x18)
+#define IO_MUX_GPIO35_REG PERIPHS_IO_MUX_GPIO35_U
#define FUNC_GPIO35_GPIO35 2
#define FUNC_GPIO35_GPIO35_0 0
#define PERIPHS_IO_MUX_GPIO36_U (DR_REG_IO_MUX_BASE +0x04)
+#define IO_MUX_GPIO36_REG PERIPHS_IO_MUX_GPIO36_U
#define FUNC_GPIO36_GPIO36 2
#define FUNC_GPIO36_GPIO36_0 0
#define PERIPHS_IO_MUX_GPIO37_U (DR_REG_IO_MUX_BASE +0x08)
+#define IO_MUX_GPIO37_REG PERIPHS_IO_MUX_GPIO37_U
#define FUNC_GPIO37_GPIO37 2
#define FUNC_GPIO37_GPIO37_0 0
#define PERIPHS_IO_MUX_GPIO38_U (DR_REG_IO_MUX_BASE +0x0c)
+#define IO_MUX_GPIO38_REG PERIPHS_IO_MUX_GPIO38_U
#define FUNC_GPIO38_GPIO38 2
#define FUNC_GPIO38_GPIO38_0 0
#define PERIPHS_IO_MUX_GPIO39_U (DR_REG_IO_MUX_BASE +0x10)
+#define IO_MUX_GPIO39_REG PERIPHS_IO_MUX_GPIO39_U
#define FUNC_GPIO39_GPIO39 2
#define FUNC_GPIO39_GPIO39_0 0
default 3
range 1 10
help
- Define maximum number of partitions
- that can be mounted.
+ Define maximum number of partitions that can be mounted.
menu "SPIFFS Cache Configuration"
config SPIFFS_CACHE
bool "Enable SPIFFS Cache"
default "y"
help
- Enables/disable memory read
- caching of nucleus file system
+ Enables/disable memory read caching of nucleus file system
operations.
config SPIFFS_CACHE_WR
default "y"
depends on SPIFFS_CACHE
help
- Enables memory write caching for
- file descriptors in hydrogen.
+ Enables memory write caching for file descriptors in hydrogen.
config SPIFFS_CACHE_STATS
bool "Enable SPIFFS Cache Statistics"
default "n"
depends on SPIFFS_CACHE
help
- Enable/disable statistics on caching.
- Debug/test purpose only.
+ Enable/disable statistics on caching. Debug/test purpose only.
endmenu
bool "Enable SPIFFS Page Check"
default "y"
help
- Always check header of each
- accessed page to ensure consistent state.
- If enabled it will increase number
- of reads, will increase flash.
+ Always check header of each accessed page to ensure consistent state.
+ If enabled it will increase number of reads from flash, especially
+ if cache is disabled.
config SPIFFS_GC_MAX_RUNS
int "Set Maximum GC Runs"
default 10
range 1 255
help
- Define maximum number of gc runs to
- perform to reach desired free pages.
+ Define maximum number of GC runs to perform to reach desired free pages.
config SPIFFS_GC_STATS
bool "Enable SPIFFS GC Statistics"
default "n"
help
- Enable/disable statistics on gc.
- Debug/test purpose only.
+ Enable/disable statistics on gc. Debug/test purpose only.
+
+config SPIFFS_PAGE_SIZE
+ int "SPIFFS logical page size"
+ default 256
+ range 256 1024
+ help
+ Logical page size of SPIFFS partition, in bytes. Must be multiple
+ of flash page size (which is usually 256 bytes).
+ Larger page sizes reduce overhead when storing large files, and
+ improve filesystem performance when reading large files.
+ Smaller page sizes reduce overhead when storing small (< page size)
+ files.
config SPIFFS_OBJ_NAME_LEN
int "Set SPIFFS Maximum Name Length"
default 32
range 1 256
help
- Object name maximum length. Note that this length
- include the zero-termination character,
- meaning maximum string of characters can at most be
- SPIFFS_OBJ_NAME_LEN - 1.
+ Object name maximum length. Note that this length include the
+ zero-termination character, meaning maximum string of characters
+ can at most be SPIFFS_OBJ_NAME_LEN - 1.
+
+ SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
+ SPIFFS_PAGE_SIZE - 64.
config SPIFFS_USE_MAGIC
bool "Enable SPIFFS Filesystem Magic"
default "y"
help
Enable this to have an identifiable spiffs filesystem.
- This will look for a magic in all sectors
- to determine if this is a valid spiffs system
- or not on mount point.
+ This will look for a magic in all sectors to determine if this
+ is a valid spiffs system or not at mount time.
config SPIFFS_USE_MAGIC_LENGTH
bool "Enable SPIFFS Filesystem Length Magic"
These bytes can be used in an application-specific manner.
Set this to at least 4 bytes to enable support for saving file
modification time.
+
+ SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
+ SPIFFS_PAGE_SIZE - 64.
config SPIFFS_USE_MTIME
bool "Save file modification time"
bool "Enable general SPIFFS debug"
default "n"
help
- Enabling this option will print
- general debug mesages to the console
+ Enabling this option will print general debug mesages to the console.
config SPIFFS_API_DBG
bool "Enable SPIFFS API debug"
default "n"
help
- Enabling this option will print
- API debug mesages to the console
+ Enabling this option will print API debug mesages to the console.
config SPIFFS_GC_DBG
bool "Enable SPIFFS Garbage Cleaner debug"
default "n"
help
- Enabling this option will print
- GC debug mesages to the console
+ Enabling this option will print GC debug mesages to the console.
config SPIFFS_CACHE_DBG
bool "Enable SPIFFS Cache debug"
default "n"
depends on SPIFFS_CACHE
help
- Enabling this option will print
- Cache debug mesages to the console
+ Enabling this option will print cache debug mesages to the console.
config SPIFFS_CHECK_DBG
bool "Enable SPIFFS Filesystem Check debug"
default "n"
help
- Enabling this option will print
- Filesystem Check debug mesages
- to the console
+ Enabling this option will print Filesystem Check debug mesages
+ to the console.
config SPIFFS_TEST_VISUALISATION
bool "Enable SPIFFS Filesystem Visualization"
default "n"
help
- Enable this option to enable SPIFFS_vis function
- in the api.
+ Enable this option to enable SPIFFS_vis function in the API.
endmenu
return ESP_ERR_INVALID_STATE;
}
+ uint32_t flash_page_size = g_rom_flashchip.page_size;
+ uint32_t log_page_size = CONFIG_SPIFFS_PAGE_SIZE;
+ if (log_page_size % flash_page_size != 0) {
+ ESP_LOGE(TAG, "SPIFFS_PAGE_SIZE is not multiple of flash chip page size (%d)",
+ flash_page_size);
+ return ESP_ERR_INVALID_ARG;
+ }
+
esp_partition_subtype_t subtype = conf->partition_label ?
ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS;
const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
efs->cfg.hal_read_f = spiffs_api_read;
efs->cfg.hal_write_f = spiffs_api_write;
efs->cfg.log_block_size = g_rom_flashchip.sector_size;
- efs->cfg.log_page_size = g_rom_flashchip.page_size;
+ efs->cfg.log_page_size = log_page_size;
efs->cfg.phys_addr = 0;
efs->cfg.phys_erase_block = g_rom_flashchip.sector_size;
efs->cfg.phys_size = partition->size;
esp_err_t esp_spiffs_format(const char* partition_label)
{
- bool mount_on_success = false;
+ bool partition_was_mounted = false;
int index;
+ /* If the partition is not mounted, need to create SPIFFS structures
+ * and mount the partition, unmount, format, delete SPIFFS structures.
+ * See SPIFFS wiki for the reason why.
+ */
esp_err_t err = esp_spiffs_by_label(partition_label, &index);
if (err != ESP_OK) {
esp_vfs_spiffs_conf_t conf = {
return err;
}
err = esp_spiffs_by_label(partition_label, &index);
- if (err != ESP_OK) {
- return err;
- }
- esp_spiffs_free(&_efs[index]);
- return ESP_OK;
+ assert(err == ESP_OK && "failed to get index of the partition just mounted");
} else if (SPIFFS_mounted(_efs[index]->fs)) {
- SPIFFS_unmount(_efs[index]->fs);
- mount_on_success = true;
+ partition_was_mounted = true;
}
+
+ SPIFFS_unmount(_efs[index]->fs);
+
s32_t res = SPIFFS_format(_efs[index]->fs);
if (res != SPIFFS_OK) {
ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs));
SPIFFS_clearerr(_efs[index]->fs);
+ /* If the partition was previously mounted, but format failed, don't
+ * try to mount the partition back (it will probably fail). On the
+ * other hand, if it was not mounted, need to clean up.
+ */
+ if (!partition_was_mounted) {
+ esp_spiffs_free(&_efs[index]);
+ }
return ESP_FAIL;
}
- if (mount_on_success) {
+ if (partition_was_mounted) {
res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work,
_efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache,
_efs[index]->cache_sz, spiffs_api_check);
SPIFFS_clearerr(_efs[index]->fs);
return ESP_FAIL;
}
+ } else {
+ esp_spiffs_free(&_efs[index]);
}
return ESP_OK;
}
// changes the on-disk format, so the change is not backward-compatible.
//
// Do note: the meta length must never exceed
-// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE)
//
// This is derived from following:
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
// spiffs_object_ix_header fields + at least some LUT entries)
#define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH)
+#define SPIFFS_PAGE_EXTRA_SIZE (64)
+_Static_assert(SPIFFS_OBJ_META_LEN + SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE
+ <= CONFIG_SPIFFS_PAGE_SIZE, "SPIFFS_OBJ_META_LEN or SPIFFS_OBJ_NAME_LEN too long");
// Size of buffer allocated on stack used when copying data.
// Lower value generates more read/writes. No meaning having it bigger
TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
}
-TEST_CASE("can format partition", "[spiffs]")
+TEST_CASE("can initialize SPIFFS in erased partition", "[spiffs]")
{
const esp_partition_t* part = get_test_data_partition();
TEST_ASSERT_NOT_NULL(part);
test_teardown();
}
+TEST_CASE("can format mounted partition", "[spiffs]")
+{
+ // Mount SPIFFS, create file, format, check that the file does not exist.
+ const esp_partition_t* part = get_test_data_partition();
+ TEST_ASSERT_NOT_NULL(part);
+ test_setup();
+ const char* filename = "/spiffs/hello.txt";
+ test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
+ esp_spiffs_format(part->label);
+ FILE* f = fopen(filename, "r");
+ TEST_ASSERT_NULL(f);
+ test_teardown();
+}
+
+TEST_CASE("can format unmounted partition", "[spiffs]")
+{
+ // Mount SPIFFS, create file, unmount. Format. Mount again, check that
+ // the file does not exist.
+ const esp_partition_t* part = get_test_data_partition();
+ TEST_ASSERT_NOT_NULL(part);
+ test_setup();
+ const char* filename = "/spiffs/hello.txt";
+ test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
+ test_teardown();
+ esp_spiffs_format(part->label);
+ // Don't use test_setup here, need to mount without formatting
+ esp_vfs_spiffs_conf_t conf = {
+ .base_path = "/spiffs",
+ .partition_label = spiffs_test_partition_label,
+ .max_files = 5,
+ .format_if_mount_failed = false
+ };
+ TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
+ FILE* f = fopen(filename, "r");
+ TEST_ASSERT_NULL(f);
+ test_teardown();
+}
+
TEST_CASE("can create and write file", "[spiffs]")
{
test_setup();
A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x8000 in the flash.
-Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data.
+Partition table length is 0xC00 bytes (maximum 95 partition table entries). An MD5 checksum is appended after the table data. If the partition table is signed due to `secure boot`, the signature is appended after the partition table.
Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded.
``gen_esp32part.py`` takes one optional argument, ``--verify``, which will also verify the partition table during conversion (checking for overlapping partitions, unaligned partitions, etc.)
+MD5 checksum
+~~~~~~~~~~~~
+
+The binary format of the partition table contains an MD5 checksum computed based on the partition table. This checksum is used for checking the integrity of the partition table during the boot.
+
Flashing the partition table
----------------------------
ULP source files are passed through C preprocessor before the assembler. This allows certain macros to be used to facilitate access to peripheral registers.\r
\r
Some existing macros are defined in ``soc/soc_ulp.h`` header file. These macros allow access to the fields of peripheral registers by their names.\r
-Peripheral registers names which can be used with these macros are the ones defined in ``soc/rtc_cntl_reg.h``, ``soc/rtc_io_reg.h``, ``soc/sens_reg.h``, and ``soc_rtc_i2c_reg.h``.\r
+Peripheral registers names which can be used with these macros are the ones defined in ``soc/rtc_cntl_reg.h``, ``soc/rtc_io_reg.h``, ``soc/sens_reg.h``, and ``soc/rtc_i2c_reg.h``.\r
\r
READ_RTC_REG(rtc_reg, low_bit, bit_width)\r
Read up to 16 bits from rtc_reg[low_bit + bit_width - 1 : low_bit] into R0. For example::\r
--- /dev/null
+ESP32-DevKitC V2 Getting Started Guide\r
+======================================\r
+\r
+This user guide shows how to get started with ESP32-DevKitC development board.\r
+\r
+\r
+What You Need\r
+-------------\r
+\r
+* 1 × :ref:`ESP32-DevKitC V2 board <get-started-esp32-devkitc-v2-board-front>`\r
+* 1 × USB A / micro USB B cable \r
+* 1 × PC loaded with Windows, Linux or Mac OS\r
+\r
+\r
+Overview\r
+--------\r
+\r
+ESP32-DevKitC is a small-sized ESP32-based development board produced by `Espressif <https://espressif.com>`_. Most of the I/O pins are broken out to the pin headers on both sides for easy interfacing. Developers can connect these pins to peripherals as needed. Standard headers also make development easy and convenient when using a breadboard.\r
+\r
+\r
+Functional Description\r
+----------------------\r
+\r
+The following list and figure below describe key components, interfaces and controls of ESP32-DevKitC board.\r
+\r
+ESP-WROOM-32\r
+ Standard `ESP-WROOM-32 <https://www.espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ module soldered to the ESP32-DevKitC board.\r
+EN\r
+ Reset button: pressing this button resets the system.\r
+Boot\r
+ Download button: holding down the **Boot** button and pressing the **EN** button initiates the firmware download mode. Then user can download firmware through the serial port.\r
+USB\r
+ USB interface. It functions as the power supply for the board and the communication interface between PC and ESP-WROOM-32.\r
+I/O\r
+ Most of the pins on the ESP-WROOM-32 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc.\r
+\r
+.. _get-started-esp32-devkitc-v2-board-front:\r
+\r
+.. figure:: ../_static/esp32-devkitc-v2-functional-overview.png\r
+ :align: center\r
+ :alt: ESP32-DevKitC V2 board layout\r
+ :figclass: align-center\r
+\r
+ ESP32-DevKitC V2 board layout\r
+\r
+\r
+Power Supply Options\r
+--------------------\r
+\r
+There following options are available to provide power supply to this board:\r
+\r
+1. Micro USB port, this is default power supply connection\r
+2. 5V / GND header pins\r
+3. 3V3 / GND header pins\r
+\r
+.. warning::\r
+\r
+ Above options are mutually exclusive, i.e. the power supply may be provided using only one of the above options. Attempt to power the board using more than one connection at a time may damage the board and/or the power supply source.\r
+\r
+\r
+Start Application Development\r
+------------------------------\r
+\r
+Before powering up the ESP32-DevKitC, please make sure that the board has been received in good condition with no obvious signs of damage.\r
+\r
+To start development of applications, proceed to section :doc:`index`, that will walk you through the following steps:\r
+\r
+* :ref:`get-started-setup-toolchain` in your PC to develop applications for ESP32 in C language\r
+* :ref:`get-started-connect` the module to the PC and verify if it is accessible\r
+* :ref:`get-started-build-flash` an example application to the ESP32\r
+* :ref:`get-started-build-monitor` instantly what the application is doing\r
+\r
+\r
+Related Documents\r
+-----------------\r
+\r
+* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`_ (PDF)\r
+* `ESP32 Datasheet <https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf>`_ (PDF)\r
+* `ESP-WROOM-32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ (PDF)\r
Overview\r
--------\r
\r
-ESP32-DevKitC is a small-sized ESP32-based development board produced by `Espressif <https://espressif.com>`_. Most of the I/O pins are broken out to the pin headers on both sides for easy interfacing. Developers can connect these pins to peripherals as needed. Standard headers also make development easy and convenient when using a breadboard.\r
+ESP32-DevKitC is a small-sized ESP32-based development board produced by `Espressif <https://espressif.com>`_. Most of the I/O pins are broken out to the pin headers on both sides for easy interfacing. Developers can connect these pins to peripherals as needed. Standard headers also make development easy and convenient when using a breadboard. \r
+\r
+The board comes in two versions, either with :ref:`esp-modules-and-boards-esp-wroom-32` or :ref:`esp-modules-and-boards-esp32-wrover` module soldered.\r
\r
\r
Functional Description\r
The following list and figure below describe key components, interfaces and controls of ESP32-DevKitC board.\r
\r
ESP-WROOM-32\r
- Standard `ESP-WROOM-32 <https://www.espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ module soldered to the ESP32-DevKitC board.\r
-EN\r
- Reset button: pressing this button resets the system.\r
+ :ref:`esp-modules-and-boards-esp-wroom-32` module soldered to the ESP32-DevKitC board.\r
+ESP32-WROVER\r
+ Optionally :ref:`esp-modules-and-boards-esp32-wrover` module may be soldered instead of the ESP-WROOM-32.\r
+USB-UART Bridge\r
+ A single chip USB-UART bridge provides up to 3 Mbps transfers rates.\r
Boot\r
Download button: holding down the **Boot** button and pressing the **EN** button initiates the firmware download mode. Then user can download firmware through the serial port.\r
-USB\r
- USB interface. It functions as the power supply for the board and the communication interface between PC and ESP-WROOM-32.\r
+Micro USB Port\r
+ USB interface. It functions as the power supply for the board and the communication interface between PC and the ESP module.\r
+5V Power On LED\r
+ This LED lights when the USB or an external 5V power supply is applied to the board. For details see schematic in `Related Documents`_.\r
+EN\r
+ Reset button: pressing this button resets the system.\r
I/O\r
- Most of the pins on the ESP-WROOM-32 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM,ADC, DAC, I2C, I2S, SPI, etc.\r
+ Most of the pins on the ESP module are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc.\r
+\r
+ .. note::\r
+\r
+ Some of broken out pins are used internally be the ESP32 module to communicate with SPI memory. They are grouped on one side of the board besides the USB connector and labeled D0, D1, D2, D3, CMD and CLK. In general these pins should be left unconnected or access to the SPI flash memory / SPI RAM may be disturbed. \r
+\r
+ .. note::\r
+\r
+ GPIO16 and 17 are used internally by the ESP32-WROVER module. They are broken out and avialable for use only for boards that have the ESP-WROOM-32 module installed.\r
+\r
\r
.. _get-started-esp32-devkitc-board-front:\r
\r
-.. figure:: ../_static/esp32-devkitc-functional-overview.png\r
+.. figure:: ../_static/esp32-devkitc-functional-overview.jpg\r
:align: center\r
- :alt: ESP32-DevKitC board layout\r
+ :alt: ESP32-DevKitC with ESP-WROOM-32 module soldered\r
:figclass: align-center\r
\r
- ESP32-DevKitC board layout\r
+ ESP32-DevKitC with ESP-WROOM-32 module soldered\r
\r
\r
Power Supply Options\r
--------------------\r
\r
-There following options are available to provide power supply to the ESP32-PICO-KIT V4:\r
+There following options are available to provide power supply to this board:\r
\r
1. Micro USB port, this is default power supply connection\r
2. 5V / GND header pins\r
* :ref:`get-started-build-monitor` instantly what the application is doing\r
\r
\r
+Board Dimensions\r
+----------------\r
+\r
+.. figure:: ../_static/esp32-devkitc-dimensions-back.jpg\r
+ :align: center\r
+ :alt: ESP32 DevKitC board dimensions - back\r
+ :figclass: align-center\r
+\r
+ ESP32 DevKitC board dimensions - back\r
+\r
+\r
Related Documents\r
-----------------\r
\r
-* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`_ (PDF)\r
+* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/esp32_devkitc_v4-sch.pdf>`_ (PDF)\r
* `ESP32 Datasheet <https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf>`_ (PDF)\r
* `ESP-WROOM-32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ (PDF)\r
+* `ESP32-WROVER Datasheet <https://espressif.com/sites/default/files/documentation/esp32-wrover_datasheet_en.pdf>`_ (PDF)\r
+\r
+.. toctree::\r
+ :hidden:\r
+\r
+ get-started-devkitc-v2
\ No newline at end of file
* `ESP32-PICO-D4 Datasheet <http://espressif.com/sites/default/files/documentation/esp32-pico-d4_datasheet_en.pdf>`_ (PDF)
+.. _esp-modules-and-boards-esp32-devkitc-v2:
+
+ESP32 Core Board V2 / ESP32 DevKitC
+-----------------------------------
+
+Small and convenient development board with :ref:`esp-modules-and-boards-esp-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode.
+
+.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png
+ :align: center
+ :alt: ESP32 Core Board V2 / ESP32 DevKitC board
+ :width: 50%
+
+ ESP32 Core Board V2 / ESP32 DevKitC board
+
+Documentation
+"""""""""""""
+
+* :doc:`../get-started/get-started-devkitc-v2`
+* `ESP32 DevKitC V2 Schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF)
+* `CP210x USB to UART Bridge VCP Drivers <https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>`_
+
+
.. _esp-modules-and-boards-esp-wrover-kit-v1:
ESP-WROVER-KIT V1 / ESP32 DevKitJ V1
* `ESP32-WROVER Datasheet <https://espressif.com/sites/default/files/documentation/esp32-wrover_datasheet_en.pdf>`__ (PDF)
* `ESP-PSRAM32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-psram32_datasheet_en.pdf>`__ (PDF)
+* `ESP32-WROVER Reference Design <http://www.espressif.com/sites/default/files/documentation/esp32-wrover_reference_design.zip>`_ PDF containing OrCAD schematic, PCB layout, gerbers and BOM
.. _esp-modules-and-boards-esp32-pico-pit-v4:
.. _esp-modules-and-boards-esp32-devkitc:
-ESP32 Core Board V2 / ESP32 DevKitC
-===================================
+ESP32 DevKitC V4
+================
-Small and convenient development board with :ref:`esp-modules-and-boards-esp-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode.
+Small and convenient development board with :ref:`esp-modules-and-boards-esp-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode. Comparing to the previous :ref:`esp-modules-and-boards-esp32-devkitc-v2`, instead of ESP-WROOM-32 it can accommodate :ref:`esp-modules-and-boards-esp32-wrover` module and has CP2102N chip that supports faster baud rates.
-.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png
+.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-devkitc-v4-front.jpg
:align: center
- :alt: ESP32 Core Board V2 / ESP32 DevKitC board
+ :alt: ESP32 DevKitC V4 board
:width: 50%
- ESP32 Core Board V2 / ESP32 DevKitC board
+ ESP32 DevKitC V4 board
Documentation
-------------
* :doc:`../get-started/get-started-devkitc`
-* `ESP32 DevKitC Schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF)
-* `ESP32 Development Board Reference Design <https://espressif.com/sites/default/files/documentation/esp32_development_board_reference_design.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM
+* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/esp32_devkitc_v4-sch.pdf>`_ (PDF)
+* `ESP32-DevKitC Reference Design <http://www.espressif.com/sites/default/files/documentation/esp32-devkitc-v4_reference_design_0.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM
* `CP210x USB to UART Bridge VCP Drivers <https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>`_
+Previous Versions
+-----------------
+
+* :ref:`esp-modules-and-boards-esp32-devkitc-v2`
+
.. _esp-modules-and-boards-esp-wrover-kit-v3:
{
int ret;
- ret = xTaskCreate(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL);
+ ret = xTaskCreatePinnedToCore(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL, portNUM_PROCESSORS-1);
+
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
return ESP_FAIL;
return ESP_OK;
}
-esp_err_t iperf_run_udp_server(void)
+esp_err_t IRAM_ATTR iperf_run_udp_server(void)
{
socklen_t addr_len = sizeof(struct sockaddr_in);
struct sockaddr_in addr;
esp_err_t iperf_run_tcp_client(void)
{
- struct sockaddr_in addr;
+ struct sockaddr_in local_addr;
+ struct sockaddr_in remote_addr;
int actual_send = 0;
int want_send = 0;
uint8_t *buffer;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
- addr.sin_family = AF_INET;
- addr.sin_port = 0;
- addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
- if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr.sin_family = AF_INET;
+ local_addr.sin_port = 0;
+ local_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+ if (bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr)) != 0) {
iperf_show_socket_error_reason("tcp client bind", sockfd);
return ESP_FAIL;
}
- addr.sin_family = AF_INET;
- addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
- addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
- if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ memset(&remote_addr, 0, sizeof(remote_addr));
+ remote_addr.sin_family = AF_INET;
+ remote_addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
+ remote_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
+ if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(remote_addr)) < 0) {
iperf_show_socket_error_reason("tcp client connect", sockfd);
return ESP_FAIL;
}
while (!s_iperf_ctrl.finish) {
actual_send = send(sockfd, buffer, want_send, 0);
if (actual_send <= 0) {
- vTaskDelay(1);
+ iperf_show_socket_error_reason("tcp client send", sockfd);
+ break;
} else {
s_iperf_ctrl.total_len += actual_send;
}
ESP_LOGE(TAG, "create buffer: out of memory");
return ESP_FAIL;
}
+ memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
- ret = xTaskCreate(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL);
+ ret = xTaskCreatePinnedToCore(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL, portNUM_PROCESSORS-1);
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
free(s_iperf_ctrl.buffer);
#define IPERF_DEFAULT_TIME 30
#define IPERF_TRAFFIC_TASK_NAME "iperf_traffic"
-#define IPERF_TRAFFIC_TASK_PRIORITY 19
+#define IPERF_TRAFFIC_TASK_PRIORITY 10
#define IPERF_TRAFFIC_TASK_STACK 4096
#define IPERF_REPORT_TASK_NAME "iperf_report"
-#define IPERF_REPORT_TASK_PRIORITY 10
+#define IPERF_REPORT_TASK_PRIORITY 20
#define IPERF_REPORT_TASK_STACK 4096
#define IPERF_REPORT_TASK_NAME "iperf_report"
#define IPERF_UDP_TX_LEN (1472)
-#define IPERF_UDP_RX_LEN (32<<10)
-#define IPERF_TCP_TX_LEN (32<<10)
-#define IPERF_TCP_RX_LEN (32<<10)
+#define IPERF_UDP_RX_LEN (16<<10)
+#define IPERF_TCP_TX_LEN (16<<10)
+#define IPERF_TCP_RX_LEN (16<<10)
#define IPERF_MAX_DELAY 64
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=64
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=64
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
-CONFIG_ESP32_WIFI_TX_BA_WIN=12
+CONFIG_ESP32_WIFI_TX_BA_WIN=32
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
-CONFIG_ESP32_WIFI_RX_BA_WIN=12
+CONFIG_ESP32_WIFI_RX_BA_WIN=32
CONFIG_FREERTOS_UNICORE=
CONFIG_FREERTOS_HZ=1000
CONFIG_TCPIP_RECVMBOX_SIZE=64
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=
+CONFIG_FLASHMODE_QIO=y
+CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
+CONFIG_LWIP_IRAM_OPTIMIZATION=y
+
components/aws_iot/aws-iot-device-sdk-embedded-C @GENERAL_MIRROR_SERVER@/idf/aws-iot-device-sdk-embedded-C.git ALLOW_TO_SYNC_FROM_PUBLIC
components/coap/libcoap @GENERAL_MIRROR_SERVER@/idf/libcoap.git ALLOW_TO_SYNC_FROM_PUBLIC
components/esptool_py/esptool @GENERAL_MIRROR_SERVER@/idf/esptool.git ALLOW_TO_SYNC_FROM_PUBLIC
+components/json/cJSON @GENERAL_MIRROR_SERVER@/idf/cJSON.git ALLOW_TO_SYNC_FROM_PUBLIC
components/libsodium/libsodium @GENERAL_MIRROR_SERVER@/idf/libsodium.git ALLOW_TO_SYNC_FROM_PUBLIC
components/micro-ecc/micro-ecc @GENERAL_MIRROR_SERVER@/idf/micro-ecc.git ALLOW_TO_SYNC_FROM_PUBLIC
components/nghttp/nghttp2 @GENERAL_MIRROR_SERVER@/idf/nghttp2.git ALLOW_TO_SYNC_FROM_PUBLIC
with self: # disable console control
sys.stderr.write(ANSI_NORMAL)
try:
- subprocess.call(["%sgdb" % self.toolchain_prefix,
+ process = subprocess.Popen(["%sgdb" % self.toolchain_prefix,
"-ex", "set serial baud %d" % self.serial.baudrate,
"-ex", "target remote %s" % self.serial.port,
"-ex", "interrupt", # monitor has already parsed the first 'reason' command, need a second
self.elf_file], cwd=".")
+ process.wait()
except KeyboardInterrupt:
pass # happens on Windows, maybe other OSes
+ finally:
+ try:
+ # on Linux, maybe other OSes, gdb sometimes seems to be alive even after wait() returns...
+ process.terminate()
+ except:
+ pass
+ try:
+ # also on Linux, maybe other OSes, gdb sometimes exits uncleanly and breaks the tty mode
+ subprocess.call(["stty", "sane"])
+ except:
+ pass # don't care if there's no stty, we tried...
self.prompt_next_action("gdb exited")
def output_enable(self, enable):
self.handle = GetStdHandle(STD_ERROR_HANDLE if self.output == sys.stderr else STD_OUTPUT_HANDLE)
self.matched = b''
+ def _output_write(self, data):
+ # Windows 10 bug since the Fall Creators Update, sometimes writing to console randomly fails
+ # (but usually succeeds afterwards, it seems.)
+ # Ref https://github.com/espressif/esp-idf/issues/1136
+ for tries in range(3):
+ try:
+ self.output.write(data)
+ return
+ except IOError:
+ pass
+
def write(self, data):
for b in data:
l = len(self.matched)
color |= FOREGROUND_INTENSITY
SetConsoleTextAttribute(self.handle, color)
else:
- self.output.write(self.matched) # not an ANSI color code, display verbatim
+ self._output_write(self.matched) # not an ANSI color code, display verbatim
self.matched = b''
else:
- try:
- self.output.write(b)
- except IOError:
- # Windows 10 bug since the Fall Creators Update, sometimes writing to console randomly fails
- # (but usually succeeds the second time, it seems.) Ref https://github.com/espressif/esp-idf/issues/1136
- try:
- self.output.write(b)
- except IOError:
- pass
+ self._output_write(b)
self.matched = b''
def flush(self):
import re
import argparse
-import yaml
-
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path:
sys.path.insert(0, test_fw_path)
-from Utility import CaseConfig, SearchCases, GitlabCIJob
-
-
-class Group(object):
-
- MAX_EXECUTION_TIME = 30
- MAX_CASE = 15
- SORT_KEYS = ["env_tag"]
-
- def __init__(self, case):
- self.execution_time = 0
- self.case_list = [case]
- self.filters = dict(zip(self.SORT_KEYS, [case.case_info[x] for x in self.SORT_KEYS]))
-
- def accept_new_case(self):
- """
- check if allowed to add any case to this group
-
- :return: True or False
- """
- max_time = (sum([x.case_info["execution_time"] for x in self.case_list]) < self.MAX_EXECUTION_TIME)
- max_case = (len(self.case_list) < self.MAX_CASE)
- return max_time and max_case
-
- def add_case(self, case):
- """
- add case to current group
-
- :param case: test case
- :return: True if add succeed, else False
- """
- added = False
- if self.accept_new_case():
- for key in self.filters:
- if case.case_info[key] != self.filters[key]:
- break
- else:
- self.case_list.append(case)
- added = True
- return added
-
- def output(self):
- """
- output data for job configs
-
- :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
- """
- output_data = {
- "Filter": self.filters,
- "CaseConfig": [{"name": x.case_info["name"]} for x in self.case_list],
- }
- return output_data
-
+from Utility.CIAssignTest import AssignTest
-class AssignTest(object):
- """
- Auto assign tests to CI jobs.
-
- :param test_case: path of test case file(s)
- :param ci_config_file: path of ``.gitlab-ci.yml``
- """
+class CIExampleAssignTest(AssignTest):
CI_TEST_JOB_PATTERN = re.compile(r"^example_test_.+")
- def __init__(self, test_case, ci_config_file):
- self.test_cases = self._search_cases(test_case)
- self.jobs = self._parse_gitlab_ci_config(ci_config_file)
-
- def _parse_gitlab_ci_config(self, ci_config_file):
-
- with open(ci_config_file, "r") as f:
- ci_config = yaml.load(f)
-
- job_list = list()
- for job_name in ci_config:
- if self.CI_TEST_JOB_PATTERN.search(job_name) is not None:
- job_list.append(GitlabCIJob.Job(ci_config[job_name], job_name))
- return job_list
-
- @staticmethod
- def _search_cases(test_case, case_filter=None):
- """
- :param test_case: path contains test case folder
- :param case_filter: filter for test cases
- :return: filtered test case list
- """
- test_methods = SearchCases.Search.search_test_cases(test_case)
- return CaseConfig.filter_test_cases(test_methods, case_filter if case_filter else dict())
-
- def _group_cases(self):
- """
- separate all cases into groups according group rules. each group will be executed by one CI job.
-
- :return: test case groups.
- """
- groups = []
- for case in self.test_cases:
- for group in groups:
- # add to current group
- if group.add_case(case):
- break
- else:
- # create new group
- groups.append(Group(case))
- return groups
-
- def assign_cases(self):
- """
- separate test cases to groups and assign test cases to CI jobs.
-
- :raise AssertError: if failed to assign any case to CI job.
- :return: None
- """
- failed_to_assign = []
- test_groups = self._group_cases()
- for group in test_groups:
- for job in self.jobs:
- if job.match_group(group):
- job.assign_group(group)
- break
- else:
- failed_to_assign.append(group)
- assert not failed_to_assign
-
- def output_configs(self, output_path):
- """
-
- :param output_path: path to output config files for each CI job
- :return: None
- """
- if not os.path.exists(output_path):
- os.makedirs(output_path)
- for job in self.jobs:
- job.output_config(output_path)
-
if __name__ == '__main__':
parser = argparse.ArgumentParser()
help="output path of config files")
args = parser.parse_args()
- assign_test = AssignTest(args.test_case, args.ci_config_file)
+ assign_test = CIExampleAssignTest(args.test_case, args.ci_config_file)
assign_test.assign_cases()
assign_test.output_configs(args.output_path)
--- /dev/null
+"""
+Command line tool to assign unit tests to CI test jobs.
+"""
+
+import re
+import os
+import sys
+import argparse
+
+import yaml
+
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path:
+ sys.path.insert(0, test_fw_path)
+
+from Utility import CIAssignTest
+
+
+class Group(CIAssignTest.Group):
+ SORT_KEYS = ["Test App", "SDK", "test environment"]
+ MAX_CASE = 30
+ ATTR_CONVERT_TABLE = {
+ "execution_time": "execution time"
+ }
+
+ @staticmethod
+ def _get_case_attr(case, attr):
+ if attr in Group.ATTR_CONVERT_TABLE:
+ attr = Group.ATTR_CONVERT_TABLE[attr]
+ return case[attr]
+
+ @staticmethod
+ def _get_ut_config(test_app):
+ # we format test app "UT_ + config" when parsing test cases
+ # now we need to extract config
+ assert test_app[:3] == "UT_"
+ return test_app[3:]
+
+ def _create_extra_data(self):
+ case_data = []
+ for case in self.case_list:
+ if self._get_case_attr(case, "cmd set") == "multiple_devices_case":
+ case_data.append({
+ "config": self._get_ut_config(self._get_case_attr(case, "Test App")),
+ "name": self._get_case_attr(case, "summary"),
+ "child case num": self._get_case_attr(case, "child case num")
+ })
+ else:
+ case_data.append({
+ "config": self._get_ut_config(self._get_case_attr(case, "Test App")),
+ "name": self._get_case_attr(case, "summary"),
+ "reset": self._get_case_attr(case, "reset") ,
+ })
+ return case_data
+
+ def output(self):
+ """
+ output data for job configs
+
+ :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
+ """
+ output_data = {
+ # we don't need filter for test function, as UT uses a few test functions for all cases
+ "CaseConfig": [
+ {
+ "name": self.case_list[0]["cmd set"] if isinstance(self.case_list[0]["cmd set"], str) else self.case_list[0]["cmd set"][0],
+ "extra_data": self._create_extra_data(),
+ }
+ ]
+ }
+ return output_data
+
+
+class UnitTestAssignTest(CIAssignTest.AssignTest):
+ CI_TEST_JOB_PATTERN = re.compile(r"^UT_.+")
+
+ def __init__(self, test_case_path, ci_config_file):
+ CIAssignTest.AssignTest.__init__(self, test_case_path, ci_config_file, case_group=Group)
+
+ @staticmethod
+ def _search_cases(test_case_path, case_filter=None):
+ """
+ For unit test case, we don't search for test functions.
+ The unit test cases is stored in a yaml file which is created in job build-idf-test.
+ """
+
+ with open(test_case_path, "r") as f:
+ raw_data = yaml.load(f)
+ test_cases = raw_data["test cases"]
+ if case_filter:
+ for key in case_filter:
+ filtered_cases = []
+ for case in test_cases:
+ try:
+ # bot converts string to lower case
+ if isinstance(case[key], str):
+ _value = case[key].lower()
+ else:
+ _value = case[key]
+ if _value in case_filter[key]:
+ filtered_cases.append(case)
+ except KeyError:
+ # case don't have this key, regard as filter success
+ filtered_cases.append(case)
+ test_cases = filtered_cases
+ return test_cases
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument("test_case",
+ help="test case folder or file")
+ parser.add_argument("ci_config_file",
+ help="gitlab ci config file")
+ parser.add_argument("output_path",
+ help="output path of config files")
+ args = parser.parse_args()
+
+ assign_test = UnitTestAssignTest(args.test_case, args.ci_config_file)
+ assign_test.assign_cases()
+ assign_test.output_configs(args.output_path)
if flush:
self.data_cache.flush()
# do write if cache
- if data:
+ if data is not None:
self._port_write(data + eol if eol else data)
@_expect_lock
:return: formatted data (str)
"""
timestamp = time.time()
- timestamp = "{}:{}".format(time.strftime("%m-%d %H:%M:%S", time.localtime(timestamp)),
- str(timestamp % 1)[2:5])
- formatted_data = "[{}]:\r\n{}\r\n".format(timestamp, _decode_data(data))
+ timestamp = "[{}:{}]".format(time.strftime("%m-%d %H:%M:%S", time.localtime(timestamp)),
+ str(timestamp % 1)[2:5])
+ formatted_data = timestamp.encode() + b"\r\n" + data + b"\r\n"
return formatted_data
def _port_open(self):
def _port_read(self, size=1):
data = self.port_inst.read(size)
if data:
- with open(self.log_file, "a+") as _log_file:
+ with open(self.log_file, "ab+") as _log_file:
_log_file.write(self._format_data(data))
return data
def _port_write(self, data):
+ if isinstance(data, str):
+ data = data.encode()
self.port_inst.write(data)
@classmethod
class UT(IDFApp):
def get_binary_path(self, app_path):
- if app_path:
- # specified path, join it and the idf path
- path = os.path.join(self.idf_path, app_path)
- else:
- path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
+ """
+ :param app_path: app path or app config
+ :return: binary path
+ """
+ if not app_path:
+ app_path = "default"
+
+ path = os.path.join(self.idf_path, app_path)
+ if not os.path.exists(path):
+ while True:
+ # try to get by config
+ if app_path == "default":
+ # it's default config, we first try to get form build folder of unit-test-app
+ path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
+ if os.path.exists(path):
+ # found, use bin in build path
+ break
+ # ``make ut-build-all-configs`` or ``make ut-build-CONFIG`` will copy binary to output folder
+ path = os.path.join(self.idf_path, "tools", "unit-test-app", "output", app_path)
+ if os.path.exists(path):
+ break
+ raise OSError("Failed to get unit-test-app binary path")
return path
--- /dev/null
+# Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Common logic to assign test cases to CI jobs.
+
+Some background knowledge about Gitlab CI and use flow in esp-idf:
+
+* Gitlab CI jobs are static in ``.gitlab-ci.yml``. We can't dynamically create test jobs
+* For test job running on DUT, we use ``tags`` to select runners with different test environment
+* We have ``assign_test`` stage, will collect cases, and then assign them to correct test jobs
+* ``assign_test`` will fail if failed to assign any cases
+* with ``assign_test``, we can:
+ * dynamically filter test case we want to test
+ * alert user if they forget to add CI jobs and guide how to add test jobs
+* the last step of ``assign_test`` is to output config files, then test jobs will run these cases
+
+The Basic logic to assign test cases is as follow:
+
+1. do search all the cases
+2. do filter case (if filter is specified by @bot)
+3. put cases to different groups according to rule of ``Group``
+ * try to put them in existed groups
+ * if failed then create a new group and add this case
+4. parse and filter the test jobs from CI config file
+5. try to assign all groups to jobs according to tags
+6. output config files for jobs
+
+"""
+
+import os
+import re
+import json
+
+import yaml
+
+from Utility import (CaseConfig, SearchCases, GitlabCIJob)
+
+
+class Group(object):
+
+ MAX_EXECUTION_TIME = 30
+ MAX_CASE = 15
+ SORT_KEYS = ["env_tag"]
+
+ def __init__(self, case):
+ self.execution_time = 0
+ self.case_list = [case]
+ self.filters = dict(zip(self.SORT_KEYS, [self._get_case_attr(case, x) for x in self.SORT_KEYS]))
+
+ @staticmethod
+ def _get_case_attr(case, attr):
+ # we might use different type for case (dict or test_func)
+ # this method will do get attribute form cases
+ return case.case_info[attr]
+
+ def accept_new_case(self):
+ """
+ check if allowed to add any case to this group
+
+ :return: True or False
+ """
+ max_time = (sum([self._get_case_attr(x, "execution_time") for x in self.case_list])
+ < self.MAX_EXECUTION_TIME)
+ max_case = (len(self.case_list) < self.MAX_CASE)
+ return max_time and max_case
+
+ def add_case(self, case):
+ """
+ add case to current group
+
+ :param case: test case
+ :return: True if add succeed, else False
+ """
+ added = False
+ if self.accept_new_case():
+ for key in self.filters:
+ if self._get_case_attr(case, key) != self.filters[key]:
+ break
+ else:
+ self.case_list.append(case)
+ added = True
+ return added
+
+ def output(self):
+ """
+ output data for job configs
+
+ :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
+ """
+ output_data = {
+ "Filter": self.filters,
+ "CaseConfig": [{"name": self._get_case_attr(x, "name")} for x in self.case_list],
+ }
+ return output_data
+
+
+class AssignTest(object):
+ """
+ Auto assign tests to CI jobs.
+
+ :param test_case_path: path of test case file(s)
+ :param ci_config_file: path of ``.gitlab-ci.yml``
+ """
+ # subclass need to rewrite CI test job pattern, to filter all test jobs
+ CI_TEST_JOB_PATTERN = re.compile(r"^test_.+")
+
+ def __init__(self, test_case_path, ci_config_file, case_group=Group):
+ self.test_case_path = test_case_path
+ self.test_cases = []
+ self.jobs = self._parse_gitlab_ci_config(ci_config_file)
+ self.case_group = case_group
+
+ def _parse_gitlab_ci_config(self, ci_config_file):
+
+ with open(ci_config_file, "r") as f:
+ ci_config = yaml.load(f)
+
+ job_list = list()
+ for job_name in ci_config:
+ if self.CI_TEST_JOB_PATTERN.search(job_name) is not None:
+ job_list.append(GitlabCIJob.Job(ci_config[job_name], job_name))
+ return job_list
+
+ @staticmethod
+ def _search_cases(test_case_path, case_filter=None):
+ """
+ :param test_case_path: path contains test case folder
+ :param case_filter: filter for test cases
+ :return: filtered test case list
+ """
+ test_methods = SearchCases.Search.search_test_cases(test_case_path)
+ return CaseConfig.filter_test_cases(test_methods, case_filter if case_filter else dict())
+
+ def _group_cases(self):
+ """
+ separate all cases into groups according group rules. each group will be executed by one CI job.
+
+ :return: test case groups.
+ """
+ groups = []
+ for case in self.test_cases:
+ for group in groups:
+ # add to current group
+ if group.add_case(case):
+ break
+ else:
+ # create new group
+ groups.append(self.case_group(case))
+ return groups
+
+ @staticmethod
+ def _apply_bot_filter():
+ """
+ we support customize CI test with bot.
+ here we process from and return the filter which ``_search_cases`` accepts.
+
+ :return: filter for search test cases
+ """
+ bot_filter = os.getenv("BOT_CASE_FILTER")
+ if bot_filter:
+ bot_filter = json.loads(bot_filter)
+ else:
+ bot_filter = dict()
+ return bot_filter
+
+ def assign_cases(self):
+ """
+ separate test cases to groups and assign test cases to CI jobs.
+
+ :raise AssertError: if failed to assign any case to CI job.
+ :return: None
+ """
+ failed_to_assign = []
+ case_filter = self._apply_bot_filter()
+ self.test_cases = self._search_cases(self.test_case_path, case_filter)
+ test_groups = self._group_cases()
+ for group in test_groups:
+ for job in self.jobs:
+ if job.match_group(group):
+ job.assign_group(group)
+ break
+ else:
+ failed_to_assign.append(group)
+ assert not failed_to_assign
+
+ def output_configs(self, output_path):
+ """
+ :param output_path: path to output config files for each CI job
+ :return: None
+ """
+ if not os.path.exists(output_path):
+ os.makedirs(output_path)
+ for job in self.jobs:
+ job.output_config(output_path)
import TestCase
+def _convert_to_lower_case(item):
+ """
+ bot filter is always lower case string.
+ this function will convert to all string to lower case.
+ """
+ if isinstance(item, (tuple, list)):
+ output = [_convert_to_lower_case(v) for v in item]
+ elif isinstance(item, str):
+ output = item.lower()
+ else:
+ output = item
+ return output
+
+
def _filter_one_case(test_method, case_filter):
""" Apply filter for one case (the filter logic is the same as described in ``filter_test_cases``) """
filter_result = True
if key in test_method.case_info:
# the filter key is both in case and filter
# we need to check if they match
- filter_item, accepted_item = case_filter[key], test_method.case_info[key]
+ filter_item = _convert_to_lower_case(case_filter[key])
+ accepted_item = _convert_to_lower_case(test_method.case_info[key])
if isinstance(filter_item, (tuple, list)) \
and isinstance(accepted_item, (tuple, list)):
* if one is list/tuple, the other one is string/int, then check if string/int is in list/tuple
* if both are list/tuple, then check if they have common item
2. if only case attribute or filter have the key, filter succeed
+ 3. will do case insensitive compare for string
for example, the following are match succeed scenarios
(the rule is symmetric, result is same if exchange values for user filter and case attribute):
file_name = os.path.join(file_path, self["name"] + ".yml")
if "case group" in self:
with open(file_name, "w") as f:
- yaml.dump(self["case group"].output(), f)
+ yaml.dump(self["case group"].output(), f, default_flow_style=False)
print_test_menu();
}
}
+ /*use '-' to show test history. Need to do it before UNITY_BEGIN cleanup history */
+ if (cmdline[0] == '-')
+ {
+ UNITY_END();
+ continue;
+ }
UNITY_BEGIN();
"version": "v1 (2016-12-06)",
"test environment": "UT_T1_1",
"reset": "",
- "expected result": "1. set succeed"
+ "expected result": "1. set succeed",
+ "cmd set": "test_unit_test_case",
}
CONFIG_FILE_PATTERN = {
name_addr = table.get_unsigned_int(section, test_addr, 4)
desc_addr = table.get_unsigned_int(section, test_addr + 4, 4)
file_name_addr = table.get_unsigned_int(section, test_addr + 12, 4)
+ function_count = table.get_unsigned_int(section, test_addr+20, 4)
name = table.get_string("any", name_addr)
desc = table.get_string("any", desc_addr)
file_name = table.get_string("any", file_name_addr)
-
tc = self.parse_one_test_case(name, desc, file_name, app_name)
# check if duplicated case names
self.test_env_tags[tc["test environment"]].append(tc["ID"])
else:
self.test_env_tags.update({tc["test environment"]: [tc["ID"]]})
- # only add cases need to be executed
+
+ if function_count > 1:
+ tc.update({"cmd set": "multiple_devices_case",
+ "child case num": function_count})
+ del tc['reset']
+
+ # only add cases need to be executed
test_cases.append(tc)
os.remove("section_table.tmp")
test_case.update({"Test App": self.APP_NAME_PREFIX + app_name,
"module": self.module_map[prop["module"]]['module'],
"CI ready": "No" if prop["ignore"] == "Yes" else "Yes",
- "cmd set": ["IDFUnitTest/UnitTest", [name]],
"ID": tc_id,
"test point 2": prop["module"],
"steps": name,
if __name__ == '__main__':
main()
-
--- /dev/null
+"""
+Test script for unit test case.
+"""
+
+import re
+import os
+import sys
+import time
+
+import threading
+
+# if we want to run test case outside `tiny-test-fw` folder,
+# we need to insert tiny-test-fw path into sys path
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path and test_fw_path not in sys.path:
+ sys.path.insert(0, test_fw_path)
+
+import TinyFW
+import IDF
+import Utility
+from DUT import ExpectTimeout
+from IDF.IDFApp import UT
+
+
+UT_APP_BOOT_UP_DONE = "Press ENTER to see the list of tests."
+UT_TIMEOUT = 30
+
+def format_test_case_config(test_case_data):
+ """
+ convert the test case data to unified format.
+ We need to following info to run unit test cases:
+
+ 1. unit test app config
+ 2. test case name
+ 3. test case reset info
+
+ the formatted case config is a dict, with ut app config as keys. The value is a list of test cases.
+ Each test case is a dict with "name" and "reset" as keys. For example::
+
+ case_config = {
+ "default": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, {...}],
+ "psram": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}],
+ }
+
+ If config is not specified for test case, then
+
+ :param test_case_data: string, list, or a dictionary list
+ :return: formatted data
+ """
+
+ case_config = dict()
+
+ def parse_case(one_case_data):
+ """ parse and format one case """
+
+ def process_reset_list(reset_list):
+ # strip space and remove white space only items
+ _output = list()
+ for _r in reset_list:
+ _data = _r.strip(" ")
+ if _data:
+ _output.append(_data)
+ return _output
+
+ _case = dict()
+ if isinstance(one_case_data, str):
+ _temp = one_case_data.split(" [reset=")
+ _case["name"] = _temp[0]
+ try:
+ _case["reset"] = process_reset_list(_temp[1][0:-1].split(","))
+ except IndexError:
+ _case["reset"] = list()
+ elif isinstance(one_case_data, dict):
+ _case = one_case_data.copy()
+ assert "name" in _case
+ if "reset" not in _case:
+ _case["reset"] = list()
+ else:
+ if isinstance(_case["reset"], str):
+ _case["reset"] = process_reset_list(_case["reset"].split(","))
+ else:
+ raise TypeError("Not supported type during parsing unit test case")
+
+ if "config" not in _case:
+ _case["config"] = "default"
+
+ return _case
+
+ if not isinstance(test_case_data, list):
+ test_case_data = [test_case_data]
+
+ for case_data in test_case_data:
+ parsed_case = parse_case(case_data)
+ try:
+ case_config[parsed_case["config"]].append(parsed_case)
+ except KeyError:
+ case_config[parsed_case["config"]] = [parsed_case]
+
+ return case_config
+
+
+@TinyFW.test_method(app=UT, dut=IDF.IDFDUT, chip="ESP32", module="unit_test",
+ execution_time=1, env_tag="UT_T1_1")
+def test_unit_test_case(env, extra_data):
+ """
+ extra_data can be three types of value
+ 1. as string:
+ 1. "case_name"
+ 2. "case_name [reset=RESET_REASON]"
+ 2. as dict:
+ 1. with key like {"name": "Intr_alloc test, shared ints"}
+ 2. with key like {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET", "config": "psram"}
+ 3. as list of string or dict:
+ [case1, case2, case3, {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, ...]
+
+ :param extra_data: the case name or case list or case dictionary
+ :return: None
+ """
+
+ case_config = format_test_case_config(extra_data)
+
+ # compile the patterns for expect only once
+ reset_pattern = re.compile(r"(ets [\w]{3}\s+[\d]{1,2} [\d]{4} [\d]{2}:[\d]{2}:[\d]{2}[^()]*\([\w].*?\))")
+ exception_pattern = re.compile(r"(Guru Meditation Error: Core\s+\d panic'ed \([\w].*?\))")
+ abort_pattern = re.compile(r"(abort\(\) was called at PC 0x[a-eA-E\d]{8} on core \d)")
+ finish_pattern = re.compile(r"1 Tests (\d) Failures (\d) Ignored")
+
+ # we don't want stop on failed case (unless some special scenarios we can't handle)
+ # this flag is used to log if any of the case failed during executing
+ # Before exit test function this flag is used to log if the case fails
+ failed_cases = []
+
+ for ut_config in case_config:
+ dut = env.get_dut("unit-test-app", app_path=ut_config)
+ dut.start_app()
+
+ for one_case in case_config[ut_config]:
+ dut.reset()
+ # esptool ``run`` cmd takes quite long time.
+ # before reset finish, serial port is closed. therefore DUT could already bootup before serial port opened.
+ # this could cause checking bootup print failed.
+ # now we input cmd `-`, and check either bootup print or test history,
+ # to determine if DUT is ready to test.
+ dut.write("-", flush=False)
+ dut.expect_any(UT_APP_BOOT_UP_DONE,
+ "0 Tests 0 Failures 0 Ignored")
+
+ # run test case
+ dut.write("\"{}\"".format(one_case["name"]))
+ dut.expect("Running " + one_case["name"] + "...")
+
+ exception_reset_list = []
+
+ # we want to set this flag in callbacks (inner functions)
+ # use list here so we can use append to set this flag
+ test_finish = list()
+
+ # expect callbacks
+ def one_case_finish(result):
+ """ one test finished, let expect loop break and log result """
+ test_finish.append(True)
+ if result:
+ Utility.console_log("Success: " + one_case["name"], color="green")
+ else:
+ failed_cases.append(one_case["name"])
+ Utility.console_log("Failed: " + one_case["name"], color="red")
+
+ def handle_exception_reset(data):
+ """
+ just append data to exception list.
+ exception list will be checked in ``handle_reset_finish``, once reset finished.
+ """
+ exception_reset_list.append(data[0])
+
+ def handle_test_finish(data):
+ """ test finished without reset """
+ # in this scenario reset should not happen
+ assert not exception_reset_list
+ if int(data[1]):
+ # case ignored
+ Utility.console_log("Ignored: " + one_case["name"], color="orange")
+ one_case_finish(not int(data[0]))
+
+ def handle_reset_finish(data):
+ """ reset happened and reboot finished """
+ assert exception_reset_list # reboot but no exception/reset logged. should never happen
+ result = False
+ if len(one_case["reset"]) == len(exception_reset_list):
+ for i, exception in enumerate(exception_reset_list):
+ if one_case["reset"][i] not in exception:
+ break
+ else:
+ result = True
+ if not result:
+ Utility.console_log("""Reset Check Failed: \r\n\tExpected: {}\r\n\tGet: {}"""
+ .format(one_case["reset"], exception_reset_list),
+ color="orange")
+ one_case_finish(result)
+
+ while not test_finish:
+ try:
+ dut.expect_any((reset_pattern, handle_exception_reset), # reset pattern
+ (exception_pattern, handle_exception_reset), # exception pattern
+ (abort_pattern, handle_exception_reset), # abort pattern
+ (finish_pattern, handle_test_finish), # test finish pattern
+ (UT_APP_BOOT_UP_DONE, handle_reset_finish), # reboot finish pattern
+ timeout=UT_TIMEOUT)
+ except ExpectTimeout:
+ Utility.console_log("Timeout in expect", color="orange")
+ one_case_finish(False)
+ break
+
+ # raise exception if any case fails
+ if failed_cases:
+ Utility.console_log("Failed Cases:", color="red")
+ for _case_name in failed_cases:
+ Utility.console_log("\t" + _case_name, color="red")
+ raise AssertionError("Unit Test Failed")
+
+
+class Handler(threading.Thread):
+
+ WAIT_SIGNAL_PATTERN = re.compile(r'Waiting for signal: \[(.+)\]!')
+ SEND_SIGNAL_PATTERN = re.compile(r'Send signal: \[(.+)\]!')
+ FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored")
+
+ def __init__(self, dut, sent_signal_list, lock, parent_case_name, child_case_index, timeout=30):
+ self.dut = dut
+ self.sent_signal_list = sent_signal_list
+ self.lock = lock
+ self.parent_case_name = parent_case_name
+ self.child_case_name = ""
+ self.child_case_index = child_case_index + 1
+ self.finish = False
+ self.result = False
+ self.fail_name = None
+ self.timeout = timeout
+ threading.Thread.__init__(self, name="{} Handler".format(dut))
+
+ def run(self):
+ def get_child_case_name(data):
+ self.child_case_name = data[0]
+ time.sleep(1)
+ self.dut.write(str(self.child_case_index))
+
+ def one_device_case_finish(result):
+ """ one test finished, let expect loop break and log result """
+ self.finish = True
+ self.result = result
+ if not result:
+ self.fail_name = self.child_case_name
+
+ def device_wait_action(data):
+ start_time = time.time()
+ expected_signal = data[0]
+ while 1:
+ if time.time() > start_time + self.timeout:
+ Utility.console_log("Timeout in device for function: %s"%self.child_case_name, color="orange")
+ break
+ with self.lock:
+ if expected_signal in self.sent_signal_list:
+ self.dut.write(" ")
+ self.sent_signal_list.remove(expected_signal)
+ break
+ time.sleep(0.01)
+
+ def device_send_action(data):
+ with self.lock:
+ self.sent_signal_list.append(data[0].encode('utf-8'))
+
+ def handle_device_test_finish(data):
+ """ test finished without reset """
+ # in this scenario reset should not happen
+ if int(data[1]):
+ # case ignored
+ Utility.console_log("Ignored: " + self.child_case_name, color="orange")
+ one_device_case_finish(not int(data[0]))
+
+ self.dut.reset()
+ self.dut.write("-", flush=False)
+ self.dut.expect_any(UT_APP_BOOT_UP_DONE, "0 Tests 0 Failures 0 Ignored")
+ time.sleep(1)
+ self.dut.write("\"{}\"".format(self.parent_case_name))
+ self.dut.expect("Running " + self.parent_case_name + "...")
+
+ while not self.finish:
+ try:
+ self.dut.expect_any((re.compile('\(' + str(self.child_case_index) + '\)\s"(\w+)"'), get_child_case_name),
+ (self.WAIT_SIGNAL_PATTERN, device_wait_action), # wait signal pattern
+ (self.SEND_SIGNAL_PATTERN, device_send_action), # send signal pattern
+ (self.FINISH_PATTERN, handle_device_test_finish), # test finish pattern
+ timeout=UT_TIMEOUT)
+ except ExpectTimeout:
+ Utility.console_log("Timeout in expect", color="orange")
+ one_device_case_finish(False)
+ break
+
+
+def get_case_info(one_case):
+ parent_case = one_case["name"]
+ child_case_num = one_case["child case num"]
+ return parent_case, child_case_num
+
+
+def get_dut(duts, env, name, ut_config):
+ if name in duts:
+ dut = duts[name]
+ else:
+ dut = env.get_dut(name, app_path=ut_config)
+ duts[name] = dut
+ dut.start_app()
+ return dut
+
+
+def case_run(duts, ut_config, env, one_case, failed_cases):
+ lock = threading.RLock()
+ threads = []
+ send_signal_list = []
+ failed_device = []
+ result = True
+ parent_case, case_num = get_case_info(one_case)
+ for i in range(case_num):
+ dut = get_dut(duts, env, "dut%d" % i, ut_config)
+ threads.append(Handler(dut, send_signal_list, lock,
+ parent_case, i))
+ for thread in threads:
+ thread.setDaemon(True)
+ thread.start()
+ for thread in threads:
+ thread.join()
+ result = result and thread.result
+ if not thread.result:
+ failed_device.append(thread.fail_name)
+ if result:
+ Utility.console_log("Success: " + one_case["name"], color="green")
+ else:
+ failed_cases.append(one_case["name"])
+ Utility.console_log("Failed: " + one_case["name"], color="red")
+
+
+@TinyFW.test_method(app=UT, dut=IDF.IDFDUT, chip="ESP32", module="master_slave_test_case", execution_time=1,
+ env_tag="UT_T2_1")
+def multiple_devices_case(env, extra_data):
+ """
+ extra_data can be two types of value
+ 1. as dict:
+ e.g.
+ {"name": "gpio master/slave test example",
+ "child case num": 2,
+ "config": "release",
+ "env_tag": "UT_T2_1"}
+ 2. as list dict:
+ e.g.
+ [{"name": "gpio master/slave test example1",
+ "child case num": 2,
+ "config": "release",
+ "env_tag": "UT_T2_1"},
+ {"name": "gpio master/slave test example2",
+ "child case num": 2,
+ "config": "release",
+ "env_tag": "UT_T2_1"}]
+
+ """
+ failed_cases = []
+ case_config = format_test_case_config(extra_data)
+ DUTS = {}
+ for ut_config in case_config:
+ for one_case in case_config[ut_config]:
+ case_run(DUTS, ut_config, env, one_case, failed_cases)
+
+ if failed_cases:
+ Utility.console_log("Failed Cases:", color="red")
+ for _case_name in failed_cases:
+ Utility.console_log("\t" + _case_name, color="red")
+ raise AssertionError("Unit Test Failed")
+
+if __name__ == '__main__':
+ multiple_devices_case(extra_data={"name": "gpio master/slave test example",
+ "child case num": 2,
+ "config": "release",
+ "env_tag": "UT_T2_1"})
+
+
+