From 58f046715ef925b77af0180d1a61db1d6d12cac3 Mon Sep 17 00:00:00 2001 From: luc lebosse Date: Sat, 14 Oct 2017 14:52:40 +0200 Subject: [PATCH 01/30] Unify the time file creation for SPIFFS and SD --- components/fatfs/src/vfs_fat.c | 5 +++++ components/spiffs/esp_spiffs.c | 22 ++++++++++++++++++++++ components/spiffs/include/spiffs_config.h | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index a5a12d5b65..b1192b602c 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -389,6 +389,9 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) FIL* file = &fat_ctx->files[fd]; st->st_size = f_size(file); st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; + st->st_mtime = 0; + st->st_atime = 0; + st->st_ctime = 0; return 0; } @@ -422,6 +425,8 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) ftime >>= 6; tm.tm_hour = (ftime & 0x1f); st->st_mtime = mktime(&tm); + st->st_atime = 0; + st->st_ctime = 0; return 0; } diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index 3f81b151d5..4520c81fa6 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -513,6 +513,16 @@ static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode) SPIFFS_clearerr(efs->fs); return -1; } +#if SPIFFS_OBJ_META_LEN > 0 + if (!(spiffs_mode_conv(flags) & SPIFFS_O_RDONLY)) + { + time_t t = time(NULL); + struct tm tmr; + localtime_r(&t, &tmr); + time_t meta = mktime(&tmr); + SPIFFS_fupdate_meta(efs->fs, fd, &meta); + } +#endif return fd; } @@ -578,6 +588,9 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) } st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; + st->st_mtime = 0; + st->st_atime = 0; + st->st_ctime = 0; return res; } @@ -597,6 +610,15 @@ static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; +#if SPIFFS_OBJ_META_LEN > 0 + time_t t =0; + memcpy(&t, s.meta, sizeof(time_t)); + st->st_mtime = t; +#else + st->st_mtime = 0; +#endif + st->st_atime = 0; + st->st_ctime = 0; return res; } diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h index e412bfd0cf..c96a3412d3 100755 --- a/components/spiffs/include/spiffs_config.h +++ b/components/spiffs/include/spiffs_config.h @@ -158,7 +158,7 @@ extern void spiffs_api_unlock(struct spiffs_t *fs); // 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 (0) +#define SPIFFS_OBJ_META_LEN (4) // Size of buffer allocated on stack used when copying data. // Lower value generates more read/writes. No meaning having it bigger From c03b068931b484990bbee55299502eea4c02994a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 22 Nov 2017 00:17:11 +0800 Subject: [PATCH 02/30] spiffs: make OBJ_META_LEN configurable, make mtime support optional - SPIFFS_OBJ_META_LEN can be set in sdkconfig - mtime support can be enabled in sdkconfig, if META_LENGTH is sufficient - add test for mtime updates --- components/spiffs/Kconfig | 19 +++++++ components/spiffs/esp_spiffs.c | 61 +++++++++++++++-------- components/spiffs/include/spiffs_config.h | 2 +- components/spiffs/test/test_spiffs.c | 42 ++++++++++++++++ 4 files changed, 103 insertions(+), 21 deletions(-) diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig index d82ceec73e..1f7a196d72 100644 --- a/components/spiffs/Kconfig +++ b/components/spiffs/Kconfig @@ -88,6 +88,25 @@ config SPIFFS_USE_MAGIC_LENGTH configured and formatted for 4 megabytes will not be accepted for mounting with a configuration defining the filesystem as 2 megabytes. +config SPIFFS_META_LENGTH + int "Size of per-file metadata field" + default 4 + help + This option sets the number of extra bytes stored in the file header. + 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. + +config SPIFFS_USE_MTIME + bool "Save file modification time" + default "y" + depends on SPIFFS_META_LENGTH >= 4 + help + If enabled, then the first 4 bytes of per-file metadata will be used + to store file modification time (mtime), accessible through + stat/fstat functions. + Modification time is updated when the file is opened. + menu "Debug Configuration" config SPIFFS_DBG diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index 4520c81fa6..2c454e9dd3 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -33,6 +33,10 @@ static const char * TAG = "SPIFFS"; +#ifdef CONFIG_SPIFFS_USE_MTIME +_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t), + "SPIFFS_META_LENGTH size should be >= sizeof(time_t)"); +#endif //CONFIG_SPIFFS_USE_MTIME /** * @brief SPIFFS definition structure */ @@ -80,6 +84,8 @@ static long vfs_spiffs_telldir(void* ctx, DIR* pdir); static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset); static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode); static int vfs_spiffs_rmdir(void* ctx, const char* name); +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f); +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s); static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS]; @@ -507,22 +513,16 @@ static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode) { assert(path); esp_spiffs_t * efs = (esp_spiffs_t *)ctx; - int fd = SPIFFS_open(efs->fs, path, spiffs_mode_conv(flags), mode); + int spiffs_flags = spiffs_mode_conv(flags); + int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode); if (fd < 0) { errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); SPIFFS_clearerr(efs->fs); return -1; } -#if SPIFFS_OBJ_META_LEN > 0 - if (!(spiffs_mode_conv(flags) & SPIFFS_O_RDONLY)) - { - time_t t = time(NULL); - struct tm tmr; - localtime_r(&t, &tmr); - time_t meta = mktime(&tmr); - SPIFFS_fupdate_meta(efs->fs, fd, &meta); - } -#endif + if (!(spiffs_flags & SPIFFS_RDONLY)) { + vfs_spiffs_update_mtime(efs->fs, fd); + } return fd; } @@ -572,7 +572,6 @@ static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode) return -1; } return res; - } static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) @@ -588,7 +587,7 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) } st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; - st->st_mtime = 0; + st->st_mtime = vfs_spiffs_get_mtime(&s); st->st_atime = 0; st->st_ctime = 0; return res; @@ -610,13 +609,7 @@ static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; -#if SPIFFS_OBJ_META_LEN > 0 - time_t t =0; - memcpy(&t, s.meta, sizeof(time_t)); - st->st_mtime = t; -#else - st->st_mtime = 0; -#endif + st->st_mtime = vfs_spiffs_get_mtime(&s); st->st_atime = 0; st->st_ctime = 0; return res; @@ -786,3 +779,31 @@ static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2) errno = ENOTSUP; return -1; } + +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd) +{ +#ifdef CONFIG_SPIFFS_USE_MTIME + time_t t = time(NULL); + spiffs_stat s; + int ret = SPIFFS_OK; + if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) { + ret = SPIFFS_fstat(fs, fd, &s); + } + if (ret == SPIFFS_OK) { + memcpy(s.meta, &t, sizeof(t)); + ret = SPIFFS_fupdate_meta(fs, fd, s.meta); + } + if (ret != SPIFFS_OK) { + ESP_LOGW(TAG, "Failed to update mtime (%d)", ret); + } +#endif //CONFIG_SPIFFS_USE_MTIME +} + +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s) +{ + time_t t = 0; +#ifdef CONFIG_SPIFFS_USE_MTIME + memcpy(&t, s->meta, sizeof(t)); +#endif + return t; +} diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h index c96a3412d3..28414facf9 100755 --- a/components/spiffs/include/spiffs_config.h +++ b/components/spiffs/include/spiffs_config.h @@ -158,7 +158,7 @@ extern void spiffs_api_unlock(struct spiffs_t *fs); // 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 (4) +#define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH) // Size of buffer allocated on stack used when copying data. // Lower value generates more read/writes. No meaning having it bigger diff --git a/components/spiffs/test/test_spiffs.c b/components/spiffs/test/test_spiffs.c index daf11ddc94..b428de4ceb 100644 --- a/components/spiffs/test/test_spiffs.c +++ b/components/spiffs/test/test_spiffs.c @@ -505,3 +505,45 @@ TEST_CASE("multiple tasks can use same volume", "[spiffs]") test_spiffs_concurrent("/spiffs/f"); test_teardown(); } + +#ifdef CONFIG_SPIFFS_USE_MTIME +TEST_CASE("mtime is updated when file is opened", "[spiffs]") +{ + /* Open a file, check that mtime is set correctly */ + const char* filename = "/spiffs/time"; + test_setup(); + time_t t_before_create = time(NULL); + test_spiffs_create_file_with_text(filename, "\n"); + time_t t_after_create = time(NULL); + + struct stat st; + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(st.st_mtime >= t_before_create + && st.st_mtime <= t_after_create); + + /* Wait a bit, open again, check that mtime is updated */ + vTaskDelay(2000 / portTICK_PERIOD_MS); + time_t t_before_open = time(NULL); + FILE *f = fopen(filename, "a"); + time_t t_after_open = time(NULL); + TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(st.st_mtime >= t_before_open + && st.st_mtime <= t_after_open); + fclose(f); + + /* Wait a bit, open for reading, check that mtime is not updated */ + vTaskDelay(2000 / portTICK_PERIOD_MS); + time_t t_before_open_ro = time(NULL); + f = fopen(filename, "r"); + TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(t_before_open_ro > t_after_open + && st.st_mtime >= t_before_open + && st.st_mtime <= t_after_open); + fclose(f); + + test_teardown(); +} +#endif // CONFIG_SPIFFS_USE_MTIME From 892b3ff14b923dc15eb136f1c2b1c76895027d9e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 6 Oct 2017 15:38:01 +1100 Subject: [PATCH 03/30] spi_flash: Add option to verify all writes by reading back data Helpful when debugging SPI flash hardware related issues. TW15203 --- components/spi_flash/Kconfig | 17 ++++++++ components/spi_flash/flash_ops.c | 69 ++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 4028cf3894..5a05d859bd 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -1,5 +1,22 @@ menu "SPI Flash driver" +config SPI_FLASH_VERIFY_WRITE + bool "Verify SPI flash writes" + default n + help + If this option is enabled, any time SPI flash is written then the data will be read + back and verified. This can catch hardware problems with SPI flash, or flash which + was not erased before verification. + +config SPI_FLASH_LOG_FAILED_WRITE + bool "Log errors if verification fails" + depends on SPI_FLASH_VERIFY_WRITE + default n + help + If this option is enabled, if SPI flash write verification fails then a log error line + will be written with the address, expected & actual values. This can be useful when + debugging hardware SPI flash problems. + config SPI_FLASH_ENABLE_COUNTERS bool "Enable operation counters" default 0 diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 7fd47dcca4..eb2b23cf49 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -44,8 +44,9 @@ #define MAX_WRITE_CHUNK 8192 #define MAX_READ_CHUNK 16384 +static const char *TAG __attribute__((unused)) = "spi_flash"; + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS -static const char *TAG = "spi_flash"; static spi_flash_counters_t s_flash_stats; #define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() @@ -233,6 +234,66 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) return spi_flash_translate_rc(rc); } +/* Wrapper around esp_rom_spiflash_write() that verifies data as written if CONFIG_SPI_FLASH_VERIFY_WRITE is set. + + If CONFIG_SPI_FLASH_VERIFY_WRITE is not set, this is esp_rom_spiflash_write(). +*/ +static IRAM_ATTR esp_rom_spiflash_result_t spi_flash_write_inner(uint32_t target, const uint32_t *src_addr, int32_t len) +{ +#ifndef CONFIG_SPI_FLASH_VERIFY_WRITE + return esp_rom_spiflash_write(target, src_addr, len); +#else // CONFIG_SPI_FLASH_VERIFY_WRITE + esp_rom_spiflash_result_t res = ESP_ROM_SPIFLASH_RESULT_OK; + assert(len % sizeof(uint32_t) == 0); + + uint32_t before_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)]; + uint32_t after_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)]; + int32_t remaining = len; + for(int i = 0; i < len; i += sizeof(before_buf)) { + int i_w = i / sizeof(uint32_t); // index in words (i is an index in bytes) + + int32_t read_len = MIN(sizeof(before_buf), remaining); + + // Read "before" contents from flash + res = esp_rom_spiflash_read(target + i, before_buf, read_len); + if (res != ESP_ROM_SPIFLASH_RESULT_OK) { + break; + } + + res = esp_rom_spiflash_write(target + i, &src_addr[i_w], read_len); + if (res != ESP_ROM_SPIFLASH_RESULT_OK) { + break; + } + + res = esp_rom_spiflash_read(target + i, after_buf, read_len); + if (res != ESP_ROM_SPIFLASH_RESULT_OK) { + break; + } + + for (int r = 0; r < read_len; r += sizeof(uint32_t)) { + int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes) + + uint32_t expected = src_addr[i_w + r_w] & before_buf[r_w]; + uint32_t actual = after_buf[r_w]; + if (expected != actual) { +#ifdef CONFIG_SPI_FLASH_LOG_FAILED_WRITE + spi_flash_guard_end(); + ESP_LOGE(TAG, "Bad write at offset 0x%x expected 0x%08x readback 0x%08x", target + i + r, expected, actual); + spi_flash_guard_start(); +#endif + res = ESP_ROM_SPIFLASH_RESULT_ERR; + } + } + if (res != ESP_ROM_SPIFLASH_RESULT_OK) { + break; + } + remaining -= read_len; + } + return res; +#endif // CONFIG_SPI_FLASH_VERIFY_WRITE +} + + esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) { CHECK_WRITE_ADDRESS(dst, size); @@ -269,7 +330,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); spi_flash_guard_start(); - rc = esp_rom_spiflash_write(left_off, &t, 4); + rc = spi_flash_write_inner(left_off, &t, 4); spi_flash_guard_end(); if (rc != ESP_ROM_SPIFLASH_RESULT_OK) { goto out; @@ -296,7 +357,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) write_src = (const uint8_t *)write_buf; } spi_flash_guard_start(); - rc = esp_rom_spiflash_write(dst + mid_off, (const uint32_t *) write_src, write_size); + rc = spi_flash_write_inner(dst + mid_off, (const uint32_t *) write_src, write_size); spi_flash_guard_end(); COUNTER_ADD_BYTES(write, write_size); mid_size -= write_size; @@ -311,7 +372,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); spi_flash_guard_start(); - rc = esp_rom_spiflash_write(dst + right_off, &t, 4); + rc = spi_flash_write_inner(dst + right_off, &t, 4); spi_flash_guard_end(); if (rc != ESP_ROM_SPIFLASH_RESULT_OK) { goto out; From f7ac41c2daf92920310b5975cbb6f3318a5a53f6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 29 Nov 2017 14:26:57 +1100 Subject: [PATCH 04/30] spi_flash: Add option to log warnings if (spuriously) writing zero bits to ones Won't work for SPIFFS, maybe some other implementations? --- components/spi_flash/Kconfig | 14 ++++++++++++++ components/spi_flash/flash_ops.c | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 5a05d859bd..f5e3c7ecd9 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -17,6 +17,20 @@ config SPI_FLASH_LOG_FAILED_WRITE will be written with the address, expected & actual values. This can be useful when debugging hardware SPI flash problems. +config SPI_FLASH_WARN_SETTING_ZERO_TO_ONE + bool "Log warning if writing zero bits to ones" + depends on SPI_FLASH_VERIFY_WRITE + default n + help + If this option is enabled, any SPI flash write which tries to set zero bits in the flash to + ones will log a warning. Such writes will not result in the requested data appearing identically + in flash once written, as SPI NOR flash can only set bits to one when an entire sector is erased. + After erasing, individual bits can only be written from one to zero. + + Note that some software (such as SPIFFS) which is aware of SPI NOR flash may write one bits as an + optimisation, relying on the data in flash becoming a bitwise AND of the new data and any existing data. + Such software will log spurious warnings if this option is enabled. + config SPI_FLASH_ENABLE_COUNTERS bool "Enable operation counters" default 0 diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index eb2b23cf49..aab0c1210f 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -260,6 +260,21 @@ static IRAM_ATTR esp_rom_spiflash_result_t spi_flash_write_inner(uint32_t target break; } +#ifdef CONFIG_SPI_FLASH_WARN_SETTING_ZERO_TO_ONE + for (int r = 0; r < read_len; r += sizeof(uint32_t)) { + int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes) + + uint32_t write = src_addr[i_w + r_w]; + uint32_t before = before_buf[r_w]; + if ((before & write) != write) { + spi_flash_guard_end(); + ESP_LOGW(TAG, "Write at offset 0x%x requests 0x%08x but will write 0x%08x -> 0x%08x", + target + i + r, write, before, before & write); + spi_flash_guard_start(); + } + } +#endif + res = esp_rom_spiflash_write(target + i, &src_addr[i_w], read_len); if (res != ESP_ROM_SPIFLASH_RESULT_OK) { break; From 519edc332dae0160069fd790467cde8de78f1a0e Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 25 Oct 2017 23:23:42 +0200 Subject: [PATCH 05/30] Fix compilation errors when using gcc-7.2.0 for the crosstool-ng toolchain * Change snprintf for strlcat does not complain w/gcc7.2.0 and it is safer, thanks @projectgus * Use proper quotes for character literals Merges https://github.com/espressif/esp-idf/pull/1163 --- components/console/argtable3/argtable3.c | 2 +- components/mdns/mdns.c | 3 ++- components/nvs_flash/src/nvs_item_hash_list.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/console/argtable3/argtable3.c b/components/console/argtable3/argtable3.c index f3772f1a9b..ed577c83aa 100644 --- a/components/console/argtable3/argtable3.c +++ b/components/console/argtable3/argtable3.c @@ -270,7 +270,7 @@ extern char *suboptarg; /* getsubopt(3) external variable */ */ #ifndef lint -static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $"; +//static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $"; #endif /* lint */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c index c77283e071..171da177c4 100644 --- a/components/mdns/mdns.c +++ b/components/mdns/mdns.c @@ -569,7 +569,8 @@ static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * s && (strcmp(buf, MDNS_DEFAULT_DOMAIN) != 0) && (strcmp(buf, "ip6") != 0) && (strcmp(buf, "in-addr") != 0)) { - snprintf((char*)name, MDNS_NAME_BUF_LEN, "%s.%s", name->host, buf); + strlcat(name->host, ".", sizeof(name->host)); + strlcat(name->host, buf, sizeof(name->host)); } else if (strcmp(buf, MDNS_SUB_STR) == 0) { name->sub = 1; } else { diff --git a/components/nvs_flash/src/nvs_item_hash_list.cpp b/components/nvs_flash/src/nvs_item_hash_list.cpp index cf48477d61..e483c59697 100644 --- a/components/nvs_flash/src/nvs_item_hash_list.cpp +++ b/components/nvs_flash/src/nvs_item_hash_list.cpp @@ -62,7 +62,7 @@ void HashList::insert(const Item& item, size_t index) void HashList::erase(size_t index) { - for (auto it = std::begin(mBlockList); it != std::end(mBlockList);) { + for (auto it = mBlockList.begin(); it != mBlockList.end();) { bool haveEntries = false; for (size_t i = 0; i < it->mCount; ++i) { if (it->mNodes[i].mIndex == index) { @@ -88,7 +88,7 @@ void HashList::erase(size_t index) size_t HashList::find(size_t start, const Item& item) { const uint32_t hash_24 = item.calculateCrc32WithoutValue() & 0xffffff; - for (auto it = std::begin(mBlockList); it != std::end(mBlockList); ++it) { + for (auto it = mBlockList.begin(); it != mBlockList.end(); ++it) { for (size_t index = 0; index < it->mCount; ++index) { HashListNode& e = it->mNodes[index]; if (e.mIndex >= start && From a3da93d834b2adba72e3e62a38c640404c392f07 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sun, 19 Nov 2017 02:13:52 +1100 Subject: [PATCH 06/30] Update cJSON to 1.6.0 * Fixes compilation errors/warnings with gcc 7.2.0 Merges https://github.com/espressif/esp-idf/pull/1163 --- components/json/include/cJSON.h | 261 +- components/json/library/cJSON.c | 3189 ++++++++++++++++---- components/json/port/cJSON_Utils.c | 1692 +++++++++-- components/json/port/include/cJSON_Utils.h | 74 +- 4 files changed, 4212 insertions(+), 1004 deletions(-) diff --git a/components/json/include/cJSON.h b/components/json/include/cJSON.h index 92ed8c3b48..7c4f8e7cdb 100644 --- a/components/json/include/cJSON.h +++ b/components/json/include/cJSON.h @@ -1,16 +1,16 @@ /* - Copyright (c) 2009 Dave Gamble - + 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 @@ -28,120 +28,233 @@ extern "C" { #endif +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 6 +#define CJSON_VERSION_PATCH 0 + +#include + /* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 - +#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 { - struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ +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; - int type; /* The type of the item, as above. */ + /* The type of the item, as above. */ + int type; - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ + /* 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; - char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + /* 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 { +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 */ -extern void cJSON_InitHooks(cJSON_Hooks* hooks); +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); -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern char *cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ -extern char *cJSON_PrintUnformatted(cJSON *item); +/* 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 */ -extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt); +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. */ -extern void cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); /* Returns the number of items in an array (or object). */ -extern int cJSON_GetArraySize(cJSON *array); +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ -extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); - +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. */ -extern const char *cJSON_GetErrorPtr(void); - +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. */ -extern cJSON *cJSON_CreateNull(void); -extern cJSON *cJSON_CreateTrue(void); -extern cJSON *cJSON_CreateFalse(void); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateDouble(double num,int i_num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(void); -extern cJSON *cJSON_CreateObject(void); +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. */ -extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); -extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); -extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); -extern cJSON *cJSON_CreateStringArray(const char **strings,int count); +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. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); -extern void cJSON_AddItemToObjectCS(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 */ +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. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); +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. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); -extern void cJSON_DeleteItemFromArray(cJSON *array,int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); - +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. */ -extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */ -extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +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 */ -extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +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); -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ -extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); -extern void cJSON_Minify(char *json); +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_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,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) -#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) +#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 } diff --git a/components/json/library/cJSON.c b/components/json/library/cJSON.c index 2a5c392161..cef719f576 100644 --- a/components/json/library/cJSON.c +++ b/components/json/library/cJSON.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble + 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 @@ -23,6 +23,20 @@ /* 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 #include #include @@ -30,722 +44,2711 @@ #include #include #include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + #include "cJSON.h" -static const char *ep; +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) -const char *cJSON_GetErrorPtr(void) {return ep;} +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; -static int cJSON_strcasecmp(const char *s1,const char *s2) +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); + return (const char*) (global_error.json + global_error.position); } -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; +/* 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 -static char* cJSON_strdup(const char* str) +CJSON_PUBLIC(const char*) cJSON_Version(void) { - size_t len; - char* copy; + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; + return version; } -void cJSON_InitHooks(cJSON_Hooks* hooks) +/* 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 (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; + 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; } - cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; - cJSON_free = (hooks->free_fn)?hooks->free_fn:free; + 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(void) +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) { - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; } /* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } + 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 const char *parse_number(cJSON *item,const char *num) +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { - double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + 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 (*num=='-') sign=-1,num++; /* Has sign? */ - if (*num=='0') num++; /* is zero */ - if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ - if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ - if (*num=='e' || *num=='E') /* Exponent? */ - { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ - while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ - } + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } - n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - - item->valuedouble=n; - item->valueint=(int)n; - item->type=cJSON_Number; - return num; + /* 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; } -static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; } - -typedef struct {char *buffer; int length; int offset; } printbuffer; - -static char* ensure(printbuffer *p,int needed) +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { - char *newbuffer;int newsize; - if (!p || !p->buffer) return 0; - needed+=p->offset; - if (needed<=p->length) return p->buffer+p->offset; + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } - newsize=pow2gt(needed); - newbuffer=(char*)cJSON_malloc(newsize); - if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;} - if (newbuffer) memcpy(newbuffer,p->buffer,p->length); - cJSON_free(p->buffer); - p->length=newsize; - p->buffer=newbuffer; - return newbuffer+p->offset; + return object->valuedouble = number; } -static int update(printbuffer *p) +typedef struct { - char *str; - if (!p || !p->buffer) return 0; - str=p->buffer+p->offset; - return p->offset+strlen(str); + 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 char *print_number(cJSON *item,printbuffer *p) +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { - char *str=0; - double d=item->valuedouble; - if (d==0) - { - if (p) str=ensure(p,2); - else str=(char*)cJSON_malloc(2); /* special case for 0. */ - if (str) strcpy(str,"0"); - } - else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) - { - if (p) str=ensure(p,21); - else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ - if (str) sprintf(str,"%d",item->valueint); - } - else - { - if (p) str=ensure(p,64); - else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ - if (str) - { - if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); - else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); - else sprintf(str,"%f",d); - } - } - return str; + 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; } -static unsigned parse_hex4(const char *str) +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) { - unsigned h=0; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - return h; + 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; } -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item,const char *str) +/* 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) { - const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; - if (*str!='\"') {ep=str;return 0;} /* not a string! */ - - while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ - - out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ - if (!out) return 0; - - ptr=str+1;ptr2=out; - while (*ptr!='\"' && *ptr) - { - if (*ptr!='\\') *ptr2++=*ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++='\b'; break; - case 'f': *ptr2++='\f'; break; - case 'n': *ptr2++='\n'; break; - case 'r': *ptr2++='\r'; break; - case 't': *ptr2++='\t'; break; - case 'u': /* transcode utf16 to utf8. */ - uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + 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 ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } - if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ - { - if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ - uc2=parse_hex4(ptr+3);ptr+=6; - if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ - uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); - } + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); - len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; - - switch (len) { - case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 =(uc | firstByteMark[len]); - } - ptr2+=len; - break; - default: *ptr2++=*ptr; break; - } - ptr++; - } - } - *ptr2=0; - if (*ptr=='\"') ptr++; - item->valuestring=out; - item->type=cJSON_String; - return ptr; + /* 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 char *print_string_ptr(const char *str,printbuffer *p) +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) { - const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; - - for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; - if (!flag) - { - len=ptr-str; - if (p) out=ensure(p,len+3); - else out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - ptr2=out;*ptr2++='\"'; - strcpy(ptr2,str); - ptr2[len]='\"'; - ptr2[len+1]=0; - return out; - } - - if (!str) - { - if (p) out=ensure(p,3); - else out=(char*)cJSON_malloc(3); - if (!out) return 0; - strcpy(out,"\"\""); - return out; - } - ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} - - if (p) out=ensure(p,len+3); - else out=(char*)cJSON_malloc(len+3); - if (!out) return 0; + 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; - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (token=*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; + 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); } -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);} /* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item,const char *value); -static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p); -static const char *parse_array(cJSON *item,const char *value); -static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p); -static const char *parse_object(cJSON *item,const char *value); -static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p); +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 const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} +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 *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { - const char *end=0; - cJSON *c=cJSON_New_Item(); - ep=0; - if (!c) return 0; /* memory fail */ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; - end=parse_value(c,skip(value)); - if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} - if (return_parse_end) *return_parse_end=end; - return c; + 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 *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} +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. */ -char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);} - -char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt) +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { - printbuffer p; - p.buffer=(char*)cJSON_malloc(prebuffer); - p.length=prebuffer; - p.offset=0; - return print_value(item,0,fmt,&p); - return p.buffer; + 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 const char *parse_value(cJSON *item,const char *value) +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) { - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value); } - if (*value=='{') { return parse_object(item,value); } + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } - ep=value;return 0; /* failure. */ + /* 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 char *print_value(cJSON *item,int depth,int fmt,printbuffer *p) +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) { - char *out=0; - if (!item) return 0; - if (p) - { - switch ((item->type)&255) - { - case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;} - case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;} - case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;} - case cJSON_Number: out=print_number(item,p);break; - case cJSON_String: out=print_string(item,p);break; - case cJSON_Array: out=print_array(item,depth,fmt,p);break; - case cJSON_Object: out=print_object(item,depth,fmt,p);break; - } - } - else - { - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item,0);break; - case cJSON_String: out=print_string(item,0);break; - case cJSON_Array: out=print_array(item,depth,fmt,0);break; - case cJSON_Object: out=print_object(item,depth,fmt,0);break; - } - } - return out; + 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 const char *parse_array(cJSON *item,const char *value) +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) { - cJSON *child; - if (*value!='[') {ep=value;return 0;} /* not an array! */ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; /* memory fail */ - value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ - if (!value) return 0; + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_value(child,skip(value+1))); - if (!value) return 0; /* memory 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; + } - if (*value==']') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ + /* 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 char *print_array(cJSON *item,int depth,int fmt,printbuffer *p) +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) { - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *child=item->child; - int numentries=0,i=0,fail=0; - size_t tmplen=0; - - /* How many entries in the array? */ - while (child) numentries++,child=child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) - { - if (p) out=ensure(p,3); - else out=(char*)cJSON_malloc(3); - if (out) strcpy(out,"[]"); - return out; - } + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; - if (p) - { - /* Compose the output array. */ - i=p->offset; - ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; - child=item->child; - while (child && !fail) - { - print_value(child,depth+1,fmt,p); - p->offset=update(p); - if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;} - child=child->next; - } - ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; - out=(p->buffer)+i; - } - else - { - /* Allocate an array to hold the values for each */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - /* Retrieve all the results: */ - child=item->child; - while (child && !fail) - { - ret=print_value(child,depth+1,fmt,0); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - child=child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) fail=1; + if (output_buffer == NULL) + { + return false; + } - /* Handle failure. */ - if (fail) - { - for (i=0;ioffset++; + 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 const char *parse_object(cJSON *item,const char *value) +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) { - cJSON *child; - if (*value!='{') {ep=value;return 0;} /* not an object! */ - - item->type=cJSON_Object; - value=skip(value+1); - if (*value=='}') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; - value=skip(parse_string(child,skip(value))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_string(child,skip(value+1))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - } - - if (*value=='}') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ + 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 char *print_object(cJSON *item,int depth,int fmt,printbuffer *p) +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) { - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *child=item->child; - int numentries=0,fail=0; - size_t tmplen=0; - /* Count the number of entries. */ - while (child) numentries++,child=child->next; - /* Explicitly handle empty object case */ - if (!numentries) - { - if (p) out=ensure(p,fmt?depth+4:3); - else out=(char*)cJSON_malloc(fmt?depth+4:3); - if (!out) return 0; - ptr=out;*ptr++='{'; - if (fmt) {*ptr++='\n';for (i=0;ioffset; - len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; - *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; - child=item->child;depth++; - while (child) - { - if (fmt) - { - ptr=ensure(p,depth); if (!ptr) return 0; - for (j=0;joffset+=depth; - } - print_string_ptr(child->string,p); - p->offset=update(p); - - len=fmt?2:1; - ptr=ensure(p,len); if (!ptr) return 0; - *ptr++=':';if (fmt) *ptr++='\t'; - p->offset+=len; - - print_value(child,depth,fmt,p); - p->offset=update(p); + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; - len=(fmt?1:0)+(child->next?1:0); - ptr=ensure(p,len+1); if (!ptr) return 0; - if (child->next) *ptr++=','; - if (fmt) *ptr++='\n';*ptr=0; - p->offset+=len; - child=child->next; - } - ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; - if (fmt) for (i=0;ibuffer)+i; - } - else - { - /* Allocate space for the names and the objects */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - names=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!names) {cJSON_free(entries);return 0;} - memset(entries,0,sizeof(char*)*numentries); - memset(names,0,sizeof(char*)*numentries); + if (output_buffer == NULL) + { + return false; + } - /* Collect all the results into our arrays: */ - child=item->child;depth++;if (fmt) len+=depth; - while (child) - { - names[i]=str=print_string_ptr(child->string,0); - entries[i++]=ret=print_value(child,depth,fmt,0); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - child=child->next; - } - - /* Try to allocate the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; + /* 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; + } - /* Handle failure */ - if (fail) - { - for (i=0;idepth++; + 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. */ -int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} +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;} +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + /* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} +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. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} -void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; - if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + 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. */ -void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;} - newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;} -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; - newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; - if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} +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 *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateDouble(double num,int i_num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=i_num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} -cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} +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 *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +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 *cJSON_Duplicate(cJSON *item,int recurse) +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { - cJSON *newitem,*cptr,*nptr=0,*newchild; - /* Bail on bad ptr */ - if (!item) return 0; - /* Create new item */ - newitem=cJSON_New_Item(); - if (!newitem) return 0; - /* Copy over all vars */ - newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; - if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} - if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} - /* If non-recursive, then we're done! */ - if (!recurse) return newitem; - /* Walk the ->next chain for the child. */ - cptr=item->child; - while (cptr) - { - newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) {cJSON_Delete(newitem);return 0;} - if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ - cptr=cptr->next; - } - return newitem; + 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; } -void cJSON_Minify(char *json) +CJSON_PUBLIC(void) cJSON_Minify(char *json) { - char *into=json; - while (*json) - { - if (*json==' ') json++; - else if (*json=='\t') json++; /* Whitespace characters. */ - else if (*json=='\r') json++; - else if (*json=='\n') json++; - else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */ - else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */ - else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */ - else *into++=*json++; /* All other characters. */ - } - *into=0; /* and null-terminate. */ + 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); } diff --git a/components/json/port/cJSON_Utils.c b/components/json/port/cJSON_Utils.c index dd65c7c60c..b83cfcd4ab 100644 --- a/components/json/port/cJSON_Utils.c +++ b/components/json/port/cJSON_Utils.c @@ -1,395 +1,1443 @@ +/* + 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 #include #include #include +#include + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUCC__ +#pragma GCC visibility pop +#endif + #include "cJSON_Utils.h" -static int cJSONUtils_strcasecmp(const char *s1,const char *s2) +/* 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) { - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); + 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; } -/* JSON Pointer implementation: */ -static int cJSONUtils_Pstrcasecmp(const char *a,const char *e) +/* 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 (!a || !e) return (a==e)?0:1; - for (;*a && *e && *e!='/';a++,e++) { - if (*e=='~') {if (!(e[1]=='0' && *a=='~') && !(e[1]=='1' && *a=='/')) return 1; else e++;} - else if (tolower(*(const unsigned char *)a)!=tolower(*(const unsigned char *)e)) return 1; - } - if ((*e!=0 && *e!='/') != (*a!=0)) return 1; - return 0; + 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); } -static int cJSONUtils_PointerEncodedstrlen(const char *s) {int l=0;for (;*s;s++,l++) if (*s=='~' || *s=='/') l++;return l;} - -static void cJSONUtils_PointerEncodedstrcpy(char *d,const char *s) +/* 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) { - for (;*s;s++) - { - if (*s=='/') {*d++='~';*d++='1';} - else if (*s=='~') {*d++='~';*d++='0';} - else *d++=*s; - } - *d=0; + 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; } -char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target) +/* 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) { - int type=object->type,c=0;cJSON *obj=0; + size_t length; + for (length = 0; *string != '\0'; (void)string++, length++) + { + /* character needs to be escaped? */ + if ((*string == '~') || (*string == '/')) + { + length++; + } + } - if (object==target) return strdup(""); - - for (obj=object->child;obj;obj=obj->next,c++) - { - char *found=cJSONUtils_FindPointerFromObjectTo(obj,target); - if (found) - { - if (type==cJSON_Array) - { - char *ret=(char*)malloc(strlen(found)+23); - sprintf(ret,"/%d%s",c,found); - free(found); - return ret; - } - else if (type==cJSON_Object) - { - char *ret=(char*)malloc(strlen(found)+cJSONUtils_PointerEncodedstrlen(obj->string)+2); - *ret='/';cJSONUtils_PointerEncodedstrcpy(ret+1,obj->string); - strcat(ret,found); - free(found); - return ret; - } - free(found); - return 0; - } - } - return 0; + return length; } -cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer) +/* 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) { - while (*pointer++=='/' && object) - { - if (object->type==cJSON_Array) - { - int which=0; while (*pointer>='0' && *pointer<='9') which=(10*which) + *pointer++ - '0'; - if (*pointer && *pointer!='/') return 0; - object=cJSON_GetArrayItem(object,which); - } - else if (object->type==cJSON_Object) - { - object=object->child; while (object && cJSONUtils_Pstrcasecmp(object->string,pointer)) object=object->next; /* GetObjectItem. */ - while (*pointer && *pointer!='/') pointer++; - } - else return 0; - } - return object; + 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); /* / */ + 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 cJSONUtils_InplaceDecodePointerString(char *string) +static void decode_pointer_inplace(unsigned char *string) { - char *s2=string; - for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/'); - *s2=0; + 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'; } -static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path) +/* non-broken cJSON_DetachItemFromArray */ +static cJSON *detach_item_from_array(cJSON *array, size_t which) { - char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0; + 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; - parentptr=strdup(path); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0; - parent=cJSONUtils_GetPointer(object,parentptr); - cJSONUtils_InplaceDecodePointerString(childptr); - - if (!parent) ret=0; /* Couldn't find object to remove child from. */ - else if (parent->type==cJSON_Array) ret=cJSON_DetachItemFromArray(parent,atoi(childptr)); - else if (parent->type==cJSON_Object) ret=cJSON_DetachItemFromObject(parent,childptr); - free(parentptr); - return ret; + return c; } -static int cJSONUtils_Compare(cJSON *a,cJSON *b) +/* detach an item at the given path */ +static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) { - if (a->type!=b->type) return -1; /* mismatched type. */ - switch (a->type) - { - case cJSON_Number: return (a->valueint!=b->valueint || a->valuedouble!=b->valuedouble)?-2:0; /* numeric mismatch. */ - case cJSON_String: return (strcmp(a->valuestring,b->valuestring)!=0)?-3:0; /* string mismatch. */ - case cJSON_Array: for (a=a->child,b=b->child;a && b;a=a->next,b=b->next) {int err=cJSONUtils_Compare(a,b);if (err) return err;} - return (a || b)?-4:0; /* array size mismatch. */ - case cJSON_Object: - cJSONUtils_SortObject(a); - cJSONUtils_SortObject(b); - a=a->child,b=b->child; - while (a && b) - { - int err; - if (cJSONUtils_strcasecmp(a->string,b->string)) return -6; /* missing member */ - err=cJSONUtils_Compare(a,b);if (err) return err; - a=a->next,b=b->next; - } - return (a || b)?-5:0; /* object length mismatch */ + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + cJSON *parent = NULL; + cJSON *detached_item = NULL; - default: break; - } - return 0; + /* 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; } -static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch) +/* sort lists using mergesort */ +static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) { - cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0; + cJSON *first = list; + cJSON *second = list; + cJSON *current_item = list; + cJSON *result = list; + cJSON *result_tail = NULL; - op=cJSON_GetObjectItem(patch,"op"); - path=cJSON_GetObjectItem(patch,"path"); - if (!op || !path) return 2; /* malformed patch. */ + if ((list == NULL) || (list->next == NULL)) + { + /* One entry is sorted already. */ + return result; + } - if (!strcmp(op->valuestring,"add")) opcode=0; - else if (!strcmp(op->valuestring,"remove")) opcode=1; - else if (!strcmp(op->valuestring,"replace"))opcode=2; - else if (!strcmp(op->valuestring,"move")) opcode=3; - else if (!strcmp(op->valuestring,"copy")) opcode=4; - else if (!strcmp(op->valuestring,"test")) return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value")); - else return 3; /* unknown opcode. */ + 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; + } - if (opcode==1 || opcode==2) /* Remove/Replace */ - { - cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring)); /* Get rid of old. */ - if (opcode==1) return 0; /* For Remove, this is job done. */ - } + /* 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; + } - if (opcode==3 || opcode==4) /* Copy/Move uses "from". */ - { - cJSON *from=cJSON_GetObjectItem(patch,"from"); if (!from) return 4; /* missing "from" for copy/move. */ + /* Recursively sort the sub-lists. */ + first = sort_list(first, case_sensitive); + second = sort_list(second, case_sensitive); + result = NULL; - if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring); - if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring); - if (!value) return 5; /* missing "from" for copy/move. */ - if (opcode==4) value=cJSON_Duplicate(value,1); - if (!value) return 6; /* out of memory for copy/move. */ - } - else /* Add/Replace uses "value". */ - { - value=cJSON_GetObjectItem(patch,"value"); - if (!value) return 7; /* missing "value" for add/replace. */ - value=cJSON_Duplicate(value,1); - if (!value) return 8; /* out of memory for add/replace. */ - } - - /* Now, just add "value" to "path". */ + /* 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; + } - parentptr=strdup(path->valuestring); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0; - parent=cJSONUtils_GetPointer(object,parentptr); - cJSONUtils_InplaceDecodePointerString(childptr); + 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; + } - /* add, remove, replace, move, copy, test. */ - if (!parent) {free(parentptr); cJSON_Delete(value); return 9;} /* Couldn't find object to add to. */ - else if (parent->type==cJSON_Array) - { - if (!strcmp(childptr,"-")) cJSON_AddItemToArray(parent,value); - else cJSON_InsertItemInArray(parent,atoi(childptr),value); - } - else if (parent->type==cJSON_Object) - { - cJSON_DeleteItemFromObject(parent,childptr); - cJSON_AddItemToObject(parent,childptr,value); - } - else - { - cJSON_Delete(value); - } - free(parentptr); - return 0; + 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; } - -int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches) +static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) { - int err; - if (patches->type!=cJSON_Array) return 1; /* malformed patches. */ - if (patches) patches=patches->child; - while (patches) - { - if ((err=cJSONUtils_ApplyPatch(object,patches))) return err; - patches=patches->next; - } - return 0; + if (object == NULL) + { + return; + } + object->child = sort_list(object->child, case_sensitive); } -static void cJSONUtils_GeneratePatch(cJSON *patches,const char *op,const char *path,const char *suffix,cJSON *val) +static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive) { - cJSON *patch=cJSON_CreateObject(); - cJSON_AddItemToObject(patch,"op",cJSON_CreateString(op)); - if (suffix) - { - char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(suffix)+2); - cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),suffix); - cJSON_AddItemToObject(patch,"path",cJSON_CreateString(newpath)); - free(newpath); - } - else cJSON_AddItemToObject(patch,"path",cJSON_CreateString(path)); - if (val) cJSON_AddItemToObject(patch,"value",cJSON_Duplicate(val,1)); - cJSON_AddItemToArray(patches,patch); + 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; } -void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val) {cJSONUtils_GeneratePatch(array,op,path,0,val);} - -static void cJSONUtils_CompareToPatch(cJSON *patches,const char *path,cJSON *from,cJSON *to) +/* non broken version of cJSON_InsertItemInArray */ +static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem) { - if (from->type!=to->type) {cJSONUtils_GeneratePatch(patches,"replace",path,0,to); return; } - - switch (from->type) - { - case cJSON_Number: - if (from->valueint!=to->valueint || from->valuedouble!=to->valuedouble) - cJSONUtils_GeneratePatch(patches,"replace",path,0,to); - return; - - case cJSON_String: - if (strcmp(from->valuestring,to->valuestring)!=0) - cJSONUtils_GeneratePatch(patches,"replace",path,0,to); - return; + 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; + } - case cJSON_Array: - { - int c;char *newpath=(char*)malloc(strlen(path)+23); /* Allow space for 64bit int. */ - for (c=0,from=from->child,to=to->child;from && to;from=from->next,to=to->next,c++){ - sprintf(newpath,"%s/%d",path,c); cJSONUtils_CompareToPatch(patches,newpath,from,to); - } - for (;from;from=from->next,c++) {sprintf(newpath,"%d",c); cJSONUtils_GeneratePatch(patches,"remove",path,newpath,0); } - for (;to;to=to->next,c++) cJSONUtils_GeneratePatch(patches,"add",path,"-",to); - free(newpath); - return; - } + /* insert into the linked list */ + newitem->next = child; + newitem->prev = child->prev; + child->prev = newitem; - case cJSON_Object: - { - cJSON *a,*b; - cJSONUtils_SortObject(from); - cJSONUtils_SortObject(to); - - a=from->child,b=to->child; - while (a || b) - { - int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string); - if (!diff) - { - char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2); - cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string); - cJSONUtils_CompareToPatch(patches,newpath,a,b); - free(newpath); - a=a->next; - b=b->next; - } - else if (diff<0) {cJSONUtils_GeneratePatch(patches,"remove",path,a->string,0); a=a->next;} - else {cJSONUtils_GeneratePatch(patches,"add",path,b->string,b); b=b->next;} - } - return; - } + /* was it at the beginning */ + if (child == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } - default: break; - } + return 1; } - -cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to) +static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive) { - cJSON *patches=cJSON_CreateArray(); - cJSONUtils_CompareToPatch(patches,"",from,to); - return patches; + if (case_sensitive) + { + return cJSON_GetObjectItemCaseSensitive(object, name); + } + + return cJSON_GetObjectItem(object, name); } +enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST }; -static cJSON *cJSONUtils_SortList(cJSON *list) +static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive) { - cJSON *first=list,*second=list,*ptr=list; + cJSON *operation = get_object_item(patch, "op", case_sensitive); + if (!cJSON_IsString(operation)) + { + return INVALID; + } - if (!list || !list->next) return list; /* One entry is sorted already. */ - - while (ptr && ptr->next && cJSONUtils_strcasecmp(ptr->string,ptr->next->string)<0) ptr=ptr->next; /* Test for list sorted. */ - if (!ptr || !ptr->next) return list; /* Leave sorted lists unmodified. */ - ptr=list; + if (strcmp(operation->valuestring, "add") == 0) + { + return ADD; + } - while (ptr) {second=second->next;ptr=ptr->next;if (ptr) ptr=ptr->next;} /* Walk two pointers to find the middle. */ - if (second && second->prev) second->prev->next=0; /* Split the lists */ + if (strcmp(operation->valuestring, "remove") == 0) + { + return REMOVE; + } - first=cJSONUtils_SortList(first); /* Recursively sort the sub-lists. */ - second=cJSONUtils_SortList(second); - list=ptr=0; + if (strcmp(operation->valuestring, "replace") == 0) + { + return REPLACE; + } - while (first && second) /* Merge the sub-lists */ - { - if (cJSONUtils_strcasecmp(first->string,second->string)<0) - { - if (!list) list=ptr=first; - else {ptr->next=first;first->prev=ptr;ptr=first;} - first=first->next; - } - else - { - if (!list) list=ptr=second; - else {ptr->next=second;second->prev=ptr;ptr=second;} - second=second->next; - } - } - if (first) { if (!list) return first; ptr->next=first; first->prev=ptr; } /* Append any tails. */ - if (second) { if (!list) return second; ptr->next=second; second->prev=ptr; } + if (strcmp(operation->valuestring, "move") == 0) + { + return MOVE; + } - return list; + if (strcmp(operation->valuestring, "copy") == 0) + { + return COPY; + } + + if (strcmp(operation->valuestring, "test") == 0) + { + return TEST; + } + + return INVALID; } -void cJSONUtils_SortObject(cJSON *object) {object->child=cJSONUtils_SortList(object->child);} - -cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch) +/* overwrite and existing item with another one and free resources on the way */ +static void overwrite_item(cJSON * const root, const cJSON replacement) { - if (!patch || patch->type != cJSON_Object) {cJSON_Delete(target);return cJSON_Duplicate(patch,1);} - if (!target || target->type != cJSON_Object) {cJSON_Delete(target);target=cJSON_CreateObject();} + if (root == NULL) + { + return; + } - patch=patch->child; - while (patch) - { - if (patch->type == cJSON_NULL) cJSON_DeleteItemFromObject(target,patch->string); - else - { - cJSON *replaceme=cJSON_DetachItemFromObject(target,patch->string); - cJSON_AddItemToObject(target,patch->string,cJSONUtils_MergePatch(replaceme,patch)); - } - patch=patch->next; - } - return target; + 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)); } -cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to) +static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) { - cJSON *patch=0; - if (!to) return cJSON_CreateNull(); - if (to->type!=cJSON_Object || !from || from->type!=cJSON_Object) return cJSON_Duplicate(to,1); - cJSONUtils_SortObject(from); - cJSONUtils_SortObject(to); - from=from->child;to=to->child; - patch=cJSON_CreateObject(); - while (from || to) - { - int compare=from?(to?strcmp(from->string,to->string):-1):1; - if (compare<0) - { - cJSON_AddItemToObject(patch,from->string,cJSON_CreateNull()); - from=from->next; - } - else if (compare>0) - { - cJSON_AddItemToObject(patch,to->string,cJSON_Duplicate(to,1)); - to=to->next; - } - else - { - if (cJSONUtils_Compare(from,to)) cJSON_AddItemToObject(patch,to->string,cJSONUtils_GenerateMergePatch(from,to)); - from=from->next;to=to->next; - } - } - if (!patch->child) {cJSON_Delete(patch);return 0;} - return patch; -} \ No newline at end of file + 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); +} diff --git a/components/json/port/include/cJSON_Utils.h b/components/json/port/include/cJSON_Utils.h index b129b06252..03ec10c9e2 100644 --- a/components/json/port/include/cJSON_Utils.h +++ b/components/json/port/include/cJSON_Utils.h @@ -1,30 +1,74 @@ +/* + 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 *cJSONUtils_GetPointer(cJSON *object,const char *pointer); +/* 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. */ -cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to); -void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val); /* Utility for generating patch array entries. */ -int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches); /* Returns 0 for success. */ +/* 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; +// 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. */ -cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); /* target will be modified by patch. return value is new ptr for target. */ -cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to); /* generates a patch to move from -> to */ +/* 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); -char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target); /* Given a root object and a target object, construct a pointer from one to the other. */ +/* 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); -void cJSONUtils_SortObject(cJSON *object); /* Sorts the members of the object into alphabetical order. */ +/* 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); From f60fef81366ab3095cf93633b09bc311f8fbb29e Mon Sep 17 00:00:00 2001 From: panfeng Date: Fri, 1 Dec 2017 18:48:12 +0800 Subject: [PATCH 07/30] bugfix: io setting useless when io_num > 32 --- examples/peripherals/gpio/main/gpio_example_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/peripherals/gpio/main/gpio_example_main.c b/examples/peripherals/gpio/main/gpio_example_main.c index 3b306ac14c..b6e71339ec 100644 --- a/examples/peripherals/gpio/main/gpio_example_main.c +++ b/examples/peripherals/gpio/main/gpio_example_main.c @@ -33,10 +33,10 @@ #define GPIO_OUTPUT_IO_0 18 #define GPIO_OUTPUT_IO_1 19 -#define GPIO_OUTPUT_PIN_SEL ((1< Date: Mon, 4 Dec 2017 20:22:24 +0100 Subject: [PATCH 08/30] LEDC Driver: Added back original definitions of 'duty_resolution' and 'clock_divider'. This update is to provide backward compatibility with ESP-IDF 2.1. --- components/driver/include/driver/ledc.h | 11 +++++++---- .../soc/esp32/include/soc/ledc_struct.h | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index f004ae64a9..c5250213d6 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -99,10 +99,13 @@ typedef struct { * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function */ typedef struct { - ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ - ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */ - ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */ - uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */ + ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ + union { + ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */ + ledc_timer_bit_t bit_num __attribute__((deprecated)); /*!< Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1 */ + }; + ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */ + uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */ } ledc_timer_config_t; typedef intr_handle_t ledc_isr_handle_t; diff --git a/components/soc/esp32/include/soc/ledc_struct.h b/components/soc/esp32/include/soc/ledc_struct.h index f90c3cb9d9..4c87dfc265 100644 --- a/components/soc/esp32/include/soc/ledc_struct.h +++ b/components/soc/esp32/include/soc/ledc_struct.h @@ -69,13 +69,18 @@ typedef volatile struct { struct { union { struct { - uint32_t duty_resolution: 5; /*This register controls resolution of PWN duty by defining the bith width of timer's counter. The max bit width of the counter is 20.*/ - uint32_t clock_divider: 18; /*This register is used to configure the divider of clock at the entry of timer. The least significant eight bits represent the decimal part.*/ - uint32_t pause: 1; /*This bit is used to pause the counter in high speed timer*/ - uint32_t rst: 1; /*This bit is used to reset high speed timer the counter will be 0 after reset.*/ - uint32_t tick_sel: 1; /*This bit is used to choose apb_clk or ref_tick for high speed timer. 1'b1:apb_clk 0:ref_tick*/ - uint32_t low_speed_update: 1; /*This bit is only useful for low speed timer channels, reserved for high speed timers*/ - uint32_t reserved26: 5; + uint32_t duty_resolution: 5; /*This register controls resolution of PWN duty by defining the bit width of timer's counter. The max bit width of the counter is 20.*/ + uint32_t clock_divider: 18; /*This register is used to configure the divider of clock at the entry of timer. The least significant eight bits represent the decimal part.*/ + uint32_t pause: 1; /*This bit is used to pause the counter in high speed timer*/ + uint32_t rst: 1; /*This bit is used to reset high speed timer the counter will be 0 after reset.*/ + uint32_t tick_sel: 1; /*This bit is used to choose apb_clk or ref_tick for high speed timer. 1'b1:apb_clk 0:ref_tick*/ + uint32_t low_speed_update: 1; /*This bit is only useful for low speed timer channels, reserved for high speed timers*/ + uint32_t reserved26: 5; + }; + struct { + uint32_t bit_num: 5 __attribute__((deprecated)); /*Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1.*/ + uint32_t div_num: 18 __attribute__((deprecated)); /*Deprecated in ESP-IDF 3.0. This is an alias to 'clock_divider' for backward compatibility with ESP-IDF 2.1.*/ + uint32_t place_holder: 9 __attribute__((deprecated)); /*A place holder to accommodate deprecated members*/ }; uint32_t val; } conf; From 210d349fbd47381fb115b3f29e9f9cacb949f2fc Mon Sep 17 00:00:00 2001 From: zhangyanjiao Date: Mon, 4 Dec 2017 19:23:43 +0800 Subject: [PATCH 09/30] when netconn created directly,netconn_delete() will not call netconn_free(), which will lead to memory leak Closes https://github.com/espressif/esp-idf/issues/784 --- components/lwip/api/api_lib.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/components/lwip/api/api_lib.c b/components/lwip/api/api_lib.c index dfe207f964..fd2ca16772 100755 --- a/components/lwip/api/api_lib.c +++ b/components/lwip/api/api_lib.c @@ -136,6 +136,15 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal return conn; } +static inline bool is_created_by_socket(struct netconn *conn) +{ +#if LWIP_SOCKET + if (conn && (conn->socket != -1)) { + return true; + } +#endif + return false; +} /** * Close a netconn 'connection' and free its resources. * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate @@ -174,7 +183,12 @@ netconn_delete(struct netconn *conn) return err; } -#if !ESP_THREAD_SAFE +#if ESP_THREAD_SAFE + if (is_created_by_socket(conn) == false) { + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n")); + netconn_free(conn); + } +#else LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n")); netconn_free(conn); #endif From 38afa32cfb23f41eaab20f5037ef88b41d30acb1 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Thu, 23 Nov 2017 22:35:54 +0800 Subject: [PATCH 10/30] freertos/fix SMP bug with Idle task clean up This commit backports vTaskDelete() behavior from FreeRTOS v9.0.0 which allows for the immediate freeing of task memory if the task being deleted is not currently running and not pinned to the other core. This commit also fixes a bug in prvCheckTasksWaitingTermination which prevented the Idle Task from cleaning up all tasks awaiting deletion. Each iteration of the Idle Task should traverse the xTasksWaitingTermination list and clean up all tasks not pinned to the other core. The previous implementation would cause prvCheckTasksWaitingTermination to return when encountering a task pinned to the other core whilst traversing the xTasksWaitingTermination list. The test case for vTaskDelete() has been updated to test for the bugfix and backported deletion behavior. --- components/freertos/tasks.c | 156 ++++++++++++------ .../freertos/test/test_freertos_task_delete.c | 64 +++++-- docs/api-guides/freertos-smp.rst | 119 +++++++++---- tools/unit-test-app/main/app_main.c | 2 +- 4 files changed, 247 insertions(+), 94 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index ff549183a1..ff5c46bb40 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -541,6 +541,12 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); #endif +//Function to call the Thread Local Storage Pointer Deletion Callbacks. Will be +//called during task deletion before prvDeleteTCB is called. +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + static void prvDeleteTLS( TCB_t *pxTCB ); +#endif + /* * Used only by the idle task. This checks to see if anything has been placed * in the list of tasks waiting to be deleted. If so the task is cleaned up @@ -1201,19 +1207,25 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskDelete == 1 ) + void vTaskDelete( TaskHandle_t xTaskToDelete ) { + //The following vTaskDelete() is backported from FreeRTOS v9.0.0 and modified for SMP. + //v9.0.0 vTaskDelete() will immediately free task memory if the task being deleted is + //NOT currently running and not pinned to the other core. Otherwise, freeing of task memory + //will still be delegated to the Idle Task. + TCB_t *pxTCB; + int core = xPortGetCoreID(); //Current core + UBaseType_t free_now; //Flag to indicate if task memory can be freed immediately + taskENTER_CRITICAL(&xTaskQueueMutex); { /* If null is passed in here then it is the calling task that is being deleted. */ pxTCB = prvGetTCBFromHandle( xTaskToDelete ); - /* Remove task from the ready list and place in the termination list. - This will stop the task from be scheduled. The idle task will check - the termination list and free up any memory allocated by the - scheduler for the TCB and stack. */ + /* Remove task from the ready list. */ if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); @@ -1233,29 +1245,67 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode mtCOVERAGE_TEST_MARKER(); } - vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); - - /* Increment the ucTasksDeleted variable so the idle task knows - there is a task that has been deleted and that it should therefore - check the xTasksWaitingTermination list. */ - ++uxTasksDeleted; - - /* Increment the uxTaskNumberVariable also so kernel aware debuggers - can detect that the task lists need re-generating. */ + /* Increment the uxTaskNumber also so kernel aware debuggers can + detect that the task lists need re-generating. This is done before + portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will + not return. */ uxTaskNumber++; + //If task to be deleted is currently running on either core or is pinned to the other core. Let Idle free memory + if( pxTCB == pxCurrentTCB[ core ] || + (portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core ]) || + (portNUM_PROCESSORS > 1 && pxTCB->xCoreID == (!core)) ) + { + /* Deleting a currently running task. This cannot complete + within the task itself, as a context switch to another task is + required. Place the task in the termination list. The idle task + will check the termination list and free up any memory allocated + by the scheduler for the TCB and stack of the deleted task. */ + vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxTasksDeleted; + + /* The pre-delete hook is primarily for the Windows simulator, + in which Windows specific clean up operations are performed, + after which it is not possible to yield away from this task - + hence xYieldPending is used to latch that a context switch is + required. */ + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + + free_now = pdFALSE; //Let Idle Task free task memory + } + else //Task is not currently running and not pinned to the other core + { + --uxCurrentNumberOfTasks; + + /* Reset the next expected unblock time in case it referred to + the task that has just been deleted. */ + prvResetNextTaskUnblockTime(); + free_now = pdTRUE; //Set flag to free task memory immediately + } + traceTASK_DELETE( pxTCB ); } taskEXIT_CRITICAL(&xTaskQueueMutex); + if(free_now == pdTRUE){ //Free task memory. Outside critical section due to deletion callbacks + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + prvDeleteTLS( pxTCB ); //Run deletion callbacks before deleting TCB + #endif + prvDeleteTCB( pxTCB ); //Must only be called after del cb + } + /* Force a reschedule if it is the currently running task that has just been deleted. */ if( xSchedulerRunning != pdFALSE ) { //No mux; no harm done if this misfires. The deleted task won't get scheduled anyway. - if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) + if( pxTCB == pxCurrentTCB[ core ] ) //If task was currently running on this core { - configASSERT( uxSchedulerSuspended[ xPortGetCoreID() ] == 0 ); + configASSERT( uxSchedulerSuspended[ core ] == 0 ); /* The pre-delete hook is primarily for the Windows simulator, in which Windows specific clean up operations are performed, @@ -1265,20 +1315,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] ); portYIELD_WITHIN_API(); } - else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !xPortGetCoreID() ] ) + else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core] ) //If task was currently running on the other core { /* if task is running on the other CPU, force a yield on that CPU to take it off */ - vPortYieldOtherCore( !xPortGetCoreID() ); + vPortYieldOtherCore( !core ); } else { - /* Reset the next expected unblock time in case it referred to - the task that has just been deleted. */ - taskENTER_CRITICAL(&xTaskQueueMutex); - { - prvResetNextTaskUnblockTime(); - } - taskEXIT_CRITICAL(&xTaskQueueMutex); + mtCOVERAGE_TEST_MARKER(); } } } @@ -3583,52 +3627,48 @@ static void prvCheckTasksWaitingTermination( void ) #if ( INCLUDE_vTaskDelete == 1 ) { BaseType_t xListIsEmpty; + int core = xPortGetCoreID(); /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called too often in the idle task. */ while(uxTasksDeleted > ( UBaseType_t ) 0U ) { TCB_t *pxTCB = NULL; + taskENTER_CRITICAL(&xTaskQueueMutex); { xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); - } - - if( xListIsEmpty == pdFALSE ) - { + if( xListIsEmpty == pdFALSE ) { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /* We only want to kill tasks that ran on this core because e.g. _xt_coproc_release needs to - be called on the core the process is pinned on, if any */ - if( pxTCB->xCoreID == tskNO_AFFINITY || pxTCB->xCoreID == xPortGetCoreID()) { - ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + be called on the core the process is pinned on, if any */ + ListItem_t *target = listGET_HEAD_ENTRY(&xTasksWaitingTermination); + for( ; target != listGET_END_MARKER(&xTasksWaitingTermination); target = listGET_NEXT(target) ){ + int coreid = (( TCB_t * )listGET_LIST_ITEM_OWNER(target))->xCoreID; + if(coreid == core || coreid == tskNO_AFFINITY){ //Find first item not pinned to other core + pxTCB = ( TCB_t * )listGET_LIST_ITEM_OWNER(target); + break; + } + } + if(pxTCB != NULL){ + ( void ) uxListRemove( target ); //Remove list item from list --uxCurrentNumberOfTasks; --uxTasksDeleted; - } else { - /* Need to wait until the idle task on the other processor kills that task first. */ - taskEXIT_CRITICAL(&xTaskQueueMutex); - break; } } } - taskEXIT_CRITICAL(&xTaskQueueMutex); + taskEXIT_CRITICAL(&xTaskQueueMutex); //Need to call deletion callbacks outside critical section - if (pxTCB != NULL) { - #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) - int x; - for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) - { - if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL) - { - pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]); - } - } - #endif - prvDeleteTCB( pxTCB ); + if (pxTCB != NULL) { //Call deletion callbacks and free TCB memory + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + prvDeleteTLS( pxTCB ); + #endif + prvDeleteTCB( pxTCB ); } else { mtCOVERAGE_TEST_MARKER(); + break; //No TCB found that could be freed by this core, break out of loop } } } @@ -3831,7 +3871,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) #if ( INCLUDE_vTaskDelete == 1 ) - static void prvDeleteTCB( TCB_t *pxTCB ) { /* Free up the memory allocated by the scheduler for the task. It is up @@ -3886,6 +3925,23 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) #endif /* INCLUDE_vTaskDelete */ /*-----------------------------------------------------------*/ +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + + static void prvDeleteTLS( TCB_t *pxTCB ) + { + configASSERT( pxTCB ); + for( int x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) + { + if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL) //If del cb is set + { + pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]); //Call del cb + } + } + } + +#endif /* ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) */ +/*-----------------------------------------------------------*/ + static void prvResetNextTaskUnblockTime( void ) { TCB_t *pxTCB; diff --git a/components/freertos/test/test_freertos_task_delete.c b/components/freertos/test/test_freertos_task_delete.c index 68a6683fcb..72f5cc85ad 100644 --- a/components/freertos/test/test_freertos_task_delete.c +++ b/components/freertos/test/test_freertos_task_delete.c @@ -1,26 +1,66 @@ +/* + * Test backported deletion behavior by creating tasks of various affinities and + * check if the task memory is freed immediately under the correct conditions. + * + * The behavior of vTaskDelete() has been backported form FreeRTOS v9.0.0. This + * results in the immediate freeing of task memory and the immediate execution + * of deletion callbacks under the following conditions... + * - When deleting a task that is not currently running on either core + * - When deleting a task that is pinned to the same core (with respect to + * the core that calls vTaskDelete() + * + * If the two conditions are not met, freeing of task memory and execution of + * deletion callbacks will still be carried out by the Idle Task. + */ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "freertos/event_groups.h" +#include "esp_heap_caps.h" + #include "unity.h" -static void task_delete_self(void *param) +#define NO_OF_TSKS 3 +#define DELAY_TICKS 2 +#define HEAP_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT) + + +static void tsk_self_del(void *param) { - printf("Task %p running on core %d. Deleting shortly...\n", xTaskGetCurrentTaskHandle(), xPortGetCoreID()); - vTaskDelay(5); - vTaskDelete(NULL); + vTaskDelete(NULL); //Deleting self means deleting currently running task +} + +static void tsk_extern_del(void *param) +{ + vTaskDelay(portMAX_DELAY); //Await external deletion } TEST_CASE("FreeRTOS Delete Tasks", "[freertos]") { +/* -------------- Test vTaskDelete() on currently running tasks ----------------*/ uint32_t before_count = uxTaskGetNumberOfTasks(); - - xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); - xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); - TEST_ASSERT_EQUAL(before_count + 2, uxTaskGetNumberOfTasks()); - vTaskDelay(200 / portTICK_PERIOD_MS); + uint32_t before_heap = heap_caps_get_free_size(HEAP_CAPS); + for(int i = 0; i < portNUM_PROCESSORS; i++){ + for(int j = 0; j < NO_OF_TSKS; j++){ + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_self_del, "tsk_self", 1024, NULL, configMAX_PRIORITIES - 1, NULL, i)); + } + } + vTaskDelay(DELAY_TICKS); //Minimal delay to see if Idle task cleans up all tasks awaiting deletion in a single tick TEST_ASSERT_EQUAL(before_count, uxTaskGetNumberOfTasks()); + TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS)); + +/* ------------- Test vTaskDelete() on not currently running tasks ------------ */ + TaskHandle_t handles[NO_OF_TSKS]; + before_heap = heap_caps_get_free_size(HEAP_CAPS); + //Create task pinned to the same core that will not run during task deletion + for(int j = 0 ; j < NO_OF_TSKS; j++){ + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_extern_del, "tsk_extern", 4096, NULL, configMAX_PRIORITIES - 1, &handles[j], xPortGetCoreID())); + } + TEST_ASSERT_NOT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS)); //Check tasks have been created + //Delete the tasks, memory should be freed immediately + for(int j = 0; j < NO_OF_TSKS; j++){ + vTaskDelete(handles[j]); + } + TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS)); + } diff --git a/docs/api-guides/freertos-smp.rst b/docs/api-guides/freertos-smp.rst index 6d0c29bc4f..16d4c139b4 100644 --- a/docs/api-guides/freertos-smp.rst +++ b/docs/api-guides/freertos-smp.rst @@ -50,12 +50,25 @@ scheduler and interrupts of the calling core. However the other core is left unaffected. If the other core attemps to take same mutex, it will spin until the calling core has released the mutex by exiting the critical section. -:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has -backported the Thread Local Storage Pointers feature. However they have the -extra feature of deletion callbacks. Deletion callbacks are used to -automatically free memory used by Thread Local Storage Pointers during the task -deletion. Call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` -to set Thread Local Storage Pointers and deletion callbacks. +:ref:`floating-points`: The ESP32 supports hardware acceleration of single +precision floating point arithmetic (`float`). However the use of hardware +acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS. +Therefore, tasks that utilize `float` will automatically be pinned to a core if +not done so already. Furthermore, `float` cannot be used in interrupt service +routines. + +:ref:`task-deletion`: Task deletion behavior has been backported from FreeRTOS +v9.0.0 and modified to be SMP compatible. Task memory will be freed immediately +when `vTaskDelete()` is called to delete a task that is not currently running +and not pinned to the other core. Otherwise, freeing of task memory will still +be delegated to the Idle Task. + +:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has backported the Thread Local +Storage Pointers (TLSP) feature. However the extra feature of Deletion Callbacks has been +added. Deletion callbacks are called automatically during task deletion and are +used to free memory pointed to by TLSP. Call +``vTaskSetThreadLocalStoragePointerAndDelCallback()`` to set TLSP and Deletion +Callbacks. :ref:`FreeRTOS Hooks`: Vanilla FreeRTOS Hooks were not designed for SMP. ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS @@ -375,39 +388,83 @@ mutex is provided upon entering and exiting, the type of call should not matter. +.. _floating-points: + +Floating Point Aritmetic +------------------------ + +The ESP32 supports hardware acceleration of single precision floating point +arithmetic (`float`) via Floating Point Units (FPU, also known as coprocessors) +attached to each core. The use of the FPUs imposes some behavioral restrictions +on ESP-IDF FreeRTOS. + +ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words, +the state of a core's FPU registers are not immediately saved when a context +switch occurs. Therefore, tasks that utilize `float` must be pinned to a +particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin +the task in question to whichever core the task was running on upon the task's +first use of `float`. Likewise due to Lazy Context Switching, interrupt service +routines must also not use `float`. + +ESP32 does not support hardware acceleration for double precision floating point +arithmetic (`double`). Instead `double` is implemented via software hence the +behavioral restrictions with regards to `float` do not apply to `double`. Note +that due to the lack of hardware acceleration, `double` operations may consume +significantly larger amount of CPU time in comparison to `float`. + + +.. _task-deletion: + +Task Deletion +------------- + +FreeRTOS task deletion prior to v9.0.0 delegated the freeing of task memory +entirely to the Idle Task. Currently, the freeing of task memory will occur +immediately (within `vTaskDelete()`) if the task being deleted is not currently +running or is not pinned to the other core (with respect to the core +`vTaskDelete()` is called on). TLSP deletion callbacks will also run immediately +if the same conditions are met. + +However, calling `vTaskDelete()` to delete a task that is either currently +running or pinned to the other core will still result in the freeing of memory +being delegated to the Idle Task. + + .. _deletion-callbacks: Thread Local Storage Pointers & Deletion Callbacks -------------------------------------------------- -Thread Local Storage Pointers are pointers stored directly in the TCB which -allows each task to have a pointer to a data structure containing that is -specific to that task. However vanilla FreeRTOS provides no functionality to -free the memory pointed to by the Thread Local Storage Pointers. Therefore if -the memory pointed to by the Thread Local Storage Pointers is not explicitly -freed by the user before a task is deleted, memory leak will occur. +Thread Local Storage Pointers (TLSP) are pointers stored directly in the TCB. +TLSP allow each task to have its own unique set of pointers to data structures. +However task deletion behavior in vanilla FreeRTOS does not automatically +free the memory pointed to by TLSP. Therefore if the memory pointed to by +TLSP is not explicitly freed by the user before task deletion, memory leak will +occur. -ESP-IDF FreeRTOS provides the added feature of deletion callbacks. These -deletion callbacks are used to automatically free the memory pointed to by the -Thread Local Storage Pointers when a task is deleted. Each Thread Local Storage -Pointer can have its own call back, and these call backs are called when the -Idle tasks cleans up a deleted tasks. +ESP-IDF FreeRTOS provides the added feature of Deletion Callbacks. Deletion +Callbacks are called automatically during task deletion to free memory pointed +to by TLSP. Each TLSP can have its own Deletion Callback. Note that due to the +to :ref:`task-deletion` behavior, there can be instances where Deletion +Callbacks are called in the context of the Idle Tasks. Therefore Deletion +Callbacks **should never attempt to block** and critical sections should be kept +as short as possible to minimize priority inversion. -Vanilla FreeRTOS sets a Thread Local Storage Pointers using -``vTaskSetThreadLocalStoragePointer()`` whereas ESP-IDF FreeRTOS sets a Thread -Local Storage Pointers and Deletion Callbacks using -``vTaskSetThreadLocalStoragePointerAndDelCallback()`` which accepts a pointer -to the deletion call back as an extra parameter of type -```TlsDeleteCallbackFunction_t``. Calling the vanilla FreeRTOS API -``vTaskSetThreadLocalStoragePointer()`` is still valid however it is internally -defined to call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` with a -``NULL`` pointer as the deletion call back. This results in the selected Thread -Local Storage Pointer to have no deletion call back. +Deletion callbacks are of type +``void (*TlsDeleteCallbackFunction_t)( int, void * )`` where the first parameter +is the index number of the associated TLSP, and the second parameter is the +TLSP itself. -In IDF the FreeRTOS thread local storage at index 0 is reserved and is used to implement -the pthreads API thread local storage (pthread_getspecific() & pthread_setspecific()). -Other indexes can be used for any purpose, provided -:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` is set to a high enough value. +Deletion callbacks are set alongside TLSP by calling +``vTaskSetThreadLocalStoragePointerAndDelCallback()``. Calling the vanilla +FreeRTOS function ``vTaskSetThreadLocalStoragePointer()`` will simply set the +TLSP's associated Deletion Callback to `NULL` meaning that no callback will be +called for that TLSP during task deletion. If a deletion callback is `NULL`, +users should manually free the memory pointed to by the associated TLSP before +task deletion in order to avoid memory leak. + +:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` in menuconfig can be used +to configure the number TLSP and Deletion Callbacks a TCB will have. For more details see :component_file:`freertos/include/freertos/task.h` diff --git a/tools/unit-test-app/main/app_main.c b/tools/unit-test-app/main/app_main.c index b3d249fc50..a7a7e87542 100644 --- a/tools/unit-test-app/main/app_main.c +++ b/tools/unit-test-app/main/app_main.c @@ -6,7 +6,7 @@ void unityTask(void *pvParameters) { - vTaskDelay(30); /* Delay a bit to let the main task be deleted */ + vTaskDelay(2); /* Delay a bit to let the main task be deleted */ unity_run_menu(); /* Doesn't return */ } From 062cc89abd75d3d741ab07cd4654a63301494b8d Mon Sep 17 00:00:00 2001 From: krzychb Date: Mon, 4 Dec 2017 06:10:50 +0100 Subject: [PATCH 11/30] Pin descriptions, overview diagram and board dimensions carried over from ESP32-PICO-KIT_Datasheet_EN.pdf. The datasheet looks redundant and will not be used. --- .../esp32-pico-kit-v4-dimensions-back.jpg | Bin 0 -> 50486 bytes .../esp32-pico-kit-v4-dimensions-side.jpg | Bin 0 -> 37351 bytes ...2-pico-kit-v4-functional-block-diagram.png | Bin 0 -> 32776 bytes docs/_static/esp32-pico-kit-v4-layout.jpg | Bin 78738 -> 82848 bytes docs/get-started/get-started-pico-kit.rst | 135 ++++++++++++++++-- 5 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 docs/_static/esp32-pico-kit-v4-dimensions-back.jpg create mode 100644 docs/_static/esp32-pico-kit-v4-dimensions-side.jpg create mode 100644 docs/_static/esp32-pico-kit-v4-functional-block-diagram.png diff --git a/docs/_static/esp32-pico-kit-v4-dimensions-back.jpg b/docs/_static/esp32-pico-kit-v4-dimensions-back.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f782daf8aa7fccf69de5093b4f525bb0439d096 GIT binary patch literal 50486 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

m3-|g?dNh;*j!KxLppdpe< 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 literal 0 HcmV?d00001 diff --git a/docs/_static/esp32-pico-kit-v4-dimensions-side.jpg b/docs/_static/esp32-pico-kit-v4-dimensions-side.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa1380ba9be5deef505bb15eb2aa20b73859e94b GIT binary patch literal 37351 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 literal 0 HcmV?d00001 diff --git a/docs/_static/esp32-pico-kit-v4-functional-block-diagram.png b/docs/_static/esp32-pico-kit-v4-functional-block-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..0369b176066f401a74394391de119e6f403de2d6 GIT binary patch literal 32776 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

8sHsA30&>hvxj~LT=B#UbuMK4A3G{ zGvParo?cnvjqZmjE?#-Z<3NZt!oR}GDJ$`>lGeCRgYZwkWZ$)mfPi>@_MHTFdRX2} zRnYoCxgIPvC2kja_Crsc{YX)uJde+D_$-`kssy&6OUCmvtU38>wj+Zld}T~fK+Vkz z4h1fXxcIHDm4G*hn(FaVSnb858S&A_P7qq|{md=$piA(~j&)ymed#di{0BNRp5+KS zvHxr7llu9xyeTUM-Pj2UYwKqLq!dd+V=&XHg_?m^RpwIoBQyRR2}i4eyULb0G2WJ( zigfwo0nj5gL+IXC!OQ&03z7BnM83b%=}g8N*ssAZoe{J*G15J!1Xa@o!p`Hy{!e@J zJxY+0;klW7cg|Ihqg|sZrIV!iw66lWf+v9}bsSaC>jX4Z>jTKbV;?Di*}m*8<-F=R z4Q~TxK|5S4fjs`vW)`@zE(!*MjJ3j>$|=hExau#cnHq^H&oc7Qu)4$k#%4@ z760gHt>qmUW??AGG;>DGl4r~O=A+GgNea(U6(5{@&$}!;Q56FUM0O-u>?OOvI?0zN0Es}xt)TSg|V9JV5a600^yA-MZV$0TMM(Ahdy zCjaFbDAaTF&_X|=L9D=6+}hEd>FDKWXv{po(Zr5A1tMl~^eF=aT8uN>Kv|Z;`_kM` zzuL+K9)veXg?BRJ#HL5a&t%QA#*8zmMMJ^S$bP?f&`C>{q(nWA`ft$I$W>J-K2`gN z^ZB8q2}Y7MEwn7~{4KO)0ZxI%OH#nX-(R9TOo0n;t=mP4dTLB7Cz~0G1dE}@8G2)> zf7+X!dSNS-?w4B%FNI$Crk>}a2L*AAD(QhE0E;L}98C^INd-G0aEy4H93vL#v}udh=ad(V{Qt^V4_O8(Gw zGY=RLx89J2GC>Er^FLRXyrGUY`|^G}qdg`ql9H7{2{`EkrqvQYP2(P%#O-_0I+6{< zVpXPocVDC#g9e7BW{03fo|Pe{oBqal^x+alJv&zNM%azMB zRb2%*IQbhXN-|^Ntga{J$&q550eW^69d^QeTS9*;#Hnvu(4k+urKb6&g$y>OCniMy zP$*Vh(g;BiJm&XrxNjyA6#Y;GmJ)^K%^$g|_0po$rhQ!=SeY$EC5scIBS>wJAh;vl#e~N95e*2@Ri=+D{2=VdoSl5tjF81|@hY>Mq>x53Q0l zfffh^huq#{0IvC81Z*m#V z5P1V+euxPP>iP3maLUYYg#Nwv>uoHZgnrA8{r5J_ncprPFC3(BPveWF@FFlf#Rm06 z$~;Ohu6%h8eyBIiJ0(HFsy|rjVRESJ3nGLwBPijiLPwrVgTo-a zIV3qQUXT5OOi#t@&g*YBXwb+PhEpCqBI;WYUDgV+0xjan_++UiNB>uW`O!a;p9sy zQ0PF{$K7LTE+>Mo5Q#BZn0ijyKj&>6vwH3;EXW}9JAHhosU9r+{K@o#&{HgvUUEI@#0XVd^5?%_hNeC^T! zpLmFv$H@7_uGe~Hs7L?06G^j^mbO%eU}w9Rin?*b`B-0B`6x~$QNff~HA~m?frF0D z>6}JoU8S6jj*i;|i}AyHa0YLbRGvjQV)+^gPhvCpa5-sv%ceKQk%U>b;}tx&Ilf9| z)sjg+#`~yB)2T#p1K#<0%NmGqb)8X;1gBruCqqM;i%UaN@Sjqq@@T0i12?&BX$B{z zNre(PnR=n1yxd&feT9}Qd=(ov{^XCHXPc)tyTNi_dkQi(0>~SFhXm^y2XqI#B_6^f zCkVJ%?bdq6faj=00YlDFJgjkXs$;47rr;VO!Wc zYa4AcqfybQa9c*lrCL<5(fskX{V1BcCv`}rku4F@_a9<78NFS+{EF440`@uWjYXlcJ!c%A>^ zV?lqo=lFGZmy~0n;OzOpjDtKsTc+8`$5;9pY3FeGPj%x4kA<#2dkQ##kykjw5jN7& zs%G?FpS-@y|p~QWpiczTNGanAhLO7{rr>*S> zG^5tM-%%nm$(5u`qaTnR$V)$-rhG?}gWl>Y6SWVc*DyZ7@rw2x6lI2mMD~$Bf34;| zJjf23y~Ea`_?tIOY>~BAqtj;pcy1Jr*5oCKFkkq6hdyQc`=Zr{_LnidQo)Z8cU6gm z<+tBb5>-c5-6I$qDkHD}~bN1^a_T8aLW+e+P+KY)hA~-MZjob)>uc--vaVE}1;Jy~nS{6y8E%fr|53+4P!E z?e+v|(xLQO<;2h>`5=(6b)0fbOX`YL%vMr1+Xpau zZHjZ|j~nzSwhEH4|7P@Lz_plXGcq5>Al+bCp1-_XF#bxT4|(dv^SEKTxG}a|Jau}- zb;vzgCuBUbhQjT9|BkaS(9&V!mA#EF0unKdv`kRPbS3%5W4kDC!URLu=6q`~HWv5z zGBGjn4bJ;hCJhBWgXL!D6aL*9GOdk`4Ycx0TX{sT=!cd^x|a2Fo7E-8xKbGx_xpYX+H8#+39LF8C@37Rwm$Se(f#^IS&fz&vxRR%LN0m~2z0i-WqkH$laO#az*Sk_-jCUBlne9!mN6zqRcC~*vOX~}(jNdwn!EcXz_WdY zACq*u+#C#^X7y1%2q)Yd?TL;RUztV4!a(6E8U-%J(&Wk@L zkk{-oK8|`SaG}FH?26DDpq zu%}rK>oV1#+(A72%xaRUkux2J4M9}RZhPgIrlsHl;p6u^d@Uo>uvHihA{8i`V- z2&OR3FYo_w5AJRZyJZz7bZ--#_hE8*e!O$Pf%W$GGOO1NjE*u33TmZ@ROqy`;D=OK zR}&c+kEOoBz$kCBaB*?jMS_QiuOV_6PZvC1tex_wrJ>;kgUvq@H)^(Riqv1VKi?k$ zEHw^&0=SmO?>H%|S*8C)UOsZ{=f4qgQHHIjlJgP!dTHgdTrW<~`T?IBLq~!kEAu%+ zNS@y2md(Qht>1dlx60sb415cHK|>neCgjEX{+-tWSx?V@^X90tQjZXfFBAXCWY2bQ zQX!Pk8;7pantZXINAosMW^ajrlwWs=yE_Mi-QRSo((nzpRZk?PKWap%*vTp~N*8QJ z)2BJvk>u%)?)|r}qlMc?`|eIVK16g;Lz)xr#U{KQiOs9IDx0kJ?*T*g6VFeYOC~Q~ zXhgpWv^x%F5dG7F!^JDKk&!!KD>gQ4njA%YW%aAk(s}w-yu7@(dlNY@7z`+OEvk`` zk&TUwfh;U7LmULn1)O1;a&mIVP(TcVf`j3)h=`IB6X$azFpn>nmzOUu9pvQ3=jZ3W zp6-hji-A{GR#wp2*;yC~&#XU}E0MpFXzmCan}fjmi#7iaO$nB<{kE(1zR zCcY%b)zY$Yt>(n9eXAZZhk9+%VQw#s_1P6o=}pcNrds@OEHh zO-?^wzt%i6S`kMj;PC$Cc|!^W1ZCr+DPbo6)HntQ*p23dK|_lX!frqpMxR);Tv8r# zHD?&B)VD3=$;ru&V}l_o^%i$Z%e^54m}CMCnebua)s{aEtq%t!p(uS3_n%~g-U~dvSn#fA&>FF_jtO3+D4V)B*m%`@F=sC1qDo4I^ zG{WGZ_IJ1I_0Exn*nR59AgR2g3`he?yGK%EV-;*=G+HQZMf~M~9AQ3{PqVYnirVr% zRVa<|pSg58G2$R{e{$>wca&+242L)#OYcwN8VzOETdka5UXBb7>V?wM(T%6@M6h39 zj0;D+lDyKxH5oH%Jm)j`z_693WXXzD@(j&1VLS@s%$s=w3-}}ggo7H#KRm| zWQby?i$e&KJ+uJ9Jbv;N5863sPCD!wV1aF|QFc~U*aKF)7 z%{pm&oD4+5Co&Kbu{hOLRv*G^abgA{!iVcK{MDKdhtQ0hECnFqIh;%S)Pc{m#=Kl; zNt3|I$Vgo`p`yc%iMi^HCnJ+o%jN!HS>B~lXXyEG4lGDLU%BMRj~_~Sd*y!iycc0+t!S!f`UxrS?W`rgl)&+&kxh&)&YPN@aJ;^Kl77uG! zT~{iviM^@S1jR}t`(Xu*C^V>}_k|X=sG+LRQ$*RcnxcXXEyOr&M`zpwyrMF(O+%QuAZ=smGTCBt&i)L*{;J6PFgqN$Jn z_r#fnrk?TN(*^Kk{ohk{Hk!Kd|Fi>oVE%iGRR1*YGs}ZFRUjX@J$!ah5?_u39M&YJ z5zAEm8@mkX4H1)|7D%0>1Af463f6%52IORdK)#|(8shsgSfV2#iayb7tt^m+j0Ctd{i6#vk z`x1ZupP&O1vDfy410A*Ix~12%g%hz)wnv1isbxfOJbK8ebUl`q zy~Q`$myT=-1~#|HB!$OdOHxK9yYBIpdewb=GLFI#8(4ba1Z?#+2rN;XQ!+H^$ez!u{b!Ft#fT9U%zzTcd^w1rij>UfxpIF3>y`VcLt`DUARVuww1_KJ zLnjkK&?bTIB6N=rE?he>yl+m2njYEnL4Sq;7jNcK`hyR)LP@@yL;}m+U%1uPy~)>F zO}ck>noYiaVNod>`1YHK$z1M<1dOD&R8(s_{%4z6ituceOhpyk)_uoI^h`a;T=O+= ztlRDeO2N)HyBK2`{n=kI3s^P$=X3%->FU}V8+$xCT6&^!+T2J~yt&l{_pyFDgu{|L zg+g4elx}ZzH^fqTzsDSAqe)AaskglYf;1N_A)&xvQAON**)MbDgddT0JYR!C_0A1P zGB8!<{(NB|z0J z5>oI%8=tzVEf3G*DZ=}&zr+pdi07!11Q*eJeQ6V~d78p~$~dX0&(8W!&qSbY^ELZb zR<4O>uJWSD!UP15&=K3Ubs-w5XZwxUBI>UTX2;-lELlq;Ca5^guT|B_aDf)JrRDqAx}+X zx1)J4E0)47o*-o@FM1HnZea7ozgQ2+JzVSZOZ>RM4$*OY!>p0*>F#m<1`*@qze#Cm zI>sxGPUclOLcqmZ(Uu*Ne%N>8V z_H8#2%eB9ptp0rQywMs-@|n~2`KrwQbAsw?>OgZtdW z5%ntB-I9`Jw%4T-&X9j%OsClz{BxKMBD6Ew?fd2@CZ(d_sjJIcl5wRej?l}yprHGB zS~;HD>X9}-P~V#moWbRU1>}G{7jM~ zp+eH=%9-wt!Obns%7Udkh`rpV)y-8Q_s@97^B7~aw6K{eEK?-b`wbN}obCV?>9A1RN8)dGntxP2vj!i?VKY*v=5&IxeS(_iFfy{%VkTs_6FO{b36r$Dy# z8_J^^S9j6lz1Zx(sFxz%E!xpDe;6HFWN7$#TKFMH;7I_E>DExhNcue}OMY)_XS&7U z0_M{k(vvN{ZSOyLvHjU6;Mw!(By&duuYi2sMfVD`q+ZODy<4Kzwi(&pJu zk@D8_e%<&wg}{nz&TF}kgoIR!6w?~~Zd#!5a|T9e)n)EwqxHDppQnK%rE12waU!D= zLrC#$Y>zbj`LsNJWQ-5DkKXXaA6%)4N`r6ZY5)9Yi&Mqw(|th&W*VE~15|e0=^pC- zA|og3?BMw7Z?-|mx;5t97z#3K;?w<+8l%yhFL7W#WrBP~dgrpm8B3e`l@ezjb0`xf zf7aZoHpUnRL!Q|S^dUw+M%~Fwt?FH^w5QE*XYPEiag)&I%-_TNq{Aoeqk=TY1HSz=Nx)YyinkvG*GI`GIe2I#tz!bt0`L0M~}f>LI2CYLQ9e9;UNT#LLZ7e<2}(iffxd5JVEhQ032~`7dvJ1ZrJ8}c+a2NQz5Fg+N%?491EL*!B> zL6T*;+)mte*L7qQ_A6)`C#jJ3{zsDtbCf` z0$bmL^PsUx7s}tBtvG(2uC7;j+y--()-*Vn#AGccSdp_A**Qx+={bcv;rXCmp#J3k zbN&+lqNT2BW@)Y^)ap^x)gqcD>GCktU4_us0v0clcXFq+K2ax-kZ{w`T#1V{9K0F) zfk@i}l29wkDgF4_%Kn;3K#&=LdFnlTjHCwTTZYuUSgt`cH(JF(Q3O<;@SbA&-}|0I zwMv}AJgos@i_`D`4gFTn{iYtL7~lM-<@Wk!RCHuEyVf=}(=T>v z$|*6hGP1h6>{AclkOWPB#{V}^;yhlHotGi4CUjwYw zOY1989u*@2Q?1%u+wn1rQ!i5>U+^+Kjxg78d!Yr**3Fjel-;kVkq=obXbae#UA9fF zw+@vzGvEzncVz>ABe5gQA$7}5HjLR2u^A;Be^b*3C`_xJ;FGf1Z09J_Z~-IU(2BVe zf@DjYs!;2f0(?Lk%%AJUAE1DajC!@6=WzHcegPcrz$P)TRi=QB&`a7~M%Ys*`_2(e zV1G%9nl9p%?ygDw-f1>D^r5OkLPtWSU*EMVEoX%!qy77{$q;$Q(^_A>J1bmbU58f7a z;_xjoqF5{i1o3aATy{ePM>Q7SpVp4nnF>#3OMxMo_-iDKTk97C#9U^hs3NQa;GKmt@~{Y*z@_`$S@x-ZMYKxiKNX|I(ftMT6x{fmUfv|* zCAhCDiza0CVnvuU#qtmf1 zb0aSO|1u~Q>D$|#%g8*XrM4U$6(bgYOm0X`EzQmrmz707x+0a}RVh|1xsaBzq`@YU z+8$=S%gb~AQymg3Z4GYr4-bz%aBskmjqfuw9>zmq1vdlT6h3?EG0uJi%}eKTu+PbA z7s|&LDvog^NlK<69581|-cE&QJl`HgBrLTZ?H%ww9@FZ6sN;EcAo22)pdddxm*Z1$(&dei zady2^0hx(QZ*i^wkYMDF`{xkP``7?2X{o!*wN4*fTZV57F5F|YvzzL*_xZ+h?j;iG zt>Bc{%ii_w0NARMaG*o%pg51UP_wZdrpD4iTWhhSedV8nwZjLq66Kr}o(dMQ5di_u z%VtFE@85?pbSjVPl222)N--JE&ldaYk#4^^3VXkrLff=_5S0q*udWdz73BX~_M&)y zI(_&B*9s4xT2?l-eg2Kd+RORT{DuYv535+K6)0qrk=m6ZoORxPfa z^|@OWS)coB&GOPV^vsu*yM5pw+6^`Wlv&QYx~?|6{^*SOT(zDLteE7_J9`s(2`rvi zZ_kz*?Z0Vi@q`j)S$g|5HPY^EkAhv#O+>qXt?7bZz>Y)O3@cZYG8oFrAkpRJ=<^F> zazVFK*a~G<*369ji*!jJ-R^FCi(^TeqT5mJT-Wk&leNR?73oUL9gjqWm+S4RF%l-r zyN}2n$EBq_&v*SReLM5!6|a>D1DzyI_68~|3Ail?t6eX8gK(}iC(0D-`C8o}?nm<~ z0hkesHT&(hn+&h5+Z+#1Qh0PMXUw+a7-#S#ktvlXm^1hp3UXh4njwkTx!H^2HW2T< zESegqWCN)v{2(&W5D!}va&zcKO$^fl@_dCuh+iH&*E*gmS~ldf8i@{e5Gm#c*i(j8 zitS+)#^Zo$dO-{1T<@7~)atEd5HX*#-JOpiFU|*wLDE-Czu-ia`fSPPa|gFhPf7Oo z0cKsOe5J?oSZI9+8BHM%4Ow#DjY4_>1W*d>Ec*tN)mje7htUmXIx1p9ARx}`llFTP z(c;_4Uu0jTX+cS{DT3Z;lBc`OJKwXYnhC6CS zV~y^x<)~mW1C5$Oei?eKmWBLpccUwU&M@9V!x=7!4rGT2q(My`R!s8C^mqwj9v@H1 zcB6+B45&l<$A!tWU!pPpI`YY7cdSv5iORYmIZ>P@s999k?;GCW6WLC@_-N+Apkega{K8yNZS zc!bqzOA@=MnL~L^c4BGi`||QxrTcAV;F8VC@bKYeWmaXfy&paq8N4?I3td3FmAYk1 zE1a0IQM{a*BLFF%n4r^!w3=D#i5}EsX9whTO6OHpiFgTFSdtSpsFnF^hMRfqTmhcI z-F=JpQ{l&VOwk>K+%>=a_&zLJ#8!=XBE;#RwP0A$1Yd6tg zA-ml^j32KwE5z7tj;8QTPSrw;2?zim(HO}6P_1~#GNV2BGsx$5_fH@Mz`Zv{Zehod?>1e(^w)O zXLaG;SA|J}01W0vhy6$2H>(kKCJCA?ysmTQ>2Pp>qD)?QeH$1hZL>9S?btfCIsT$J z78j56#*bxoR_)6d#CCk+q_0%iKUFd)@XQ>e6!uMx3kt0Gf8yeq5;C^eKK2Q}-172{ zz!s)-#W@L{ccJhI>kcNgJ{2iLY=?ET9xasd3J6yW4@n~^^ zm4%{#=x}fV#mPWN*=T`&mDGCs*4w&GL;<^opin^}GV<408nYI2^^YPf93Nxj?jJWl zxh{Z&J3-OMWFW^w%P^Dq{u{&S0Vx&*HVTfNJzvOB(zWUEl z!3e*dA?$2Z`0`L?Uj&yNDd0FqBITByjH}diic?Aobu8_nNxC|Mo!jN<{u(e8um)hy z+W6cY0D~CQ)m;)_y){V-0{kdXu@ytu4x~U1!KLV@3`k{a5;P)Ulc_Mj1a^j(_qxkV z1gcM)PRF8O(b8QJDJek|_&L@(0MvvB`s$aU``>oq*74cpLRKp+8eHV}$IE*IRRt4yyin1 zmP_~iypPZzoMr=k23g}!&q~P4*itV5NlQRonqXH!a+EDd)zHJQD zD6f@vfXi=fO_oc&4>tvldw+Xo@H$%BJW++7YBt&dMkCo+b@~356ULkB@X8G*;doW1 zo>hp<^Yd$~{FqdG@1FB51rQylp1v?r zzyUUEHOqQ=jd7?c+n!C_-hLrq;nmPE4|v)Hg21+=tnQW=I;1j<8>eO`Kot^x{AdR| zjYuQtx}1Xh=?qQnzY{fbzvdk z{l{J0E8cO4y(rDLI>U1D@!{#e7j4+taYLKpC=((~C-hf+3Y2rHzURRamGe{P_0y)fuO1NL2C<0F}QlRw} zPP#2rc?wIDY1%&vmv|OyiaOCyGGj8JfFB2T$)Y=qgv5GvTGnEJztQ1z?Oy}Jg>LA} zMo*A^1Ub~@Vh1qNH8=>VY3;`|hdzPGK0dvy?v;V6^p|z@5vLdKn@Ps$bJ|B#xk6{x z{^AIJ@Qa-7-UwCBtkY&@zh*H+?h3bajTRQO3z1)EjMc^{KM>=SnT5Uo#xnkar7ljI zgr8seCYC0mrmL%EQb8CA$p`aiGKm3GZ6#ZK&2)dK2qYzfJ^p>dZhWlE!UCTX=du;^ zX`(RoXRDZG6gfE=r7Wu?VRBC6jcv@y{Fg|>{iei;rVK$ZkMWZ<=hI{P`<36xX<>m-iYgUOo&1;E>Yz-dj_-t+y zEjnxzlKG_zv|QcDg0!@P@|)xtA>d?XX?-$!mh}qWJ0UG?Y_96<`K8;Hl8b9r4KCGN zqj4|ZRuP1TrmivA2dNBv3E*HRn*LFvo{Ad5$*ItdxXCbxmcfvn>Mb#s=rUYx>UJ>Z z!5jCL3d@hLfiw)V>ttYW;m%d797z?Sm6hbwN{JX!5LYsKS!3;-suX}~lRQ?SkBs+E zx9-Y12WL27sdZMn{!bnZxZR6qFfih4%t#y)h^0V1TkazGVqw^( zH2qpZX%JObLc^HhEQ3!aCNVIP(BTB*bA;(z(ycU*CxTxxo+X>;LBjr&?S^lo3I`$K zJKxx0p_>JQ!ggwkHZKP&o4;IQ2;?b{pGtj+G@6s#@J-M{RaBEp4xm>(X<_8~Q zhEd*hB`=}uY*5D{F|&H2CD)ZoDwi}<-T8gX4bJ(;KYth=Hse&-Tq={4|JVuvMS)?T zDgG>>2q}l9lI|wS41$ikaN>|E@vD ziT$p#w7o|v*R8@+4;9Txb`PNjhy*I3c+=8sVe4#O(}=XdJ?dNq2HPt$^p=#~kN;~j(;uEB%H)Qszs{4w zj8gyxBRpO(u5x6=MUk27e)JVZBhufO#;TTT3}bAzab(@%15v~f)${&W?o+SnDEjg5 z>UBQDNMCJhLkmuk^HBar%iuRSSY%e3d_Y?HAEk;Y%X}dv&2L?C&G10He0tOLE$1iz zDs|gV@SiAAUM)Y-YcBYiKMa@828-=YAyu^eeoL);L72AwU(K#41BJt8lY-^=liAmW z*h;ogl7O?#^?fyjZ6H|$;D9R%aRd3o8Jn@}iHW&D4Gp?I$N>6tv94?o@B4oZ2k%TOrXd?HVeYOFaRfn6=Y_8{6DKMQm*I= z!nxf*<>gn`{a@ew(W)O=+$8md|LdzIVqiq|9a5>>+z$}jk>1w zU)o#B8LdCEfbfcjss|gmNb$fOD_!1rcPC#${zkqeD#Ln}7+VyrKUmKFe_;W#0C}s0 zbz=^f|L*JCNYGh3vDStCPjJ~M5PG*up33LrdSO;E<{QZX?3%)%(dh3qQV>x0h+||o zkN0jwK-69MBf+r;1S(P_CM4Y3-c~Zt2n6&(;jrcn z_OE8}EBJ1EYATx-p=h$uq_pVYzONi$ny6f~xrp1_+gC>m)eQ{|9z(0G?!vcg2**&U z$HgdsRRLf;&HyqjLC4GWa&H1OZRQ9-#UM{tbF?z?9#=Ce-rn9N%4NmC#2u4)GLZZI z0t{0Aujv9w)k4;-SF;9QR6zPv;Sd+a(1?ottwx>h&ev}HUj57CkXHAzofJ@|G3NbE z#mt288+Oh(>8lkWIPdGbzr6(kH{j&n%cZ9#CGBr*y;=iMzE@XQuPXsO6>yvnCE+Qb z+}MT#kR~D`A^=iSVRtMoPI?=*()>d~LA}m`=>M04o1H8x zv|AC;i49e}7R_k>BTV1m?704R9qIP|z~v@-tnq)3TxwM`L`US9@WEiP^>V}aJO#MQ zsw%ZcyS$VXjl9axP&8p-VZrVBg@wEOdm}@`!kL3|t!CIlb^p-N!DfW;^Fc`kh=zs+ z*p0hxOyRFGGBUEV%JjHNNl6FNE0>|gb%Qe4qF<+LK0MzdfSisxLWxxCMoyQFMl+SE z`H4eCn8ah=*J_)Jj+g^*!yg5*PxprvU}xArT&jwW=giEE64-oeAWB(T835e?cs~I3 zSJq@sio{0UgS=;zPE4@aC;svbA66&yNQ7gyJx zMnf*4$i&BDs(v7sV(v5T~(jC%`bcdw0G)Q+`y1N^syE~-2VYcslGxM!k-^`jdckvJB z-hK8yyMD2s=Q*8YS)7iqAMa;eb6yp|prWFJXqKEj%U#OWwxTAZKp9$nC_zG!R<7R* zz+SDSq@<0GhvNZYXzuRs0|NrSq;OBo&)bo9qlkL-+?R2CEgUD;0vo$jG-OBrA0` z8&Z^-t)6_0j3uvnwL%%S8j?9}GRw-|fCY!(@BO@c24y;3GjH_4#uOyQMmq3ROfQ9i5%k5x=;(xER18w#)f8yvk&u5)?q> zYOPoJ-@SWBO8Tuk7%TQga!LvSt*KX;l$%WzvRlq4T-p4$or#b+yDF!e=x13ol3w}* z2KI&%@od_<2nzmSSBo3@&1^UjL!(epQ2~j$CPfeFzDo*neZl{9vw_EAw7Iz%bG>J| z+~hLuyHg1FqJ0Xg5)u;f*)s<2pDBnaNY7urk6rQNxm^fWXi>d^gU|&jrCaMhe|aQu z;h;@xeC)f@qISf>#tOI#Y~kAcOoEnHi2UN$Ps-2u>@Iut@fHT`ANtpj1HJC=Ym3io zl~ZC<(hk=@;dCM&0|EZ5`O6 zqF-{AJUg)&T)<}LFW<}4rerpizBiecI0*qej*wZZ2>sqz4S+@Q@bOgbj85xopdB!O z)uz{}bo+B?S$k-C0OJ?vKbTz+;rVECUD^m`L`>J|^$cGkny8rI)#Amm)@nL!4SRvu zP`!86ouu=j$LNXs{2;6{tcif_%xsdeC?znYH}{$S@#0AqV#JiV1gF#F~3uTm9nk;ZB>}UozR9PK25I9-A zKH_(t51BB#<{_6Q1{(yfyu2;dZqBEEzSmXupF^45*o{Wmk7>z9&P3QQ%x247diCSO zX_N+tE!|8e@}|qQJNX|UC;nnDI_SkW7dJz*_Wd<*ur1Z>&AMp{RJm^++j%06W2CaF zOOiQTE}V_5jG!Nj@$cEnLe_eo@937=e{DqW6!+) znYdx34O)6IXlPuLrdDCuKi4{}E?ETMD&YLMdDFgC-pA}*;>y1Jn z^Bl*ywUy|Bm7NQGovvS1Rp4O&H0z60@Q;cZ>e0D&wE}sg9peRc0o&`r<(?KrIRFRu z>!{$7s)wsD?yZ6U&UC3gxEkl?wLpg*gr>uJU~pVza!v-354zaR$znuh^q9YX8`7m+ zqOGOwG*)RQAlq6i-o)p1`Ebx=kt3BDl_>AIb2+=E|HyM0FKGyYw?3Qk;2R{V0 z5MXAVT+>p*w7z$)wq8G@ywgebIyfu$B)Xqlm^a=wdNFFkibKwHg?e1fumk2llf74l zMv_%fL6F?39Br)ByEi7R_z+8>g@>{`1k2g_q@8*HYN%$=cFJ29?9PC-Q!!b#poUk$0Y}ciN7irc!OtDTK69_# z3H0Ek=O&$3HsblV>&JxSQE&JZSJYG_IDv3@W7-?%tKlm9*AggK#Y*V7JyoCA4)5m& zDk34wpNJp;8n&rOp}u)YCK;wX_so~VECD{33g@A;NYYx#&p8>W#H!Uutxv%%O!{5+ z7;#U}S4TX&g|v?^Z?3lE0Gl9(^kzB;G()29sS-huy_EOTbuSE@)s`l=&ob z0Qao`seu(HHv9q{h8!D4N#?Gc-|OprVou~@G5xeWvax|-w9Iuqv}kwiHpsQ}o59F; z?9DFDd(IU3*0At3_ntpsPgFE2SI5_-IF!>f8}hDp}%Uk1B;| zYANME9@)&B`HSu^LjLSQjoQc`d>-ARzOJa?sJD;%0b%gBJa`dI@%{Tdvfmf^ikT3+ zvj8SY`yU>*{tosAp78!?_9^;4)^^K5R+~CjV#zDu*R~jlCTx}D`>=f7(5&{=e>sM^ zdc6f<$@2dWqjmQE4@SF=@-MCs`pNNMr1bysOQTD3wFQ<9_syD`5;#-Zkd$04huk7a z-gUJDrMkS0qsEN4=Gc4R8qD5}SSk+|Xfd78Zxp^4w`9H6HYRkamX`a4RlKsq?PBv- z1L+J~1$kw5#z_p#x$v#es1Pa2094C%xWiePB$UoTVMgor98Fqk1mod!VFdfVaVcs0 z-9^0i_Tq*vy7{tioZlgFjwc5i1(+K>p!Pnj;M6h?OKunRrN)<^3@Fad)V~YDz>Mz8 zX``l;z{T4Z0I$8t#d(*MUTa=T8epOGb}KLeHue8kKm4zIFmbD2$#iR=q2N4dW_-L% zS5QO6=NA;u@T+ZXP!Npwx*jG@t%FjDZFe{D9!${((l=?N;{p`&zA8*iU)KG`!q)46 z9o0`nGzCH8=HKX;$!WH_0Yl?j2NcA5s;{zS|3XWG|6XbFY9V78a)8t_gt2muMx`M}1LJTB}|8l@hk7sY-w33$uIa zyp4BF303?VB&bOUj}{<5_gaZ^^Ec@S*RPM&yPJ|ARRoEQ>>D)SpVvG^LMn6TYI!E! z8DaVGR`*lZ0-S!?JjIK5@ga=TSP&SWY;%=w^Q?;=94}rbHLpVe;Ew+hQ{V|Pt6X%T z6z7e1fx<^8Mi7iS3i@d>y!X{kdhct?oOHVErrMM+g?pe|^Yhob;0#d^Djh0PzJ*?Y z4i*fT|5_uewu<^&SiwT%7`|uTqZWdS?ZzX!i}wHodUXhS7x~lop73D&)IIRTJ*Q(y zGM_q>N=eysa;#D^@XICS9by6>K?pV}StcHiP@hr|^KLaz(~CH;kVy;2=snb(4kCA4 zVQ8Gf$r(Y>ud;Hn+cz;jh`nC5vS;<(>a}*)rq>Rnll^zV)HY@R;B@cUz z0tpTqcYULoM(=BFj4Qc4&sO%1Jv{=)vd^x2|C3}9#gXWX4gasYd=0m2y=nRwObwoH zgQm-&)<9|!i?Qy!A_GgHckk7F$Z!i<6f3NhoLnp=kWx@5;oj9xu8PmQhcN z&02mp*pLd`t*JcMpTuO%$ z!2_kZY}B^-xmdQP;{N+{+q>?o!=Dk?70C$1Z54)JY3b7*_6sz6O3kOaZNJIf>{Pfg zd7L2XcZid$rMjuNBKc{)HDN_IbwWb^XoysJF}=6JUh26MG%cA1dGUB@{(L@L*WNfV zNI#J$TzZ}Ri({p$!tg{cv6i7V=kqu$Ziy=Tz2zUHygxW4=3MvFEL?%cw7zW``%R@ zfMt13f-QlqI*|SI7 zRJA_p@u?Ak#NahRhg$VMEbu3?U-e=`(-D@P*Q;TgQFnGWrc@RhX#J|-0K=5%ttjs` zVOtUM(LLw;HYhaz4=H=a>exc7L1wJ6kgx0h^tYWae(-S&?yD)-(BH<{==VG$4+?K) zHLS-D9mnTos0euiIs=)sU)*jJs22ZGxY3Vjw{Ro@2BM0*(UFuz;Q%+*ZYHMMQX~4d z)(dM>$8q-!k7x2VIh``3&F;Esr?l9rKG=meXD5;-iz7+qW;>~-1j$skminn#E<-Y4LN8!@m$Loz5opMV8z^t#a1WRePm*g$>bRk>D zYpMCjG|v|a9)A;xdD}A)lTz3oWHF!7M^Aj&d1i3S3-oKf=Qyml3vY*0J|ulCdR{I| z9}k$aYvz;_`I2$yUh>Dpigj32M!!|~(bgLGA*3{SdsPs#`XCh5Z<2-){=1D4dP>Pr z1f}%fFNAoV!B}<2{7tu1K%7LQR{M3|NG~$eYDQq0wa!wA@yI&KbK7D)pzx=LA7z7< zg#;sEVlx*vzdq_vy z>vpRlDITCp&W&V};2{&a-zIWHV9@w~{&+c-ht?Bb%(Jag0h_`NNDocd#QgU|mA^+T z)g;{Hl;I)Y1Py)K+>+@N#)=74n_};3U(wh{eZg@6>Py5=e zSsM|_79%6vFzK@IlM}mf#31ubs{RI&o+(B6UHZrJyOz^B ztAlsojD6ES*I~@|@h1;*t0pG%xfqJ^P%H8DpR~gr)%8smN{=W^kStEYCBkxrLCx8+v^&5c(V;y zJHvuho%wQoeK&&$X`c8m;X!`CdbXKBA#Xh^;_q_9`Im!vf1ab0^m$b9lGcp9FB(hOY3khYz(riJ4K2BqzEl)SqwwE2-XxZS zqS#@_8Ewz4XBk3LU7%#OeISbMW3;gqA6ngy*^dwSlBnZ;EN!R{VnsL!XFYDQe(?8H z^ugx^#bcw+(=V3Tj@NAlFY4#_E)&@QJia_rm(V1%s`sapr|MLY{WeTElms&50QpVf zr7g$zFV&jZ%zit8)$v&{(7%;m~ zAoA(c0=@bh-XjD|+@B)?y>peEHtXvl@_Ra{+l+40r5i|L8)!Xc<~Ylq1W=WYQBvOL zre23!yRSm{hWS{v51uY&GKV>*Q@2jRq~;0(v+vAorbgHx z^z4K@=C>wFmyJML`I_2c=9v$bf?_d4b} z-EhBR{JwS8k=fbFDflvK{e5Cje1qq5x)g)CXoE1ED!MbApWnu@qC1`~7BVm-;?C>i zOB$?Nk8Y+CpPUT*VTml}^C6Crk*0%gqBMuU(VKRAbk_VtvGx-M@o`u!+ydyu`DH^- z>lIRv%%7ReZZpmbexa6-lf@^OlMtH63zXw4D8r;+f@#fnuW#PKi~cu zd`lQw!ThizP*+7QsEP}ye4POqZceJjfJkiXk|p{%0Yso54G=BZFOH8-o85so&RS?p zohC3%QICQ=-5jo{ zFi4TdBx?`t#%p(OxTFoM6XvDcF(%eysk$`e)QO4+aD_^dxvoH?Xt5)ql^3LBxYu0PB=( z+Pb7@cN4Q!k$@AY8~`UdAy(+3H@_M3W?Tdr`yD?qpX=(?!s%Wfp!ViqgLJH>zrnZRO3`(fvg3!RRk}aA{@hr?Awit2=;gdi?eOBq{ot9e4#oartd8TAxl++v zb(Qk9SiJ+U^HMYvAHu8S_e=St6HWJZCZZ|w%1x zh%n6UHWwmapvyDeYx*!@*U@sDu|DZiViPH9D99#Otpib6{}&(>J>*O4@Fp34PF3UR z75e#QJ}cUN!|fXf3bYYFNBLEwKm_oL2E07dy>6y#A@Z+!P|t2JLw5q1J8AwjPSFV7Ag72p87w! z32k$;opo_u{>hche4qH?$2hgA6HtS2G`O=zMBlqRoY}$hZ-}sx%p2)FoJ}iIlpG(g zTc<=HN`Qep-)sDG4Iv#fa5V8dt|O0?9%5>h8J{5l@q)unr8kTIRDb#n9)_%YP+ zyk?b9t5&VmG{M7mgbew$5R~X^qVw-Z!*Nv?`#i&%Kaa=ZFsP~=ln_lFz^uGK8=(7Y zZUFTnKJR1_UMNIOD^97?*RlOtcHoc0d!y*iOYB9M`M&--N5%ZJN*CM&c&PX|z89;1 zI5N`9v$7vEUb|XWJl#T~&d%7~hB{(txbcy(5oNwEH|wsED92izZ@w+MLbH1|z0MyvP*dy$Om$f19ZniF(p;zXW`>feoozP0X0`W(jqa5h z?bo?!yZ#QMR(chaATeClzh+Z|j}2>jkqaB~`(S7;9|dDflqQkn69i5-g1 zz(5;Pce;G7{J1EkWfx!i<$gifS_R)H_e*TkMO3`M{hrSh5KG#KW}E08e$1@u=$x3{ z{{Eo3GpT0oB{0rG1;0NtA%c|sn0cB$L*H{UD(w9H*xW?mzB(EA!j$z+(5| zLU!e`9eCS7Q0#l@6z-IDQ9`51?W0i$85@@Q3>j||7Pd6V0f6TUjmp;;jMrZi+NHC! zEwGvNZsv$sV0K+yPKS6@Vrr}6S8ZKS9&<#@l?$jD1poRvw_mD1Uu8r?aC=*~&-ieG z0kKD9N^sc-FxB_kUD{HV&X&(otKR8zSh4yI>>@|p{!0q={ z6dRk}g?NrxUGF~tAMfeOphgWcE7}|HgBq0Ned4t{0973-3TvU8nKqq%4n>DO=0K5) ziP?WdsWwAI|CFmJd0dY$!u!(e*{F!~Ujmpqtt*km;W=i|;>S}ge%ic|riepWjpng6 z&c2ZZ0sCv~=B@Vda7{ycY?%aaTQuTdqZY6*$IXE;m?f)om2zsY3NV$F>h`F3?@Sb! z4xB-fwmKr}eMAK_IM`(~|GxX?b-?!A`-eyWO#_*f;2?}70jFvG*KSv<3)bbP7|hJ? zYT6Xa4VeMevGoz9(kMftcAF_pQ}8u>D4tz8hZmjOt>NPLmnxeFbIw<4Aet|v9ZHKJ zuxh*5W6TmTJ?kpcwGYzoM1-P^}IQnD8i&xg0o zu2Xz3*BXz3@-8M4;3|g`i^{yVYN;>D@H&O-3G2LG(eFZ>=?%TGh;sx^P1-XgUph01 z9gsqc)PyA`xl)EVB7dtOl8`iTNd5*oJ+BF-r(;h<#NYw+WA$=IPSFpK<*QmyURuxj z2stI(&TvK(VeJZP2%L1>YMjOgW7L7cax*L-5M^H4Zqd^fG+U1`!WT#>4FPP}Wd1Ng z-^C*`8s8e6aEiZmV-VnbOJ(}~g3cW+aeCnQMgX%5c$0v!X^t~p^g|OB=-u)^;nL9H zvcG=5_NseyB;@p7>jZS=?ICc6hRY1g#Ra5DfF4>9Ea2jHz{bRpapFRrt~gS8Dp-lD zh~C1|YXu{a6y4{M2mP)~HIzgoRY_RX#KcUOC2`OAM5h3OkU0H&N5CW7!vi2*9WCtx zG18+Yo3$5^i#^{V8Qieh@|P?|={4AFY_pXJ!hvLbM53Yz%y6cr3A}h%Snu=QR;`w> zbe(nyt2}Hd- z{r!>4tw~%j3mdx~@bFY@v+YJ51OFADNhjt5l;vpZ{ioZ*T3oimqvLO#CtLBm%R~@( zv>vCG3_D|q9Pl5Aw;paAKJ;=y|4l3Ln15yQ!{7mw1jwT-)+ewbQ(+u6RUQ=!`hGva zEomi&++DQqO#q=U$ioAmx)Qr0Uav%GShfG1N@a`|tE|!qU3fWC;rD78UzXk17mzd$_?!wWLPdp`_>F$*cHB66LEZHG zn-m_Pp&%jM>2Y0;#-4c6fn4_;CIlx-U;O$MEWMog503QPf9$**)G<$c2xhC1pYL!= z8izgB_V%3bOu<_8M_wA4*|-s`vFz;d*X*V1?si^(l|Kd2Rp!eFv2=hd3^JTDzc=~o zvSDR;VG>_kH;s|)o3y_$FxEfS>*}tLSZO8zJqH#lc|@I#xDkuCGtAzeY7=e>cRkZ$ zd3aRR$$CiBzRA5Kz2?tJMUHl;@>*8J$*M1yl@!#CrLXFZJ_`L-5`@{G4s+P+I6JFb zZ+%YfH#is*ulF%Go9&CFr|=`<8^CI{Ay=TCYi2on5>16pu?Qn>qJ`Ep==WakhBf#S2ZP#5 zu%pn#T5O12w@b%;5Y&~9w^j1hgaWT)TSc|6!O1vaEkhwevL+uk}p)nr%T`Du%A0%)Ya`Jy( z9`vuTs{+lnCUG7USy>=oYsAlQF->n&-GFAbM{(CjF2iUHRk6d;P7{%qs#{->L}yEy z%a#P9Ylm~4N_FCrikScUAScRlc9nlNQe?#=9*Vz-FATCUfV?{m#R&;B+!?V0o#)G* z#K-gYc8_{{qtj^EqMc%()i;$R0!Z9IOY4wE2J~LNDsno;mdTWqWJnMZ7Uq-AwA(qO z8mnU6J|6LWPWi9hb0Cw<0BuUXJRZ^0R}+I*Y08tFv?1%LM|<4D$(^d$n^@ovV(v-x zhy(WxVXE_~tOQU0jlB{m0f}|~rvx;b0eb8CiGyLJ;_qHi-_a7d$>)SN8LI1ch7=JU=O2<3 z7TriCx>A{6<}40s7I;;`ek5559Dj9LQBf@rEv^PXUujT9my3qf`7mHRp8t`78s>em z>E?;rO>we?n@QXHe)M>KWC#NTir4wz=_6x*2=X((fz?0gfSWES2YNTPC-FK)K5aaR zQ;{NW*W38FoxGajQ&SdwZ?NCC^Kkasa9!ub*ZCAH1IB;?bgo=q^Ys3z+#BM!55)Z( zc^WrRbLGoB1DC)FDmMmfjV+~0|7IbT$7wgtf~z{kxOTo`0y~N$PXNk>tEXiI@$L%A z+qV)j_9XPNdIOm6F(p3xEAR93Q^9A63qt|R{>YaSJv&D*N640WhSg0@4g|3HD0de& zB(HThC4lq`&8xMwBj1-VfGviF@rAd+;D$_x1h`^vNg}#LL`3VE z5;dnxv^D=b{Pv`eZB%R7y$NbedX{WwY`9 zj@9~s=XkY#r_H5JM=c0k*+jHn+n-(w=K*6KO}(*`R!>Iafl1=gr9of+_q(Q4%?_EUDpwBM?ieRb@n0#&x5%K*e(;7es z`T2o5TmJeq6ex!@oukUj*U?tGvH?X50_!?2Aw)<$q1QdZ(0?dXQ78`?ABoJmH`y??WmKB+fKtZD3A%2>OP)6lCkY zf)6#XZU6+ zm&v3DLMsU0Xu!cqSuW{QP1V&7{o*qZdTu2mM zo_NP6B7gria2oy9@Ar7WwgD6u>XEKZh~^G~xQ7^d0NM_1Q$C`2c)A9Mh5anyZ%oayYI zc)9IY|609l)BU7`r7~^{40dE>6SW2#TYk|+x7~t%liWSS2|_QCuPvB`yce-#X?Q~9jzhOS8VNU zlr$!{it+^k8rJ`LzBlGsx_$;_TY#Ds+a^+cB9E84Im%}p*2ADPr-#_9`)kfmlH(?6 z(Q~Ncsd}qt#Bfxz${EB~D_1IAC=9X=mKUlgjp?0VMw%=A867VSoyL#;TpV*- zu=QCB#k<`A%C7E#$t+MU)8Ud;WFr=$rDGl|37eukb|I!$6C7KO4I`AU%xE4U)cPZeBc>E7$+v{`*_?G#k4AknF@H-z&pWv5x^S@wOUeV>e^*h?$Y)V z`z@bPqr>WM%iJfmYQw>?4t+J(tnMRcO71>m)!NaPSKLhMs?}o+TPCM#L&5mbe@>zsd!#_=A4}Cs zhaX!ctJ#*X{Z0G84(~ff2J=9RynMDRgE}ipZ`FB zHH|@(MnKP1=%oco)$RASIC6g!mBM$r?khhd`8s)NEw2lr9PZCj*rO(g1t;JgPu?G< z^h2NAd*Py|mQRXH#yo3vNOs0dzbZ@qq1IplzpMNDM~j#}Y5;$~SA!XeHkmc*olf^s z)<)+8pM;_j^k(mBsPgs;eo%yv>rP)dF(s{>0iD<2oL5QHng(;h2am6Tp0C_fTA!;n zoKJgvZ&79nA)yCY>@C)Q;yH%`Do>BrT;VeAiIZOhInd55WNoQh@6Hd02_*IEuEHaU zz$4#o^+eRB&djJ1v|wTFvKY%WiSJ~^vQ0lPM9J+IMZ%h4+4a2Y5{kuE3UGNv3$tle?6g>*Ft$c9zw1BAnq(t~r($W%G08MFYg;PY*cB zDo=5CyYBuIwFhK8amB}kdj#ogMC~X0d*dXi+Z7eH*21X8*L5f*x{Q~ zxEPAT$KkdwNFdRMk~}4Ub94-aORpNx%;dP<>*L`pTGUmm;mYb+G$HS2EIy1+$GEv# zQf{oXs64-T)n-zjHdOpKK7B*>B#emrWf@SlweUEeg|IpK=+p{4U$YG7g`(qq*;MTI zH}i3KiFko#q*$~Zy)fIDD9T1#&&n8cZQEV2SjgmAze?}+;*)m$7^7<$HBAw#$Aw+M z+~20i7y^11!=9E`O$(l}V|UBBc9(gn@dfPj!#GnE)bO@c&PiGitJErCn~GLSwtBky zJAebDT8rA!A|c)Q!0=acvkTl_f6f&LhMh;FmMNl=)g$aOParvO#C88_i-!-+qK~lx zOj}JIj+-qJNx7W!8e2U02Pf;2;>e4=vS&>fBO4T1QwTc=qY1qScQ%e9iG$<#_GI`* zcUX>W;5h9$5Qx0`lF~PwX0e&y*KUC|b8OBUs6|y-*RvNS%GcO8ec(`;`*$kAf^j^r zGvM*|Ql&?;a<;4b2*1BZzx#5gVo|-3W5nT}_qUP@_sEpBTR|AY8G&>fD%d7F!w7$~ zR^2O_3i^+U{@Vsy#lm$6xT94(7Po4wD`veoRom3m1^sQVX;h-gf8>t0Lho^GjBfj1|6knrekb^R^|U ztL&jW2rysGYa01^i|NAjr=IclJnQc#22-^2ElgHGh(I(lJ@dOB)$l5r!6}I_Q>bLe+E#*TiSu*C~;_7)5zNNmP`y6LwYzS0qlgn>DX*fyn zI}h&T+NtRz1e?a3Ts?%ZK3Rewcs zyCs%)t;wWc9m0!HF(pQ~Gj^T>vaaDDCmVQ1?ef=CxCFkHVIHqE}7+wiu8E0f3mQGI|v|O$p(Zb&`vz(6Wf5dXOB{qIp6Ef6ilRDEB z2D>9U|5FEn zjf14NIkvP$))M%($Itar3Vjs3ome-Gho_3Nv~M}@qrQ8pEvJ2HuO++rO?Yg!Wzpu| zT85@$O6wn!r-5Mn5*xNe0uBF_4#jow>f`6%)vP5{y=wT^o&{X31tMhcIRlFA*tt$D zbsjnsCsQL#GhY7{L1=;bolHc|CB30#@=hgQYxrYs^0#D%kjdMVY_Z`LhFRJE%EgglF1>7H3-1Lx z8o^I1&L!hyFFi6je{+v0ziiWCIPUuR$)~da)u&eH>&WH}`(zf!Q`TWWM1~y+_SBUw z@B_?twzeu2P~Ms}7V(j@1|Oo8^|*+-h+d~6@~f0ATz@>mpk>@0bNq;C@fhv?{BR3$ z@<8Rmb9N-jD2g{3)N$bfI_rf8r&Beg=K|G<&t!f^oIlrz)7bP8!=c3Z zDpJa?N^v>R+2MF~=kB}Un_qG0-CWwA^OQml){Hv*J~nqg+&C_pAGyXI z;Db@+^BlK5npQqPl4`p+=_{@rY`?@Cd|#|H$3 zc26mR>IZx6>Iv<4M8a%Np$B~eKjM@rLy#`GQj4ADRo+kPuo=tnKAYI*Xhz|#xiRMr z?5TR{kjyP*tqVW6h-V@Dj28RqxcOndNu+lN-;-2l2j-r1(i%_G1!8s6lIKhM@0(1C(bAO=gcu z(7j9{=Q)!dJlH3XczwZ@xL`tFZ*0#y;*Sk-$Kzb%*9PIsn--kE#3&o>6vdzP;RHX; zm^@$jnB|3uP{l5Cv&QWZHfYON=~$^1PuP(Ac)xliM{r#8G@^e)Q20^(V2*N_XX{E9 zQARp!Jvr#bCxi3|8M0)$@5>7{=2a^U!h9wmY!XdZcBzOIL(e!*WZ_PIZmCwM2|*c4 z?wjTzDkjcX8p2Cy6UYmbq1_|gb0ZT!wjW9mqiHl(>@DjtR<-|_^}_J$4kokYGi-`H zBh7fyzL$N_I|{LzWp4Uh<51&~ZEV;KV~bBe(<-tD+)bTIQQQBTzT~On6SmB^%eIy& zYxs9L9PLVBd6LOi@A`b`W{yeOq~=DgNGk(+ZVGvmlqY2wj^p2$REJQyRvwP5gGSlv0r4oCWEIVQnyqoq zS3l%~zABm)9y2n~{y(Ss#`A47uQf&F4}LH^Rd9>8;H4iK|A6)` z(Yep3o5=2@qMWK+1tWh*u*jK;yCZJGAqWbr3^Z4&0BunQDEVx!{~DM&%mY{1k+*xzl>Xsz}vuAP=2oA4W98@*E12Hf;rX}ur_KkE%a7yf^Bs2}a{>GU>fydnc ztW>R>sEGbIZ&QHPN~XSD{#zO1ix<^WvIK&{ck1S9)&3u`(<9GEP^NLSy0sNeE9ON7 zNL)<33xi}CK4^6NNM&`?nkiL$Pbmx<5hxESO7BoKuep7n{gzt^BTbczSy|Dvgk2rW zR#L=Uc7nuQp@>6eN<9aiv`c610%7c7PQveKN+t%}8S z+Swd)2bpQ=#D7;n2B191AB!q_szk1z?DwLCg`@J;JEf5s(nh2?Sz227OtDAr&K za|Oz?K@X~#$07>-OzW7)@(Z9fqu-js7WDoC9kr1qp2PL+SI*>E5Zx{{4G>$OjaIez zSH*Uz8320~IhL1o7*x$pNvZjLnd&konG*%o6tOKRA({Gp>9+gvr@N`{KVb7OvHHIB z{~O}@@cPw)sHlXj_5VoP#IioQr(7in64h1Ii zthaOFH)l`U*XjTBhtKn&ZYH*WXvn^CG}k1e@(vIHOi3LZJ-U9rji#1WhE5q(x~#N# za4gwR045>{AtA5Foqg)a_U5LDxHylt8ldw6p4@ba*1neEh3jLDg9F3+&ojPkvC&$bm`@wdzkKf~t_pFZ`JH)U*iV}lL z29WLhm5bFKX0)6vXUj;*$n?fCMInl`Q-e&c2N#vhiS#Tic7Xr7wzj72_4$8vJQ!mv zB!3yI{mFs}rq%~^boBMUbO_+fI&c#~!o(CbG!{-OOPjCMRaFx9oL+{Xm;D5n0crQmCxvyFEJj_e@e-!J>Z}?$GfP?*y(Fq97 zzCJ!JcYmRPxC;6D9uqsH`|U>G=obMEd3mz}LtktO zAfb;4JiALt4fwsn5DO>7!@)s#$G~6-=HW(97=eK20~i5!KyVce!44zjVrFBj7LOnn z4n&31t(;5ttWiS@E#IaM0SnZWEAxF^ObmE*=^Xi9I>*N5>}(2FRz(*)j literal 0 HcmV?d00001 diff --git a/docs/_static/esp32-pico-kit-v4-layout.jpg b/docs/_static/esp32-pico-kit-v4-layout.jpg index 25dca043785bf1db5a78dcb8c7456eed45d831b3..c8dd4478877a24e75a140cbc091c4757d0cd7897 100644 GIT binary patch literal 82848 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 literal 78738 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. For easy interfacing, all the IO signals and system power on ESP32-PICO-D4 are led out through two 20 x 0.1" pitch header pads on both sides of the development board. To make the ESP32-PICO-KIT V4 fit into mini breadboards, the header pads are populated with two 17 pin headers. Remaining 2 x 3 pads grouped on one side of the board besides the antenna are not populated. If required, the additional 2 x 3 pin headers may be soldered later by the user. The development board integrates a USB-UART Bridge circuit, allowing the developers to connect the board to a PC's USB port for downloads and debugging. +ESP32-PICO-KIT V4 is a mini development board produced by `Espressif `_. At the core of this board is the ESP32-PICO-D4, a System-in-Package (SIP) module with complete Wi-Fi and Bluetooth functionalities. Comparing to other ESP32 chips, the ESP32-PICO-D4 integrates several peripheral components in one single package, that otherwise would need to be installed separately. This includes a 40 MHz crystal oscillator, 4 MB flash, filter capacitors and RF matching links in. This greatly reduces quantity and costs of additional components, subsequent assembly and testing cost, as well as overall product complexity. + +The development board integrates a USB-UART Bridge circuit, allowing the developers to connect the board to a PC's USB port for downloads and debugging. + +For easy interfacing, all the IO signals and system power on ESP32-PICO-D4 are led out through two rows of 20 x 0.1" pitch header pads on both sides of the development board. To make the ESP32-PICO-KIT V4 fit into mini breadboards, the header pads are populated with two rows of 17 pin headers. Remaining 2 x 3 pads grouped on each side of the board besides the antenna are not populated. The remaining 2 x 3 pin headers may be soldered later by the user. .. note:: - The 2 x 3 pads not populated with pin headers are internally connected to the flash memory embedded in the ESP32-PICO-D4 SIP module. For more details see module's datasheet in :ref:`get-started-pico-kit-v4-related-documents`. + The 2 x 3 pads not populated with pin headers are internally connected to the flash memory embedded in the ESP32-PICO-D4 SIP module. For more details see module's datasheet in `Related Documents`_. + +The board dimensions are 52 x 20.3 x 10 mm (2.1" x 0.8" x 0.4"), see section `Board Dimensions`_. An overview functional block diagram is shown below. + +.. figure:: ../_static/esp32-pico-kit-v4-functional-block-diagram.png + :align: center + :alt: ESP32-PICO-KIT V4 functional block diagram + :figclass: align-center + + ESP32-PICO-KIT V4 functional block diagram Functional Description @@ -29,16 +44,20 @@ The following list and figure below describe key components, interfaces and cont ESP32-PICO-D4 Standard ESP32-PICO-D4 module soldered to the ESP32-PICO-KIT V4 board. The complete system of the ESP32 chip has been integrated into the SIP module, requiring only external antenna with LC matching network, decoupling capacitors and pull-up resistors for EN signals to function properly. +LDO + 5V-to-3.3V Low dropout voltage regulator (LDO). USB-UART Bridge A single chip USB-UART bridge provides up to 1 Mbps transfers rates. -I/O - All the pins on ESP32-PICO-D4 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. Micro USB Port USB interface. It functions as the power supply for the board and the communication interface between PC and ESP32-PICO-KIT V4. -EN Button - Reset button; pressing this button resets the system. +5V Power On LED + This light emitting diode lits when the USB or an external 5V power supply is applied to the board. For details see schematic in `Related Documents`_. +I/O + All the pins on ESP32-PICO-D4 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. For details please see section `Pin Descriptions`_. BOOT Button Holding down the Boot button and pressing the EN button initiates the firmware download mode. Then user can download firmware through the serial port. +EN Button + Reset button; pressing this button resets the system. .. _get-started-pico-kit-v4-board-front: @@ -65,7 +84,7 @@ There following options are available to provide power supply to the ESP32-PICO- Start Application Development ------------------------------- +----------------------------- Before powering up the ESP32-PICO-KIT V4, please make sure that the board has been received in good condition with no obvious signs of damage. @@ -77,7 +96,106 @@ To start development of applications, proceed to section :doc:`index`, that will * :ref:`get-started-build-monitor` instantly what the application is doing -.. _get-started-pico-kit-v4-related-documents: +Pin Descriptions +---------------- + +The two tables below provide the **Name** and **Function** of I/O headers on both sides of the board, see :ref:`get-started-pico-kit-v4-board-front`. The pin numbering and header names are the same as on a schematic in `Related Documents`_. + + +Header J2 +""""""""" + +====== ================= ====== ====================================================== +No. Name Type Function +====== ================= ====== ====================================================== +1 FLASH_SD1 (FSD1) I/O | GPIO8, SD_DATA1, SPID, HS1_DATA1 :ref:`(1) ` , U2CTS +2 FLASH_SD3 (FSD3) I/O | GPIO7, SD_DATA0, SPIQ, HS1_DATA0 :ref:`(1) ` , U2RTS +3 FLASH_CLK (FCLK) I/O | GPIO11, SD_CMD, SPICS0, HS1_CMD :ref:`(1) ` , U1RTS +4 IO21 I/O | GPIO21, VSPIHD, EMAC_TX_EN +5 IO22 I/O | GPIO22, VSPIWP, U0RTS, EMAC_TXD1 +6 IO19 I/O | GPIO19, VSPIQ, U0CTS, EMAC_TXD0 +7 IO23 I/O | GPIO23, VSPID, HS1_STROBE +8 IO18 I/O | GPIO18, VSPICLK, HS1_DATA7 +9 IO5 I/O | GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK +10 IO10 I/O | GPIO10, SD_DATA3, SPIWP, HS1_DATA3, U1TXD +11 IO9 I/O | GPIO9, SD_DATA2, SPIHD, HS1_DATA2, U1RXD +12 RXD0 I/O | GPIO3, U0RXD :ref:`(4) ` , CLK_OUT2 +13 TXD0 I/O | GPIO1, U0TXD :ref:`(4) ` , CLK_OUT3, EMAC_RXD2 +14 IO35 I | ADC1_CH7, RTC_GPIO5 +15 IO34 I | ADC1_CH6, RTC_GPIO4 +16 IO38 I | GPIO38, ADC1_CH2, ADC_PRE_AMP :ref:`(2b) ` , RTC_GPIO2 +17 IO37 I | GPIO37, ADC_PRE_AMP :ref:`(2a) ` , ADC1_CH1, RTC_GPIO1 +18 EN I | CHIP_PU +19 GND P | Ground +20 VDD33 (3V3) P | 3.3V power supply +====== ================= ====== ====================================================== + + +Header J3 +""""""""" + +====== ================= ====== ====================================================== +No. Name Type Function +====== ================= ====== ====================================================== +1 FLASH_CS (FCS) I/O | GPIO16, HS1_DATA4 :ref:`(1) ` , U2RXD, EMAC_CLK_OUT +2 FLASH_SD0 (FSD0) I/O | GPIO17, HS1_DATA5 :ref:`(1) ` , U2TXD, EMAC_CLK_OUT_180 +3 FLASH_SD2 (FSD2) I/O | GPIO6, SD_CLK, SPICLK, HS1_CLK :ref:`(1) ` , U1CTS +4 SENSOR_VP (FSVP) I | GPIO36, ADC1_CH0, ADC_PRE_AMP :ref:`(2a) ` , RTC_GPIO0 +5 SENSOR_VN (FSVN) I | GPIO39, ADC1_CH3, ADC_PRE_AMP :ref:`(2b) ` , RTC_GPIO3 +6 IO25 I/O | GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0 +7 IO26 I/O | GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1 +8 IO32 I/O | 32K_XP :ref:`(3a) ` , ADC1_CH4, TOUCH9, RTC_GPIO9 +9 IO33 I/O | 32K_XN :ref:`(3b) ` , ADC1_CH5, TOUCH8, RTC_GPIO8 +10 IO27 I/O | GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17 + | EMAC_RX_DV +11 IO14 I/O | ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, + | HS2_CLK, SD_CLK, EMAC_TXD2 +12 IO12 I/O | ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI :ref:`(5) ` , HSPIQ, + | HS2_DATA2, SD_DATA2, EMAC_TXD3 +13 IO13 I/O | ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, + | HS2_DATA3, SD_DATA3, EMAC_RX_ER +14 IO15 I/O | ADC2_CH3, TOUCH3, RTC_GPIO13, MTDO, HSPICS0 + | HS2_CMD, SD_CMD, EMAC_RXD3 +15 IO2 I/O | ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, + | HS2_DATA0, SD_DATA0 +16 IO4 I/O | ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, + | HS2_DATA1, SD_DATA1, EMAC_TX_ER +17 IO0 I/O | ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1 + | EMAC_TX_CLK +18 VDD33 (3V3) P | 3.3V power supply +19 GND P | Ground +20 EXT_5V (5V) P | 5V power supply +====== ================= ====== ====================================================== + + +.. _get-started-pico-kit-v4-pin-notes: + +**Notes to** `Pin Descriptions`_ + + 1. This pin is connected to the flash pin of ESP32-PICO-D4. + 2. When used as ADC_PRE_AMP, connect 270 pF capacitors between: (a) SENSOR_VP and IO37, (b) SENSOR_VN and IO38. + 3. 32.768 kHz crystal oscillator: (a) input, (b) output. + 4. This pin is connected to the pin of the USB bridge chip on the board. + 5. The operating voltage of ESP32-PICO-KIT’s embedded SPI flash is 3.3V. Therefore, the strapping pin MTDI should hold bit ”0” during the module power-on reset. + + +Board Dimensions +---------------- + +.. figure:: ../_static/esp32-pico-kit-v4-dimensions-back.jpg + :align: center + :alt: ESP32-PICO-KIT V4 dimensions - back + :figclass: align-center + + ESP32-PICO-KIT V4 dimensions - back + +.. figure:: ../_static/esp32-pico-kit-v4-dimensions-side.jpg + :align: center + :alt: ESP32-PICO-KIT V4 dimensions - side + :figclass: align-center + + ESP32-PICO-KIT V4 dimensions - side + Related Documents ----------------- @@ -86,7 +204,6 @@ Related Documents * `ESP32-PICO-D4 Datasheet `_ (PDF) * :doc:`../hw-reference/index` -.. * `ESP32-PICO-KIT Datasheet `_ (PDF) .. toctree:: :hidden: From 2efef48e3ef06239736601bfec60f8ec6ab7209d Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 6 Dec 2017 12:05:11 +0530 Subject: [PATCH 12/30] docs: fix i2s code snippet for interrupt flags setting Signed-off-by: Mahavir Jain --- components/esp32/include/esp_intr_alloc.h | 4 ++-- docs/api-reference/peripherals/i2s.rst | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h index ad121abb39..02ddac57ef 100644 --- a/components/esp32/include/esp_intr_alloc.h +++ b/components/esp32/include/esp_intr_alloc.h @@ -37,13 +37,13 @@ extern "C" { */ //Keep the LEVELx values as they are here; they match up with (1< Date: Thu, 23 Nov 2017 20:51:17 +0100 Subject: [PATCH 13/30] Bugfix I_DELAY macro When compiling > const ulp_insn_t program[] = { > I_DELAY(1) > }; with the xtensa-esp32-elf-g++ compiler i always got the error: > sorry, unimplemented: non-trivial designated initializers not supported > > }; This was due to the different order in the macro and the struct. The struct has another order of the fields (opcode, unused, cycles) vs (cycles, unused, opcode): > struct { > uint32_t cycles : 16; /*!< Number of cycles to sleep */ > uint32_t unused : 12; /*!< Unused */ > uint32_t opcode : 4; /*!< Opcode (OPCODE_DELAY) */ > } delay; /*!< Format of DELAY instruction */ After updating the order in the macro it is possible to compile with the g++ compiler. Merges https://github.com/espressif/esp-idf/pull/1310 --- components/ulp/include/esp32/ulp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 302a47a0ca..ae539d84da 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -266,9 +266,9 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should * Delay (nop) for a given number of cycles */ #define I_DELAY(cycles_) { .delay = {\ - .opcode = OPCODE_DELAY, \ + .cycles = cycles_, \ .unused = 0, \ - .cycles = cycles_ } } + .opcode = OPCODE_DELAY } } /** * Halt the coprocessor. From 78855211fe00e5bf5d1532c135fe8c622b0f43c7 Mon Sep 17 00:00:00 2001 From: Paul Reimer Date: Sat, 25 Nov 2017 16:28:34 -0800 Subject: [PATCH 14/30] build system: Add *.cc files to list of file extensions compiled by default Merges https://github.com/espressif/esp-idf/pull/1318 --- docs/api-guides/build-system.rst | 2 +- make/component_wrapper.mk | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/api-guides/build-system.rst b/docs/api-guides/build-system.rst index f3d6153cab..2ef47a4689 100644 --- a/docs/api-guides/build-system.rst +++ b/docs/api-guides/build-system.rst @@ -156,7 +156,7 @@ Minimal Component Makefile The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set: -- All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.S``) will be compiled into the component library +- All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.cc``, ``*.S``) will be compiled into the component library - A sub-directory "include" will be added to the global include search path for all other components. - The component library will be linked into the project app. diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 84edc0a835..cc0afaa05d 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -85,11 +85,12 @@ include $(COMPONENT_MAKEFILE) ifndef COMPONENT_CONFIG_ONLY # Skip steps 3-5 if COMPONENT_CONFIG_ONLY is set # Object files which need to be linked into the library -# By default we take all .c, .cpp & .S files in COMPONENT_SRCDIRS. +# By default we take all .c, .cpp, .cc & .S files in COMPONENT_SRCDIRS. ifndef COMPONENT_OBJS # Find all source files in all COMPONENT_SRCDIRS COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) +COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cc,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cc))) COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S))) # Make relative by removing COMPONENT_PATH from all found object paths COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) @@ -221,6 +222,11 @@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(call AppendSourceToDependencies,$$<,$$@) +$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cc $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE) | $(COMPONENT_SRCDIRS) + $$(summary) CXX $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@ + $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $(call AppendSourceToDependencies,$$<,$$@) + $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE) | $(COMPONENT_SRCDIRS) $$(summary) AS $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@ $$(CC) $$(CPPFLAGS) $$(DEBUG_FLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ From 8a47679d046aa1f256d87060653568e9e4d595a7 Mon Sep 17 00:00:00 2001 From: Paul Reimer Date: Wed, 6 Dec 2017 09:09:40 -0800 Subject: [PATCH 15/30] Add #include guards and __cplusplus guards to esp_debug.h Merges https://github.com/espressif/esp-idf/pull/1358 --- components/mbedtls/port/include/mbedtls/esp_debug.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/mbedtls/port/include/mbedtls/esp_debug.h b/components/mbedtls/port/include/mbedtls/esp_debug.h index bf39cc73ba..8e23a5ea32 100644 --- a/components/mbedtls/port/include/mbedtls/esp_debug.h +++ b/components/mbedtls/port/include/mbedtls/esp_debug.h @@ -11,6 +11,12 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#ifndef _ESP_DEBUG_H_ +#define _ESP_DEBUG_H_ + +#ifdef __cplusplus +extern "C" { +#endif #include "sdkconfig.h" #ifdef CONFIG_MBEDTLS_DEBUG @@ -43,3 +49,9 @@ void mbedtls_esp_disable_debug_log(mbedtls_ssl_config *conf); #endif + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_DEBUG_H__ */ From cda22d9aaf553c420422cd6b3c5ecfc3ccc40a28 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 1 Dec 2017 12:50:45 +0800 Subject: [PATCH 16/30] docs: add FreeRTOS API docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use `code` tags instead of a mix of `

` and
  `@verbatim .. @endverbatim`
- Remove manually added function prototypes from comment blocks
- Remove of grouping (`\defgroup`) — some extra work is needed
  to make groups compatible with the way we auto-generate API
  reference from Doxygen XML files. It's pretty easy to add the
  grouping directives back if/when we implement support for
  Doxygen groups in the later stages of documentation build
  process.
- Hide private APIs under `@cond .. @endcond`
- Convert some comments into Doxygen-compatible ones
- Fix various documentation issues: missing documentation for
  some parameters, mismatch between parameter names in comment
  block and in function prototype.
- Add doxygen comments for functions which didn't have them
  (thread local storage).
- Add [out] param tags where necessary
- Redefine `xTaskCreate` and `xTaskCreateStatic` as inline
  functions instead of macros.
---
 .../freertos/include/freertos/event_groups.h  |  617 +++----
 components/freertos/include/freertos/queue.h  | 1518 +++++++---------
 .../freertos/include/freertos/ringbuf.h       |  254 +--
 components/freertos/include/freertos/semphr.h |  876 +++++----
 components/freertos/include/freertos/task.h   | 1577 +++++++++--------
 components/freertos/include/freertos/timers.h |  125 +-
 docs/Doxyfile                                 |   23 +-
 docs/api-reference/system/freertos.rst        |   42 +
 docs/api-reference/system/hooks.rst           |    4 +-
 docs/api-reference/system/index.rst           |   11 +-
 10 files changed, 2420 insertions(+), 2627 deletions(-)
 create mode 100644 docs/api-reference/system/freertos.rst

diff --git a/components/freertos/include/freertos/event_groups.h b/components/freertos/include/freertos/event_groups.h
index a2d70a43f3..6b83e04e18 100644
--- a/components/freertos/include/freertos/event_groups.h
+++ b/components/freertos/include/freertos/event_groups.h
@@ -104,7 +104,6 @@ extern "C" {
  * used to create a synchronisation point between multiple tasks (a
  * 'rendezvous').
  *
- * \defgroup EventGroup
  */
 
 
@@ -116,7 +115,6 @@ extern "C" {
  * xEventGroupCreate() returns an EventGroupHandle_t variable that can then
  * be used as a parameter to other event group functions.
  *
- * \defgroup EventGroupHandle_t EventGroupHandle_t
  * \ingroup EventGroup
  */
 typedef void * EventGroupHandle_t;
@@ -126,17 +124,11 @@ typedef void * EventGroupHandle_t;
  * number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1,
  * 32 bits if set to 0. 
  *
- * \defgroup EventBits_t EventBits_t
  * \ingroup EventGroup
  */
 typedef TickType_t EventBits_t;
 
 /**
- * event_groups.h
- *
- EventGroupHandle_t xEventGroupCreate( void );
- 
- * * Create a new event group. * * Internally, within the FreeRTOS implementation, event groups use a [small] @@ -162,25 +154,24 @@ typedef TickType_t EventBits_t; * event group then NULL is returned. See http://www.freertos.org/a00111.html * * Example usage: -
-	// Declare a variable to hold the created event group.
-	EventGroupHandle_t xCreatedEventGroup;
-
-	// Attempt to create the event group.
-	xCreatedEventGroup = xEventGroupCreate();
-
-	// Was the event group created successfully?
-	if( xCreatedEventGroup == NULL )
-	{
-		// The event group was not created because there was insufficient
-		// FreeRTOS heap available.
-	}
-	else
-	{
-		// The event group was created.
-	}
-   
- * \defgroup xEventGroupCreate xEventGroupCreate + * @code{c} + * // Declare a variable to hold the created event group. + * EventGroupHandle_t xCreatedEventGroup; + * + * // Attempt to create the event group. + * xCreatedEventGroup = xEventGroupCreate(); + * + * // Was the event group created successfully? + * if( xCreatedEventGroup == NULL ) + * { + * // The event group was not created because there was insufficient + * // FreeRTOS heap available. + * } + * else + * { + * // The event group was created. + * } + * @endcode * \ingroup EventGroup */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -188,11 +179,6 @@ typedef TickType_t EventBits_t; #endif /** - * event_groups.h - *
- EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
- 
- * * Create a new event group. * * Internally, within the FreeRTOS implementation, event groups use a [small] @@ -221,35 +207,26 @@ typedef TickType_t EventBits_t; * returned. If pxEventGroupBuffer was NULL then NULL is returned. * * Example usage: -
-	// StaticEventGroup_t is a publicly accessible structure that has the same
-	// size and alignment requirements as the real event group structure.  It is
-	// provided as a mechanism for applications to know the size of the event
-	// group (which is dependent on the architecture and configuration file
-	// settings) without breaking the strict data hiding policy by exposing the
-	// real event group internals.  This StaticEventGroup_t variable is passed
-	// into the xSemaphoreCreateEventGroupStatic() function and is used to store
-	// the event group's data structures
-	StaticEventGroup_t xEventGroupBuffer;
-
-	// Create the event group without dynamically allocating any memory.
-	xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
-   
+ * @code{c} + * // StaticEventGroup_t is a publicly accessible structure that has the same + * // size and alignment requirements as the real event group structure. It is + * // provided as a mechanism for applications to know the size of the event + * // group (which is dependent on the architecture and configuration file + * // settings) without breaking the strict data hiding policy by exposing the + * // real event group internals. This StaticEventGroup_t variable is passed + * // into the xSemaphoreCreateEventGroupStatic() function and is used to store + * // the event group's data structures + * StaticEventGroup_t xEventGroupBuffer; + * + * // Create the event group without dynamically allocating any memory. + * xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer ); + * @endcode */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION; #endif /** - * event_groups.h - *
-	EventBits_t xEventGroupWaitBits( 	EventGroupHandle_t xEventGroup,
-										const EventBits_t uxBitsToWaitFor,
-										const BaseType_t xClearOnExit,
-										const BaseType_t xWaitForAllBits,
-										const TickType_t xTicksToWait );
- 
- * * [Potentially] block to wait for one or more bits to be set within a * previously created event group. * @@ -292,54 +269,48 @@ typedef TickType_t EventBits_t; * pdTRUE. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-   const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-		// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
-		// the event group.  Clear the bits before exiting.
-		uxBits = xEventGroupWaitBits(
-					xEventGroup,	// The event group being tested.
-					BIT_0 | BIT_4,	// The bits within the event group to wait for.
-					pdTRUE,			// BIT_0 and BIT_4 should be cleared before returning.
-					pdFALSE,		// Don't wait for both bits, either bit will do.
-					xTicksToWait );	// Wait a maximum of 100ms for either bit to be set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// xEventGroupWaitBits() returned because both bits were set.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_0 was set.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_4 was set.
-		}
-		else
-		{
-			// xEventGroupWaitBits() returned because xTicksToWait ticks passed
-			// without either BIT_0 or BIT_4 becoming set.
-		}
-   }
-   
- * \defgroup xEventGroupWaitBits xEventGroupWaitBits + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * void aFunction( EventGroupHandle_t xEventGroup ) + * { + * EventBits_t uxBits; + * const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS; + * + * // Wait a maximum of 100ms for either bit 0 or bit 4 to be set within + * // the event group. Clear the bits before exiting. + * uxBits = xEventGroupWaitBits( + * xEventGroup, // The event group being tested. + * BIT_0 | BIT_4, // The bits within the event group to wait for. + * pdTRUE, // BIT_0 and BIT_4 should be cleared before returning. + * pdFALSE, // Don't wait for both bits, either bit will do. + * xTicksToWait ); // Wait a maximum of 100ms for either bit to be set. + * + * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) + * { + * // xEventGroupWaitBits() returned because both bits were set. + * } + * else if( ( uxBits & BIT_0 ) != 0 ) + * { + * // xEventGroupWaitBits() returned because just BIT_0 was set. + * } + * else if( ( uxBits & BIT_4 ) != 0 ) + * { + * // xEventGroupWaitBits() returned because just BIT_4 was set. + * } + * else + * { + * // xEventGroupWaitBits() returned because xTicksToWait ticks passed + * // without either BIT_0 or BIT_4 becoming set. + * } + * } + * @endcode{c} * \ingroup EventGroup */ EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** - * event_groups.h - *
-	EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
- 
- * * Clear bits within an event group. This function cannot be called from an * interrupt. * @@ -352,51 +323,45 @@ EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits * @return The value of the event group before the specified bits were cleared. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Clear bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupClearBits(
-								xEventGroup,	// The event group being updated.
-								BIT_0 | BIT_4 );// The bits being cleared.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 were set before xEventGroupClearBits() was
-			// called.  Both will now be clear (not set).
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else
-		{
-			// Neither bit 0 nor bit 4 were set in the first place.
-		}
-   }
-   
- * \defgroup xEventGroupClearBits xEventGroupClearBits + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * void aFunction( EventGroupHandle_t xEventGroup ) + * { + * EventBits_t uxBits; + * + * // Clear bit 0 and bit 4 in xEventGroup. + * uxBits = xEventGroupClearBits( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 );// The bits being cleared. + * + * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) + * { + * // Both bit 0 and bit 4 were set before xEventGroupClearBits() was + * // called. Both will now be clear (not set). + * } + * else if( ( uxBits & BIT_0 ) != 0 ) + * { + * // Bit 0 was set before xEventGroupClearBits() was called. It will + * // now be clear. + * } + * else if( ( uxBits & BIT_4 ) != 0 ) + * { + * // Bit 4 was set before xEventGroupClearBits() was called. It will + * // now be clear. + * } + * else + * { + * // Neither bit 0 nor bit 4 were set in the first place. + * } + * } + * @endcode * \ingroup EventGroup */ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; /** - * event_groups.h - *
-	BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- 
- * * A version of xEventGroupClearBits() that can be called from an interrupt. * * Setting bits in an event group is not a deterministic operation because there @@ -420,28 +385,27 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit * if the timer service queue was full. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-		// Clear bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupClearBitsFromISR(
-							xEventGroup,	 // The event group being updated.
-							BIT_0 | BIT_4 ); // The bits being set.
-
-		if( xResult == pdPASS )
-		{
-			// The message was posted successfully.
-		}
-  }
-   
- * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * // An event group which it is assumed has already been created by a call to + * // xEventGroupCreate(). + * EventGroupHandle_t xEventGroup; + * + * void anInterruptHandler( void ) + * { + * // Clear bit 0 and bit 4 in xEventGroup. + * xResult = xEventGroupClearBitsFromISR( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 ); // The bits being set. + * + * if( xResult == pdPASS ) + * { + * // The message was posted successfully. + * } + * } + * @endcode * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) @@ -451,11 +415,6 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit #endif /** - * event_groups.h - *
-	EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- 
- * * Set bits within an event group. * This function cannot be called from an interrupt. xEventGroupSetBitsFromISR() * is a version that can be called from an interrupt. @@ -480,56 +439,50 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit * event group value before the call to xEventGroupSetBits() returns. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupSetBits(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4 );// The bits being set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 remained set when the function returned.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 remained set when the function returned, but bit 4 was
-			// cleared.  It might be that bit 4 was cleared automatically as a
-			// task that was waiting for bit 4 was removed from the Blocked
-			// state.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 remained set when the function returned, but bit 0 was
-			// cleared.  It might be that bit 0 was cleared automatically as a
-			// task that was waiting for bit 0 was removed from the Blocked
-			// state.
-		}
-		else
-		{
-			// Neither bit 0 nor bit 4 remained set.  It might be that a task
-			// was waiting for both of the bits to be set, and the bits were
-			// cleared as the task left the Blocked state.
-		}
-   }
-   
- * \defgroup xEventGroupSetBits xEventGroupSetBits + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * void aFunction( EventGroupHandle_t xEventGroup ) + * { + * EventBits_t uxBits; + * + * // Set bit 0 and bit 4 in xEventGroup. + * uxBits = xEventGroupSetBits( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 );// The bits being set. + * + * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) + * { + * // Both bit 0 and bit 4 remained set when the function returned. + * } + * else if( ( uxBits & BIT_0 ) != 0 ) + * { + * // Bit 0 remained set when the function returned, but bit 4 was + * // cleared. It might be that bit 4 was cleared automatically as a + * // task that was waiting for bit 4 was removed from the Blocked + * // state. + * } + * else if( ( uxBits & BIT_4 ) != 0 ) + * { + * // Bit 4 remained set when the function returned, but bit 0 was + * // cleared. It might be that bit 0 was cleared automatically as a + * // task that was waiting for bit 0 was removed from the Blocked + * // state. + * } + * else + * { + * // Neither bit 0 nor bit 4 remained set. It might be that a task + * // was waiting for both of the bits to be set, and the bits were + * // cleared as the task left the Blocked state. + * } + * } + * @endcode{c} * \ingroup EventGroup */ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; /** - * event_groups.h - *
-	BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
- 
- * * A version of xEventGroupSetBits() that can be called from an interrupt. * * Setting bits in an event group is not a deterministic operation because there @@ -561,39 +514,38 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_ * if the timer service queue was full. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-   BaseType_t xHigherPriorityTaskWoken, xResult;
-
-		// xHigherPriorityTaskWoken must be initialised to pdFALSE.
-		xHigherPriorityTaskWoken = pdFALSE;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupSetBitsFromISR(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4   // The bits being set.
-							&xHigherPriorityTaskWoken );
-
-		// Was the message posted successfully?
-		if( xResult == pdPASS )
-		{
-			// If xHigherPriorityTaskWoken is now set to pdTRUE then a context
-			// switch should be requested.  The macro used is port specific and 
-			// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - 
-			// refer to the documentation page for the port being used.
-			portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-		}
-  }
-   
- * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * // An event group which it is assumed has already been created by a call to + * // xEventGroupCreate(). + * EventGroupHandle_t xEventGroup; + * + * void anInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken, xResult; + * + * // xHigherPriorityTaskWoken must be initialised to pdFALSE. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Set bit 0 and bit 4 in xEventGroup. + * xResult = xEventGroupSetBitsFromISR( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 // The bits being set. + * &xHigherPriorityTaskWoken ); + * + * // Was the message posted successfully? + * if( xResult == pdPASS ) + * { + * // If xHigherPriorityTaskWoken is now set to pdTRUE then a context + * // switch should be requested. The macro used is port specific and + * // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - + * // refer to the documentation page for the port being used. + * portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + * } + * } + * @endcode * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) @@ -603,14 +555,6 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_ #endif /** - * event_groups.h - *
-	EventBits_t xEventGroupSync(	EventGroupHandle_t xEventGroup,
-									const EventBits_t uxBitsToSet,
-									const EventBits_t uxBitsToWaitFor,
-									TickType_t xTicksToWait );
- 
- * * Atomically set bits within an event group, then wait for a combination of * bits to be set within the same event group. This functionality is typically * used to synchronise multiple tasks, where each task has to wait for the other @@ -648,93 +592,87 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_ * automatically cleared. * * Example usage: -
- // Bits used by the three tasks.
- #define TASK_0_BIT		( 1 << 0 )
- #define TASK_1_BIT		( 1 << 1 )
- #define TASK_2_BIT		( 1 << 2 )
-
- #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
-
- // Use an event group to synchronise three tasks.  It is assumed this event
- // group has already been created elsewhere.
- EventGroupHandle_t xEventBits;
-
- void vTask0( void *pvParameters )
- {
- EventBits_t uxReturn;
- TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 0 in the event flag to note this task has reached the
-		// sync point.  The other two tasks will set the other two bits defined
-		// by ALL_SYNC_BITS.  All three tasks have reached the synchronisation
-		// point when all the ALL_SYNC_BITS are set.  Wait a maximum of 100ms
-		// for this to happen.
-		uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
-
-		if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
-		{
-			// All three tasks reached the synchronisation point before the call
-			// to xEventGroupSync() timed out.
-		}
-	}
- }
-
- void vTask1( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 1 in the event flag to note this task has reached the
-		// synchronisation point.  The other two tasks will set the other two
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// this task will only reach here if the syncrhonisation was made by all
-		// three tasks, so there is no need to test the return value.
-	 }
- }
-
- void vTask2( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 2 in the event flag to note this task has reached the
-		// synchronisation point.  The other two tasks will set the other two
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// this task will only reach here if the syncrhonisation was made by all
-		// three tasks, so there is no need to test the return value.
-	}
- }
-
- 
- * \defgroup xEventGroupSync xEventGroupSync + * @code{c} + * // Bits used by the three tasks. + * #define TASK_0_BIT ( 1 << 0 ) + * #define TASK_1_BIT ( 1 << 1 ) + * #define TASK_2_BIT ( 1 << 2 ) + * + * #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT ) + * + * // Use an event group to synchronise three tasks. It is assumed this event + * // group has already been created elsewhere. + * EventGroupHandle_t xEventBits; + * + * void vTask0( void *pvParameters ) + * { + * EventBits_t uxReturn; + * TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS; + * + * for( ;; ) + * { + * // Perform task functionality here. + * + * // Set bit 0 in the event flag to note this task has reached the + * // sync point. The other two tasks will set the other two bits defined + * // by ALL_SYNC_BITS. All three tasks have reached the synchronisation + * // point when all the ALL_SYNC_BITS are set. Wait a maximum of 100ms + * // for this to happen. + * uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait ); + * + * if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS ) + * { + * // All three tasks reached the synchronisation point before the call + * // to xEventGroupSync() timed out. + * } + * } + * } + * + * void vTask1( void *pvParameters ) + * { + * for( ;; ) + * { + * // Perform task functionality here. + * + * // Set bit 1 in the event flag to note this task has reached the + * // synchronisation point. The other two tasks will set the other two + * // bits defined by ALL_SYNC_BITS. All three tasks have reached the + * // synchronisation point when all the ALL_SYNC_BITS are set. Wait + * // indefinitely for this to happen. + * xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY ); + * + * // xEventGroupSync() was called with an indefinite block time, so + * // this task will only reach here if the syncrhonisation was made by all + * // three tasks, so there is no need to test the return value. + * } + * } + * + * void vTask2( void *pvParameters ) + * { + * for( ;; ) + * { + * // Perform task functionality here. + * + * // Set bit 2 in the event flag to note this task has reached the + * // synchronisation point. The other two tasks will set the other two + * // bits defined by ALL_SYNC_BITS. All three tasks have reached the + * // synchronisation point when all the ALL_SYNC_BITS are set. Wait + * // indefinitely for this to happen. + * xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY ); + * + * // xEventGroupSync() was called with an indefinite block time, so + * // this task will only reach here if the syncrhonisation was made by all + * // three tasks, so there is no need to test the return value. + * } + * } + * + * @endcode * \ingroup EventGroup */ EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** - * event_groups.h - *
-	EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
- 
- * * Returns the current value of the bits in an event group. This function * cannot be used from an interrupt. * @@ -742,33 +680,22 @@ EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t u * * @return The event group bits at the time xEventGroupGetBits() was called. * - * \defgroup xEventGroupGetBits xEventGroupGetBits * \ingroup EventGroup */ #define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 ) /** - * event_groups.h - *
-	EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
- 
- * * A version of xEventGroupGetBits() that can be called from an ISR. * * @param xEventGroup The event group being queried. * * @return The event group bits at the time xEventGroupGetBitsFromISR() was called. * - * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR * \ingroup EventGroup */ EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); /** - * event_groups.h - *
-	void xEventGroupDelete( EventGroupHandle_t xEventGroup );
- 
* * Delete an event group that was previously created by a call to * xEventGroupCreate(). Tasks that are blocked on the event group will be @@ -778,6 +705,8 @@ EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); */ void vEventGroupDelete( EventGroupHandle_t xEventGroup ); +/** @cond */ + /* For internal use only. */ void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ); void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ); @@ -786,6 +715,8 @@ void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToCl UBaseType_t uxEventGroupGetNumber( void* xEventGroup ); #endif +/** @endcond */ + #ifdef __cplusplus } #endif diff --git a/components/freertos/include/freertos/queue.h b/components/freertos/include/freertos/queue.h index 638157765a..e15152ee97 100644 --- a/components/freertos/include/freertos/queue.h +++ b/components/freertos/include/freertos/queue.h @@ -101,6 +101,7 @@ typedef void * QueueSetHandle_t; */ typedef void * QueueSetMemberHandle_t; +/** @cond */ /* For internal use only. */ #define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) #define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) @@ -114,15 +115,9 @@ typedef void * QueueSetMemberHandle_t; #define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) #define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) +/** @endcond */ + /** - * queue. h - *
- QueueHandle_t xQueueCreate(
-							  UBaseType_t uxQueueLength,
-							  UBaseType_t uxItemSize
-						  );
- * 
- * * Creates a new queue instance. This allocates the storage required by the * new queue and returns a handle for the queue. * @@ -138,36 +133,35 @@ typedef void * QueueSetMemberHandle_t; * returned. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- };
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-	if( xQueue1 == 0 )
-	{
-		// Queue was not created and must not be used.
-	}
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue2 == 0 )
-	{
-		// Queue was not created and must not be used.
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueCreate xQueueCreate + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * }; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * if( xQueue1 == 0 ) + * { + * // Queue was not created and must not be used. + * } + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue2 == 0 ) + * { + * // Queue was not created and must not be used. + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -175,16 +169,6 @@ typedef void * QueueSetMemberHandle_t; #endif /** - * queue. h - *
- QueueHandle_t xQueueCreateStatic(
-							  UBaseType_t uxQueueLength,
-							  UBaseType_t uxItemSize,
-							  uint8_t *pucQueueStorageBuffer,
-							  StaticQueue_t *pxQueueBuffer
-						  );
- * 
- * * Creates a new queue instance, and returns a handle by which the new queue * can be referenced. * @@ -207,7 +191,7 @@ typedef void * QueueSetMemberHandle_t; * that will be copied for each posted item. Each item on the queue must be * the same size. * - * @param pucQueueStorageBuffer If uxItemSize is not zero then + * @param pucQueueStorage If uxItemSize is not zero then * pucQueueStorageBuffer must point to a uint8_t array that is at least large * enough to hold the maximum number of items that can be in the queue at any * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is @@ -220,40 +204,39 @@ typedef void * QueueSetMemberHandle_t; * returned. If pxQueueBuffer is NULL then NULL is returned. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- };
-
- #define QUEUE_LENGTH 10
- #define ITEM_SIZE sizeof( uint32_t )
-
- // xQueueBuffer will hold the queue structure.
- StaticQueue_t xQueueBuffer;
-
- // ucQueueStorage will hold the items posted to the queue.  Must be at least
- // [(queue length) * ( queue item size)] bytes long.
- uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
-							ITEM_SIZE	  // The size of each item in the queue
-							&( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
-							&xQueueBuffer ); // The buffer that will hold the queue structure.
-
-	// The queue is guaranteed to be created successfully as no dynamic memory
-	// allocation is used.  Therefore xQueue1 is now a handle to a valid queue.
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueCreateStatic xQueueCreateStatic + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * }; + * + * #define QUEUE_LENGTH 10 + * #define ITEM_SIZE sizeof( uint32_t ) + * + * // xQueueBuffer will hold the queue structure. + * StaticQueue_t xQueueBuffer; + * + * // ucQueueStorage will hold the items posted to the queue. Must be at least + * // [(queue length) * ( queue item size)] bytes long. + * uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ]; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold. + * ITEM_SIZE // The size of each item in the queue + * &( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue. + * &xQueueBuffer ); // The buffer that will hold the queue structure. + * + * // The queue is guaranteed to be created successfully as no dynamic memory + * // allocation is used. Therefore xQueue1 is now a handle to a valid queue. + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -261,15 +244,6 @@ typedef void * QueueSetMemberHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * queue. h - *
- BaseType_t xQueueSendToToFront(
-								   QueueHandle_t	xQueue,
-								   const void		*pvItemToQueue,
-								   TickType_t		xTicksToWait
-							   );
- * 
- * * This is a macro that calls xQueueGenericSend(). * * Post an item to the front of a queue. The item is queued by copy, not by @@ -293,65 +267,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) /** - * queue. h - *
- BaseType_t xQueueSendToBack(
-								   QueueHandle_t	xQueue,
-								   const void		*pvItemToQueue,
-								   TickType_t		xTicksToWait
-							   );
- * 
- * * This is a macro that calls xQueueGenericSend(). * * Post an item to the back of a queue. The item is queued by copy, not by @@ -375,65 +339,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) /** - * queue. h - *
- BaseType_t xQueueSend(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue,
-							  TickType_t xTicksToWait
-						 );
- * 
- * * This is a macro that calls xQueueGenericSend(). It is included for * backward compatibility with versions of FreeRTOS.org that did not * include the xQueueSendToFront() and xQueueSendToBack() macros. It is @@ -459,64 +413,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) /** - * queue. h - *
- BaseType_t xQueueOverwrite(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue
-						 );
- * 
- * * Only for use with queues that have a length of one - so the queue is either * empty or full. * @@ -539,70 +484,59 @@ typedef void * QueueSetMemberHandle_t; * to the queue even when the queue is already full. * * Example usage: -
-
- void vFunction( void *pvParameters )
- {
- QueueHandle_t xQueue;
- uint32_t ulVarToSend, ulValReceived;
-
-	// Create a queue to hold one uint32_t value.  It is strongly
-	// recommended *not* to use xQueueOverwrite() on queues that can
-	// contain more than one value, and doing so will trigger an assertion
-	// if configASSERT() is defined.
-	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
-
-	// Write the value 10 to the queue using xQueueOverwrite().
-	ulVarToSend = 10;
-	xQueueOverwrite( xQueue, &ulVarToSend );
-
-	// Peeking the queue should now return 10, but leave the value 10 in
-	// the queue.  A block time of zero is used as it is known that the
-	// queue holds a value.
-	ulValReceived = 0;
-	xQueuePeek( xQueue, &ulValReceived, 0 );
-
-	if( ulValReceived != 10 )
-	{
-		// Error unless the item was removed by a different task.
-	}
-
-	// The queue is still full.  Use xQueueOverwrite() to overwrite the
-	// value held in the queue with 100.
-	ulVarToSend = 100;
-	xQueueOverwrite( xQueue, &ulVarToSend );
-
-	// This time read from the queue, leaving the queue empty once more.
-	// A block time of 0 is used again.
-	xQueueReceive( xQueue, &ulValReceived, 0 );
-
-	// The value read should be the last value written, even though the
-	// queue was already full when the value was written.
-	if( ulValReceived != 100 )
-	{
-		// Error!
-	}
-
-	// ...
-}
- 
- * \defgroup xQueueOverwrite xQueueOverwrite + * @code{c} + * + * void vFunction( void *pvParameters ) + * { + * QueueHandle_t xQueue; + * uint32_t ulVarToSend, ulValReceived; + * + * // Create a queue to hold one uint32_t value. It is strongly + * // recommended *not* to use xQueueOverwrite() on queues that can + * // contain more than one value, and doing so will trigger an assertion + * // if configASSERT() is defined. + * xQueue = xQueueCreate( 1, sizeof( uint32_t ) ); + * + * // Write the value 10 to the queue using xQueueOverwrite(). + * ulVarToSend = 10; + * xQueueOverwrite( xQueue, &ulVarToSend ); + * + * // Peeking the queue should now return 10, but leave the value 10 in + * // the queue. A block time of zero is used as it is known that the + * // queue holds a value. + * ulValReceived = 0; + * xQueuePeek( xQueue, &ulValReceived, 0 ); + * + * if( ulValReceived != 10 ) + * { + * // Error unless the item was removed by a different task. + * } + * + * // The queue is still full. Use xQueueOverwrite() to overwrite the + * // value held in the queue with 100. + * ulVarToSend = 100; + * xQueueOverwrite( xQueue, &ulVarToSend ); + * + * // This time read from the queue, leaving the queue empty once more. + * // A block time of 0 is used again. + * xQueueReceive( xQueue, &ulValReceived, 0 ); + * + * // The value read should be the last value written, even though the + * // queue was already full when the value was written. + * if( ulValReceived != 100 ) + * { + * // Error! + * } + * + * // ... + * } + * @endcode * \ingroup QueueManagement */ #define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) /** - * queue. h - *
- BaseType_t xQueueGenericSend(
-									QueueHandle_t xQueue,
-									const void * pvItemToQueue,
-									TickType_t xTicksToWait
-									BaseType_t xCopyPosition
-								);
- * 
- * * It is preferred that the macros xQueueSend(), xQueueSendToFront() and * xQueueSendToBack() are used in place of calling this function directly. * @@ -630,64 +564,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; /** - * queue. h - *
- BaseType_t xQueuePeek(
-							 QueueHandle_t xQueue,
-							 void *pvBuffer,
-							 TickType_t xTicksToWait
-						 );
- * * This is a macro that calls the xQueueGenericReceive() function. * * Receive an item from a queue without removing the item from the queue. @@ -719,70 +644,62 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQ * otherwise pdFALSE. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to peek the data from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Peek a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask, but the item still remains on the queue.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to peek the data from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Peek a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask, but the item still remains on the queue. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) /** - * queue. h - *
- BaseType_t xQueuePeekFromISR(
-									QueueHandle_t xQueue,
-									void *pvBuffer,
-								);
- * * A version of xQueuePeek() that can be called from an interrupt service * routine (ISR). * @@ -803,7 +720,6 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQ * @return pdTRUE if an item was successfully received from the queue, * otherwise pdFALSE. * - * \defgroup xQueuePeekFromISR xQueuePeekFromISR * \ingroup QueueManagement */ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; @@ -845,73 +761,63 @@ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIV * otherwise pdFALSE. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to receive from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Receive a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to receive from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Receive a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) /** - * queue. h - *
- BaseType_t xQueueGenericReceive(
-									   QueueHandle_t	xQueue,
-									   void	*pvBuffer,
-									   TickType_t	xTicksToWait
-									   BaseType_t	xJustPeek
-									);
- * * It is preferred that the macro xQueueReceive() be used rather than calling * this function directly. * @@ -944,81 +850,73 @@ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIV * otherwise pdFALSE. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to receive from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Receive a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to receive from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Receive a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek ) PRIVILEGED_FUNCTION; /** - * queue. h - *
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
- * * Return the number of messages stored in a queue. * * @param xQueue A handle to the queue being queried. * * @return The number of messages available in the queue. * - * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** - * queue. h - *
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
- * * Return the number of free spaces available in a queue. This is equal to the * number of items that can be sent to the queue before the queue becomes full * if no items are removed. @@ -1027,35 +925,21 @@ UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNC * * @return The number of spaces available in the queue. * - * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** - * queue. h - *
void vQueueDelete( QueueHandle_t xQueue );
- * * Delete a queue - freeing all the memory allocated for storing of items * placed on the queue. * * @param xQueue A handle to the queue to be deleted. * - * \defgroup vQueueDelete vQueueDelete * \ingroup QueueManagement */ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** - * queue. h - *
- BaseType_t xQueueSendToFrontFromISR(
-										 QueueHandle_t xQueue,
-										 const void *pvItemToQueue,
-										 BaseType_t *pxHigherPriorityTaskWoken
-									  );
- 
- * * This is a macro that calls xQueueGenericSendFromISR(). * * Post an item to the front of a queue. It is safe to use this macro from @@ -1072,7 +956,7 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then @@ -1083,50 +967,39 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPrioritTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		portYIELD_FROM_ISR ();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPrioritTaskWoken; * - * \defgroup xQueueSendFromISR xQueueSendFromISR + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * portYIELD_FROM_ISR (); + * } + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) /** - * queue. h - *
- BaseType_t xQueueSendToBackFromISR(
-										 QueueHandle_t xQueue,
-										 const void *pvItemToQueue,
-										 BaseType_t *pxHigherPriorityTaskWoken
-									  );
- 
- * * This is a macro that calls xQueueGenericSendFromISR(). * * Post an item to the back of a queue. It is safe to use this macro from @@ -1143,7 +1016,7 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then @@ -1154,49 +1027,38 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		portYIELD_FROM_ISR ();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWoken; * - * \defgroup xQueueSendFromISR xQueueSendFromISR + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * portYIELD_FROM_ISR (); + * } + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) /** - * queue. h - *
- BaseType_t xQueueOverwriteFromISR(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue,
-							  BaseType_t *pxHigherPriorityTaskWoken
-						 );
- * 
- * * A version of xQueueOverwrite() that can be used in an interrupt service * routine (ISR). * @@ -1213,7 +1075,7 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueOverwriteFromISR() sets this value to pdTRUE then @@ -1226,64 +1088,53 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * the queue is already full. * * Example usage: -
-
- QueueHandle_t xQueue;
-
- void vFunction( void *pvParameters )
- {
- 	// Create a queue to hold one uint32_t value.  It is strongly
-	// recommended *not* to use xQueueOverwriteFromISR() on queues that can
-	// contain more than one value, and doing so will trigger an assertion
-	// if configASSERT() is defined.
-	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
-}
-
-void vAnInterruptHandler( void )
-{
-// xHigherPriorityTaskWoken must be set to pdFALSE before it is used.
-BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-uint32_t ulVarToSend, ulValReceived;
-
-	// Write the value 10 to the queue using xQueueOverwriteFromISR().
-	ulVarToSend = 10;
-	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
-
-	// The queue is full, but calling xQueueOverwriteFromISR() again will still
-	// pass because the value held in the queue will be overwritten with the
-	// new value.
-	ulVarToSend = 100;
-	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
-
-	// Reading from the queue will now return 100.
-
-	// ...
-
-	if( xHigherPrioritytaskWoken == pdTRUE )
-	{
-		// Writing to the queue caused a task to unblock and the unblocked task
-		// has a priority higher than or equal to the priority of the currently
-		// executing task (the task this interrupt interrupted).  Perform a context
-		// switch so this interrupt returns directly to the unblocked task.
-		portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port.
-	}
-}
- 
- * \defgroup xQueueOverwriteFromISR xQueueOverwriteFromISR + * @code{c} + * QueueHandle_t xQueue; + * + * void vFunction( void *pvParameters ) + * { + * // Create a queue to hold one uint32_t value. It is strongly + * // recommended *not* to use xQueueOverwriteFromISR() on queues that can + * // contain more than one value, and doing so will trigger an assertion + * // if configASSERT() is defined. + * xQueue = xQueueCreate( 1, sizeof( uint32_t ) ); + * } + * + * void vAnInterruptHandler( void ) + * { + * // xHigherPriorityTaskWoken must be set to pdFALSE before it is used. + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * uint32_t ulVarToSend, ulValReceived; + * + * // Write the value 10 to the queue using xQueueOverwriteFromISR(). + * ulVarToSend = 10; + * xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken ); + * + * // The queue is full, but calling xQueueOverwriteFromISR() again will still + * // pass because the value held in the queue will be overwritten with the + * // new value. + * ulVarToSend = 100; + * xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken ); + * + * // Reading from the queue will now return 100. + * + * // ... + * + * if( xHigherPrioritytaskWoken == pdTRUE ) + * { + * // Writing to the queue caused a task to unblock and the unblocked task + * // has a priority higher than or equal to the priority of the currently + * // executing task (the task this interrupt interrupted). Perform a context + * // switch so this interrupt returns directly to the unblocked task. + * portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port. + * } + * } + * @endcode * \ingroup QueueManagement */ #define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE ) /** - * queue. h - *
- BaseType_t xQueueSendFromISR(
-									 QueueHandle_t xQueue,
-									 const void *pvItemToQueue,
-									 BaseType_t *pxHigherPriorityTaskWoken
-								);
- 
- * * This is a macro that calls xQueueGenericSendFromISR(). It is included * for backward compatibility with versions of FreeRTOS.org that did not * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() @@ -1303,7 +1154,7 @@ uint32_t ulVarToSend, ulValReceived; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueSendFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueSendFromISR() sets this value to pdTRUE then @@ -1314,51 +1165,41 @@ uint32_t ulVarToSend, ulValReceived; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		// Actual macro used here is port specific.
-		portYIELD_FROM_ISR ();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWoken; + * + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * // Actual macro used here is port specific. + * portYIELD_FROM_ISR (); + * } + * } + * @endcode * - * \defgroup xQueueSendFromISR xQueueSendFromISR * \ingroup QueueManagement */ #define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) +/**@{*/ /** - * queue. h - *
- BaseType_t xQueueGenericSendFromISR(
-										   QueueHandle_t		xQueue,
-										   const	void	*pvItemToQueue,
-										   BaseType_t	*pxHigherPriorityTaskWoken,
-										   BaseType_t	xCopyPosition
-									   );
- 
- * * It is preferred that the macros xQueueSendFromISR(), * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place * of calling this function directly. xQueueGiveFromISR() is an @@ -1378,7 +1219,7 @@ uint32_t ulVarToSend, ulValReceived; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then @@ -1393,51 +1234,41 @@ uint32_t ulVarToSend, ulValReceived; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWokenByPost;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWokenByPost = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post each byte.
-		xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.  Note that the
-	// name of the yield function required is port specific.
-	if( xHigherPriorityTaskWokenByPost )
-	{
-		taskYIELD_YIELD_FROM_ISR();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWokenByPost; * - * \defgroup xQueueSendFromISR xQueueSendFromISR + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWokenByPost = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post each byte. + * xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. Note that the + * // name of the yield function required is port specific. + * if( xHigherPriorityTaskWokenByPost ) + * { + * taskYIELD_YIELD_FROM_ISR(); + * } + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +/**@}*/ /** - * queue. h - *
- BaseType_t xQueueReceiveFromISR(
-									   QueueHandle_t	xQueue,
-									   void	*pvBuffer,
-									   BaseType_t *pxTaskWoken
-								   );
- * 
- * * Receive an item from a queue. It is safe to use this function from within an * interrupt service routine. * @@ -1447,7 +1278,7 @@ BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherP * @param pvBuffer Pointer to the buffer into which the received item will * be copied. * - * @param pxTaskWoken A task may be blocked waiting for space to become + * @param[out] pxHigherPriorityTaskWoken A task may be blocked waiting for space to become * available on the queue. If xQueueReceiveFromISR causes such a task to * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will * remain unchanged. @@ -1456,77 +1287,77 @@ BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherP * otherwise pdFALSE. * * Example usage: -
-
- QueueHandle_t xQueue;
-
- // Function to create a queue and post some values.
- void vAFunction( void *pvParameters )
- {
- char cValueToPost;
- const TickType_t xTicksToWait = ( TickType_t )0xff;
-
-	// Create a queue capable of containing 10 characters.
-	xQueue = xQueueCreate( 10, sizeof( char ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Post some characters that will be used within an ISR.  If the queue
-	// is full then this task will block for xTicksToWait ticks.
-	cValueToPost = 'a';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
-	cValueToPost = 'b';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
-
-	// ... keep posting characters ... this task may block when the queue
-	// becomes full.
-
-	cValueToPost = 'c';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
- }
-
- // ISR that outputs all the characters received on the queue.
- void vISR_Routine( void )
- {
- BaseType_t xTaskWokenByReceive = pdFALSE;
- char cRxedChar;
-
-	while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
-	{
-		// A character was received.  Output the character now.
-		vOutputCharacter( cRxedChar );
-
-		// If removing the character from the queue woke the task that was
-		// posting onto the queue cTaskWokenByReceive will have been set to
-		// pdTRUE.  No matter how many times this loop iterates only one
-		// task will be woken.
-	}
-
-	if( cTaskWokenByPost != ( char ) pdFALSE;
-	{
-		taskYIELD ();
-	}
- }
- 
- * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * @code{c} + * QueueHandle_t xQueue; + * + * // Function to create a queue and post some values. + * void vAFunction( void *pvParameters ) + * { + * char cValueToPost; + * const TickType_t xTicksToWait = ( TickType_t )0xff; + * + * // Create a queue capable of containing 10 characters. + * xQueue = xQueueCreate( 10, sizeof( char ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Post some characters that will be used within an ISR. If the queue + * // is full then this task will block for xTicksToWait ticks. + * cValueToPost = 'a'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * cValueToPost = 'b'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * + * // ... keep posting characters ... this task may block when the queue + * // becomes full. + * + * cValueToPost = 'c'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * } + * + * // ISR that outputs all the characters received on the queue. + * void vISR_Routine( void ) + * { + * BaseType_t xTaskWokenByReceive = pdFALSE; + * char cRxedChar; + * + * while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) ) + * { + * // A character was received. Output the character now. + * vOutputCharacter( cRxedChar ); + * + * // If removing the character from the queue woke the task that was + * // posting onto the queue cTaskWokenByReceive will have been set to + * // pdTRUE. No matter how many times this loop iterates only one + * // task will be woken. + * } + * + * if( cTaskWokenByPost != ( char ) pdFALSE; + * { + * taskYIELD (); + * } + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; -/* +/**@{*/ +/** * Utilities to query queues that are safe to use from an ISR. These utilities * should be used only from witin an ISR, or within a critical section. */ BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +/**@}*/ - -/* +/** @cond */ +/** * xQueueAltGenericSend() is an alternative version of xQueueGenericSend(). * Likewise xQueueAltGenericReceive() is an alternative version of * xQueueGenericReceive(). @@ -1578,16 +1409,20 @@ void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; */ BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION; +/** @endcond */ -/* +/** * Reset a queue back to its original empty state. pdPASS is returned if the * queue is successfully reset. pdFAIL is returned if the queue could not be * reset because there are tasks blocked on the queue waiting to either * receive from the queue or send to the queue. + * + * @param xQueue The queue to reset + * @return always returns pdPASS */ #define xQueueReset( xQueue ) xQueueGenericReset( xQueue, pdFALSE ) -/* +/** * The registry is provided as a means for kernel aware debuggers to * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add * a queue, semaphore or mutex handle to the registry if you want the handle @@ -1613,7 +1448,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif -/* +/** * The registry is provided as a means for kernel aware debuggers to * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add * a queue, semaphore or mutex handle to the registry if you want the handle @@ -1627,7 +1462,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; #endif -/* +/** * @note This function has been back ported from FreeRTOS v9.0.0 * * The queue registry is provided as a means for kernel aware debuggers to @@ -1641,10 +1476,10 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION * returned. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif -/* +/** * Generic version of the function used to creaet a queue using dynamic memory * allocation. This is called by other functions and macros that create other * RTOS objects that use the queue structure as their base. @@ -1653,7 +1488,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif -/* +/** * Generic version of the function used to creaet a queue using dynamic memory * allocation. This is called by other functions and macros that create other * RTOS objects that use the queue structure as their base. @@ -1662,7 +1497,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif -/* +/** * Queue sets provide a mechanism to allow a task to block (pend) on a read * operation from multiple queues or semaphores simultaneously. * @@ -1712,7 +1547,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION */ QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; -/* +/** * Adds a queue or semaphore to a queue set that was previously created by a * call to xQueueCreateSet(). * @@ -1736,7 +1571,7 @@ QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILE */ BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; -/* +/** * Removes a queue or semaphore from a queue set. A queue or semaphore can only * be removed from a set if the queue or semaphore is empty. * @@ -1755,7 +1590,7 @@ BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHan */ BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; -/* +/** * xQueueSelectFromSet() selects from the members of a queue set a queue or * semaphore that either contains data (in the case of a queue) or is available * to take (in the case of a semaphore). xQueueSelectFromSet() effectively @@ -1791,18 +1626,19 @@ BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueS */ QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; -/* +/** * A version of xQueueSelectFromSet() that can be used from an ISR. */ QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; +/** @cond */ /* Not public API functions. */ void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; - +/** @endcond */ #ifdef __cplusplus } diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 23ea868680..88e570aa59 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -9,50 +9,58 @@ extern "C" { #endif -/* -Header definitions for a FreeRTOS ringbuffer object - -A ringbuffer instantiated by these functions essentially acts like a FreeRTOS queue, with the -difference that it's strictly FIFO and with the main advantage that you can put in randomly-sized -items. The capacity, accordingly, isn't measured in the amount of items, but the amount of memory -that is used for storing the items. Dependent on the size of the items, more or less of them will -fit in the ring buffer. - -This ringbuffer tries to be efficient with memory: when inserting an item, the item data will -be copied to the ringbuffer memory. When retrieving an item, however, a reference to ringbuffer -memory will be returned. The returned memory is guaranteed to be 32-bit aligned and contiguous. -The application can use this memory, but as long as it does, ringbuffer writes that would write -to this bit of memory will block. - -The requirement for items to be contiguous is slightly problematic when the only way to place -the next item would involve a wraparound from the end to the beginning of the ringbuffer. This can -be solved (or not) in a few ways: -- type = RINGBUF_TYPE_ALLOWSPLIT: The insertion code will split the item in two items; one which fits -in the space left at the end of the ringbuffer, one that contains the remaining data which is placed -in the beginning. Two xRingbufferReceive calls will be needed to retrieve the data. -- type = RINGBUF_TYPE_NOSPLIT: The insertion code will leave the room at the end of the ringbuffer -unused and instead will put the entire item at the start of the ringbuffer, as soon as there is -enough free space. -- type = RINGBUF_TYPE_BYTEBUF: This is your conventional byte-based ringbuffer. It does have no -overhead, but it has no item contiguousness either: a read will just give you the entire written -buffer space, or the space up to the end of the buffer, and writes can be broken up in any way -possible. Note that this type cannot do a 2nd read before returning the memory of the 1st. - -The maximum size of an item will be affected by this decision. When split items are allowed, it's -acceptable to push items of (buffer_size)-16 bytes into the buffer. When it's not allowed, the -maximum size is (buffer_size/2)-8 bytes. The bytebuf can fill the entire buffer with data, it has -no overhead. -*/ - #include //An opaque handle for a ringbuff object. typedef void * RingbufHandle_t; -//The various types of buffer +/** + * @brief The various types of buffer + * + * A ringbuffer instantiated by these functions essentially acts like a + * FreeRTOS queue, with the difference that it's strictly FIFO and with + * the main advantage that you can put in randomly-sized items. The capacity, + * accordingly, isn't measured in the amount of items, but the amount of + * memory that is used for storing the items. Dependent on the size of + * the items, more or less of them will fit in the ring buffer. + * + * This ringbuffer tries to be efficient with memory: when inserting an item, + * the item data will be copied to the ringbuffer memory. When retrieving + * an item, however, a reference to ringbuffer memory will be returned. + * The returned memory is guaranteed to be 32-bit aligned and contiguous. + * The application can use this memory, but as long as it does, ringbuffer + * writes that would write to this bit of memory will block. + * + * The requirement for items to be contiguous is slightly problematic when + * the only way to place the next item would involve a wraparound from the end + * to the beginning of the ringbuffer. This can be solved (or not) in a few ways, + * see descriptions of possible ringbuf_type_t types below. + * + * The maximum size of an item will be affected by ringbuffer type. + * When split items are allowed, it is acceptable to push items of + * (buffer_size)-16 bytes into the buffer. + * When it's not allowed, the maximum size is (buffer_size/2)-8 bytes. + * The bytebuf can fill the entire buffer with data, it has no overhead. + */ typedef enum { + /** The insertion code will leave the room at the end of the ringbuffer + * unused and instead will put the entire item at the start of the ringbuffer, + * as soon as there is enough free space. + */ RINGBUF_TYPE_NOSPLIT = 0, + /** The insertion code will split the item in two items; one which fits + * in the space left at the end of the ringbuffer, one that contains + * the remaining data which is placed in the beginning. + * Two xRingbufferReceive calls will be needed to retrieve the data. + */ RINGBUF_TYPE_ALLOWSPLIT, + /** This is your conventional byte-based ringbuffer. It does have no + * overhead, but it has no item contiguousness either: a read will just + * give you the entire written buffer space, or the space up to the end + * of the buffer, and writes can be broken up in any way possible. + * Note that this type cannot do a 2nd read before returning the memory + * of the 1st. + */ RINGBUF_TYPE_BYTEBUF } ringbuf_type_t; @@ -60,22 +68,19 @@ typedef enum { /** * @brief Create a ring buffer * - * @param buf_length : Length of circular buffer, in bytes. Each entry will take up its own length, plus a header - * that at the moment is equal to sizeof(size_t). - * @param allow_split_items : pdTRUE if it is acceptable that item data is inserted as two - * items instead of one. + * @param buf_length Length of circular buffer, in bytes. Each entry will + * take up its own length, plus a header that at the moment + * is equal to sizeof(size_t). + * @param type Type of ring buffer, see ringbuf_type_t. * * @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error. */ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type); - /** * @brief Delete a ring buffer * - * @param ringbuf - Ring buffer to delete - * - * @return void + * @param ringbuf Ring buffer to delete */ void vRingbufferDelete(RingbufHandle_t ringbuf); @@ -83,7 +88,7 @@ void vRingbufferDelete(RingbufHandle_t ringbuf); /** * @brief Get maximum size of an item that can be placed in the ring buffer * - * @param ringbuf - Ring buffer to query + * @param ringbuf Ring buffer to query * * @return Maximum size, in bytes, of an item that can be placed in a ring buffer. */ @@ -93,13 +98,15 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf); /** * @brief Insert an item into the ring buffer * - * @param ringbuf - Ring buffer to insert the item into - * @param data - Pointer to data to insert. NULL is allowed if data_size is 0. - * @param data_size - Size of data to insert. A value of 0 is allowed. - * @param xTicksToWait - Ticks to wait for room in the ringbuffer. + * @param ringbuf Ring buffer to insert the item into + * @param data Pointer to data to insert. NULL is allowed if data_size is 0. + * @param data_size Size of data to insert. A value of 0 is allowed. + * @param ticks_to_wait Ticks to wait for room in the ringbuffer. * - * @return pdTRUE if succeeded, pdFALSE on time-out or when the buffer is larger - * than indicated by xRingbufferGetMaxItemSize(ringbuf). + * @return + * - pdTRUE if succeeded + * - pdFALSE on time-out or when the buffer is larger than indicated + * by xRingbufferGetMaxItemSize(ringbuf). */ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size, TickType_t ticks_to_wait); @@ -107,11 +114,11 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size /** * @brief Insert an item into the ring buffer from an ISR * - * @param ringbuf - Ring buffer to insert the item into - * @param data - Pointer to data to insert. NULL is allowed if data_size is 0. - * @param data_size - Size of data to insert. A value of 0 is allowed. - * @param higher_prio_task_awoken - Value pointed to will be set to pdTRUE if the push woke up a higher - * priority task. + * @param ringbuf Ring buffer to insert the item into + * @param data Pointer to data to insert. NULL is allowed if data_size is 0. + * @param data_size Size of data to insert. A value of 0 is allowed. + * @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE + * if the push woke up a higher priority task. * * @return pdTRUE if succeeded, pdFALSE when the ring buffer does not have space. */ @@ -120,14 +127,18 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da /** * @brief Retrieve an item from the ring buffer * - * @note A call to vRingbufferReturnItem() is required after this to free up the data received. + * @note A call to vRingbufferReturnItem() is required after this to free up + * the data received. * - * @param ringbuf - Ring buffer to retrieve the item from - * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. - * @param xTicksToWait - Ticks to wait for items in the ringbuffer. + * @param ringbuf Ring buffer to retrieve the item from + * @param[out] item_size Pointer to a variable to which the size of the + * retrieved item will be written. + * @param ticks_to_wait Ticks to wait for items in the ringbuffer. * - * @return Pointer to the retrieved item on success; *item_size filled with the length of the - * item. NULL on timeout, *item_size is untouched in that case. + * @return + * - pointer to the retrieved item on success; *item_size filled with + * the length of the item. + * - NULL on timeout, *item_size is untouched in that case. */ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait); @@ -135,44 +146,58 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t /** * @brief Retrieve an item from the ring buffer from an ISR * - * @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received + * @note A call to vRingbufferReturnItemFromISR() is required after this to + * free up the data received * - * @param ringbuf - Ring buffer to retrieve the item from - * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. + * @param ringbuf Ring buffer to retrieve the item from + * @param[out] item_size Pointer to a variable to which the size of the + * retrieved item will be written. * - * @return Pointer to the retrieved item on success; *item_size filled with the length of the - * item. NULL when the ringbuffer is empty, *item_size is untouched in that case. + * @return + * - Pointer to the retrieved item on success; *item_size filled with + * the length of the item. + * - NULL when the ringbuffer is empty, *item_size is untouched in that case. */ void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size); /** - * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes - * to return - - * @note A call to vRingbufferReturnItem() is required after this to free up the data received. + * @brief Retrieve bytes from a ByteBuf type of ring buffer, + * specifying the maximum amount of bytes to return * - * @param ringbuf - Ring buffer to retrieve the item from - * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. - * @param xTicksToWait - Ticks to wait for items in the ringbuffer. + * @note A call to vRingbufferReturnItem() is required after this to free up + * the data received. * - * @return Pointer to the retrieved item on success; *item_size filled with the length of the - * item. NULL on timeout, *item_size is untouched in that case. + * @param ringbuf Ring buffer to retrieve the item from + * @param[out] item_size Pointer to a variable to which the size + * of the retrieved item will be written. + * @param ticks_to_wait Ticks to wait for items in the ringbuffer. + * @param wanted_size Maximum number of bytes to return. + * + * @return + * - Pointer to the retrieved item on success; *item_size filled with + * the length of the item. + * - NULL on timeout, *item_size is untouched in that case. */ void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size); /** - * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes - * to return. Call this from an ISR. + * @brief Retrieve bytes from a ByteBuf type of ring buffer, + * specifying the maximum amount of bytes to return. Call this from an ISR. * - * @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received + * @note A call to vRingbufferReturnItemFromISR() is required after this + * to free up the data received. * - * @param ringbuf - Ring buffer to retrieve the item from - * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. + * @param ringbuf Ring buffer to retrieve the item from + * @param[out] item_size Pointer to a variable to which the size of the + * retrieved item will be written. + * @param wanted_size Maximum number of bytes to return. * - * @return Pointer to the retrieved item on success; *item_size filled with the length of the - * item. NULL when the ringbuffer is empty, *item_size is untouched in that case. + * @return + * - Pointer to the retrieved item on success; *item_size filled with + * the length of the item. + * - NULL when the ringbuffer is empty, *item_size is untouched in that case. */ void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size); @@ -181,10 +206,8 @@ void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, /** * @brief Return a previously-retrieved item to the ringbuffer * - * @param ringbuf - Ring buffer the item was retrieved from - * @param item - Item that was received earlier - * - * @return void + * @param ringbuf Ring buffer the item was retrieved from + * @param item Item that was received earlier */ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item); @@ -193,34 +216,37 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item); /** * @brief Return a previously-retrieved item to the ringbuffer from an ISR * - * @param ringbuf - Ring buffer the item was retrieved from - * @param item - Item that was received earlier - * @param higher_prio_task_awoken - Value pointed to will be set to pdTRUE if the push woke up a higher - * priority task. - * - * @return void + * @param ringbuf Ring buffer the item was retrieved from + * @param item Item that was received earlier + * @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE + * if the push woke up a higher priority task. */ void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken); /** - * @brief Add the ringbuffer to a queue set. This specifically adds the semaphore that indicates - * more space has become available in the ringbuffer. + * @brief Add the ringbuffer to a queue set. * - * @param ringbuf - Ring buffer to add to the queue set - * @param xQueueSet - Queue set to add the ringbuffer to + * This specifically adds the semaphore that indicates more space + * has become available in the ringbuffer. * - * @return pdTRUE on success, pdFALSE otherwise + * @param ringbuf Ring buffer to add to the queue set + * @param xQueueSet Queue set to add the ringbuffer to + * + * @return + * - pdTRUE on success, pdFALSE otherwise */ BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet); /** - * @brief Add the ringbuffer to a queue set. This specifically adds the semaphore that indicates - * something has been written into the ringbuffer. + * @brief Add the ringbuffer to a queue set. * - * @param ringbuf - Ring buffer to add to the queue set - * @param xQueueSet - Queue set to add the ringbuffer to + * This specifically adds the semaphore that indicates something has been + * written into the ringbuffer. + * + * @param ringbuf Ring buffer to add to the queue set + * @param xQueueSet Queue set to add the ringbuffer to * * @return pdTRUE on success, pdFALSE otherwise */ @@ -228,11 +254,13 @@ BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle /** - * @brief Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates - * more space has become available in the ringbuffer. + * @brief Remove the ringbuffer from a queue set. * - * @param ringbuf - Ring buffer to remove from the queue set - * @param xQueueSet - Queue set to remove the ringbuffer from + * This specifically removes the semaphore that indicates more space + * has become available in the ringbuffer. + * + * @param ringbuf Ring buffer to remove from the queue set + * @param xQueueSet Queue set to remove the ringbuffer from * * @return pdTRUE on success, pdFALSE otherwise */ @@ -240,11 +268,13 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHa /** - * @brief Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates - * something has been written to the ringbuffer. + * @brief Remove the ringbuffer from a queue set. * - * @param ringbuf - Ring buffer to remove from the queue set - * @param xQueueSet - Queue set to remove the ringbuffer from + * This specifically removes the semaphore that indicates something + * has been written to the ringbuffer. + * + * @param ringbuf Ring buffer to remove from the queue set + * @param xQueueSet Queue set to remove the ringbuffer from * * @return pdTRUE on success, pdFALSE otherwise */ @@ -254,9 +284,7 @@ BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetH /** * @brief Debugging function to print the internal pointers in the ring buffer * - * @param ringbuf - Ring buffer to show - * - * @return void + * @param ringbuf Ring buffer to show */ void xRingbufferPrintInfo(RingbufHandle_t ringbuf); diff --git a/components/freertos/include/freertos/semphr.h b/components/freertos/include/freertos/semphr.h index 049b994e53..abe3819f8f 100644 --- a/components/freertos/include/freertos/semphr.h +++ b/components/freertos/include/freertos/semphr.h @@ -82,11 +82,8 @@ typedef QueueHandle_t SemaphoreHandle_t; #define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) #define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U ) - +/** @cond */ /** - * semphr. h - *
vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
- * * This old vSemaphoreCreateBinary() macro is now deprecated in favour of the * xSemaphoreCreateBinary() function. Note that binary semaphores created using * the vSemaphoreCreateBinary() macro are created in a state such that the @@ -109,23 +106,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * @param xSemaphore Handle to the created semaphore. Should be of type SemaphoreHandle_t. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
-    // This is a macro so pass the variable in directly.
-    vSemaphoreCreateBinary( xSemaphore );
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to vSemaphoreCreateBinary (). + * // This is a macro so pass the variable in directly. + * vSemaphoreCreateBinary( xSemaphore ); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -138,11 +134,9 @@ typedef QueueHandle_t SemaphoreHandle_t; } \ } #endif +/** @endcond */ /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateBinary( void )
- * * Creates a new binary semaphore instance, and returns a handle by which the * new semaphore can be referenced. * @@ -181,23 +175,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return Handle to the created semaphore. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateBinary();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to vSemaphoreCreateBinary (). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateBinary(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -205,9 +198,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
- * * Creates a new binary semaphore instance, and returns a handle by which the * new semaphore can be referenced. * @@ -231,7 +221,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore does not use a priority inheritance mechanism. For an alternative * that does use priority inheritance see xSemaphoreCreateMutex(). * - * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * @param pxStaticSemaphore Must point to a variable of type StaticSemaphore_t, * which will then be used to hold the semaphore's data structure, removing the * need for the memory to be allocated dynamically. * @@ -239,24 +229,23 @@ typedef QueueHandle_t SemaphoreHandle_t; * returned. If pxSemaphoreBuffer is NULL then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
- StaticSemaphore_t xSemaphoreBuffer;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
-    // The semaphore's data structures will be placed in the xSemaphoreBuffer
-    // variable, the address of which is passed into the function.  The
-    // function's parameter is not NULL, so the function will not attempt any
-    // dynamic memory allocation, and therefore the function will not return
-    // return NULL.
-    xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer );
-
-    // Rest of task code goes here.
- }
- 
- * \defgroup xSemaphoreCreateBinaryStatic xSemaphoreCreateBinaryStatic + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * StaticSemaphore_t xSemaphoreBuffer; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateBinary(). + * // The semaphore's data structures will be placed in the xSemaphoreBuffer + * // variable, the address of which is passed into the function. The + * // function's parameter is not NULL, so the function will not attempt any + * // dynamic memory allocation, and therefore the function will not return + * // return NULL. + * xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer ); + * + * // Rest of task code goes here. + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -264,12 +253,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * semphr. h - *
xSemaphoreTake(
- *                   SemaphoreHandle_t xSemaphore,
- *                   TickType_t xBlockTime
- *               )
- * * Macro to obtain a semaphore. The semaphore must have previously been * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or * xSemaphoreCreateCounting(). @@ -287,56 +270,49 @@ typedef QueueHandle_t SemaphoreHandle_t; * if xBlockTime expired without the semaphore becoming available. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- // A task that creates a semaphore.
- void vATask( void * pvParameters )
- {
-    // Create the semaphore to guard a shared resource.
-    vSemaphoreCreateBinary( xSemaphore );
- }
-
- // A task that uses the semaphore.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xSemaphore != NULL )
-    {
-        // See if we can obtain the semaphore.  If the semaphore is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the semaphore and can now access the
-            // shared resource.
-
-            // ...
-
-            // We have finished accessing the shared resource.  Release the
-            // semaphore.
-            xSemaphoreGive( xSemaphore );
-        }
-        else
-        {
-            // We could not obtain the semaphore and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
- * \defgroup xSemaphoreTake xSemaphoreTake + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * // A task that creates a semaphore. + * void vATask( void * pvParameters ) + * { + * // Create the semaphore to guard a shared resource. + * vSemaphoreCreateBinary( xSemaphore ); + * } + * + * // A task that uses the semaphore. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xSemaphore != NULL ) + * { + * // See if we can obtain the semaphore. If the semaphore is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the semaphore and can now access the + * // shared resource. + * + * // ... + * + * // We have finished accessing the shared resource. Release the + * // semaphore. + * xSemaphoreGive( xSemaphore ); + * } + * else + * { + * // We could not obtain the semaphore and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) /** - * semphr. h - * xSemaphoreTakeRecursive( - * SemaphoreHandle_t xMutex, - * TickType_t xBlockTime - * ) - * * Macro to recursively obtain, or 'take', a mutex type semaphore. * The mutex must have previously been created using a call to * xSemaphoreCreateRecursiveMutex(); @@ -366,64 +342,63 @@ typedef QueueHandle_t SemaphoreHandle_t; * expired without the semaphore becoming available. * * Example usage: -
- SemaphoreHandle_t xMutex = NULL;
-
- // A task that creates a mutex.
- void vATask( void * pvParameters )
- {
-    // Create the mutex to guard a shared resource.
-    xMutex = xSemaphoreCreateRecursiveMutex();
- }
-
- // A task that uses the mutex.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xMutex != NULL )
-    {
-        // See if we can obtain the mutex.  If the mutex is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the mutex and can now access the
-            // shared resource.
-
-            // ...
-            // For some reason due to the nature of the code further calls to
-			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
-			// code these would not be just sequential calls as this would make
-			// no sense.  Instead the calls are likely to be buried inside
-			// a more complex call structure.
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-
-            // The mutex has now been 'taken' three times, so will not be
-			// available to another task until it has also been given back
-			// three times.  Again it is unlikely that real code would have
-			// these calls sequentially, but instead buried in a more complex
-			// call structure.  This is just for illustrative purposes.
-            xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-
-			// Now the mutex can be taken by other tasks.
-        }
-        else
-        {
-            // We could not obtain the mutex and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
- * \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive + * @code{c} + * SemaphoreHandle_t xMutex = NULL; + * + * // A task that creates a mutex. + * void vATask( void * pvParameters ) + * { + * // Create the mutex to guard a shared resource. + * xMutex = xSemaphoreCreateRecursiveMutex(); + * } + * + * // A task that uses the mutex. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xMutex != NULL ) + * { + * // See if we can obtain the mutex. If the mutex is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the mutex and can now access the + * // shared resource. + * + * // ... + * // For some reason due to the nature of the code further calls to + * // xSemaphoreTakeRecursive() are made on the same mutex. In real + * // code these would not be just sequential calls as this would make + * // no sense. Instead the calls are likely to be buried inside + * // a more complex call structure. + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * + * // The mutex has now been 'taken' three times, so will not be + * // available to another task until it has also been given back + * // three times. Again it is unlikely that real code would have + * // these calls sequentially, but instead buried in a more complex + * // call structure. This is just for illustrative purposes. + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * + * // Now the mutex can be taken by other tasks. + * } + * else + * { + * // We could not obtain the mutex and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) - +/** @cond */ /* * xSemaphoreAltTake() is an alternative version of xSemaphoreTake(). * @@ -437,11 +412,9 @@ typedef QueueHandle_t SemaphoreHandle_t; * sacrifices execution speed to ensure better interrupt responsiveness. */ #define xSemaphoreAltTake( xSemaphore, xBlockTime ) xQueueAltGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) +/** @endcond */ /** - * semphr. h - *
xSemaphoreGive( SemaphoreHandle_t xSemaphore )
- * * Macro to release a semaphore. The semaphore must have previously been * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or * xSemaphoreCreateCounting(). and obtained using sSemaphoreTake(). @@ -461,50 +434,46 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore was not first obtained correctly. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Create the semaphore to guard a shared resource.
-    vSemaphoreCreateBinary( xSemaphore );
-
-    if( xSemaphore != NULL )
-    {
-        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
-        {
-            // We would expect this call to fail because we cannot give
-            // a semaphore without first "taking" it!
-        }
-
-        // Obtain the semaphore - don't block if the semaphore is not
-        // immediately available.
-        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
-        {
-            // We now have the semaphore and can access the shared resource.
-
-            // ...
-
-            // We have finished accessing the shared resource so can free the
-            // semaphore.
-            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
-            {
-                // We would not expect this call to fail because we must have
-                // obtained the semaphore to get here.
-            }
-        }
-    }
- }
- 
- * \defgroup xSemaphoreGive xSemaphoreGive + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Create the semaphore to guard a shared resource. + * vSemaphoreCreateBinary( xSemaphore ); + * + * if( xSemaphore != NULL ) + * { + * if( xSemaphoreGive( xSemaphore ) != pdTRUE ) + * { + * // We would expect this call to fail because we cannot give + * // a semaphore without first "taking" it! + * } + * + * // Obtain the semaphore - don't block if the semaphore is not + * // immediately available. + * if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) + * { + * // We now have the semaphore and can access the shared resource. + * + * // ... + * + * // We have finished accessing the shared resource so can free the + * // semaphore. + * if( xSemaphoreGive( xSemaphore ) != pdTRUE ) + * { + * // We would not expect this call to fail because we must have + * // obtained the semaphore to get here. + * } + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) /** - * semphr. h - *
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
- * * Macro to recursively release, or 'give', a mutex type semaphore. * The mutex must have previously been created using a call to * xSemaphoreCreateRecursiveMutex(); @@ -527,64 +496,64 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return pdTRUE if the semaphore was given. * * Example usage: -
- SemaphoreHandle_t xMutex = NULL;
-
- // A task that creates a mutex.
- void vATask( void * pvParameters )
- {
-    // Create the mutex to guard a shared resource.
-    xMutex = xSemaphoreCreateRecursiveMutex();
- }
-
- // A task that uses the mutex.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xMutex != NULL )
-    {
-        // See if we can obtain the mutex.  If the mutex is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the mutex and can now access the
-            // shared resource.
-
-            // ...
-            // For some reason due to the nature of the code further calls to
-			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
-			// code these would not be just sequential calls as this would make
-			// no sense.  Instead the calls are likely to be buried inside
-			// a more complex call structure.
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-
-            // The mutex has now been 'taken' three times, so will not be
-			// available to another task until it has also been given back
-			// three times.  Again it is unlikely that real code would have
-			// these calls sequentially, it would be more likely that the calls
-			// to xSemaphoreGiveRecursive() would be called as a call stack
-			// unwound.  This is just for demonstrative purposes.
-            xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-
-			// Now the mutex can be taken by other tasks.
-        }
-        else
-        {
-            // We could not obtain the mutex and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
- * \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive + * @code{c} + * SemaphoreHandle_t xMutex = NULL; + * + * // A task that creates a mutex. + * void vATask( void * pvParameters ) + * { + * // Create the mutex to guard a shared resource. + * xMutex = xSemaphoreCreateRecursiveMutex(); + * } + * + * // A task that uses the mutex. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xMutex != NULL ) + * { + * // See if we can obtain the mutex. If the mutex is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the mutex and can now access the + * // shared resource. + * + * // ... + * // For some reason due to the nature of the code further calls to + * // xSemaphoreTakeRecursive() are made on the same mutex. In real + * // code these would not be just sequential calls as this would make + * // no sense. Instead the calls are likely to be buried inside + * // a more complex call structure. + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * + * // The mutex has now been 'taken' three times, so will not be + * // available to another task until it has also been given back + * // three times. Again it is unlikely that real code would have + * // these calls sequentially, it would be more likely that the calls + * // to xSemaphoreGiveRecursive() would be called as a call stack + * // unwound. This is just for demonstrative purposes. + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * + * // Now the mutex can be taken by other tasks. + * } + * else + * { + * // We could not obtain the mutex and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) ) +/** @cond */ /* * xSemaphoreAltGive() is an alternative version of xSemaphoreGive(). * @@ -599,14 +568,9 @@ typedef QueueHandle_t SemaphoreHandle_t; */ #define xSemaphoreAltGive( xSemaphore ) xQueueAltGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) +/** @endcond */ + /** - * semphr. h - *
- xSemaphoreGiveFromISR(
-                          SemaphoreHandle_t xSemaphore,
-                          BaseType_t *pxHigherPriorityTaskWoken
-                      )
- * * Macro to release a semaphore. The semaphore must have previously been * created with a call to vSemaphoreCreateBinary() or xSemaphoreCreateCounting(). * @@ -618,7 +582,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * @param xSemaphore A handle to the semaphore being released. This is the * handle returned when the semaphore was created. * - * @param pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if giving the semaphore caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xSemaphoreGiveFromISR() sets this value to pdTRUE then @@ -627,77 +591,69 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL. * * Example usage: -
- \#define LONG_TIME 0xffff
- \#define TICKS_TO_WAIT	10
- SemaphoreHandle_t xSemaphore = NULL;
-
- // Repetitive task.
- void vATask( void * pvParameters )
- {
-    for( ;; )
-    {
-        // We want this task to run every 10 ticks of a timer.  The semaphore
-        // was created before this task was started.
-
-        // Block waiting for the semaphore to become available.
-        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
-        {
-            // It is time to execute.
-
-            // ...
-
-            // We have finished our task.  Return to the top of the loop where
-            // we will block on the semaphore until it is time to execute
-            // again.  Note when using the semaphore for synchronisation with an
-			// ISR in this manner there is no need to 'give' the semaphore back.
-        }
-    }
- }
-
- // Timer ISR
- void vTimerISR( void * pvParameters )
- {
- static uint8_t ucLocalTickCount = 0;
- static BaseType_t xHigherPriorityTaskWoken;
-
-    // A timer tick has occurred.
-
-    // ... Do other time functions.
-
-    // Is it time for vATask () to run?
-	xHigherPriorityTaskWoken = pdFALSE;
-    ucLocalTickCount++;
-    if( ucLocalTickCount >= TICKS_TO_WAIT )
-    {
-        // Unblock the task by releasing the semaphore.
-        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
-
-        // Reset the count so we release the semaphore again in 10 ticks time.
-        ucLocalTickCount = 0;
-    }
-
-    if( xHigherPriorityTaskWoken != pdFALSE )
-    {
-        // We can force a context switch here.  Context switching from an
-        // ISR uses port specific syntax.  Check the demo task for your port
-        // to find the syntax required.
-    }
- }
- 
- * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR + * @code{c} + * \#define LONG_TIME 0xffff + * \#define TICKS_TO_WAIT 10 + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Repetitive task. + * void vATask( void * pvParameters ) + * { + * for( ;; ) + * { + * // We want this task to run every 10 ticks of a timer. The semaphore + * // was created before this task was started. + * + * // Block waiting for the semaphore to become available. + * if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) + * { + * // It is time to execute. + * + * // ... + * + * // We have finished our task. Return to the top of the loop where + * // we will block on the semaphore until it is time to execute + * // again. Note when using the semaphore for synchronisation with an + * // ISR in this manner there is no need to 'give' the semaphore back. + * } + * } + * } + * + * // Timer ISR + * void vTimerISR( void * pvParameters ) + * { + * static uint8_t ucLocalTickCount = 0; + * static BaseType_t xHigherPriorityTaskWoken; + * + * // A timer tick has occurred. + * + * // ... Do other time functions. + * + * // Is it time for vATask () to run? + * xHigherPriorityTaskWoken = pdFALSE; + * ucLocalTickCount++; + * if( ucLocalTickCount >= TICKS_TO_WAIT ) + * { + * // Unblock the task by releasing the semaphore. + * xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); + * + * // Reset the count so we release the semaphore again in 10 ticks time. + * ucLocalTickCount = 0; + * } + * + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // We can force a context switch here. Context switching from an + * // ISR uses port specific syntax. Check the demo task for your port + * // to find the syntax required. + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) ) /** - * semphr. h - *
- xSemaphoreTakeFromISR(
-                          SemaphoreHandle_t xSemaphore,
-                          BaseType_t *pxHigherPriorityTaskWoken
-                      )
- * * Macro to take a semaphore from an ISR. The semaphore must have * previously been created with a call to vSemaphoreCreateBinary() or * xSemaphoreCreateCounting(). @@ -713,7 +669,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * @param xSemaphore A handle to the semaphore being taken. This is the * handle returned when the semaphore was created. * - * @param pxHigherPriorityTaskWoken xSemaphoreTakeFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xSemaphoreTakeFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if taking the semaphore caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xSemaphoreTakeFromISR() sets this value to pdTRUE then @@ -725,9 +681,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) ) /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateMutex( void )
- * * Macro that implements a mutex semaphore by using the existing queue * mechanism. * @@ -760,23 +713,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * data structures then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateMutex();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateMutex(). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateMutex(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -784,9 +736,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
- * * Creates a new mutex type semaphore instance, and returns a handle by which * the new mutex can be referenced. * @@ -822,22 +771,21 @@ typedef QueueHandle_t SemaphoreHandle_t; * mutex is returned. If pxMutexBuffer was NULL then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xMutexBuffer;
-
- void vATask( void * pvParameters )
- {
-    // A mutex cannot be used before it has been created.  xMutexBuffer is
-    // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is
-    // attempted.
-    xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
-
-    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
-    // so there is no need to check it.
- }
- 
- * \defgroup xSemaphoreCreateMutexStatic xSemaphoreCreateMutexStatic + * @code + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xMutexBuffer; + * + * void vATask( void * pvParameters ) + * { + * // A mutex cannot be used before it has been created. xMutexBuffer is + * // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is + * // attempted. + * xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer ); + * + * // As no dynamic memory allocation was performed, xSemaphore cannot be NULL, + * // so there is no need to check it. + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -846,9 +794,6 @@ typedef QueueHandle_t SemaphoreHandle_t; /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
- * * Creates a new recursive mutex type semaphore instance, and returns a handle * by which the new recursive mutex can be referenced. * @@ -889,23 +834,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * SemaphoreHandle_t. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateRecursiveMutex();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateMutex(). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateRecursiveMutex(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) @@ -913,9 +857,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
- * * Creates a new recursive mutex type semaphore instance, and returns a handle * by which the new recursive mutex can be referenced. * @@ -952,7 +893,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore and another always 'takes' the semaphore) and from within interrupt * service routines. * - * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * @param pxStaticSemaphore Must point to a variable of type StaticSemaphore_t, * which will then be used to hold the recursive mutex's data structure, * removing the need for the memory to be allocated dynamically. * @@ -961,24 +902,23 @@ typedef QueueHandle_t SemaphoreHandle_t; * returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xMutexBuffer;
-
- void vATask( void * pvParameters )
- {
-    // A recursive semaphore cannot be used before it is created.  Here a
-    // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic().
-    // The address of xMutexBuffer is passed into the function, and will hold
-    // the mutexes data structures - so no dynamic memory allocation will be
-    // attempted.
-    xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
-
-    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
-    // so there is no need to check it.
- }
- 
- * \defgroup xSemaphoreCreateRecursiveMutexStatic xSemaphoreCreateRecursiveMutexStatic + * @code + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xMutexBuffer; + * + * void vATask( void * pvParameters ) + * { + * // A recursive semaphore cannot be used before it is created. Here a + * // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic(). + * // The address of xMutexBuffer is passed into the function, and will hold + * // the mutexes data structures - so no dynamic memory allocation will be + * // attempted. + * xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer ); + * + * // As no dynamic memory allocation was performed, xSemaphore cannot be NULL, + * // so there is no need to check it. + * } + * @endcode * \ingroup Semaphores */ #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) @@ -986,9 +926,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
- * * Creates a new counting semaphore instance, and returns a handle by which the * new counting semaphore can be referenced. * @@ -1039,26 +976,25 @@ typedef QueueHandle_t SemaphoreHandle_t; * created. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
- SemaphoreHandle_t xSemaphore = NULL;
-
-    // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
-    // The max value to which the semaphore can count should be 10, and the
-    // initial value assigned to the count should be 0.
-    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Semaphore cannot be used before a call to xSemaphoreCreateCounting(). + * // The max value to which the semaphore can count should be 10, and the + * // initial value assigned to the count should be 0. + * xSemaphore = xSemaphoreCreateCounting( 10, 0 ); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -1066,9 +1002,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer )
- * * Creates a new counting semaphore instance, and returns a handle by which the * new counting semaphore can be referenced. * @@ -1123,27 +1056,26 @@ typedef QueueHandle_t SemaphoreHandle_t; * then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xSemaphoreBuffer;
-
- void vATask( void * pvParameters )
- {
- SemaphoreHandle_t xSemaphore = NULL;
-
-    // Counting semaphore cannot be used before they have been created.  Create
-    // a counting semaphore using xSemaphoreCreateCountingStatic().  The max
-    // value to which the semaphore can count is 10, and the initial value
-    // assigned to the count will be 0.  The address of xSemaphoreBuffer is
-    // passed in and will be used to hold the semaphore structure, so no dynamic
-    // memory allocation will be used.
-    xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer );
-
-    // No memory allocation was attempted so xSemaphore cannot be NULL, so there
-    // is no need to check its value.
- }
- 
- * \defgroup xSemaphoreCreateCountingStatic xSemaphoreCreateCountingStatic + * @code{c} + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xSemaphoreBuffer; + * + * void vATask( void * pvParameters ) + * { + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Counting semaphore cannot be used before they have been created. Create + * // a counting semaphore using xSemaphoreCreateCountingStatic(). The max + * // value to which the semaphore can count is 10, and the initial value + * // assigned to the count will be 0. The address of xSemaphoreBuffer is + * // passed in and will be used to hold the semaphore structure, so no dynamic + * // memory allocation will be used. + * xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer ); + * + * // No memory allocation was attempted so xSemaphore cannot be NULL, so there + * // is no need to check its value. + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -1151,23 +1083,16 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * semphr. h - *
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
- * * Delete a semaphore. This function must be used with care. For example, * do not delete a mutex type semaphore if the mutex is held by a task. * * @param xSemaphore A handle to the semaphore to be deleted. * - * \defgroup vSemaphoreDelete vSemaphoreDelete * \ingroup Semaphores */ #define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) ) /** - * semphr.h - *
TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
- * * If xMutex is indeed a mutex type semaphore, return the current mutex holder. * If xMutex is not a mutex type semaphore, or the mutex is available (not held * by a task), return NULL. @@ -1180,9 +1105,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) /** - * semphr.h - *
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
- * * If the semaphore is a counting semaphore then uxSemaphoreGetCount() returns * its current count value. If the semaphore is a binary semaphore then * uxSemaphoreGetCount() returns 1 if the semaphore is available, and 0 if the diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index 073267445f..ab45f1c43e 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -102,38 +102,38 @@ extern "C" { * returns (via a pointer parameter) an TaskHandle_t variable that can then * be used as a parameter to vTaskDelete to delete the task. * - * \defgroup TaskHandle_t TaskHandle_t * \ingroup Tasks */ typedef void * TaskHandle_t; -/* +/** * Defines the prototype to which the application task hook function must * conform. */ typedef BaseType_t (*TaskHookFunction_t)( void * ); -/* Task states returned by eTaskGetState. */ +/** Task states returned by eTaskGetState. */ typedef enum { - eRunning = 0, /* A task is querying the state of itself, so must be running. */ - eReady, /* The task being queried is in a read or pending ready list. */ - eBlocked, /* The task being queried is in the Blocked state. */ - eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ - eDeleted /* The task being queried has been deleted, but its TCB has not yet been freed. */ + eRunning = 0, /*!< A task is querying the state of itself, so must be running. */ + eReady, /*!< The task being queried is in a read or pending ready list. */ + eBlocked, /*!< The task being queried is in the Blocked state. */ + eSuspended, /*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + eDeleted /*!< The task being queried has been deleted, but its TCB has not yet been freed. */ } eTaskState; -/* Actions that can be performed when vTaskNotify() is called. */ +/** Actions that can be performed when vTaskNotify() is called. */ typedef enum { - eNoAction = 0, /* Notify the task without updating its notify value. */ - eSetBits, /* Set bits in the task's notification value. */ - eIncrement, /* Increment the task's notification value. */ - eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ - eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */ + eNoAction = 0, /*!< Notify the task without updating its notify value. */ + eSetBits, /*!< Set bits in the task's notification value. */ + eIncrement, /*!< Increment the task's notification value. */ + eSetValueWithOverwrite, /*!< Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ + eSetValueWithoutOverwrite /*!< Set the task's notification value if the previous value has been read by the task. */ } eNotifyAction; -/* +/** @cond */ +/** * Used internally only. */ typedef struct xTIME_OUT @@ -142,7 +142,7 @@ typedef struct xTIME_OUT TickType_t xTimeOnEntering; } TimeOut_t; -/* +/** * Defines the memory ranges allocated to the task when an MPU is used. */ typedef struct xMEMORY_REGION @@ -152,7 +152,7 @@ typedef struct xMEMORY_REGION uint32_t ulParameters; } MemoryRegion_t; -/* +/** * Parameters required to create an MPU protected task. */ typedef struct xTASK_PARAMETERS @@ -165,40 +165,44 @@ typedef struct xTASK_PARAMETERS StackType_t *puxStackBuffer; MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; } TaskParameters_t; +/** @endcond */ -/* Used with the uxTaskGetSystemState() function to return the state of each task -in the system. */ +/** + * Used with the uxTaskGetSystemState() function to return the state of each task in the system. +*/ typedef struct xTASK_STATUS { - TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ - const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - UBaseType_t xTaskNumber; /* A number unique to the task. */ - eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ - UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ - UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ - uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ - StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ - uint32_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ + TaskHandle_t xHandle; /*!< The handle of the task to which the rest of the information in the structure relates. */ + const char *pcTaskName; /*!< A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + UBaseType_t xTaskNumber; /*!< A number unique to the task. */ + eTaskState eCurrentState; /*!< The state in which the task existed when the structure was populated. */ + UBaseType_t uxCurrentPriority; /*!< The priority at which the task was running (may be inherited) when the structure was populated. */ + UBaseType_t uxBasePriority; /*!< The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ + uint32_t ulRunTimeCounter; /*!< The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + StackType_t *pxStackBase; /*!< Points to the lowest address of the task's stack area. */ + uint32_t usStackHighWaterMark; /*!< The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; -/* +/** * Used with the uxTaskGetSnapshotAll() function to save memory snapshot of each task in the system. * We need this struct because TCB_t is defined (hidden) in tasks.c. */ typedef struct xTASK_SNAPSHOT { - void *pxTCB; /* Address of task control block. */ - StackType_t *pxTopOfStack; /* Points to the location of the last item placed on the tasks stack. */ - StackType_t *pxEndOfStack; /* Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo - pxTopOfStack > pxEndOfStack, stack grows lo2hi*/ + void *pxTCB; /*!< Address of task control block. */ + StackType_t *pxTopOfStack; /*!< Points to the location of the last item placed on the tasks stack. */ + StackType_t *pxEndOfStack; /*!< Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo + pxTopOfStack > pxEndOfStack, stack grows lo2hi*/ } TaskSnapshot_t; -/* Possible return values for eTaskConfirmSleepModeStatus(). */ +/** + * Possible return values for eTaskConfirmSleepModeStatus(). + */ typedef enum { - eAbortSleep = 0, /* A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ - eStandardSleep, /* Enter a sleep mode that will not last any longer than the expected idle time. */ - eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ + eAbortSleep = 0, /*!< A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ + eStandardSleep, /*!< Enter a sleep mode that will not last any longer than the expected idle time. */ + eNoTasksWaitingTimeout /*!< No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ } eSleepModeStatus; @@ -214,7 +218,6 @@ typedef enum * * Macro for forcing a context switch. * - * \defgroup taskYIELD taskYIELD * \ingroup SchedulerControl */ #define taskYIELD() portYIELD() @@ -225,10 +228,9 @@ typedef enum * Macro to mark the start of a critical code region. Preemptive context * switches cannot occur when in a critical region. * - * NOTE: This may alter the stack (depending on the portable implementation) + * @note This may alter the stack (depending on the portable implementation) * so must be used with care! * - * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL * \ingroup SchedulerControl */ #define taskENTER_CRITICAL(mux) portENTER_CRITICAL(mux) @@ -240,10 +242,9 @@ typedef enum * Macro to mark the end of a critical code region. Preemptive context * switches cannot occur when in a critical region. * - * NOTE: This may alter the stack (depending on the portable implementation) + * @note This may alter the stack (depending on the portable implementation) * so must be used with care! * - * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL * \ingroup SchedulerControl */ #define taskEXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) @@ -254,7 +255,6 @@ typedef enum * * Macro to disable all maskable interrupts. * - * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS * \ingroup SchedulerControl */ #define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() @@ -264,7 +264,6 @@ typedef enum * * Macro to enable microcontroller interrupts. * - * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS * \ingroup SchedulerControl */ #define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() @@ -282,17 +281,58 @@ is used in assert() statements. */ *----------------------------------------------------------*/ /** - * task. h - *
- BaseType_t xTaskCreate(
-							  TaskFunction_t pvTaskCode,
-							  const char * const pcName,
-							  uint32_t usStackDepth,
-							  void *pvParameters,
-							  UBaseType_t uxPriority,
-							  TaskHandle_t *pvCreatedTask
-						  );
+ * Create a new task with a specified affinity. * + * This function is similar to xTaskCreate, but allows setting task affinity + * in SMP system. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @param xCoreID If the value is tskNO_AFFINITY, the created task is not + * pinned to any CPU, and the scheduler can run it on any core available. + * Other values indicate the index number of the CPU which the task should + * be pinned to. Specifying values larger than (portNUM_PROCESSORS - 1) will + * cause the function to fail. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * \ingroup Tasks + */ +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pvCreatedTask, + const BaseType_t xCoreID); + +#endif + +/** * Create a new task and add it to the list of tasks that are ready to run. * * Internally, within the FreeRTOS implementation, tasks use two blocks of @@ -341,64 +381,113 @@ is used in assert() statements. */ * list, otherwise an error code defined in the file projdefs.h * * Example usage: -
- // Task to be created.
- void vTaskCode( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-	 }
- }
-
- // Function that creates a task.
- void vOtherFunction( void )
- {
- static uint8_t ucParameterToPass;
- TaskHandle_t xHandle = NULL;
-
-	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
-	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
-	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
-	 // the new task attempts to access it.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
-     configASSERT( xHandle );
-
-	 // Use the handle to delete the task.
-     if( xHandle != NULL )
-     {
-	     vTaskDelete( xHandle );
-     }
- }
-   
- * \defgroup xTaskCreate xTaskCreate + * @code{c} + * // Task to be created. + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * static uint8_t ucParameterToPass; + * TaskHandle_t xHandle = NULL; + * + * // Create the task, storing the handle. Note that the passed parameter ucParameterToPass + * // must exist for the lifetime of the task, so in this case is declared static. If it was just an + * // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time + * // the new task attempts to access it. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle ); + * configASSERT( xHandle ); + * + * // Use the handle to delete the task. + * if( xHandle != NULL ) + * { + * vTaskDelete( xHandle ); + * } + * } + * @endcode * \ingroup Tasks */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint32_t usStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask, - const BaseType_t xCoreID); -#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskCreatePinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), tskNO_AFFINITY ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + static inline IRAM_ATTR BaseType_t xTaskCreate( + TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pvCreatedTask) + { + return xTaskCreatePinnedToCore( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask, tskNO_AFFINITY ); + } + #endif -/** - * task. h - *
- TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
-								 const char * const pcName,
-								 uint32_t ulStackDepth,
-								 void *pvParameters,
-								 UBaseType_t uxPriority,
-								 StackType_t *pxStackBuffer,
-								 StaticTask_t *pxTaskBuffer,
-                                 const BaseType_t xCoreID );
+ + +/** + * Create a new task with a specified affinity. * + * This function is similar to xTaskCreateStatic, but allows specifying + * task affinity in an SMP system. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param ulStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param pxStackBuffer Must point to a StackType_t array that has at least + * ulStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @param xCoreID If the value is tskNO_AFFINITY, the created task is not + * pinned to any CPU, and the scheduler can run it on any core available. + * Other values indicate the index number of the CPU which the task should + * be pinned to. Specifying values larger than (portNUM_PROCESSORS - 1) will + * cause the function to fail. + * + * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will + * be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer + * are NULL then the task will not be created and + * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. + * + * \ingroup Tasks + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const pxStackBuffer, + StaticTask_t * const pxTaskBuffer, + const BaseType_t xCoreID ); +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** * Create a new task and add it to the list of tasks that are ready to run. * * Internally, within the FreeRTOS implementation, tasks use two blocks of @@ -442,77 +531,75 @@ is used in assert() statements. */ * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. * * Example usage: -
-
-    // Dimensions the buffer that the task being created will use as its stack.
-    // NOTE:  This is the number of words the stack will hold, not the number of
-    // bytes.  For example, if each stack item is 32-bits, and this is set to 100,
-    // then 400 bytes (100 * 32-bits) will be allocated.
-    #define STACK_SIZE 200
-
-    // Structure that will hold the TCB of the task being created.
-    StaticTask_t xTaskBuffer;
-
-    // Buffer that the task being created will use as its stack.  Note this is
-    // an array of StackType_t variables.  The size of StackType_t is dependent on
-    // the RTOS port.
-    StackType_t xStack[ STACK_SIZE ];
-
-    // Function that implements the task being created.
-    void vTaskCode( void * pvParameters )
-    {
-        // The parameter value is expected to be 1 as 1 is passed in the
-        // pvParameters value in the call to xTaskCreateStatic().
-        configASSERT( ( uint32_t ) pvParameters == 1UL );
-
-        for( ;; )
-        {
-            // Task code goes here.
-        }
-    }
-
-    // Function that creates a task.
-    void vOtherFunction( void )
-    {
-        TaskHandle_t xHandle = NULL;
-
-        // Create the task without using any dynamic memory allocation.
-        xHandle = xTaskCreateStatic(
-                      vTaskCode,       // Function that implements the task.
-                      "NAME",          // Text name for the task.
-                      STACK_SIZE,      // Stack size in words, not bytes.
-                      ( void * ) 1,    // Parameter passed into the task.
-                      tskIDLE_PRIORITY,// Priority at which the task is created.
-                      xStack,          // Array to use as the task's stack.
-                      &xTaskBuffer );  // Variable to hold the task's data structure.
-
-        // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
-        // been created, and xHandle will be the task's handle.  Use the handle
-        // to suspend the task.
-        vTaskSuspend( xHandle );
-    }
-   
- * \defgroup xTaskCreateStatic xTaskCreateStatic + * @code{c} + * + * // Dimensions the buffer that the task being created will use as its stack. + * // NOTE: This is the number of words the stack will hold, not the number of + * // bytes. For example, if each stack item is 32-bits, and this is set to 100, + * // then 400 bytes (100 * 32-bits) will be allocated. + * #define STACK_SIZE 200 + * + * // Structure that will hold the TCB of the task being created. + * StaticTask_t xTaskBuffer; + * + * // Buffer that the task being created will use as its stack. Note this is + * // an array of StackType_t variables. The size of StackType_t is dependent on + * // the RTOS port. + * StackType_t xStack[ STACK_SIZE ]; + * + * // Function that implements the task being created. + * void vTaskCode( void * pvParameters ) + * { + * // The parameter value is expected to be 1 as 1 is passed in the + * // pvParameters value in the call to xTaskCreateStatic(). + * configASSERT( ( uint32_t ) pvParameters == 1UL ); + * + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle = NULL; + * + * // Create the task without using any dynamic memory allocation. + * xHandle = xTaskCreateStatic( + * vTaskCode, // Function that implements the task. + * "NAME", // Text name for the task. + * STACK_SIZE, // Stack size in words, not bytes. + * ( void * ) 1, // Parameter passed into the task. + * tskIDLE_PRIORITY,// Priority at which the task is created. + * xStack, // Array to use as the task's stack. + * &xTaskBuffer ); // Variable to hold the task's data structure. + * + * // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have + * // been created, and xHandle will be the task's handle. Use the handle + * // to suspend the task. + * vTaskSuspend( xHandle ); + * } + * @endcode * \ingroup Tasks */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint32_t ulStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - StackType_t * const puxStackBuffer, - StaticTask_t * const pxTaskBuffer, - const BaseType_t xCoreID ); -#define xTaskCreateStatic( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxStackBuffer, pxTaskBuffer ) xTaskCreateStaticPinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxStackBuffer ), ( pxTaskBuffer ), tskNO_AFFINITY ) +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + static inline IRAM_ATTR TaskHandle_t xTaskCreateStatic( + TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const pxStackBuffer, + StaticTask_t * const pxTaskBuffer) + { + return xTaskCreateStaticPinnedToCore( pvTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, pxStackBuffer, pxTaskBuffer, tskNO_AFFINITY ); + } #endif /* configSUPPORT_STATIC_ALLOCATION */ +/** @cond */ /** - * task. h - *
- BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
- * * xTaskCreateRestricted() should only be used in systems that include an MPU * implementation. * @@ -532,58 +619,54 @@ is used in assert() statements. */ * list, otherwise an error code defined in the file projdefs.h * * Example usage: -
-// Create an TaskParameters_t structure that defines the task to be created.
-static const TaskParameters_t xCheckTaskParameters =
-{
-	vATask,		// pvTaskCode - the function that implements the task.
-	"ATask",	// pcName - just a text name for the task to assist debugging.
-	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
-	NULL,		// pvParameters - passed into the task function as the function parameters.
-	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
-	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
-
-	// xRegions - Allocate up to three separate memory regions for access by
-	// the task, with appropriate access permissions.  Different processors have
-	// different memory alignment requirements - refer to the FreeRTOS documentation
-	// for full information.
-	{
-		// Base address					Length	Parameters
-        { cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
-        { cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
-        { cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
-	}
-};
-
-int main( void )
-{
-TaskHandle_t xHandle;
-
-	// Create a task from the const structure defined above.  The task handle
-	// is requested (the second parameter is not NULL) but in this case just for
-	// demonstration purposes as its not actually used.
-	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
-
-	// Start the scheduler.
-	vTaskStartScheduler();
-
-	// Will only get here if there was insufficient memory to create the idle
-	// and/or timer task.
-	for( ;; );
-}
-   
- * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * @code{c} + * // Create an TaskParameters_t structure that defines the task to be created. + * static const TaskParameters_t xCheckTaskParameters = + * { + * vATask, // pvTaskCode - the function that implements the task. + * "ATask", // pcName - just a text name for the task to assist debugging. + * 100, // usStackDepth - the stack size DEFINED IN WORDS. + * NULL, // pvParameters - passed into the task function as the function parameters. + * ( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state. + * cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack. + * + * // xRegions - Allocate up to three separate memory regions for access by + * // the task, with appropriate access permissions. Different processors have + * // different memory alignment requirements - refer to the FreeRTOS documentation + * // for full information. + * { + * // Base address Length Parameters + * { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + * { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + * { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } + * } + * }; + * + * int main( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task from the const structure defined above. The task handle + * // is requested (the second parameter is not NULL) but in this case just for + * // demonstration purposes as its not actually used. + * xTaskCreateRestricted( &xRegTest1Parameters, &xHandle ); + * + * // Start the scheduler. + * vTaskStartScheduler(); + * + * // Will only get here if there was insufficient memory to create the idle + * // and/or timer task. + * for( ;; ); + * } + * @endcode * \ingroup Tasks */ #if( portUSING_MPU_WRAPPERS == 1 ) BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; #endif + /** - * task. h - *
- void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions );
- * * Memory regions are assigned to a restricted task when the task is created by * a call to xTaskCreateRestricted(). These regions can be redefined using * vTaskAllocateMPURegions(). @@ -594,50 +677,51 @@ TaskHandle_t xHandle; * new memory region definitions. * * Example usage: -
-// Define an array of MemoryRegion_t structures that configures an MPU region
-// allowing read/write access for 1024 bytes starting at the beginning of the
-// ucOneKByte array.  The other two of the maximum 3 definable regions are
-// unused so set to zero.
-static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
-{
-	// Base address		Length		Parameters
-	{ ucOneKByte,		1024,		portMPU_REGION_READ_WRITE },
-	{ 0,				0,			0 },
-	{ 0,				0,			0 }
-};
-
-void vATask( void *pvParameters )
-{
-	// This task was created such that it has access to certain regions of
-	// memory as defined by the MPU configuration.  At some point it is
-	// desired that these MPU regions are replaced with that defined in the
-	// xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
-	// for this purpose.  NULL is used as the task handle to indicate that this
-	// function should modify the MPU regions of the calling task.
-	vTaskAllocateMPURegions( NULL, xAltRegions );
-
-	// Now the task can continue its function, but from this point on can only
-	// access its stack and the ucOneKByte array (unless any other statically
-	// defined or shared regions have been declared elsewhere).
-}
-   
- * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * + * @code{c} + * // Define an array of MemoryRegion_t structures that configures an MPU region + * // allowing read/write access for 1024 bytes starting at the beginning of the + * // ucOneKByte array. The other two of the maximum 3 definable regions are + * // unused so set to zero. + * static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] = + * { + * // Base address Length Parameters + * { ucOneKByte, 1024, portMPU_REGION_READ_WRITE }, + * { 0, 0, 0 }, + * { 0, 0, 0 } + * }; + * + * void vATask( void *pvParameters ) + * { + * // This task was created such that it has access to certain regions of + * // memory as defined by the MPU configuration. At some point it is + * // desired that these MPU regions are replaced with that defined in the + * // xAltRegions const struct above. Use a call to vTaskAllocateMPURegions() + * // for this purpose. NULL is used as the task handle to indicate that this + * // function should modify the MPU regions of the calling task. + * vTaskAllocateMPURegions( NULL, xAltRegions ); + * + * // Now the task can continue its function, but from this point on can only + * // access its stack and the ucOneKByte array (unless any other statically + * // defined or shared regions have been declared elsewhere). + * } + * @endcode * \ingroup Tasks */ void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; +/** @endcond */ + /** - * task. h - *
void vTaskDelete( TaskHandle_t xTask );
+ * Remove a task from the RTOS real time kernel's management. + * + * The task being deleted will be removed from all ready, blocked, suspended + * and event lists. * * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Remove a task from the RTOS real time kernel's management. The task being - * deleted will be removed from all ready, blocked, suspended and event lists. - * - * NOTE: The idle task is responsible for freeing the kernel allocated + * @note The idle task is responsible for freeing the kernel allocated * memory from tasks that have been deleted. It is therefore important that * the idle task is not starved of microcontroller processing time if your * application makes any calls to vTaskDelete (). Memory allocated by the @@ -647,23 +731,22 @@ void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const p * See the demo application file death.c for sample code that utilises * vTaskDelete (). * - * @param xTask The handle of the task to be deleted. Passing NULL will + * @param xTaskToDelete The handle of the task to be deleted. Passing NULL will * cause the calling task to be deleted. * * Example usage: -
- void vOtherFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create the task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // Use the handle to delete the task.
-	 vTaskDelete( xHandle );
- }
-   
- * \defgroup vTaskDelete vTaskDelete + * @code{c} + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create the task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // Use the handle to delete the task. + * vTaskDelete( xHandle ); + * } + * @endcode * \ingroup Tasks */ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; @@ -673,18 +756,15 @@ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; *----------------------------------------------------------*/ /** - * task. h - *
void vTaskDelay( const TickType_t xTicksToDelay );
+ * Delay a task for a given number of ticks. * - * Delay a task for a given number of ticks. The actual time that the - * task remains blocked depends on the tick rate. The constant - * portTICK_PERIOD_MS can be used to calculate real time from the tick - * rate - with the resolution of one tick period. + * The actual time that the task remains blocked depends on the tick rate. + * The constant portTICK_PERIOD_MS can be used to calculate real time from + * the tick rate - with the resolution of one tick period. * * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. * See the configuration section for more information. * - * * vTaskDelay() specifies a time at which the task wishes to unblock relative to * the time at which vTaskDelay() is called. For example, specifying a block * period of 100 ticks will cause the task to unblock 100 ticks after @@ -701,34 +781,31 @@ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; * the calling task should block. * * Example usage: - - void vTaskFunction( void * pvParameters ) - { - // Block for 500ms. - const TickType_t xDelay = 500 / portTICK_PERIOD_MS; - - for( ;; ) - { - // Simply toggle the LED every 500ms, blocking between each toggle. - vToggleLED(); - vTaskDelay( xDelay ); - } - } - - * \defgroup vTaskDelay vTaskDelay + * @code{c} + * void vTaskFunction( void * pvParameters ) + * { + * // Block for 500ms. + * const TickType_t xDelay = 500 / portTICK_PERIOD_MS; + * + * for( ;; ) + * { + * // Simply toggle the LED every 500ms, blocking between each toggle. + * vToggleLED(); + * vTaskDelay( xDelay ); + * } + * } + * @endcode * \ingroup TaskCtrl */ void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
+ * Delay a task until a specified time. * * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Delay a task until a specified time. This function can be used by periodic - * tasks to ensure a constant execution frequency. + * This function can be used by periodic tasks to ensure a constant execution frequency. * * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will * cause a task to block for the specified number of ticks from the time vTaskDelay () is @@ -756,94 +833,90 @@ void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; * a fixed interface period. * * Example usage: -
- // Perform an action every 10 ticks.
- void vTaskFunction( void * pvParameters )
- {
- TickType_t xLastWakeTime;
- const TickType_t xFrequency = 10;
-
-	 // Initialise the xLastWakeTime variable with the current time.
-	 xLastWakeTime = xTaskGetTickCount ();
-	 for( ;; )
-	 {
-		 // Wait for the next cycle.
-		 vTaskDelayUntil( &xLastWakeTime, xFrequency );
-
-		 // Perform action here.
-	 }
- }
-   
- * \defgroup vTaskDelayUntil vTaskDelayUntil + * @code{c} + * // Perform an action every 10 ticks. + * void vTaskFunction( void * pvParameters ) + * { + * TickType_t xLastWakeTime; + * const TickType_t xFrequency = 10; + * + * // Initialise the xLastWakeTime variable with the current time. + * xLastWakeTime = xTaskGetTickCount (); + * for( ;; ) + * { + * // Wait for the next cycle. + * vTaskDelayUntil( &xLastWakeTime, xFrequency ); + * + * // Perform action here. + * } + * } + * @endcode * \ingroup TaskCtrl */ void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; /** - * task. h - *
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );
+ * Obtain the priority of any task. * * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Obtain the priority of any task. - * * @param xTask Handle of the task to be queried. Passing a NULL * handle results in the priority of the calling task being returned. * * @return The priority of xTask. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to obtain the priority of the created task.
-	 // It was created with tskIDLE_PRIORITY, but may have changed
-	 // it itself.
-	 if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
-	 {
-		 // The task has changed it's priority.
-	 }
-
-	 // ...
-
-	 // Is our priority higher than the created task?
-	 if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
-	 {
-		 // Our priority (obtained using NULL handle) is higher.
-	 }
- }
-   
- * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to obtain the priority of the created task. + * // It was created with tskIDLE_PRIORITY, but may have changed + * // it itself. + * if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY ) + * { + * // The task has changed it's priority. + * } + * + * // ... + * + * // Is our priority higher than the created task? + * if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) ) + * { + * // Our priority (obtained using NULL handle) is higher. + * } + * } + * @endcode * \ingroup TaskCtrl */ UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task. h - *
UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask );
- * * A version of uxTaskPriorityGet() that can be used from an ISR. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of xTask. + * */ UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task. h - *
eTaskState eTaskGetState( TaskHandle_t xTask );
+ * Obtain the state of any task. + * + * States are encoded by the eTaskState enumerated type. * * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Obtain the state of any task. States are encoded by the eTaskState - * enumerated type. - * * @param xTask Handle of the task to be queried. * * @return The state of xTask at the time the function was called. Note the @@ -853,14 +926,11 @@ UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
+ * Set the priority of any task. * * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Set the priority of any task. - * * A context switch will occur before the function returns if the priority * being set is higher than the currently executing task. * @@ -870,39 +940,37 @@ eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; * @param uxNewPriority The priority to which the task will be set. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to raise the priority of the created task.
-	 vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
-
-	 // ...
-
-	 // Use a NULL handle to raise our priority to the same value.
-	 vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
- }
-   
- * \defgroup vTaskPrioritySet vTaskPrioritySet + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to raise the priority of the created task. + * vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 ); + * + * // ... + * + * // Use a NULL handle to raise our priority to the same value. + * vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 ); + * } + * @endcode * \ingroup TaskCtrl */ void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
+ * Suspend a task. * * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Suspend any task. When suspended a task will never get any microcontroller - * processing time, no matter what its priority. + * When suspended, a task will never get any microcontroller processing time, + * no matter what its priority. * * Calls to vTaskSuspend are not accumulative - * i.e. calling vTaskSuspend () twice on the same task still only requires one @@ -912,48 +980,44 @@ void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGE * handle will cause the calling task to be suspended. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to suspend the created task.
-	 vTaskSuspend( xHandle );
-
-	 // ...
-
-	 // The created task will not run during this period, unless
-	 // another task calls vTaskResume( xHandle ).
-
-	 //...
-
-
-	 // Suspend ourselves.
-	 vTaskSuspend( NULL );
-
-	 // We cannot get here unless another task calls vTaskResume
-	 // with our handle as the parameter.
- }
-   
- * \defgroup vTaskSuspend vTaskSuspend + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Suspend ourselves. + * vTaskSuspend( NULL ); + * + * // We cannot get here unless another task calls vTaskResume + * // with our handle as the parameter. + * } + * @endcode * \ingroup TaskCtrl */ void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskResume( TaskHandle_t xTaskToResume );
+ * Resumes a suspended task. * * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Resumes a suspended task. - * * A task that has been suspended by one or more calls to vTaskSuspend () * will be made available for running again by a single call to * vTaskResume (). @@ -961,48 +1025,44 @@ void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; * @param xTaskToResume Handle to the task being readied. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to suspend the created task.
-	 vTaskSuspend( xHandle );
-
-	 // ...
-
-	 // The created task will not run during this period, unless
-	 // another task calls vTaskResume( xHandle ).
-
-	 //...
-
-
-	 // Resume the suspended task ourselves.
-	 vTaskResume( xHandle );
-
-	 // The created task will once again get microcontroller processing
-	 // time in accordance with its priority within the system.
- }
-   
- * \defgroup vTaskResume vTaskResume + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Resume the suspended task ourselves. + * vTaskResume( xHandle ); + * + * // The created task will once again get microcontroller processing + * // time in accordance with its priority within the system. + * } + * @endcode * \ingroup TaskCtrl */ void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /** - * task. h - *
void xTaskResumeFromISR( TaskHandle_t xTaskToResume );
+ * An implementation of vTaskResume() that can be called from within an ISR. * * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be * available. See the configuration section for more information. * - * An implementation of vTaskResume() that can be called from within an ISR. - * * A task that has been suspended by one or more calls to vTaskSuspend () * will be made available for running again by a single call to * xTaskResumeFromISR (). @@ -1018,7 +1078,6 @@ void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; * otherwise pdFALSE. This is used by the ISR to determine if a context switch * may be required following the ISR. * - * \defgroup vTaskResumeFromISR vTaskResumeFromISR * \ingroup TaskCtrl */ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; @@ -1026,46 +1085,42 @@ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * SCHEDULER CONTROL *----------------------------------------------------------*/ - +/** @cond */ /** - * task. h - *
void vTaskStartScheduler( void );
+ * Starts the real time kernel tick processing. * - * Starts the real time kernel tick processing. After calling the kernel - * has control over which tasks are executed and when. + * After calling the kernel has control over which tasks are executed and when. * * See the demo application file main.c for an example of creating * tasks and starting the kernel. * * Example usage: -
- void vAFunction( void )
- {
-	 // Create at least one task before starting the kernel.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
-
-	 // Start the real time kernel with preemption.
-	 vTaskStartScheduler ();
-
-	 // Will not get here unless a task calls vTaskEndScheduler ()
- }
-   
+ * @code{c} + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will not get here unless a task calls vTaskEndScheduler () + * } + * @endcode * - * \defgroup vTaskStartScheduler vTaskStartScheduler * \ingroup SchedulerControl */ void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskEndScheduler( void );
+ * Stops the real time kernel tick. * - * NOTE: At the time of writing only the x86 real mode port, which runs on a PC + * @note At the time of writing only the x86 real mode port, which runs on a PC * in place of DOS, implements this function. * - * Stops the real time kernel tick. All created tasks will be automatically - * deleted and multitasking (either preemptive or cooperative) will - * stop. Execution then resumes from the point where vTaskStartScheduler () + * All created tasks will be automatically deleted and multitasking + * (either preemptive or cooperative) will stop. + * Execution then resumes from the point where vTaskStartScheduler () * was called, as if vTaskStartScheduler () had just returned. * * See the demo application file main. c in the demo/PC directory for an @@ -1080,44 +1135,42 @@ void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; * tasks. * * Example usage: -
- void vTaskCode( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // At some point we want to end the real time kernel processing
-		 // so call ...
-		 vTaskEndScheduler ();
-	 }
- }
-
- void vAFunction( void )
- {
-	 // Create at least one task before starting the kernel.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
-
-	 // Start the real time kernel with preemption.
-	 vTaskStartScheduler ();
-
-	 // Will only get here when the vTaskCode () task has called
-	 // vTaskEndScheduler ().  When we get here we are back to single task
-	 // execution.
- }
-   
+ * @code{c} + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. * - * \defgroup vTaskEndScheduler vTaskEndScheduler + * // At some point we want to end the real time kernel processing + * // so call ... + * vTaskEndScheduler (); + * } + * } + * + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will only get here when the vTaskCode () task has called + * // vTaskEndScheduler (). When we get here we are back to single task + * // execution. + * } + * @endcode * \ingroup SchedulerControl */ void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; +/** @endcond */ + /** - * task. h - *
void vTaskSuspendAll( void );
+ * Suspends the scheduler without disabling interrupts. * - * Suspends the scheduler without disabling interrupts. Context switches will - * not occur while the scheduler is suspended. + * Context switches will not occur while the scheduler is suspended. * * After calling vTaskSuspendAll () the calling task will continue to execute * without risk of being swapped out until a call to xTaskResumeAll () has been @@ -1128,45 +1181,41 @@ void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; * is suspended. * * Example usage: -
- void vTask1( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // ...
-
-		 // At some point the task wants to perform a long operation during
-		 // which it does not want to get swapped out.  It cannot use
-		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
-		 // operation may cause interrupts to be missed - including the
-		 // ticks.
-
-		 // Prevent the real time kernel swapping out the task.
-		 vTaskSuspendAll ();
-
-		 // Perform the operation here.  There is no need to use critical
-		 // sections as we have all the microcontroller processing time.
-		 // During this time interrupts will still operate and the kernel
-		 // tick count will be maintained.
-
-		 // ...
-
-		 // The operation is complete.  Restart the kernel.
-		 xTaskResumeAll ();
-	 }
- }
-   
- * \defgroup vTaskSuspendAll vTaskSuspendAll + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the kernel + * // tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. + * xTaskResumeAll (); + * } + * } + * @endcode * \ingroup SchedulerControl */ void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
BaseType_t xTaskResumeAll( void );
- * * Resumes scheduler activity after it was suspended by a call to * vTaskSuspendAll(). * @@ -1177,42 +1226,41 @@ void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; * returned, otherwise pdFALSE is returned. * * Example usage: -
- void vTask1( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // ...
-
-		 // At some point the task wants to perform a long operation during
-		 // which it does not want to get swapped out.  It cannot use
-		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
-		 // operation may cause interrupts to be missed - including the
-		 // ticks.
-
-		 // Prevent the real time kernel swapping out the task.
-		 vTaskSuspendAll ();
-
-		 // Perform the operation here.  There is no need to use critical
-		 // sections as we have all the microcontroller processing time.
-		 // During this time interrupts will still operate and the real
-		 // time kernel tick count will be maintained.
-
-		 // ...
-
-		 // The operation is complete.  Restart the kernel.  We want to force
-		 // a context switch - but there is no point if resuming the scheduler
-		 // caused a context switch already.
-		 if( !xTaskResumeAll () )
-		 {
-			  taskYIELD ();
-		 }
-	 }
- }
-   
- * \defgroup xTaskResumeAll xTaskResumeAll + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the real + * // time kernel tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. We want to force + * // a context switch - but there is no point if resuming the scheduler + * // caused a context switch already. + * if( !xTaskResumeAll () ) + * { + * taskYIELD (); + * } + * } + * } + * @endcode * \ingroup SchedulerControl */ BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; @@ -1222,19 +1270,16 @@ BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; *----------------------------------------------------------*/ /** - * task. h - *
TickType_t xTaskGetTickCount( void );
+ * Get tick count * * @return The count of ticks since vTaskStartScheduler was called. * - * \defgroup xTaskGetTickCount xTaskGetTickCount * \ingroup TaskUtils */ TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
TickType_t xTaskGetTickCountFromISR( void );
+ * Get tick count from ISR * * @return The count of ticks since vTaskStartScheduler was called. * @@ -1243,50 +1288,43 @@ TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; * microcontroller being used or interrupt nesting is either not supported or * not being used. * - * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR * \ingroup TaskUtils */ TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
UBaseType_t uxTaskGetNumberOfTasks( void );
+ * Get current number of tasks * * @return The number of tasks that the real time kernel is currently managing. * This includes all ready, blocked and suspended tasks. A task that * has been deleted but not yet freed by the idle task will also be * included in the count. * - * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks * \ingroup TaskUtils */ UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery );
+ * Get task name * * @return The text (human readable) name of the task referenced by the handle * xTaskToQuery. A task can query its own name by either passing in its own * handle, or by setting xTaskToQuery to NULL. INCLUDE_pcTaskGetTaskName must be * set to 1 in FreeRTOSConfig.h for pcTaskGetTaskName() to be available. * - * \defgroup pcTaskGetTaskName pcTaskGetTaskName * \ingroup TaskUtils */ char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** - * task.h - *
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
+ * Returns the high water mark of the stack associated with xTask. * * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for * this function to be available. * - * Returns the high water mark of the stack associated with xTask. That is, - * the minimum free stack space there has been (in words, so on a 32 bit machine - * a value of 1 means 4 bytes) since the task started. The smaller the returned - * number the closer the task has come to overflowing its stack. + * High water mark is the minimum free stack space there has been (in words, + * so on a 32 bit machine a value of 1 means 4 bytes) since the task started. + * The smaller the returned number the closer the task has come to overflowing its stack. * * @param xTask Handle of the task associated with the stack to be checked. * Set xTask to NULL to check the stack of the calling task. @@ -1298,14 +1336,12 @@ char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task.h - *
uint8_t* pxTaskGetStackStart( TaskHandle_t xTask);
+ * Returns the start of the stack associated with xTask. * * INCLUDE_pxTaskGetStackStart must be set to 1 in FreeRTOSConfig.h for * this function to be available. * - * Returns the start of the stack associated with xTask. That is, - * the highest stack memory address on architectures where the stack grows down + * Returns the highest stack memory address on architectures where the stack grows down * from high memory, and the lowest memory address on architectures where the * stack grows up from low memory. * @@ -1325,73 +1361,130 @@ constant. */ #ifdef configUSE_APPLICATION_TASK_TAG #if configUSE_APPLICATION_TASK_TAG == 1 /** - * task.h - *
void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction );
- * * Sets pxHookFunction to be the task hook function used by the task xTask. - * Passing xTask as NULL has the effect of setting the calling tasks hook - * function. + * @param xTask Handle of the task to set the hook function for + * Passing xTask as NULL has the effect of setting the calling + * tasks hook function. + * @param pxHookFunction Pointer to the hook function. */ void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; /** - * task.h - *
void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
- * - * Returns the pxHookFunction value assigned to the task xTask. + * Get the hook function assigned to given task. + * @param xTask Handle of the task to get the hook function for + * Passing xTask as NULL has the effect of getting the calling + * tasks hook function. + * @return The pxHookFunction value assigned to the task xTask. */ TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ #endif /* ifdef configUSE_APPLICATION_TASK_TAG */ #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) - /* Each task contains an array of pointers that is dimensioned by the - configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. The - kernel does not use the pointers itself, so the application writer can use - the pointers for any purpose they wish. The following two functions are - used to set and query a pointer respectively. */ + /** + * Set local storage pointer specific to the given task. + * + * Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. + * The kernel does not use the pointers itself, so the application writer + * can use the pointers for any purpose they wish. + * + * @param xTaskToSet Task to set thread local storage pointer for + * @param xIndex The index of the pointer to set, from 0 to + * configNUM_THREAD_LOCAL_STORAGE_POINTERS - 1. + * @param pvValue Pointer value to set. + */ void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) PRIVILEGED_FUNCTION; + + + /** + * Get local storage pointer specific to the given task. + * + * Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. + * The kernel does not use the pointers itself, so the application writer + * can use the pointers for any purpose they wish. + * + * @param xTaskToQuery Task to get thread local storage pointer for + * @param xIndex The index of the pointer to get, from 0 to + * configNUM_THREAD_LOCAL_STORAGE_POINTERS - 1. + * @return Pointer value + */ void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) PRIVILEGED_FUNCTION; - #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) - typedef void (*TlsDeleteCallbackFunction_t)( int, void * ); - void vTaskSetThreadLocalStoragePointerAndDelCallback( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue , TlsDeleteCallbackFunction_t xDelCallback); + #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + + /** + * Prototype of local storage pointer deletion callback. + */ + typedef void (*TlsDeleteCallbackFunction_t)( int, void * ); + + /** + * Set local storage pointer and deletion callback. + * + * Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. + * The kernel does not use the pointers itself, so the application writer + * can use the pointers for any purpose they wish. + * + * Local storage pointers set for a task can reference dynamically + * allocated resources. This function is similar to + * vTaskSetThreadLocalStoragePointer, but provides a way to release + * these resources when the task gets deleted. For each pointer, + * a callback function can be set. This function will be called + * when task is deleted, with the local storage pointer index + * and value as arguments. + * + * @param xTaskToSet Task to set thread local storage pointer for + * @param xIndex The index of the pointer to set, from 0 to + * configNUM_THREAD_LOCAL_STORAGE_POINTERS - 1. + * @param pvValue Pointer value to set. + * @param pvDelCallback Function to call to dispose of the local + * storage pointer when the task is deleted. + */ + void vTaskSetThreadLocalStoragePointerAndDelCallback( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue, TlsDeleteCallbackFunction_t pvDelCallback); #endif #endif /** - * task.h - *
BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter );
- * - * Calls the hook function associated with xTask. Passing xTask as NULL has + * Calls the hook function associated with xTask. Passing xTask as NULL has * the effect of calling the Running tasks (the calling task) hook function. * - * pvParameter is passed to the hook function for the task to interpret as it + * @param xTask Handle of the task to call the hook for. + * @param pvParameter Parameter passed to the hook function for the task to interpret as it * wants. The return value is the value returned by the task hook function * registered by the user. */ BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) PRIVILEGED_FUNCTION; /** + * Get the handle of idle task for the current CPU. + * * xTaskGetIdleTaskHandle() is only available if * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. * - * Simply returns the handle of the idle task. It is not valid to call + * @return The handle of the idle task. It is not valid to call * xTaskGetIdleTaskHandle() before the scheduler has been started. */ TaskHandle_t xTaskGetIdleTaskHandle( void ); /** + * Get the handle of idle task for the given CPU. + * * xTaskGetIdleTaskHandleForCPU() is only available if * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. * - * Simply returns the idle task handle of a given cpu. It is not valid to call + * @param cpuid The CPU to get the handle for + * + * @return Idle task handle of a given cpu. It is not valid to call * xTaskGetIdleTaskHandleForCPU() before the scheduler has been started. */ TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid ); /** + * Get the state of tasks in the system. + * * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for * uxTaskGetSystemState() to be available. * @@ -1401,7 +1494,7 @@ TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid ); * of run time consumed by the task. See the TaskStatus_t structure * definition in this file for the full member list. * - * NOTE: This function is intended for debugging use only as its use results in + * @note This function is intended for debugging use only as its use results in * the scheduler remaining suspended for an extended period. * * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. @@ -1426,79 +1519,78 @@ TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid ); * in the uxArraySize parameter was too small. * * Example usage: -
-    // This example demonstrates how a human readable table of run time stats
-	// information is generated from raw data provided by uxTaskGetSystemState().
-	// The human readable table is written to pcWriteBuffer
-	void vTaskGetRunTimeStats( char *pcWriteBuffer )
-	{
-	TaskStatus_t *pxTaskStatusArray;
-	volatile UBaseType_t uxArraySize, x;
-	uint32_t ulTotalRunTime, ulStatsAsPercentage;
-
-		// Make sure the write buffer does not contain a string.
-		*pcWriteBuffer = 0x00;
-
-		// Take a snapshot of the number of tasks in case it changes while this
-		// function is executing.
-		uxArraySize = uxTaskGetNumberOfTasks();
-
-		// Allocate a TaskStatus_t structure for each task.  An array could be
-		// allocated statically at compile time.
-		pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
-
-		if( pxTaskStatusArray != NULL )
-		{
-			// Generate raw status information about each task.
-			uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
-
-			// For percentage calculations.
-			ulTotalRunTime /= 100UL;
-
-			// Avoid divide by zero errors.
-			if( ulTotalRunTime > 0 )
-			{
-				// For each populated position in the pxTaskStatusArray array,
-				// format the raw data as human readable ASCII data
-				for( x = 0; x < uxArraySize; x++ )
-				{
-					// What percentage of the total run time has the task used?
-					// This will always be rounded down to the nearest integer.
-					// ulTotalRunTimeDiv100 has already been divided by 100.
-					ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime;
-
-					if( ulStatsAsPercentage > 0UL )
-					{
-						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
-					}
-					else
-					{
-						// If the percentage is zero here then the task has
-						// consumed less than 1% of the total run time.
-						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter );
-					}
-
-					pcWriteBuffer += strlen( ( char * ) pcWriteBuffer );
-				}
-			}
-
-			// The array is no longer needed, free the memory it consumes.
-			vPortFree( pxTaskStatusArray );
-		}
-	}
-	
+ * @code{c} + * // This example demonstrates how a human readable table of run time stats + * // information is generated from raw data provided by uxTaskGetSystemState(). + * // The human readable table is written to pcWriteBuffer + * void vTaskGetRunTimeStats( char *pcWriteBuffer ) + * { + * TaskStatus_t *pxTaskStatusArray; + * volatile UBaseType_t uxArraySize, x; + * uint32_t ulTotalRunTime, ulStatsAsPercentage; + * + * // Make sure the write buffer does not contain a string. + * *pcWriteBuffer = 0x00; + * + * // Take a snapshot of the number of tasks in case it changes while this + * // function is executing. + * uxArraySize = uxTaskGetNumberOfTasks(); + * + * // Allocate a TaskStatus_t structure for each task. An array could be + * // allocated statically at compile time. + * pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) ); + * + * if( pxTaskStatusArray != NULL ) + * { + * // Generate raw status information about each task. + * uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime ); + * + * // For percentage calculations. + * ulTotalRunTime /= 100UL; + * + * // Avoid divide by zero errors. + * if( ulTotalRunTime > 0 ) + * { + * // For each populated position in the pxTaskStatusArray array, + * // format the raw data as human readable ASCII data + * for( x = 0; x < uxArraySize; x++ ) + * { + * // What percentage of the total run time has the task used? + * // This will always be rounded down to the nearest integer. + * // ulTotalRunTimeDiv100 has already been divided by 100. + * ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime; + * + * if( ulStatsAsPercentage > 0UL ) + * { + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); + * } + * else + * { + * // If the percentage is zero here then the task has + * // consumed less than 1% of the total run time. + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter ); + * } + * + * pcWriteBuffer += strlen( ( char * ) pcWriteBuffer ); + * } + * } + * + * // The array is no longer needed, free the memory it consumes. + * vPortFree( pxTaskStatusArray ); + * } + * } + * @endcode */ UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ); /** - * task. h - *
void vTaskList( char *pcWriteBuffer );
+ * List all the current tasks. * * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must * both be defined as 1 for this function to be available. See the * configuration section of the FreeRTOS.org website for more information. * - * NOTE 1: This function will disable interrupts for its duration. It is + * @note This function will disable interrupts for its duration. It is * not intended for normal application runtime use but as a debug aid. * * Lists all the current tasks, along with their current state and stack @@ -1507,9 +1599,7 @@ UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or * suspended ('S'). * - * PLEASE NOTE: - * - * This function is provided for convenience only, and is used by many of the + * @note This function is provided for convenience only, and is used by many of the * demo applications. Do not consider it to be part of the scheduler. * * vTaskList() calls uxTaskGetSystemState(), then formats part of the @@ -1532,14 +1622,12 @@ UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const * enough to contain the generated report. Approximately 40 bytes per * task should be sufficient. * - * \defgroup vTaskList vTaskList * \ingroup TaskUtils */ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** - * task. h - *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
+ * Get the state of running tasks as a string * * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS * must both be defined as 1 for this function to be available. The application @@ -1549,7 +1637,7 @@ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unquali * value respectively. The counter should be at least 10 times the frequency of * the tick count. * - * NOTE 1: This function will disable interrupts for its duration. It is + * @note This function will disable interrupts for its duration. It is * not intended for normal application runtime use but as a debug aid. * * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total @@ -1560,9 +1648,7 @@ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unquali * task into a buffer, both as an absolute count value and as a percentage * of the total system execution time. * - * NOTE 2: - * - * This function is provided for convenience only, and is used by many of the + * @note This function is provided for convenience only, and is used by many of the * demo applications. Do not consider it to be part of the scheduler. * * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part of the @@ -1586,14 +1672,12 @@ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unquali * contain the generated report. Approximately 40 bytes per task should * be sufficient. * - * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats * \ingroup TaskUtils */ void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** - * task. h - *
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
+ * Send task notification. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1634,43 +1718,40 @@ void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e9 * * @param eAction Specifies how the notification updates the task's notification * value, if at all. Valid values for eAction are as follows: + * - eSetBits: + * The task's notification value is bitwise ORed with ulValue. xTaskNofify() + * always returns pdPASS in this case. * - * eSetBits - - * The task's notification value is bitwise ORed with ulValue. xTaskNofify() - * always returns pdPASS in this case. + * - eIncrement: + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. * - * eIncrement - - * The task's notification value is incremented. ulValue is not used and - * xTaskNotify() always returns pdPASS in this case. + * - eSetValueWithOverwrite: + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. * - * eSetValueWithOverwrite - - * The task's notification value is set to the value of ulValue, even if the - * task being notified had not yet processed the previous notification (the - * task already had a notification pending). xTaskNotify() always returns - * pdPASS in this case. + * - eSetValueWithoutOverwrite: + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. * - * eSetValueWithoutOverwrite - - * If the task being notified did not already have a notification pending then - * the task's notification value is set to ulValue and xTaskNotify() will - * return pdPASS. If the task being notified already had a notification - * pending then no action is performed and pdFAIL is returned. - * - * eNoAction - - * The task receives a notification without its notification value being - * updated. ulValue is not used and xTaskNotify() always returns pdPASS in - * this case. + * - eNoAction: + * The task receives a notification without its notification value being + *   updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. * * @return Dependent on the value of eAction. See the description of the * eAction parameter. * - * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction ); /** - * task. h - *
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );
+ * Send task notification from an ISR. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1714,31 +1795,30 @@ BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAct * * @param eAction Specifies how the notification updates the task's notification * value, if at all. Valid values for eAction are as follows: + * - eSetBits: + * The task's notification value is bitwise ORed with ulValue. xTaskNofify() + * always returns pdPASS in this case. * - * eSetBits - - * The task's notification value is bitwise ORed with ulValue. xTaskNofify() - * always returns pdPASS in this case. + * - eIncrement: + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. * - * eIncrement - - * The task's notification value is incremented. ulValue is not used and - * xTaskNotify() always returns pdPASS in this case. + * - eSetValueWithOverwrite: + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. * - * eSetValueWithOverwrite - - * The task's notification value is set to the value of ulValue, even if the - * task being notified had not yet processed the previous notification (the - * task already had a notification pending). xTaskNotify() always returns - * pdPASS in this case. + * - eSetValueWithoutOverwrite: + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. * - * eSetValueWithoutOverwrite - - * If the task being notified did not already have a notification pending then - * the task's notification value is set to ulValue and xTaskNotify() will - * return pdPASS. If the task being notified already had a notification - * pending then no action is performed and pdFAIL is returned. - * - * eNoAction - - * The task receives a notification without its notification value being - * updated. ulValue is not used and xTaskNotify() always returns pdPASS in - * this case. + * - eNoAction: + * The task receives a notification without its notification value being + * updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. * * @param pxHigherPriorityTaskWoken xTaskNotifyFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the @@ -1752,14 +1832,12 @@ BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAct * @return Dependent on the value of eAction. See the description of the * eAction parameter. * - * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken ); /** - * task. h - *
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
+ * Wait for task notification * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1827,14 +1905,12 @@ BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNo * already pending when xTaskNotifyWait was called) then pdPASS is * returned. Otherwise pdFAIL is returned. * - * \defgroup xTaskNotifyWait xTaskNotifyWait * \ingroup TaskNotifications */ BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); /** - * task. h - *
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
+ * Simplified macro for sending task notification. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro * to be available. @@ -1873,14 +1949,12 @@ BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClea * @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the * eAction parameter set to eIncrement - so pdPASS is always returned. * - * \defgroup xTaskNotifyGive xTaskNotifyGive * \ingroup TaskNotifications */ #define xTaskNotifyGive( xTaskToNotify ) xTaskNotify( ( xTaskToNotify ), 0, eIncrement ); /** - * task. h - *
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
+ * Simplified macro for sending task notification from ISR.
  *
  * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro
  * to be available.
@@ -1928,14 +2002,12 @@ BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClea
  * requested from an ISR is dependent on the port - see the documentation page
  * for the port in use.
  *
- * \defgroup xTaskNotifyWait xTaskNotifyWait
  * \ingroup TaskNotifications
  */
 void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken );
 
 /**
- * task. h
- * 
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
+ * Simplified macro for receiving task notification. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1997,7 +2069,6 @@ void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPri * @return The task's notification count before it is either cleared to zero or * decremented (see the xClearCountOnExit parameter). * - * \defgroup ulTaskNotifyTake ulTaskNotifyTake * \ingroup TaskNotifications */ uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); @@ -2005,7 +2076,7 @@ uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait /*----------------------------------------------------------- * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES *----------------------------------------------------------*/ - +/** @cond */ /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS @@ -2224,6 +2295,8 @@ void *pvTaskIncrementMutexHeldCount( void ); */ UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ); +/** @endcond */ + #ifdef __cplusplus } #endif diff --git a/components/freertos/include/freertos/timers.h b/components/freertos/include/freertos/timers.h index 8656d069d7..17492e64c6 100644 --- a/components/freertos/include/freertos/timers.h +++ b/components/freertos/include/freertos/timers.h @@ -117,24 +117,18 @@ or interrupt version of the queue send function should be used. */ */ typedef void * TimerHandle_t; -/* +/** * Defines the prototype to which timer callback functions must conform. */ typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer ); -/* +/** * Defines the prototype to which functions used with the * xTimerPendFunctionCallFromISR() function must conform. */ typedef void (*PendedFunction_t)( void *, uint32_t ); /** - * TimerHandle_t xTimerCreate( const char * const pcTimerName, - * TickType_t xTimerPeriodInTicks, - * UBaseType_t uxAutoReload, - * void * pvTimerID, - * TimerCallbackFunction_t pxCallbackFunction ); - * * Creates a new software timer instance, and returns a handle by which the * created software timer can be referenced. * @@ -184,7 +178,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * structures, or the timer period was set to 0) then NULL is returned. * * Example usage: - * @verbatim + * @code{c} * #define NUM_TIMERS 5 * * // An array to hold handles to the created timers. @@ -263,7 +257,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * // Should not reach here. * for( ;; ); * } - * @endverbatim + * @endcode */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) TimerHandle_t xTimerCreate( const char * const pcTimerName, @@ -274,13 +268,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); #endif /** - * TimerHandle_t xTimerCreateStatic(const char * const pcTimerName, - * TickType_t xTimerPeriodInTicks, - * UBaseType_t uxAutoReload, - * void * pvTimerID, - * TimerCallbackFunction_t pxCallbackFunction, - * StaticTimer_t *pxTimerBuffer ); - * * Creates a new software timer instance, and returns a handle by which the * created software timer can be referenced. * @@ -332,7 +319,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * returned. If pxTimerBuffer was NULL then NULL is returned. * * Example usage: - * @verbatim + * @code{c} * * // The buffer used to hold the software timer's data structure. * static StaticTimer_t xTimerBuffer; @@ -393,20 +380,18 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * // Should not reach here. * for( ;; ); * } - * @endverbatim + * @endcode */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * void *pvTimerGetTimerID( TimerHandle_t xTimer ); - * * Returns the ID assigned to the timer. * * IDs are assigned to timers using the pvTimerID parameter of the call to @@ -427,8 +412,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** - * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); - * * Sets the ID assigned to the timer. * * IDs are assigned to timers using the pvTimerID parameter of the call to @@ -448,12 +431,12 @@ void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION; /** - * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); - * * Queries a timer to see if it is active or dormant. * * A timer will be dormant if: + * * 1) It has been created but not started, or + * * 2) It is an expired one-shot timer that has not been restarted. * * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), @@ -467,7 +450,7 @@ void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION * pdFALSE will be returned if the timer is active. * * Example usage: - * @verbatim + * @code{c} * // This function assumes xTimer has already been created. * void vAFunction( TimerHandle_t xTimer ) * { @@ -480,13 +463,11 @@ void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION * // xTimer is not active, do something else. * } * } - * @endverbatim + * @endcode */ BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** - * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); - * * xTimerGetTimerDaemonTaskHandle() is only available if * INCLUDE_xTimerGetTimerDaemonTaskHandle is set to 1 in FreeRTOSConfig.h. * @@ -496,8 +477,6 @@ BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); /** - * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); - * * Returns the period of a timer. * * @param xTimer The handle of the timer being queried. @@ -507,8 +486,6 @@ TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** - * TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); - * * Returns the time in ticks at which the timer will expire. If this is less * than the current tick count then the expiry time has overflowed from the * current time. @@ -522,8 +499,6 @@ TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** - * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * * Timer functionality is provided by a timer service/daemon task. Many of the * public FreeRTOS timer API functions send commands to the timer service task * through a queue called the timer command queue. The timer command queue is @@ -574,8 +549,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; #define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) /** - * BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * * Timer functionality is provided by a timer service/daemon task. Many of the * public FreeRTOS timer API functions send commands to the timer service task * through a queue called the timer command queue. The timer command queue is @@ -616,10 +589,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; #define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) ) /** - * BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, - * TickType_t xNewPeriod, - * TickType_t xTicksToWait ); - * * Timer functionality is provided by a timer service/daemon task. Many of the * public FreeRTOS timer API functions send commands to the timer service task * through a queue called the timer command queue. The timer command queue is @@ -661,7 +630,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This function assumes xTimer has already been created. If the timer * // referenced by xTimer is already active when it is called, then the timer * // is deleted. If the timer referenced by xTimer is not active when it is @@ -691,13 +660,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * } * } * } - * @endverbatim + * @endcode */ #define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) ) /** - * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * * Timer functionality is provided by a timer service/daemon task. Many of the * public FreeRTOS timer API functions send commands to the timer service task * through a queue called the timer command queue. The timer command queue is @@ -734,8 +701,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; #define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) /** - * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * * Timer functionality is provided by a timer service/daemon task. Many of the * public FreeRTOS timer API functions send commands to the timer service task * through a queue called the timer command queue. The timer command queue is @@ -781,7 +746,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * configuration constant. * * Example usage: - * @verbatim + * @code{c} * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass * // without a key being pressed, then the LCD back-light is switched off. In * // this case, the timer is a one-shot timer. @@ -853,14 +818,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * // Should not reach here. * for( ;; ); * } - * @endverbatim + * @endcode */ #define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) /** - * BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, - * BaseType_t *pxHigherPriorityTaskWoken ); - * * A version of xTimerStart() that can be called from an interrupt service * routine. * @@ -888,7 +850,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xBacklightTimer has already been created. When a * // key is pressed, an LCD back-light is switched on. If 5 seconds pass * // without a key being pressed, then the LCD back-light is switched off. In @@ -939,14 +901,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * // depends on the FreeRTOS port being used). * } * } - * @endverbatim + * @endcode */ #define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) /** - * BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, - * BaseType_t *pxHigherPriorityTaskWoken ); - * * A version of xTimerStop() that can be called from an interrupt service * routine. * @@ -972,7 +931,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * priority is set by the configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xTimer has already been created and started. When * // an interrupt occurs, the timer should be simply stopped. * @@ -1002,15 +961,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * // depends on the FreeRTOS port being used). * } * } - * @endverbatim + * @endcode */ #define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U ) /** - * BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, - * TickType_t xNewPeriod, - * BaseType_t *pxHigherPriorityTaskWoken ); - * * A version of xTimerChangePeriod() that can be called from an interrupt * service routine. * @@ -1045,7 +1000,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * priority is set by the configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xTimer has already been created and started. When * // an interrupt occurs, the period of xTimer should be changed to 500ms. * @@ -1075,14 +1030,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * // depends on the FreeRTOS port being used). * } * } - * @endverbatim + * @endcode */ #define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) /** - * BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, - * BaseType_t *pxHigherPriorityTaskWoken ); - * * A version of xTimerReset() that can be called from an interrupt service * routine. * @@ -1110,7 +1062,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xBacklightTimer has already been created. When a * // key is pressed, an LCD back-light is switched on. If 5 seconds pass * // without a key being pressed, then the LCD back-light is switched off. In @@ -1161,18 +1113,12 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * // depends on the FreeRTOS port being used). * } * } - * @endverbatim + * @endcode */ #define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) /** - * BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, - * void *pvParameter1, - * uint32_t ulParameter2, - * BaseType_t *pxHigherPriorityTaskWoken ); - * - * * Used from application interrupt service routines to defer the execution of a * function to the RTOS daemon task (the timer service task, hence this function * is implemented in timers.c and is prefixed with 'Timer'). @@ -1214,7 +1160,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * timer daemon task, otherwise pdFALSE is returned. * * Example usage: - * @verbatim + * @code{c} * * // The callback function that will execute in the context of the daemon task. * // Note callback functions must all use this same prototype. @@ -1252,17 +1198,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); * * } - * @endverbatim + * @endcode */ BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ); /** - * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, - * void *pvParameter1, - * uint32_t ulParameter2, - * TickType_t xTicksToWait ); - * - * * Used to defer the execution of a function to the RTOS daemon task (the timer * service task, hence this function is implemented in timers.c and is prefixed * with 'Timer'). @@ -1291,8 +1231,6 @@ BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ); /** - * const char * const pcTimerGetTimerName( TimerHandle_t xTimer ); - * * Returns the name that was assigned to a timer when the timer was created. * * @param xTimer The handle of the timer being queried. @@ -1301,6 +1239,7 @@ BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvPar */ const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +/** @cond */ /* * Functions beyond this part are not part of the public API and are intended * for use by the kernel only. @@ -1308,6 +1247,8 @@ const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualifi BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +/** @endcond */ + #ifdef __cplusplus } #endif diff --git a/docs/Doxyfile b/docs/Doxyfile index 950f914214..713b6a49dd 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -153,7 +153,17 @@ INPUT = \ ../components/esp32/include/esp_pm.h \ ../components/esp32/include/esp32/pm.h \ ### esp_timer, High Resolution Timer - ../components/esp32/include/esp_timer.h + ../components/esp32/include/esp_timer.h \ + ### + ### FreeRTOS + ### + ../components/freertos/include/freertos/task.h \ + ../components/freertos/include/freertos/queue.h \ + ../components/freertos/include/freertos/semphr.h \ + ../components/freertos/include/freertos/timers.h \ + ../components/freertos/include/freertos/event_groups.h \ + ../components/freertos/include/freertos/ringbuf.h + ## Get warnings for functions that have no documentation for their parameters or return value @@ -165,7 +175,16 @@ WARN_NO_PARAMDOC = YES ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES -PREDEFINED = __attribute__(x)= +PREDEFINED = \ + __attribute__(x)= \ + IRAM_ATTR= \ + configSUPPORT_DYNAMIC_ALLOCATION=1 \ + configSUPPORT_STATIC_ALLOCATION=1 \ + configQUEUE_REGISTRY_SIZE=1 \ + configUSE_RECURSIVE_MUTEXES=1 \ + configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \ + configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \ + configUSE_APPLICATION_TASK_TAG=1 ## Do not complain about not having dot ## diff --git a/docs/api-reference/system/freertos.rst b/docs/api-reference/system/freertos.rst new file mode 100644 index 0000000000..2b0b0929fb --- /dev/null +++ b/docs/api-reference/system/freertos.rst @@ -0,0 +1,42 @@ +FreeRTOS +======== + +Overview +-------- + +This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files. + +For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`. + + +Task API +-------- + +.. include:: /_build/inc/task.inc + +Queue API +--------- + +.. include:: /_build/inc/queue.inc + +Semaphore API +------------- + +.. include:: /_build/inc/semphr.inc + +Timer API +--------- + +.. include:: /_build/inc/timers.inc + + +Event Group API +--------------- + +.. include:: /_build/inc/event_groups.inc + +Ringbuffer API +-------------- + +.. include:: /_build/inc/ringbuf.inc + diff --git a/docs/api-reference/system/hooks.rst b/docs/api-reference/system/hooks.rst index 78ac708a23..5a65b29a16 100644 --- a/docs/api-reference/system/hooks.rst +++ b/docs/api-reference/system/hooks.rst @@ -1,7 +1,7 @@ .. _hooks_api_reference: -ESP-IDF FreeRTOS Hooks -====================== +FreeRTOS Hooks +============== Overview -------- diff --git a/docs/api-reference/system/index.rst b/docs/api-reference/system/index.rst index c895ebd8b5..593fcc3d69 100644 --- a/docs/api-reference/system/index.rst +++ b/docs/api-reference/system/index.rst @@ -4,19 +4,20 @@ System API .. toctree:: :maxdepth: 1 + FreeRTOS + FreeRTOS Hooks Heap Memory Allocation Heap Memory Debugging Interrupt Allocation Watchdogs - Hooks Inter-Processor Call High Resolution Timer - Over The Air Updates (OTA) - Sleep Modes - Power Management Logging - Base MAC address Application Level Tracing + Power Management + Sleep Modes + Base MAC address + Over The Air Updates (OTA) Example code for this API section is provided in :example:`system` directory of ESP-IDF examples. From d6525fb3e604030bd55366e3a98ee66832c8a564 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 4 Dec 2017 20:09:12 +0800 Subject: [PATCH 17/30] docs: link to FreeRTOS APIs from SMP changes documentation --- docs/api-guides/freertos-smp.rst | 122 +++++++++++++++---------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/docs/api-guides/freertos-smp.rst b/docs/api-guides/freertos-smp.rst index 16d4c139b4..e1763bf593 100644 --- a/docs/api-guides/freertos-smp.rst +++ b/docs/api-guides/freertos-smp.rst @@ -20,8 +20,8 @@ found via http://www.freertos.org/a00106.html port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported to ESP-IDF. -:ref:`tasks-and-task-creation`: Use ``xTaskCreatePinnedToCore()`` or -``xTaskCreateStaticPinnedToCore()`` to create tasks in ESP-IDF FreeRTOS. The +:ref:`tasks-and-task-creation`: Use :cpp:func:`xTaskCreatePinnedToCore` or +:cpp:func:`xTaskCreateStaticPinnedToCore` to create tasks in ESP-IDF FreeRTOS. The last parameter of the two functions is ``xCoreID``. This parameter specifies which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**, ``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on @@ -34,13 +34,13 @@ enter a blocked state, or are distributed across a wider range of priorities. :ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only affect the scheduler on the the calling core. In other words, calling -``vTaskSuspendAll()`` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and +:cpp:func:`vTaskSuspendAll` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and vice versa. Use critical sections or semaphores instead for simultaneous access protection. :ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU** -are not synchronized. Do not expect to use ``vTaskDelay`` or -``vTaskDelayUntil`` as an accurate method of synchronizing task execution +are not synchronized. Do not expect to use :cpp:func:`vTaskDelay` or +:cpp:func:`vTaskDelayUntil` as an accurate method of synchronizing task execution between the two cores. Use a counting semaphore instead as their context switches are not tied to tick interrupts due to preemption. @@ -51,15 +51,15 @@ unaffected. If the other core attemps to take same mutex, it will spin until the calling core has released the mutex by exiting the critical section. :ref:`floating-points`: The ESP32 supports hardware acceleration of single -precision floating point arithmetic (`float`). However the use of hardware +precision floating point arithmetic (``float``). However the use of hardware acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS. -Therefore, tasks that utilize `float` will automatically be pinned to a core if -not done so already. Furthermore, `float` cannot be used in interrupt service +Therefore, tasks that utilize ``float`` will automatically be pinned to a core if +not done so already. Furthermore, ``float`` cannot be used in interrupt service routines. :ref:`task-deletion`: Task deletion behavior has been backported from FreeRTOS v9.0.0 and modified to be SMP compatible. Task memory will be freed immediately -when `vTaskDelete()` is called to delete a task that is not currently running +when :cpp:func:`vTaskDelete` is called to delete a task that is not currently running and not pinned to the other core. Otherwise, freeing of task memory will still be delegated to the Idle Task. @@ -67,7 +67,7 @@ be delegated to the Idle Task. Storage Pointers (TLSP) feature. However the extra feature of Deletion Callbacks has been added. Deletion callbacks are called automatically during task deletion and are used to free memory pointed to by TLSP. Call -``vTaskSetThreadLocalStoragePointerAndDelCallback()`` to set TLSP and Deletion +:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback()` to set TLSP and Deletion Callbacks. :ref:`FreeRTOS Hooks`: Vanilla FreeRTOS Hooks were not designed for SMP. @@ -94,34 +94,34 @@ This feature has been backported from FreeRTOS v9.0.0 to ESP-IDF. The in order for static allocation functions to be available. Once enabled, the following functions can be called... - - ``xTaskCreateStatic()`` See :ref:`backporting-notes` below - - ``xQueueCreateStatic()`` - - ``xSemaphoreCreateBinaryStatic()`` - - ``xSemaphoreCreateCountingStatic()`` - - ``xSemaphoreCreateMutexStatic()`` - - ``xSemaphoreCreateRecursiveMutexStatic()`` - - ``xTimerCreateStatic()`` See :ref:`backporting-notes` below - - ``xEventGroupCreateStatic()`` + - :cpp:func:`xTaskCreateStatic` (see :ref:`backporting-notes` below) + - :c:macro:`xQueueCreateStatic` + - :c:macro:`xSemaphoreCreateBinaryStatic` + - :c:macro:`xSemaphoreCreateCountingStatic` + - :c:macro:`xSemaphoreCreateMutexStatic` + - :c:macro:`xSemaphoreCreateRecursiveMutexStatic` + - :cpp:func:`xTimerCreateStatic` (see :ref:`backporting-notes` below) + - :cpp:func:`xEventGroupCreateStatic` Other Features ^^^^^^^^^^^^^^ - - ``vTaskSetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below - - ``pvTaskGetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below - - ``vTimerSetTimerID()`` - - ``xTimerGetPeriod()`` - - ``xTimerGetExpiryTime()`` - - ``pcQueueGetName()`` - - ``uxSemaphoreGetCount()`` + - :cpp:func:`vTaskSetThreadLocalStoragePointer` (see :ref:`backporting-notes` below) + - :cpp:func:`pvTaskGetThreadLocalStoragePointer` (see :ref:`backporting-notes` below) + - :cpp:func:`vTimerSetTimerID` + - :cpp:func:`xTimerGetPeriod` + - :cpp:func:`xTimerGetExpiryTime` + - :cpp:func:`pcQueueGetName` + - :c:macro:`uxSemaphoreGetCount` .. _backporting-notes: Backporting Notes ^^^^^^^^^^^^^^^^^ -**1)** ``xTaskCreateStatic`` has been made SMP compatible in a similar -fashion to ``xTaskCreate`` (see :ref:`tasks-and-task-creation`). Therefore -``xTaskCreateStaticPinnedToCore()`` can also be called. +**1)** :cpp:func:`xTaskCreateStatic` has been made SMP compatible in a similar +fashion to :cpp:func:`xTaskCreate` (see :ref:`tasks-and-task-creation`). Therefore +:cpp:func:`xTaskCreateStaticPinnedToCore` can also be called. **2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to be statically allocated, the daemon task is always dynamically allocated in @@ -130,7 +130,7 @@ defined when using statically allocated timers in ESP-IDF FreeRTOS. **3)** The Thread Local Storage Pointer feature has been modified in ESP-IDF FreeRTOS to include Deletion Callbacks (see :ref:`deletion-callbacks`). Therefore -the function ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` can also be +the function :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback` can also be called. @@ -142,9 +142,9 @@ Tasks and Task Creation Tasks in ESP-IDF FreeRTOS are designed to run on a particular core, therefore two new task creation functions have been added to ESP-IDF FreeRTOS by appending ``PinnedToCore`` to the names of the task creation functions in -vanilla FreeRTOS. The vanilla FreeRTOS functions of ``xTaskCreate()`` -and ``xTaskCreateStatic()`` have led to the addition of -``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` in +vanilla FreeRTOS. The vanilla FreeRTOS functions of :cpp:func:`xTaskCreate` +and :cpp:func:`xTaskCreateStatic` have led to the addition of +:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore` in ESP-IDF FreeRTOS (see :ref:`backported-features`). For more details see :component_file:`freertos/task.c` @@ -164,9 +164,9 @@ of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in vanilla FreeRTOS specifies a task’s stack depth in terms of the number of words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes. -Note that the vanilla FreeRTOS functions ``xTaskCreate`` and -``xTaskCreateStatic`` have been macro defined in ESP-IDF FreeRTOS to call -``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` +Note that the vanilla FreeRTOS functions :cpp:func:`xTaskCreate` and +:cpp:func:`xTaskCreateStatic` have been defined in ESP-IDF FreeRTOS as inline functions which call +:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore` respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value. Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member. @@ -283,18 +283,18 @@ different cores. Scheduler Suspension ^^^^^^^^^^^^^^^^^^^^ -In vanilla FreeRTOS, suspending the scheduler via ``vTaskSuspendAll()`` will -prevent calls of ``vTaskSwitchContext()`` from context switching until the -scheduler has been resumed with ``vTaskResumeAll()``. However servicing ISRs +In vanilla FreeRTOS, suspending the scheduler via :cpp:func:`vTaskSuspendAll` will +prevent calls of ``vTaskSwitchContext`` from context switching until the +scheduler has been resumed with :cpp:func:`xTaskResumeAll`. However servicing ISRs are still permitted. Therefore any changes in task states as a result from the current running task or ISRSs will not be executed until the scheduler is resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method against simultaneous access of data shared between tasks, whilst still allowing ISRs to be serviced. -In ESP-IDF FreeRTOS, ``vTaskSuspendAll()`` will only prevent calls of +In ESP-IDF FreeRTOS, :cpp:func:`xTaskResumeAll` will only prevent calls of ``vTaskSwitchContext()`` from switching contexts on the core that called for the -suspension. Hence if **PRO_CPU** calls ``vTaskSuspendAll()``, **APP_CPU** will +suspension. Hence if **PRO_CPU** calls :cpp:func:`vTaskSuspendAll`, **APP_CPU** will still be able to switch contexts. If data is shared between tasks that are pinned to different cores, scheduler suspension is **NOT** a valid method of protection against simultaneous access. Consider using critical sections @@ -302,7 +302,7 @@ protection against simultaneous access. Consider using critical sections protecting shared resources in ESP-IDF FreeRTOS. In general, it's better to use other RTOS primitives like mutex semaphores to protect -against data shared between tasks, rather than ``vTaskSuspendAll()``. +against data shared between tasks, rather than :cpp:func:`vTaskSuspendAll`. .. _tick-interrupt-synchronicity: @@ -316,8 +316,8 @@ each core being independent, and the tick interrupts to each core being unsynchronized. In vanilla FreeRTOS the tick interrupt triggers a call to -``xTaskIncrementTick()`` which is responsible for incrementing the tick -counter, checking if tasks which have called ``vTaskDelay()`` have fulfilled +:cpp:func:`xTaskIncrementTick` which is responsible for incrementing the tick +counter, checking if tasks which have called :cpp:func:`vTaskDelay` have fulfilled their delay period, and moving those tasks from the Delayed Task List to the Ready Task List. The tick interrupt will then call the scheduler if a context switch is necessary. @@ -372,11 +372,11 @@ The ESP-IDF FreeRTOS critical section functions have been modified as follows… - ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``, ``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro - defined to call ``vTaskEnterCritical()`` + defined to call :cpp:func:`vTaskEnterCritical` - ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``, ``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro - defined to call ``vTaskExitCritical()`` + defined to call :cpp:func:`vTaskExitCritical` For more details see :component_file:`freertos/include/freertos/portmacro.h` and :component_file:`freertos/task.c` @@ -394,23 +394,23 @@ Floating Point Aritmetic ------------------------ The ESP32 supports hardware acceleration of single precision floating point -arithmetic (`float`) via Floating Point Units (FPU, also known as coprocessors) +arithmetic (``float``) via Floating Point Units (FPU, also known as coprocessors) attached to each core. The use of the FPUs imposes some behavioral restrictions on ESP-IDF FreeRTOS. ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words, the state of a core's FPU registers are not immediately saved when a context -switch occurs. Therefore, tasks that utilize `float` must be pinned to a +switch occurs. Therefore, tasks that utilize ``float`` must be pinned to a particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin the task in question to whichever core the task was running on upon the task's -first use of `float`. Likewise due to Lazy Context Switching, interrupt service -routines must also not use `float`. +first use of ``float``. Likewise due to Lazy Context Switching, interrupt service +routines must also not use ``float``. ESP32 does not support hardware acceleration for double precision floating point -arithmetic (`double`). Instead `double` is implemented via software hence the -behavioral restrictions with regards to `float` do not apply to `double`. Note -that due to the lack of hardware acceleration, `double` operations may consume -significantly larger amount of CPU time in comparison to `float`. +arithmetic (``double``). Instead ``double`` is implemented via software hence the +behavioral restrictions with regards to ``float`` do not apply to ``double``. Note +that due to the lack of hardware acceleration, ``double`` operations may consume +significantly larger amount of CPU time in comparison to ``float``. .. _task-deletion: @@ -420,12 +420,12 @@ Task Deletion FreeRTOS task deletion prior to v9.0.0 delegated the freeing of task memory entirely to the Idle Task. Currently, the freeing of task memory will occur -immediately (within `vTaskDelete()`) if the task being deleted is not currently +immediately (within :cpp:func:`vTaskDelete`) if the task being deleted is not currently running or is not pinned to the other core (with respect to the core -`vTaskDelete()` is called on). TLSP deletion callbacks will also run immediately +:cpp:func:`vTaskDelete` is called on). TLSP deletion callbacks will also run immediately if the same conditions are met. -However, calling `vTaskDelete()` to delete a task that is either currently +However, calling :cpp:func:`vTaskDelete` to delete a task that is either currently running or pinned to the other core will still result in the freeing of memory being delegated to the Idle Task. @@ -456,8 +456,8 @@ is the index number of the associated TLSP, and the second parameter is the TLSP itself. Deletion callbacks are set alongside TLSP by calling -``vTaskSetThreadLocalStoragePointerAndDelCallback()``. Calling the vanilla -FreeRTOS function ``vTaskSetThreadLocalStoragePointer()`` will simply set the +:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback`. Calling the vanilla +FreeRTOS function :cpp:func:`vTaskSetThreadLocalStoragePointer` will simply set the TLSP's associated Deletion Callback to `NULL` meaning that no callback will be called for that TLSP during task deletion. If a deletion callback is `NULL`, users should manually free the memory pointed to by the associated TLSP before @@ -466,7 +466,7 @@ task deletion in order to avoid memory leak. :ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` in menuconfig can be used to configure the number TLSP and Deletion Callbacks a TCB will have. -For more details see :component_file:`freertos/include/freertos/task.h` +For more details see :doc:`FreeRTOS API reference<../api-reference/system/freertos>`. .. _esp-idf-freertos-configuration: @@ -491,9 +491,9 @@ number of Thread Local Storage Pointers each task will have in ESP-IDF FreeRTOS. :ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` will enable the backported -functionality of ``xTaskCreateStaticPinnedToCore()`` in ESP-IDF FreeRTOS +functionality of :cpp:func:`xTaskCreateStaticPinnedToCore` in ESP-IDF FreeRTOS :ref:`CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION` will trigger a halt in particular functions in ESP-IDF FreeRTOS which have not been fully tested in an SMP context. - + From 7a31b93fa3e6963ce2ecfb8a264f6667392b8b69 Mon Sep 17 00:00:00 2001 From: houchenyao Date: Thu, 30 Nov 2017 19:31:51 +0800 Subject: [PATCH 18/30] unit-test-app: support multiple devices test cases: current unit-test-app don't support test components need to communicate with each other (like GPIO, SPI ...). Now we add multiple devices mode to unit test app, support writing and running test with multiple DUTs. please refer to `docs/api-guides/unit-tests.rst` for detail. --- docs/api-guides/unit-tests.rst | 93 +++++++++++++++- .../components/unity/include/test_utils.h | 40 +++++++ .../components/unity/include/unity_config.h | 103 ++++++++++++++---- .../components/unity/test_utils.c | 31 ++++++ .../components/unity/unity_platform.c | 61 ++++++++++- 5 files changed, 299 insertions(+), 29 deletions(-) diff --git a/docs/api-guides/unit-tests.rst b/docs/api-guides/unit-tests.rst index a32533755e..f3b09730c5 100644 --- a/docs/api-guides/unit-tests.rst +++ b/docs/api-guides/unit-tests.rst @@ -3,8 +3,8 @@ Unit Testing in ESP32 ESP-IDF comes with a unit test app based on Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in ``test`` subdirectory of each component respectively. -Adding unit tests ------------------ +Add normal test cases +--------------------- Unit tests are added in the ``test`` subdirectory of the respective component. Tests are added in C files, a single C file can include multiple test cases. @@ -31,6 +31,59 @@ Each `test` subdirectory needs to include component.mk file with at least the fo See http://www.throwtheswitch.org/unity for more information about writing tests in Unity. + +Add multiple devices test cases +------------------------------- + +The normal test cases will be executed on one DUT (Device Under Test). Components need to communicate with each other (like GPIO, SPI ...) can't be tested with normal test cases. +Multiple devices test cases support writing and running test with multiple DUTs. + +Here's an example of multiple devices test case:: + + void gpio_master_test() + { + gpio_config_t slave_config = { + .pin_bit_mask = 1 << MASTER_GPIO_PIN, + .mode = GPIO_MODE_INPUT, + }; + gpio_config(&slave_config); + unity_wait_for_signal("output high level"); + TEST_ASSERT(gpio_get_level(MASTER_GPIO_PIN) == 1); + } + + void gpio_slave_test() + { + gpio_config_t master_config = { + .pin_bit_mask = 1 << SLAVE_GPIO_PIN, + .mode = GPIO_MODE_OUTPUT, + }; + gpio_config(&master_config); + gpio_set_level(SLAVE_GPIO_PIN, 1); + unity_send_signal("output high level"); + } + + TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test); + + +The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare multiple devices test cases. +First argument is test case name, second argument is test case description. +From the third argument, upto 5 test functions can be defined, each function will be the entry point of tests running on each DUT. + +Running test cases from different DUTs could require synchronizing between DUTs. We provide ``unity_wait_for_signal`` and ``unity_send_signal`` to support synchronizing with UART. +As the secnario in the above example, slave should get GPIO level after master set level. DUT UART console will prompt and requires user interaction: + +DUT1 (master) console:: + + Waiting for signal: [output high level]! + Please press "Enter" key to once any board send this signal. + +DUT2 (slave) console:: + + Send signal: [output high level]! + +Once the signal is set from DUT2, you need to press "Enter" on DUT1, then DUT1 unblocks from ``unity_wait_for_signal`` and starts to change GPIO level. + + Building unit test app ---------------------- @@ -55,7 +108,32 @@ Running unit tests After flashing reset the ESP32 and it will boot the unit test app. -Unit test app prints a test menu with all available tests. +When unit test app is idle, press "Enter" will make it print test menu with all available tests:: + + Here's the test menu, pick your combo: + (1) "esp_ota_begin() verifies arguments" [ota] + (2) "esp_ota_get_next_update_partition logic" [ota] + (3) "Verify bootloader image in flash" [bootloader_support] + (4) "Verify unit test app image" [bootloader_support] + (5) "can use new and delete" [cxx] + (6) "can call virtual functions" [cxx] + (7) "can use static initializers for non-POD types" [cxx] + (8) "can use std::vector" [cxx] + (9) "static initialization guards work as expected" [cxx] + (10) "global initializers run in the correct order" [cxx] + (11) "before scheduler has started, static initializers work correctly" [cxx] + (12) "adc2 work with wifi" [adc] + (13) "gpio master/slave test example" [ignore][misc][test_env=UT_T2_1] + (1) "gpio_master_test" + (2) "gpio_slave_test" + (14) "SPI Master clockdiv calculation routines" [spi] + (15) "SPI Master test" [spi][ignore] + (16) "SPI Master test, interaction of multiple devs" [spi][ignore] + (17) "SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)" [spi] + (18) "SPI Master DMA test, TX and RX in different regions" [spi] + (19) "SPI Master DMA test: length, start, not aligned" [spi] + +Normal case will print the case name and description. Master slave cases will also print the sub-menu (the registered test function names). Test cases can be run by inputting one of the following: @@ -66,3 +144,12 @@ Test cases can be run by inputting one of the following: - Module name in square brackets to run all test cases for a specific module - An asterisk to run all test cases + +After you select multiple devices test case, it will print sub menu:: + + Running gpio master/slave test example... + gpio master/slave test example + (1) "gpio_master_test" + (2) "gpio_slave_test" + +You need to input number to select the test running on the DUT. diff --git a/tools/unit-test-app/components/unity/include/test_utils.h b/tools/unit-test-app/components/unity/include/test_utils.h index ba9c62db47..746d94a083 100644 --- a/tools/unit-test-app/components/unity/include/test_utils.h +++ b/tools/unit-test-app/components/unity/include/test_utils.h @@ -36,8 +36,48 @@ void ref_clock_init(); */ void ref_clock_deinit(); + /** * @brief Get reference clock timestamp * @return number of microseconds since the reference clock was initialized */ uint64_t ref_clock_get(); + +/** + * @brief wait for signals. + * + * for multiple devices test cases, DUT might need to wait for other DUTs before continue testing. + * As all DUTs are independent, need user (or test script) interaction to make test synchronized. + * + * Here we provide signal functions for this. + * For example, we're testing GPIO, DUT1 has one pin connect to with DUT2. + * DUT2 will output high level and then DUT1 will read input. + * DUT1 should call `unity_wait_for_signal("output high level");` before it reads input. + * DUT2 should call `unity_send_signal("output high level");` after it finished setting output high level. + * According to the console logs: + * + * DUT1 console: + * + * ``` + * Waiting for signal: [output high level]! + * Please press "Enter" key to once any board send this signal. + * ``` + * + * DUT2 console: + * + * ``` + * Send signal: [output high level]! + * ``` + * + * Then we press Enter key on DUT1's console, DUT1 starts to read input and then test success. + * + * @param signal_name signal name which DUT expected to wait before proceed testing + */ +void unity_wait_for_signal(const char* signal_name); + +/** + * @brief DUT send signal. + * + * @param signal_name signal name which DUT send once it finished preparing. + */ +void unity_send_signal(const char* signal_name); diff --git a/tools/unit-test-app/components/unity/include/unity_config.h b/tools/unit-test-app/components/unity/include/unity_config.h index 2929eb6e39..5931069c0e 100644 --- a/tools/unit-test-app/components/unity/include/unity_config.h +++ b/tools/unit-test-app/components/unity/include/unity_config.h @@ -20,21 +20,50 @@ #define UNITY_OUTPUT_FLUSH unity_flush // Define helpers to register test cases from multiple files - #define UNITY_EXPAND2(a, b) a ## b #define UNITY_EXPAND(a, b) UNITY_EXPAND2(a, b) #define UNITY_TEST_UID(what) UNITY_EXPAND(what, __LINE__) #define UNITY_TEST_REG_HELPER reg_helper ## UNITY_TEST_UID #define UNITY_TEST_DESC_UID desc ## UNITY_TEST_UID + + +// get count of __VA_ARGS__ +#define PP_NARG(...) \ + PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) +#define PP_NARG_(...) \ + PP_ARG_N(__VA_ARGS__) +#define PP_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N +#define PP_RSEQ_N() 9,8,7,6,5,4,3,2,1,0 + +// support max 5 test func now +#define FN_NAME_SET_1(a) {#a} +#define FN_NAME_SET_2(a, b) {#a, #b} +#define FN_NAME_SET_3(a, b, c) {#a, #b, #c} +#define FN_NAME_SET_4(a, b, c, d) {#a, #b, #c, #d} +#define FN_NAME_SET_5(a, b, c, d, e) {#a, #b, #c, #d, #e} + +#define FN_NAME_SET2(n) FN_NAME_SET_##n +#define FN_NAME_SET(n, ...) FN_NAME_SET2(n)(__VA_ARGS__) + +#define UNITY_TEST_FN_SET(...) \ + static test_func UNITY_TEST_UID(test_functions)[] = {__VA_ARGS__}; \ + static char* UNITY_TEST_UID(test_fn_name)[] = FN_NAME_SET(PP_NARG(__VA_ARGS__), __VA_ARGS__) + + +typedef void (* test_func)(void); + struct test_desc_t { - const char* name; - const char* desc; - void (*fn)(void); - const char* file; - int line; - struct test_desc_t* next; + const char* name; + const char* desc; + test_func* fn; + const char* file; + int line; + uint8_t test_fn_count; + char ** test_fn_name; + struct test_desc_t* next; }; void unity_testcase_register(struct test_desc_t* desc); @@ -46,7 +75,7 @@ void unity_run_tests_with_filter(const char* filter); void unity_run_all_tests(); /* Test case macro, a-la CATCH framework. - First argument is a free-form description, + First argument is a free-form description, second argument is (by convention) a list of identifiers, each one in square brackets. Identifiers are used to group related tests, or tests with specific properties. Use like: @@ -56,21 +85,51 @@ void unity_run_all_tests(); // test goes here } */ + #define TEST_CASE(name_, desc_) \ - static void UNITY_TEST_UID(test_func_) (void); \ - static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \ - { \ - static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \ - .name = name_, \ - .desc = desc_, \ - .fn = &UNITY_TEST_UID(test_func_), \ - .file = __FILE__, \ - .line = __LINE__, \ - .next = NULL \ - }; \ - unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ - }\ - static void UNITY_TEST_UID(test_func_) (void) + static void UNITY_TEST_UID(test_func_) (void); \ + static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \ + { \ + static test_func test_fn_[] = {&UNITY_TEST_UID(test_func_)}; \ + static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \ + .name = name_, \ + .desc = desc_, \ + .fn = test_fn_, \ + .file = __FILE__, \ + .line = __LINE__, \ + .test_fn_count = 1, \ + .test_fn_name = NULL, \ + .next = NULL \ + }; \ + unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ + }\ + static void UNITY_TEST_UID(test_func_) (void) + + +/* + * First argument is a free-form description, + * second argument is (by convention) a list of identifiers, each one in square brackets. + * subsequent arguments are names of test functions for different DUTs + * e.g: + * TEST_CASE_MULTIPLE_DEVICES("master and slave spi","[spi][test_env=UT_T2_1]", master_test, slave_test); + * */ + +#define TEST_CASE_MULTIPLE_DEVICES(name_, desc_, ...) \ + UNITY_TEST_FN_SET(__VA_ARGS__); \ + static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \ + { \ + static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \ + .name = name_, \ + .desc = desc_, \ + .fn = UNITY_TEST_UID(test_functions), \ + .file = __FILE__, \ + .line = __LINE__, \ + .test_fn_count = PP_NARG(__VA_ARGS__), \ + .test_fn_name = UNITY_TEST_UID(test_fn_name), \ + .next = NULL \ + }; \ + unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ + } /** * Note: initialization of test_desc_t fields above has to be done exactly * in the same order as the fields are declared in the structure. diff --git a/tools/unit-test-app/components/unity/test_utils.c b/tools/unit-test-app/components/unity/test_utils.c index 08826d319d..01176a8163 100644 --- a/tools/unit-test-app/components/unity/test_utils.c +++ b/tools/unit-test-app/components/unity/test_utils.c @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include "unity.h" #include "test_utils.h" +#include "rom/ets_sys.h" +#include "rom/uart.h" const esp_partition_t *get_test_data_partition() { @@ -23,3 +26,31 @@ const esp_partition_t *get_test_data_partition() TEST_ASSERT_NOT_NULL(result); /* means partition table set wrong */ return result; } + +// wait user to send "Enter" key +static void wait_user_control() +{ + char sign[5] = {0}; + while(strlen(sign) == 0) + { + /* Flush anything already in the RX buffer */ + while(uart_rx_one_char((uint8_t *) sign) == OK) { + } + /* Read line */ + UartRxString((uint8_t*) sign, sizeof(sign) - 1); + } +} + +// signal functions, used for sync between unity DUTs for multiple devices cases +void unity_wait_for_signal(const char* signal_name) +{ + printf("Waiting for signal: [%s]!\n" + "Please press \"Enter\" key to once any board send this signal.\n", signal_name); + wait_user_control(); +} + +void unity_send_signal(const char* signal_name) +{ + printf("Send signal: [%s]!\n", signal_name); +} + diff --git a/tools/unit-test-app/components/unity/unity_platform.c b/tools/unit-test-app/components/unity/unity_platform.c index 069bd838dc..fd979f0dae 100644 --- a/tools/unit-test-app/components/unity/unity_platform.c +++ b/tools/unit-test-app/components/unity/unity_platform.c @@ -145,12 +145,57 @@ void unity_testcase_register(struct test_desc_t* desc) } } +/* print the multiple devices case name and its sub-menu + * e.g: + * (1) spi master/slave case + * (1)master case + * (2)slave case + * */ +static void print_multiple_devices_test_menu(const struct test_desc_t* test_ms) + { + unity_printf("%s\n", test_ms->name); + for (int i = 0; i < test_ms->test_fn_count; i++) + { + unity_printf("\t(%d)\t\"%s\"\n", i+1, test_ms->test_fn_name[i]); + } + } + +void multiple_devices_option(const struct test_desc_t* test_ms) +{ + int selection; + char cmdline[256] = {0}; + + print_multiple_devices_test_menu(test_ms); + while(strlen(cmdline) == 0) + { + /* Flush anything already in the RX buffer */ + while(uart_rx_one_char((uint8_t *) cmdline) == OK) { + + } + UartRxString((uint8_t*) cmdline, sizeof(cmdline) - 1); + if(strlen(cmdline) == 0) { + /* if input was newline, print a new menu */ + print_multiple_devices_test_menu(test_ms); + } + } + selection = atoi((const char *) cmdline) - 1; + if(selection >= 0 && selection < test_ms->test_fn_count) { + UnityDefaultTestRun(test_ms->fn[selection], test_ms->name, test_ms->line); + } else { + printf("Invalid selection, your should input number 1-%d!", test_ms->test_fn_count); + } +} + static void unity_run_single_test(const struct test_desc_t* test) { printf("Running %s...\n", test->name); Unity.TestFile = test->file; Unity.CurrentDetail1 = test->desc; - UnityDefaultTestRun(test->fn, test->name, test->line); + if(test->test_fn_count == 1) { + UnityDefaultTestRun(test->fn[0], test->name, test->line); + } else { + multiple_devices_option(test); + } } static void unity_run_single_test_by_index(int index) @@ -158,6 +203,7 @@ static void unity_run_single_test_by_index(int index) const struct test_desc_t* test; for (test = s_unity_tests_first; test != NULL && index != 0; test = test->next, --index) { + } if (test != NULL) { @@ -201,7 +247,7 @@ static void unity_run_single_test_by_name(const char* filter) { unity_run_single_test(test); } - } + } } void unity_run_all_tests() @@ -253,8 +299,15 @@ static int print_test_menu(void) test = test->next, ++test_counter) { unity_printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc); - } - return test_counter; + if(test->test_fn_count > 1) + { + for (int i = 0; i < test->test_fn_count; i++) + { + unity_printf("\t(%d)\t\"%s\"\n", i+1, test->test_fn_name[i]); + } + } + } + return test_counter; } static int get_test_count(void) From 18553c451a9d4dce36b0c0302e3f615f1719fcac Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Thu, 7 Dec 2017 18:19:40 +0530 Subject: [PATCH 19/30] heap_trace: fix bug in realloc for copying trace record Closes https://github.com/espressif/esp-idf/issues/1354 Signed-off-by: Mahavir Jain --- components/heap/heap_trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/heap/heap_trace.c b/components/heap/heap_trace.c index b1da4415e0..b7ef48514e 100644 --- a/components/heap/heap_trace.c +++ b/components/heap/heap_trace.c @@ -373,11 +373,11 @@ static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t s record_free(p, callers); } heap_trace_record_t rec = { - .address = p, + .address = r, .ccount = ccount, .size = size, }; - memcpy(rec.alloced_by, callers, sizeof(heap_trace_record_t) * STACK_DEPTH); + memcpy(rec.alloced_by, callers, sizeof(void *) * STACK_DEPTH); record_allocation(&rec); } return r; From 583dceb69b2670987004bb0cd424faccdf394233 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 7 Dec 2017 21:48:27 +0800 Subject: [PATCH 20/30] component/btdm : change bt.h name to esp_bt.h to form the name prefix --- components/bt/bluedroid/api/esp_bt_main.c | 2 +- components/bt/bluedroid/hci/hci_hal_h4.c | 2 +- components/bt/bluedroid/hci/hci_layer.c | 2 +- components/bt/bt.c | 2 +- components/bt/include/bt.h | 247 +----------------- components/bt/include/esp_bt.h | 244 +++++++++++++++++ examples/bluetooth/a2dp_sink/main/main.c | 2 +- examples/bluetooth/ble_adv/main/app_bt.c | 2 +- .../ble_eddystone/main/esp_eddystone_demo.c | 2 +- .../bluetooth/ble_ibeacon/main/ibeacon_demo.c | 2 +- .../ble_spp_client/main/spp_client_demo.c | 2 +- .../ble_spp_server/main/ble_spp_server_demo.c | 2 +- .../bluetooth/blufi/main/blufi_example_main.c | 2 +- .../bluetooth/blufi/main/blufi_security.c | 2 +- .../main/controller_hci_uart_demo.c | 2 +- .../bluetooth/gatt_client/main/gattc_demo.c | 2 +- .../main/example_ble_sec_gattc_demo.c | 2 +- .../main/example_ble_sec_gatts_demo.c | 2 +- .../bluetooth/gatt_server/main/gatts_demo.c | 2 +- .../main/gatts_table_creat_demo.c | 2 +- .../main/gattc_multi_connect.c | 2 +- 21 files changed, 266 insertions(+), 263 deletions(-) create mode 100644 components/bt/include/esp_bt.h diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index 1f1a42ac8c..b904a0509b 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -16,7 +16,7 @@ #include "esp_bt_main.h" #include "btc_task.h" #include "btc_main.h" -#include "bt.h" +#include "esp_bt.h" #include "future.h" #include "allocator.h" diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 6d823a58fd..b9231b1b6f 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -25,7 +25,7 @@ #include "hci_internals.h" #include "hci_layer.h" #include "thread.h" -#include "bt.h" +#include "esp_bt.h" #define HCI_HAL_SERIAL_BUFFER_SIZE 1026 #define HCI_BLE_EVENT 0x3e diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index 9519fd1d27..10b0959aae 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -16,7 +16,7 @@ * ******************************************************************************/ #include -#include "bt.h" +#include "esp_bt.h" #include "bt_defs.h" #include "bt_trace.h" #include "hcidefs.h" diff --git a/components/bt/bt.c b/components/bt/bt.c index d344914727..6890bcf126 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -31,7 +31,7 @@ #include "esp_intr.h" #include "esp_attr.h" #include "esp_phy_init.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_err.h" #include "esp_log.h" #include "esp_pm.h" diff --git a/components/bt/include/bt.h b/components/bt/include/bt.h index 0c0355b60d..a957698fb0 100644 --- a/components/bt/include/bt.h +++ b/components/bt/include/bt.h @@ -1,244 +1,3 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef __BT_H__ -#define __BT_H__ - -#include -#include -#include "esp_err.h" -#include "sdkconfig.h" -#include "esp_task.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Controller config options, depend on config mask. - * Config mask indicate which functions enabled, this means - * some options or parameters of some functions enabled by config mask. - */ -typedef struct { - uint16_t controller_task_stack_size; /*!< Bluetooth controller task stack size */ - uint8_t controller_task_prio; /*!< Bluetooth controller task priority */ - uint8_t hci_uart_no; /*!< If use UART1/2 as HCI IO interface, indicate UART number */ - uint32_t hci_uart_baudrate; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */ -} esp_bt_controller_config_t; - -#ifdef CONFIG_BT_ENABLED - -#ifdef CONFIG_BT_HCI_UART_NO -#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO -#else -#define BT_HCI_UART_NO_DEFAULT 1 -#endif /* BT_HCI_UART_NO_DEFAULT */ - -#ifdef CONFIG_BT_HCI_UART_BAUDRATE -#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE -#else -#define BT_HCI_UART_BAUDRATE_DEFAULT 921600 -#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */ - -#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() { \ - .controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK, \ - .controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ - .hci_uart_no = BT_HCI_UART_NO_DEFAULT, \ - .hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT, \ -}; -#else -#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h"); -#endif - -/** - * @brief Bluetooth mode for controller enable/disable - */ -typedef enum { - ESP_BT_MODE_IDLE = 0x00, /*!< Bluetooth is not running */ - ESP_BT_MODE_BLE = 0x01, /*!< Run BLE mode */ - ESP_BT_MODE_CLASSIC_BT = 0x02, /*!< Run Classic BT mode */ - ESP_BT_MODE_BTDM = 0x03, /*!< Run dual mode */ -} esp_bt_mode_t; - -/** - * @brief Bluetooth controller enable/disable/initialised/de-initialised status - */ -typedef enum { - ESP_BT_CONTROLLER_STATUS_IDLE = 0, - ESP_BT_CONTROLLER_STATUS_INITED, - ESP_BT_CONTROLLER_STATUS_ENABLED, - ESP_BT_CONTROLLER_STATUS_NUM, -} esp_bt_controller_status_t; - - -/** - * @brief BLE tx power type - * ESP_BLE_PWR_TYPE_CONN_HDL0-8: for each connection, and only be set after connetion completed. - * when disconnect, the correspond TX power is not effected. - * ESP_BLE_PWR_TYPE_ADV : for advertising/scan response. - * ESP_BLE_PWR_TYPE_SCAN : for scan. - * ESP_BLE_PWR_TYPE_DEFAULT : if each connection's TX power is not set, it will use this default value. - * if neither in scan mode nor in adv mode, it will use this default value. - * If none of power type is set, system will use ESP_PWR_LVL_P1 as default for ADV/SCAN/CONN0-9. - */ -typedef enum { - ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, /*!< For connection handle 0 */ - ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, /*!< For connection handle 1 */ - ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, /*!< For connection handle 2 */ - ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, /*!< For connection handle 3 */ - ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, /*!< For connection handle 4 */ - ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, /*!< For connection handle 5 */ - ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, /*!< For connection handle 6 */ - ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, /*!< For connection handle 7 */ - ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, /*!< For connection handle 8 */ - ESP_BLE_PWR_TYPE_ADV = 9, /*!< For advertising */ - ESP_BLE_PWR_TYPE_SCAN = 10, /*!< For scan */ - ESP_BLE_PWR_TYPE_DEFAULT = 11, /*!< For default, if not set other, it will use default value */ - ESP_BLE_PWR_TYPE_NUM = 12, /*!< TYPE numbers */ -} esp_ble_power_type_t; - -/** - * @brief Bluetooth TX power level(index), it's just a index corresponding to power(dbm). - */ -typedef enum { - ESP_PWR_LVL_N14 = 0, /*!< Corresponding to -14dbm */ - ESP_PWR_LVL_N11 = 1, /*!< Corresponding to -11dbm */ - ESP_PWR_LVL_N8 = 2, /*!< Corresponding to -8dbm */ - ESP_PWR_LVL_N5 = 3, /*!< Corresponding to -5dbm */ - ESP_PWR_LVL_N2 = 4, /*!< Corresponding to -2dbm */ - ESP_PWR_LVL_P1 = 5, /*!< Corresponding to 1dbm */ - ESP_PWR_LVL_P4 = 6, /*!< Corresponding to 4dbm */ - ESP_PWR_LVL_P7 = 7, /*!< Corresponding to 7dbm */ -} esp_power_level_t; - -/** - * @brief Set BLE TX power - * Connection Tx power should only be set after connection created. - * @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc - * @param power_level: Power level(index) corresponding to absolute value(dbm) - * @return ESP_OK - success, other - failed - */ -esp_err_t esp_ble_tx_power_set(esp_ble_power_type_t power_type, esp_power_level_t power_level); - -/** - * @brief Get BLE TX power - * Connection Tx power should only be get after connection created. - * @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc - * @return >= 0 - Power level, < 0 - Invalid - */ -esp_power_level_t esp_ble_tx_power_get(esp_ble_power_type_t power_type); - - -/** - * @brief Initialize BT controller to allocate task and other resource. - * @param cfg: Initial configuration of BT controller. - * This function should be called only once, before any other BT functions are called. - * @return ESP_OK - success, other - failed - */ -esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg); - -/** - * @brief De-initialize BT controller to free resource and delete task. - * - * This function should be called only once, after any other BT functions are called. - * This function is not whole completed, esp_bt_controller_init cannot called after this function. - * @return ESP_OK - success, other - failed - */ -esp_err_t esp_bt_controller_deinit(void); - -/** - * @brief Enable BT controller. - * Due to a known issue, you cannot call esp_bt_controller_enable() a second time - * to change the controller mode dynamically. To change controller mode, call - * esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode. - * @param mode : the mode(BLE/BT/BTDM) to enable. - * @return ESP_OK - success, other - failed - */ -esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode); - -/** - * @brief Disable BT controller - * @return ESP_OK - success, other - failed - */ -esp_err_t esp_bt_controller_disable(void); - -/** - * @brief Get BT controller is initialised/de-initialised/enabled/disabled - * @return status value - */ -esp_bt_controller_status_t esp_bt_controller_get_status(void); - -/** @brief esp_vhci_host_callback - * used for vhci call host function to notify what host need to do - */ -typedef struct esp_vhci_host_callback { - void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ - int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ -} esp_vhci_host_callback_t; - -/** @brief esp_vhci_host_check_send_available - * used for check actively if the host can send packet to controller or not. - * @return true for ready to send, false means cannot send packet - */ -bool esp_vhci_host_check_send_available(void); - -/** @brief esp_vhci_host_send_packet - * host send packet to controller - * @param data the packet point - *,@param len the packet length - */ -void esp_vhci_host_send_packet(uint8_t *data, uint16_t len); - -/** @brief esp_vhci_host_register_callback - * register the vhci referece callback, the call back - * struct defined by vhci_host_callback structure. - * @param callback esp_vhci_host_callback type variable - */ -void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback); - -/** @brief esp_bt_controller_mem_release - * release the memory by mode, if never use the bluetooth mode - * it can release the .bbs, .data and other section to heap. - * The total size is about 70k bytes. - * - * If esp_bt_controller_enable(mode) has already been called, calling - * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will automatically - * release all memory which is not needed for the currently enabled - * Bluetooth controller mode. - * - * For example, calling esp_bt_controller_enable(ESP_BT_MODE_BLE) then - * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will enable BLE modes - * and release memory only used by BT Classic. Also, call esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT) - * is the same. - * - * Note that once BT controller memory is released, the process cannot be reversed. - * If your firmware will later upgrade the Bluetooth controller mode (BLE -> BT Classic or disabled -> enabled) - * then do not call this function. - * - * If user never use bluetooth controller, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) - * before esp_bt_controller_init or after esp_bt_controller_deinit. - * - * For example, user only use bluetooth to config SSID and PASSWORD of WIFI, after config, will never use bluetooth. - * Then, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) after esp_bt_controller_deinit. - * - * @param mode : the mode want to release memory - * @return ESP_OK - success, other - failed - */ -esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode); - -#ifdef __cplusplus -} -#endif - -#endif /* __BT_H__ */ +#pragma once +#warning "This header is deprecated, please use functions defined in esp_bt.h instead." +#include "esp_bt.h" diff --git a/components/bt/include/esp_bt.h b/components/bt/include/esp_bt.h new file mode 100644 index 0000000000..30a535015d --- /dev/null +++ b/components/bt/include/esp_bt.h @@ -0,0 +1,244 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_BT_H__ +#define __ESP_BT_H__ + +#include +#include +#include "esp_err.h" +#include "sdkconfig.h" +#include "esp_task.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Controller config options, depend on config mask. + * Config mask indicate which functions enabled, this means + * some options or parameters of some functions enabled by config mask. + */ +typedef struct { + uint16_t controller_task_stack_size; /*!< Bluetooth controller task stack size */ + uint8_t controller_task_prio; /*!< Bluetooth controller task priority */ + uint8_t hci_uart_no; /*!< If use UART1/2 as HCI IO interface, indicate UART number */ + uint32_t hci_uart_baudrate; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */ +} esp_bt_controller_config_t; + +#ifdef CONFIG_BT_ENABLED + +#ifdef CONFIG_BT_HCI_UART_NO +#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO +#else +#define BT_HCI_UART_NO_DEFAULT 1 +#endif /* BT_HCI_UART_NO_DEFAULT */ + +#ifdef CONFIG_BT_HCI_UART_BAUDRATE +#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE +#else +#define BT_HCI_UART_BAUDRATE_DEFAULT 921600 +#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */ + +#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() { \ + .controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK, \ + .controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ + .hci_uart_no = BT_HCI_UART_NO_DEFAULT, \ + .hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT, \ +}; +#else +#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h"); +#endif + +/** + * @brief Bluetooth mode for controller enable/disable + */ +typedef enum { + ESP_BT_MODE_IDLE = 0x00, /*!< Bluetooth is not running */ + ESP_BT_MODE_BLE = 0x01, /*!< Run BLE mode */ + ESP_BT_MODE_CLASSIC_BT = 0x02, /*!< Run Classic BT mode */ + ESP_BT_MODE_BTDM = 0x03, /*!< Run dual mode */ +} esp_bt_mode_t; + +/** + * @brief Bluetooth controller enable/disable/initialised/de-initialised status + */ +typedef enum { + ESP_BT_CONTROLLER_STATUS_IDLE = 0, + ESP_BT_CONTROLLER_STATUS_INITED, + ESP_BT_CONTROLLER_STATUS_ENABLED, + ESP_BT_CONTROLLER_STATUS_NUM, +} esp_bt_controller_status_t; + + +/** + * @brief BLE tx power type + * ESP_BLE_PWR_TYPE_CONN_HDL0-8: for each connection, and only be set after connetion completed. + * when disconnect, the correspond TX power is not effected. + * ESP_BLE_PWR_TYPE_ADV : for advertising/scan response. + * ESP_BLE_PWR_TYPE_SCAN : for scan. + * ESP_BLE_PWR_TYPE_DEFAULT : if each connection's TX power is not set, it will use this default value. + * if neither in scan mode nor in adv mode, it will use this default value. + * If none of power type is set, system will use ESP_PWR_LVL_P1 as default for ADV/SCAN/CONN0-9. + */ +typedef enum { + ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, /*!< For connection handle 0 */ + ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, /*!< For connection handle 1 */ + ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, /*!< For connection handle 2 */ + ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, /*!< For connection handle 3 */ + ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, /*!< For connection handle 4 */ + ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, /*!< For connection handle 5 */ + ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, /*!< For connection handle 6 */ + ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, /*!< For connection handle 7 */ + ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, /*!< For connection handle 8 */ + ESP_BLE_PWR_TYPE_ADV = 9, /*!< For advertising */ + ESP_BLE_PWR_TYPE_SCAN = 10, /*!< For scan */ + ESP_BLE_PWR_TYPE_DEFAULT = 11, /*!< For default, if not set other, it will use default value */ + ESP_BLE_PWR_TYPE_NUM = 12, /*!< TYPE numbers */ +} esp_ble_power_type_t; + +/** + * @brief Bluetooth TX power level(index), it's just a index corresponding to power(dbm). + */ +typedef enum { + ESP_PWR_LVL_N14 = 0, /*!< Corresponding to -14dbm */ + ESP_PWR_LVL_N11 = 1, /*!< Corresponding to -11dbm */ + ESP_PWR_LVL_N8 = 2, /*!< Corresponding to -8dbm */ + ESP_PWR_LVL_N5 = 3, /*!< Corresponding to -5dbm */ + ESP_PWR_LVL_N2 = 4, /*!< Corresponding to -2dbm */ + ESP_PWR_LVL_P1 = 5, /*!< Corresponding to 1dbm */ + ESP_PWR_LVL_P4 = 6, /*!< Corresponding to 4dbm */ + ESP_PWR_LVL_P7 = 7, /*!< Corresponding to 7dbm */ +} esp_power_level_t; + +/** + * @brief Set BLE TX power + * Connection Tx power should only be set after connection created. + * @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc + * @param power_level: Power level(index) corresponding to absolute value(dbm) + * @return ESP_OK - success, other - failed + */ +esp_err_t esp_ble_tx_power_set(esp_ble_power_type_t power_type, esp_power_level_t power_level); + +/** + * @brief Get BLE TX power + * Connection Tx power should only be get after connection created. + * @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc + * @return >= 0 - Power level, < 0 - Invalid + */ +esp_power_level_t esp_ble_tx_power_get(esp_ble_power_type_t power_type); + + +/** + * @brief Initialize BT controller to allocate task and other resource. + * @param cfg: Initial configuration of BT controller. + * This function should be called only once, before any other BT functions are called. + * @return ESP_OK - success, other - failed + */ +esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg); + +/** + * @brief De-initialize BT controller to free resource and delete task. + * + * This function should be called only once, after any other BT functions are called. + * This function is not whole completed, esp_bt_controller_init cannot called after this function. + * @return ESP_OK - success, other - failed + */ +esp_err_t esp_bt_controller_deinit(void); + +/** + * @brief Enable BT controller. + * Due to a known issue, you cannot call esp_bt_controller_enable() a second time + * to change the controller mode dynamically. To change controller mode, call + * esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode. + * @param mode : the mode(BLE/BT/BTDM) to enable. + * @return ESP_OK - success, other - failed + */ +esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode); + +/** + * @brief Disable BT controller + * @return ESP_OK - success, other - failed + */ +esp_err_t esp_bt_controller_disable(void); + +/** + * @brief Get BT controller is initialised/de-initialised/enabled/disabled + * @return status value + */ +esp_bt_controller_status_t esp_bt_controller_get_status(void); + +/** @brief esp_vhci_host_callback + * used for vhci call host function to notify what host need to do + */ +typedef struct esp_vhci_host_callback { + void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ + int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ +} esp_vhci_host_callback_t; + +/** @brief esp_vhci_host_check_send_available + * used for check actively if the host can send packet to controller or not. + * @return true for ready to send, false means cannot send packet + */ +bool esp_vhci_host_check_send_available(void); + +/** @brief esp_vhci_host_send_packet + * host send packet to controller + * @param data the packet point + *,@param len the packet length + */ +void esp_vhci_host_send_packet(uint8_t *data, uint16_t len); + +/** @brief esp_vhci_host_register_callback + * register the vhci referece callback, the call back + * struct defined by vhci_host_callback structure. + * @param callback esp_vhci_host_callback type variable + */ +void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback); + +/** @brief esp_bt_controller_mem_release + * release the memory by mode, if never use the bluetooth mode + * it can release the .bbs, .data and other section to heap. + * The total size is about 70k bytes. + * + * If esp_bt_controller_enable(mode) has already been called, calling + * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will automatically + * release all memory which is not needed for the currently enabled + * Bluetooth controller mode. + * + * For example, calling esp_bt_controller_enable(ESP_BT_MODE_BLE) then + * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will enable BLE modes + * and release memory only used by BT Classic. Also, call esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT) + * is the same. + * + * Note that once BT controller memory is released, the process cannot be reversed. + * If your firmware will later upgrade the Bluetooth controller mode (BLE -> BT Classic or disabled -> enabled) + * then do not call this function. + * + * If user never use bluetooth controller, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) + * before esp_bt_controller_init or after esp_bt_controller_deinit. + * + * For example, user only use bluetooth to config SSID and PASSWORD of WIFI, after config, will never use bluetooth. + * Then, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) after esp_bt_controller_deinit. + * + * @param mode : the mode want to release memory + * @return ESP_OK - success, other - failed + */ +esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_BT_H__ */ diff --git a/examples/bluetooth/a2dp_sink/main/main.c b/examples/bluetooth/a2dp_sink/main/main.c index 9febabae23..e77aa449cb 100644 --- a/examples/bluetooth/a2dp_sink/main/main.c +++ b/examples/bluetooth/a2dp_sink/main/main.c @@ -23,7 +23,7 @@ #include "esp_system.h" #include "esp_log.h" -#include "bt.h" +#include "esp_bt.h" #include "bt_app_core.h" #include "bt_app_av.h" #include "esp_bt_main.h" diff --git a/examples/bluetooth/ble_adv/main/app_bt.c b/examples/bluetooth/ble_adv/main/app_bt.c index 2b3a5bbf29..74697844ee 100644 --- a/examples/bluetooth/ble_adv/main/app_bt.c +++ b/examples/bluetooth/ble_adv/main/app_bt.c @@ -16,7 +16,7 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_log.h" #include "nvs_flash.h" diff --git a/examples/bluetooth/ble_eddystone/main/esp_eddystone_demo.c b/examples/bluetooth/ble_eddystone/main/esp_eddystone_demo.c index c7719f6fa2..7def481ae0 100644 --- a/examples/bluetooth/ble_eddystone/main/esp_eddystone_demo.c +++ b/examples/bluetooth/ble_eddystone/main/esp_eddystone_demo.c @@ -23,7 +23,7 @@ #include #include -#include "bt.h" +#include "esp_bt.h" #include "nvs_flash.h" #include "esp_log.h" #include "esp_bt_defs.h" diff --git a/examples/bluetooth/ble_ibeacon/main/ibeacon_demo.c b/examples/bluetooth/ble_ibeacon/main/ibeacon_demo.c index 63884a9fc9..e0d71ba8ed 100644 --- a/examples/bluetooth/ble_ibeacon/main/ibeacon_demo.c +++ b/examples/bluetooth/ble_ibeacon/main/ibeacon_demo.c @@ -23,7 +23,7 @@ #include "controller.h" #include "nvs_flash.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gattc_api.h" #include "esp_gatt_defs.h" diff --git a/examples/bluetooth/ble_spp_client/main/spp_client_demo.c b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c index 0cc082297a..b2968b38ca 100644 --- a/examples/bluetooth/ble_spp_client/main/spp_client_demo.c +++ b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c @@ -27,7 +27,7 @@ #include "controller.h" #include "driver/uart.h" -#include "bt.h" +#include "esp_bt.h" #include "nvs_flash.h" #include "esp_bt_device.h" #include "esp_gap_ble_api.h" diff --git a/examples/bluetooth/ble_spp_server/main/ble_spp_server_demo.c b/examples/bluetooth/ble_spp_server/main/ble_spp_server_demo.c index f0d1567a21..399dc7e1df 100644 --- a/examples/bluetooth/ble_spp_server/main/ble_spp_server_demo.c +++ b/examples/bluetooth/ble_spp_server/main/ble_spp_server_demo.c @@ -19,7 +19,7 @@ #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" -#include "bt.h" +#include "esp_bt.h" #include "driver/uart.h" #include "string.h" diff --git a/examples/bluetooth/blufi/main/blufi_example_main.c b/examples/bluetooth/blufi/main/blufi_example_main.c index dfb2da4937..5895d5e285 100644 --- a/examples/bluetooth/blufi/main/blufi_example_main.c +++ b/examples/bluetooth/blufi/main/blufi_example_main.c @@ -23,7 +23,7 @@ #include "esp_event_loop.h" #include "esp_log.h" #include "nvs_flash.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_blufi_api.h" #include "esp_bt_defs.h" diff --git a/examples/bluetooth/blufi/main/blufi_security.c b/examples/bluetooth/blufi/main/blufi_security.c index 0a6d6a3442..fae034dd21 100644 --- a/examples/bluetooth/blufi/main/blufi_security.c +++ b/examples/bluetooth/blufi/main/blufi_security.c @@ -23,7 +23,7 @@ #include "esp_event_loop.h" #include "esp_log.h" #include "nvs_flash.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_blufi_api.h" #include "esp_bt_defs.h" diff --git a/examples/bluetooth/controller_hci_uart/main/controller_hci_uart_demo.c b/examples/bluetooth/controller_hci_uart/main/controller_hci_uart_demo.c index 333b94e4a7..872aca64f6 100644 --- a/examples/bluetooth/controller_hci_uart/main/controller_hci_uart_demo.c +++ b/examples/bluetooth/controller_hci_uart/main/controller_hci_uart_demo.c @@ -15,7 +15,7 @@ #include #include #include "nvs_flash.h" -#include "bt.h" +#include "esp_bt.h" #include "driver/uart.h" #include "esp_log.h" diff --git a/examples/bluetooth/gatt_client/main/gattc_demo.c b/examples/bluetooth/gatt_client/main/gattc_demo.c index 645d6b64c7..d9bb810f92 100644 --- a/examples/bluetooth/gatt_client/main/gattc_demo.c +++ b/examples/bluetooth/gatt_client/main/gattc_demo.c @@ -31,7 +31,7 @@ #include "nvs_flash.h" #include "controller.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gattc_api.h" #include "esp_gatt_defs.h" diff --git a/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c b/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c index 463dfc8492..a71d496df3 100644 --- a/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c +++ b/examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c @@ -30,7 +30,7 @@ #include "nvs_flash.h" #include "controller.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gattc_api.h" #include "esp_gatt_defs.h" diff --git a/examples/bluetooth/gatt_security_server/main/example_ble_sec_gatts_demo.c b/examples/bluetooth/gatt_security_server/main/example_ble_sec_gatts_demo.c index 3a71974d49..eb328c4f66 100644 --- a/examples/bluetooth/gatt_security_server/main/example_ble_sec_gatts_demo.c +++ b/examples/bluetooth/gatt_security_server/main/example_ble_sec_gatts_demo.c @@ -18,7 +18,7 @@ #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" diff --git a/examples/bluetooth/gatt_server/main/gatts_demo.c b/examples/bluetooth/gatt_server/main/gatts_demo.c index b97a4fafbb..1bc35e4fd5 100644 --- a/examples/bluetooth/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/gatt_server/main/gatts_demo.c @@ -31,7 +31,7 @@ #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" diff --git a/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c b/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c index 5c46a75ff7..25aee69649 100644 --- a/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c +++ b/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c @@ -26,7 +26,7 @@ #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" - #include "bt.h" + #include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" diff --git a/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c b/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c index b734d7fa45..6e19d57b16 100644 --- a/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c +++ b/examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c @@ -34,7 +34,7 @@ #include "nvs_flash.h" #include "controller.h" -#include "bt.h" +#include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gattc_api.h" #include "esp_gatt_defs.h" From 022b4f325145c567c804c564eac1cb387041b209 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 13:00:11 +0800 Subject: [PATCH 21/30] openssl: add feature check for MBEDTLS_SSL_ALPN Fixes https://github.com/espressif/esp-idf/issues/1342 --- components/openssl/platform/ssl_pm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 5545f958e4..cd9960da12 100755 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -154,7 +154,11 @@ int ssl_pm_new(SSL *ssl) } if (ssl->ctx->ssl_alpn.alpn_status == ALPN_ENABLE) { - mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list ); +#ifdef MBEDTLS_SSL_ALPN + mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list ); +#else + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "CONFIG_MBEDTLS_SSL_ALPN must be enabled to use ALPN", -1); +#endif // MBEDTLS_SSL_ALPN } mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); From df74211fc37382f68811da5489f6e1466aad4147 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 16:06:11 +0800 Subject: [PATCH 22/30] newlib/time: fix compilation error when only RTC is used as clock source Fixes https://github.com/espressif/esp-idf/issues/1245 --- components/newlib/time.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/newlib/time.c b/components/newlib/time.c index edb9a59fe0..8aeb5b37ef 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -74,13 +74,15 @@ static uint64_t get_rtc_time_us() // when RTC is used to persist time, two RTC_STORE registers are used to store boot time #elif defined(WITH_FRC1) static uint64_t s_boot_time; -#endif +#endif // WITH_RTC #if defined(WITH_RTC) || defined(WITH_FRC1) static _lock_t s_boot_time_lock; #endif -#ifdef WITH_RTC +// Offset between FRC timer and the RTC. +// Initialized after reset or light sleep. +#if defined(WITH_RTC) && defined(WITH_FRC1) uint64_t s_microseconds_offset; #endif @@ -171,10 +173,14 @@ static uint64_t get_time_since_boot() { uint64_t microseconds = 0; #ifdef WITH_FRC1 +#ifdef WITH_RTC microseconds = s_microseconds_offset + esp_timer_get_time(); +#else + microseconds = esp_timer_get_time(); +#endif // WITH_RTC #elif defined(WITH_RTC) microseconds = get_rtc_time_us(); -#endif +#endif // WITH_FRC1 return microseconds; } #endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) From a63ace15bd8fbf6ab7ab57c2403a4c2bcbe0a349 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 19:58:39 +0800 Subject: [PATCH 23/30] vfs/fatfs: fix stat call failing when called for mount point FATFS does not support f_stat call for drive root. When handling stat for drive root, don't call f_stat and just return struct st with S_IFDIR flag set. Closes #984 --- components/fatfs/src/vfs_fat.c | 20 ++++++++++++++++++-- components/fatfs/test/test_fatfs_common.c | 19 ++++++++++++------- components/fatfs/test/test_fatfs_common.h | 2 +- components/fatfs/test/test_fatfs_sdmmc.c | 2 +- components/fatfs/test/test_fatfs_spiflash.c | 2 +- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index a5a12d5b65..6829abdbd9 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -392,8 +392,23 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) return 0; } +static inline mode_t get_stat_mode(bool is_dir) +{ + return S_IRWXU | S_IRWXG | S_IRWXO | + ((is_dir) ? S_IFDIR : S_IFREG); +} + static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) { + if (strcmp(path, "/") == 0) { + /* FatFS f_stat function does not work for the drive root. + * Just pretend that this is a directory. + */ + memset(st, 0, sizeof(*st)); + st->st_mode = get_stat_mode(true); + return 0; + } + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; _lock_acquire(&fat_ctx->lock); prepend_drive_to_path(fat_ctx, &path, NULL); @@ -405,9 +420,10 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) errno = fresult_to_errno(res); return -1; } + + memset(st, 0, sizeof(*st)); st->st_size = info.fsize; - st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | - ((info.fattrib & AM_DIR) ? S_IFDIR : S_IFREG); + st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0); struct tm tm; uint16_t fdate = info.fdate; tm.tm_mday = fdate & 0x1f; diff --git a/components/fatfs/test/test_fatfs_common.c b/components/fatfs/test/test_fatfs_common.c index ca27cdd4ba..799d36f1b7 100644 --- a/components/fatfs/test/test_fatfs_common.c +++ b/components/fatfs/test/test_fatfs_common.c @@ -125,15 +125,15 @@ void test_fatfs_lseek(const char* filename) TEST_ASSERT_EQUAL(0, fclose(f)); } -void test_fatfs_stat(const char* filename) +void test_fatfs_stat(const char* filename, const char* root_dir) { struct tm tm; - tm.tm_year = 2016 - 1900; - tm.tm_mon = 0; - tm.tm_mday = 10; - tm.tm_hour = 16; - tm.tm_min = 30; - tm.tm_sec = 0; + tm.tm_year = 2017 - 1900; + tm.tm_mon = 11; + tm.tm_mday = 8; + tm.tm_hour = 19; + tm.tm_min = 51; + tm.tm_sec = 10; time_t t = mktime(&tm); printf("Setting time: %s", asctime(&tm)); struct timeval now = { .tv_sec = t }; @@ -151,6 +151,11 @@ void test_fatfs_stat(const char* filename) TEST_ASSERT(st.st_mode & S_IFREG); TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); + + memset(&st, 0, sizeof(st)); + TEST_ASSERT_EQUAL(0, stat(root_dir, &st)); + TEST_ASSERT(st.st_mode & S_IFDIR); + TEST_ASSERT_FALSE(st.st_mode & S_IFREG); } void test_fatfs_unlink(const char* filename) diff --git a/components/fatfs/test/test_fatfs_common.h b/components/fatfs/test/test_fatfs_common.h index dcc31e37d4..36e7ca62b6 100644 --- a/components/fatfs/test/test_fatfs_common.h +++ b/components/fatfs/test/test_fatfs_common.h @@ -43,7 +43,7 @@ void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count); void test_fatfs_lseek(const char* filename); -void test_fatfs_stat(const char* filename); +void test_fatfs_stat(const char* filename, const char* root_dir); void test_fatfs_unlink(const char* filename); diff --git a/components/fatfs/test/test_fatfs_sdmmc.c b/components/fatfs/test/test_fatfs_sdmmc.c index 6935fc7083..3610511a91 100644 --- a/components/fatfs/test/test_fatfs_sdmmc.c +++ b/components/fatfs/test/test_fatfs_sdmmc.c @@ -105,7 +105,7 @@ TEST_CASE("(SD) can lseek", "[fatfs][sdcard][ignore]") TEST_CASE("(SD) stat returns correct values", "[fatfs][ignore]") { test_setup(); - test_fatfs_stat("/sdcard/stat.txt"); + test_fatfs_stat("/sdcard/stat.txt", "/sdcard"); test_teardown(); } diff --git a/components/fatfs/test/test_fatfs_spiflash.c b/components/fatfs/test/test_fatfs_spiflash.c index bcc396b93c..47116cd8d5 100644 --- a/components/fatfs/test/test_fatfs_spiflash.c +++ b/components/fatfs/test/test_fatfs_spiflash.c @@ -100,7 +100,7 @@ TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]") TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]") { test_setup(); - test_fatfs_stat("/spiflash/stat.txt"); + test_fatfs_stat("/spiflash/stat.txt", "/spiflash"); test_teardown(); } From dfee6e825a666784023361d547695481702cf0a6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 20:03:24 +0800 Subject: [PATCH 24/30] vfs/fatfs: use structures with bit fields for FAT date/time Replace explicit masks and shifts with bit fields when working with FATFS date and time representations. Also zero-initialize remaining members of struct tm. Fixes https://github.com/espressif/esp-idf/issues/1369. --- components/fatfs/src/vfs_fat.c | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index 6829abdbd9..6b0180cb4d 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -43,6 +43,25 @@ typedef struct { struct dirent cur_dirent; } vfs_fat_dir_t; +/* Date and time storage formats in FAT */ +typedef union { + struct { + uint16_t mday : 5; /* Day of month, 1 - 31 */ + uint16_t mon : 4; /* Month, 1 - 12 */ + uint16_t year : 7; /* Year, counting from 1980. E.g. 37 for 2017 */ + }; + uint16_t as_int; +} fat_date_t; + +typedef union { + struct { + uint16_t sec : 5; /* Seconds divided by 2. E.g. 21 for 42 seconds */ + uint16_t min : 6; /* Minutes, 0 - 59 */ + uint16_t hour : 5; /* Hour, 0 - 23 */ + }; + uint16_t as_int; +} fat_time_t; + static const char* TAG = "vfs_fat"; static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size); @@ -424,19 +443,16 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) memset(st, 0, sizeof(*st)); st->st_size = info.fsize; st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0); - struct tm tm; - uint16_t fdate = info.fdate; - tm.tm_mday = fdate & 0x1f; - fdate >>= 5; - tm.tm_mon = (fdate & 0xf) - 1; - fdate >>=4; - tm.tm_year = fdate + 80; - uint16_t ftime = info.ftime; - tm.tm_sec = (ftime & 0x1f) * 2; - ftime >>= 5; - tm.tm_min = (ftime & 0x3f); - ftime >>= 6; - tm.tm_hour = (ftime & 0x1f); + fat_date_t fdate = { .as_int = info.fdate }; + fat_time_t ftime = { .as_int = info.ftime }; + struct tm tm = { + .tm_mday = fdate.mday, + .tm_mon = fdate.mon - 1, /* unlike tm_mday, tm_mon is zero-based */ + .tm_year = fdate.year + 80, + .tm_sec = ftime.sec * 2, + .tm_min = ftime.min, + .tm_hour = ftime.hour + }; st->st_mtime = mktime(&tm); return 0; } From 65fa71f565e36c20fba63cb486384e8365235366 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 7 Dec 2017 11:50:39 +0800 Subject: [PATCH 25/30] docs: add description of ULP I2C instructions --- docs/api-guides/ulp_instruction_set.rst | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/api-guides/ulp_instruction_set.rst b/docs/api-guides/ulp_instruction_set.rst index fa280b8b81..c4d21947cc 100755 --- a/docs/api-guides/ulp_instruction_set.rst +++ b/docs/api-guides/ulp_instruction_set.rst @@ -667,6 +667,45 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol 1: ADC R1, 0, 1 // Measure value using ADC1 pad 2 and store result into R1 +**I2C_RD** - read single byte from I2C slave +---------------------------------------------- + +**Syntax** + - **I2C_RD** *Sub_addr, High, Low, Slave_sel* + +**Operands** + - *Sub_addr* – Address within the I2C slave to read. + - *High*, *Low* — Define range of bits to read. Bits outside of [High, Low] range are masked. + - *Slave_sel* - Index of I2C slave address to use. + +**Description** + ``I2C_RD`` instruction reads one byte from I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``. + 8 bits of read result is stored into `R0` register. + +**Examples**:: + + 1: I2C_RD 0x10, 7, 0, 0 // Read byte from sub-address 0x10 of slave with address set in SENS_I2C_SLAVE_ADDR0 + + +**I2C_WR** - write single byte to I2C slave +---------------------------------------------- + +**Syntax** + - **I2C_WR** *Sub_addr, Value, High, Low, Slave_sel* + +**Operands** + - *Sub_addr* – Address within the I2C slave to write. + - *Value* – 8-bit value to be written. + - *High*, *Low* — Define range of bits to write. Bits outside of [High, Low] range are masked. + - *Slave_sel* - Index of I2C slave address to use. + +**Description** + ``I2C_WR`` instruction writes one byte to I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``. + +**Examples**:: + + 1: I2C_WR 0x20, 0x33, 7, 0, 1 // Write byte 0x33 to sub-address 0x20 of slave with address set in SENS_I2C_SLAVE_ADDR1. + **REG_RD** – read from peripheral register ------------------------------------------ From b6a6973b44889aed4dc6f01b1079207fa8580a4f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 7 Dec 2017 14:41:17 +0800 Subject: [PATCH 26/30] ulp: mention that instructions array must be declared in local scope Closes https://github.com/espressif/esp-idf/issues/1327 --- components/ulp/README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/ulp/README.rst b/components/ulp/README.rst index af1a748f89..f4c027cd70 100644 --- a/components/ulp/README.rst +++ b/components/ulp/README.rst @@ -18,6 +18,10 @@ In addition to the existing binutils port for the ESP32 ULP coprocessor, it is p The ``program`` array is an array of ``ulp_insn_t``, i.e. ULP coprocessor instructions. Each ``I_XXX`` preprocessor define translates into a single 32-bit instruction. Arguments of these preprocessor defines can be register numbers (``R0 — R3``) and literal constants. See `ULP coprocessor instruction defines`_ section for descriptions of instructions and arguments they take. +.. note:: + + Because some of the instruction macros expand to inline function calls, defining such array in global scope will cause the compiler to produce an "initializer element is not constant" error. To fix this error, move the definition of instructions array into local scope. + Load and store instructions use addresses expressed in 32-bit words. Address 0 corresponds to the first word of ``RTC_SLOW_MEM`` (which is address 0x50000000 as seen by the main CPUs). To generate branch instructions, special ``M_`` preprocessor defines are used. ``M_LABEL`` define can be used to define a branch target. Label identifier is a 16-bit integer. ``M_Bxxx`` defines can be used to generate branch instructions with target set to a particular label. From ca751648fa76153c21da9f3adf28b5b7ba0051a1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 7 Dec 2017 17:11:24 +0800 Subject: [PATCH 27/30] ulp: document the need to wait for RTC to be ready for wakeup --- components/soc/esp32/include/soc/rtc_cntl_reg.h | 11 ++++++++++- docs/api-guides/ulp_instruction_set.rst | 12 +++++++++--- examples/system/ulp/main/ulp/pulse_cnt.S | 5 +++++ examples/system/ulp_adc/main/ulp/adc.S | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/components/soc/esp32/include/soc/rtc_cntl_reg.h b/components/soc/esp32/include/soc/rtc_cntl_reg.h index ffcbb3c033..d54a7dde7c 100644 --- a/components/soc/esp32/include/soc/rtc_cntl_reg.h +++ b/components/soc/esp32/include/soc/rtc_cntl_reg.h @@ -1830,7 +1830,16 @@ #define RTC_CNTL_SCRATCH7_V 0xFFFFFFFF #define RTC_CNTL_SCRATCH7_S 0 -#define RTC_CNTL_DIAG0_REG (DR_REG_RTCCNTL_BASE + 0xc0) +#define RTC_CNTL_LOW_POWER_ST_REG (DR_REG_RTCCNTL_BASE + 0xc0) +/* RTC_CNTL_RDY_FOR_WAKEUP : R/0; bitpos:[19]; default: 0 */ +/*description: 1 if RTC controller is ready to execute WAKE instruction, 0 otherwise */ +#define RTC_CNTL_RDY_FOR_WAKEUP (BIT(19)) +#define RTC_CNTL_RDY_FOR_WAKEUP_M (BIT(19)) +#define RTC_CNTL_RDY_FOR_WAKEUP_V 0x1 +#define RTC_CNTL_RDY_FOR_WAKEUP_S 19 + +/* Compatibility definition */ +#define RTC_CNTL_DIAG0_REG RTC_CNTL_LOW_POWER_ST_REG /* RTC_CNTL_LOW_POWER_DIAG0 : RO ;bitpos:[31:0] ;default: 0 ; */ /*description: */ #define RTC_CNTL_LOW_POWER_DIAG0 0xFFFFFFFF diff --git a/docs/api-guides/ulp_instruction_set.rst b/docs/api-guides/ulp_instruction_set.rst index c4d21947cc..62cbe3bd5a 100755 --- a/docs/api-guides/ulp_instruction_set.rst +++ b/docs/api-guides/ulp_instruction_set.rst @@ -573,11 +573,17 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - If the SoC is not in deep sleep mode, and ULP interrupt bit (RTC_CNTL_ULP_CP_INT_ENA) is set in RTC_CNTL_INT_ENA_REG register, RTC interrupt will be triggered. + Note that before using WAKE instruction, ULP program may needs to wait until RTC controller is ready to wake up the main CPU. This is indicated using RTC_CNTL_RDY_FOR_WAKEUP bit of RTC_CNTL_LOW_POWER_ST_REG register. If WAKE instruction is executed while RTC_CNTL_RDY_FOR_WAKEUP is zero, it has no effect (wake up does not occur). + **Examples**:: - 1: WAKE // Trigger wake up - REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN) - HALT // Stop the ULP program + 1: is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit + READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) + AND r0, r0, 1 + JUMP is_rdy_for_wakeup, eq // Retry until the bit is set + WAKE // Trigger wake up + REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN) + HALT // Stop the ULP program // After these instructions, SoC will wake up, // and ULP will not run again until started by the main program. diff --git a/examples/system/ulp/main/ulp/pulse_cnt.S b/examples/system/ulp/main/ulp/pulse_cnt.S index ba7b453442..e573e3244f 100644 --- a/examples/system/ulp/main/ulp/pulse_cnt.S +++ b/examples/system/ulp/main/ulp/pulse_cnt.S @@ -130,6 +130,11 @@ edge_detected: .global wake_up wake_up: + /* Check if the system can be woken up */ + READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) + and r0, r0, 1 + jump wake_up, eq + /* Wake up the SoC, end program */ wake halt diff --git a/examples/system/ulp_adc/main/ulp/adc.S b/examples/system/ulp_adc/main/ulp/adc.S index 1f0f6d5ac9..70d0439c81 100644 --- a/examples/system/ulp_adc/main/ulp/adc.S +++ b/examples/system/ulp_adc/main/ulp/adc.S @@ -105,7 +105,7 @@ exit: .global wake_up wake_up: /* Check if the system can be woken up */ - READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1) + READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) and r0, r0, 1 jump exit, eq From bee1afaf6d5642c020a2b20e95d0ff4bba4e7623 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 16:14:20 +0800 Subject: [PATCH 28/30] time: rename time source option from FRC to "high-resolution timer" libc time function now rely on esp_timer_get_time as the source of high-resolution time, rather than FRC1 timer. Internally, on the ESP32 esp_timer implementation uses FRC2 timer. - Change help text and labels in Kconfig to use "high-resolution timer" instead of FRC1. Keep existing Kconfig option name to be backwards compatible. - Change references to "FRC1" in the source code to "FRC". --- components/esp32/Kconfig | 20 ++++++++++++-------- components/newlib/time.c | 34 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index f1d31fd199..426045ad2f 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -631,6 +631,10 @@ config BROWNOUT_DET_LVL default 7 if BROWNOUT_DET_LVL_SEL_7 +# Note about the use of "FRC1" name: currently FRC1 timer is not used for +# high resolution timekeeping anymore. Instead the esp_timer API, implemented +# using FRC2 timer, is used. +# FRC1 name in the option name is kept for compatibility. choice ESP32_TIME_SYSCALL prompt "Timers used for gettimeofday function" default ESP32_TIME_SYSCALL_USE_RTC_FRC1 @@ -638,12 +642,12 @@ choice ESP32_TIME_SYSCALL This setting defines which hardware timers are used to implement 'gettimeofday' and 'time' functions in C library. - - If only FRC1 timer is used, gettimeofday will provide time at - microsecond resolution. Time will not be preserved when going - into deep sleep mode. - - If both FRC1 and RTC timers are used, timekeeping will + - If both high-resolution and RTC timers are used, timekeeping will continue in deep sleep. Time will be reported at 1 microsecond - resolution. + resolution. This is the default, and the recommended option. + - If only high-resolution timer is used, gettimeofday will + provide time at microsecond resolution. + Time will not be preserved when going into deep sleep mode. - If only RTC timer is used, timekeeping will continue in deep sleep, but time will be measured at 6.(6) microsecond resolution. Also the gettimeofday function itself may take @@ -653,12 +657,12 @@ choice ESP32_TIME_SYSCALL - When RTC is used for timekeeping, two RTC_STORE registers are used to keep time in deep sleep mode. +config ESP32_TIME_SYSCALL_USE_RTC_FRC1 + bool "RTC and high-resolution timer" config ESP32_TIME_SYSCALL_USE_RTC bool "RTC" -config ESP32_TIME_SYSCALL_USE_RTC_FRC1 - bool "RTC and FRC1" config ESP32_TIME_SYSCALL_USE_FRC1 - bool "FRC1" + bool "High-resolution timer" config ESP32_TIME_SYSCALL_USE_NONE bool "None" endchoice diff --git a/components/newlib/time.c b/components/newlib/time.c index 8aeb5b37ef..8443d2b54b 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -42,7 +42,7 @@ #endif #if defined( CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 ) -#define WITH_FRC1 1 +#define WITH_FRC 1 #endif #ifdef WITH_RTC @@ -72,21 +72,21 @@ static uint64_t get_rtc_time_us() // s_boot_time: time from Epoch to the first boot time #ifdef WITH_RTC // when RTC is used to persist time, two RTC_STORE registers are used to store boot time -#elif defined(WITH_FRC1) +#elif defined(WITH_FRC) static uint64_t s_boot_time; #endif // WITH_RTC -#if defined(WITH_RTC) || defined(WITH_FRC1) +#if defined(WITH_RTC) || defined(WITH_FRC) static _lock_t s_boot_time_lock; #endif // Offset between FRC timer and the RTC. // Initialized after reset or light sleep. -#if defined(WITH_RTC) && defined(WITH_FRC1) +#if defined(WITH_RTC) && defined(WITH_FRC) uint64_t s_microseconds_offset; #endif -#if defined(WITH_RTC) || defined(WITH_FRC1) +#if defined(WITH_RTC) || defined(WITH_FRC) static void set_boot_time(uint64_t time_us) { _lock_acquire(&s_boot_time_lock); @@ -111,7 +111,7 @@ static uint64_t get_boot_time() _lock_release(&s_boot_time_lock); return result; } -#endif //defined(WITH_RTC) || defined(WITH_FRC1) +#endif //defined(WITH_RTC) || defined(WITH_FRC) void esp_clk_slowclk_cal_set(uint32_t new_cal) @@ -141,10 +141,10 @@ uint32_t esp_clk_slowclk_cal_get() void esp_set_time_from_rtc() { -#if defined( WITH_FRC1 ) && defined( WITH_RTC ) +#if defined( WITH_FRC ) && defined( WITH_RTC ) // initialize time from RTC clock s_microseconds_offset = get_rtc_time_us() - esp_timer_get_time(); -#endif // WITH_FRC1 && WITH_RTC +#endif // WITH_FRC && WITH_RTC } uint64_t esp_clk_rtc_time(void) @@ -168,11 +168,11 @@ clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) return (clock_t) tv.tv_sec; } -#if defined( WITH_FRC1 ) || defined( WITH_RTC ) +#if defined( WITH_FRC ) || defined( WITH_RTC ) static uint64_t get_time_since_boot() { uint64_t microseconds = 0; -#ifdef WITH_FRC1 +#ifdef WITH_FRC #ifdef WITH_RTC microseconds = s_microseconds_offset + esp_timer_get_time(); #else @@ -180,15 +180,15 @@ static uint64_t get_time_since_boot() #endif // WITH_RTC #elif defined(WITH_RTC) microseconds = get_rtc_time_us(); -#endif // WITH_FRC1 +#endif // WITH_FRC return microseconds; } -#endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) +#endif // defined( WITH_FRC ) || defined( WITH_RTC ) int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { (void) tz; -#if defined( WITH_FRC1 ) || defined( WITH_RTC ) +#if defined( WITH_FRC ) || defined( WITH_RTC ) if (tv) { uint64_t microseconds = get_boot_time() + get_time_since_boot(); tv->tv_sec = microseconds / 1000000; @@ -198,13 +198,13 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) #else __errno_r(r) = ENOSYS; return -1; -#endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) +#endif // defined( WITH_FRC ) || defined( WITH_RTC ) } int settimeofday(const struct timeval *tv, const struct timezone *tz) { (void) tz; -#if defined( WITH_FRC1 ) || defined( WITH_RTC ) +#if defined( WITH_FRC ) || defined( WITH_RTC ) if (tv) { uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec; uint64_t since_boot = get_time_since_boot(); @@ -239,7 +239,7 @@ unsigned int sleep(unsigned int seconds) uint32_t system_get_time(void) { -#if defined( WITH_FRC1 ) || defined( WITH_RTC ) +#if defined( WITH_FRC ) || defined( WITH_RTC ) return get_time_since_boot(); #else return 0; @@ -250,7 +250,7 @@ uint32_t system_get_current_time(void) __attribute__((alias("system_get_time"))) uint32_t system_relative_time(uint32_t current_time) { -#if defined( WITH_FRC1 ) || defined( WITH_RTC ) +#if defined( WITH_FRC ) || defined( WITH_RTC ) return get_time_since_boot() - current_time; #else return 0; From a99483a727a772c92db9dc445c0a6551c4868570 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 11 Dec 2017 12:11:24 +0800 Subject: [PATCH 29/30] docs: add information about execution time of ULP instructions --- .gitlab-ci.yml | 7 ++ components/soc/esp32/test/test_rtc_clk.c | 9 ++ docs/api-guides/ulp_instruction_set.rst | 128 +++++++++++++++++++---- 3 files changed, 125 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b327068c2a..6e4c2eccaf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -751,6 +751,13 @@ UT_003_08: - UT_T1_1 - UT_single_core +UT_003_09: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + - UT_single_core + UT_004_01: <<: *unit_test_template tags: diff --git a/components/soc/esp32/test/test_rtc_clk.c b/components/soc/esp32/test/test_rtc_clk.c index 00fb0040ff..3773f9401e 100644 --- a/components/soc/esp32/test/test_rtc_clk.c +++ b/components/soc/esp32/test/test_rtc_clk.c @@ -112,6 +112,15 @@ static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) ref_clock_deinit(); } +TEST_CASE("Calculate 8M clock frequency", "[rtc_clk]") +{ + // calibrate 8M/256 clock against XTAL, get 8M/256 clock period + uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100); + uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period; + printf("RTC_FAST_CLK=%d Hz\n", rtc_fast_freq_hz); + TEST_ASSERT_INT32_WITHIN(500000, RTC_FAST_CLK_FREQ_APPROX, rtc_fast_freq_hz); +} + TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]") { test_clock_switching(rtc_clk_cpu_freq_set); diff --git a/docs/api-guides/ulp_instruction_set.rst b/docs/api-guides/ulp_instruction_set.rst index 62cbe3bd5a..893fcc56e1 100755 --- a/docs/api-guides/ulp_instruction_set.rst +++ b/docs/api-guides/ulp_instruction_set.rst @@ -62,14 +62,32 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol ST R2, R1, 0 // write value of R2 into the third array element, // i.e. array[2] +Note about instruction execution time +------------------------------------- + +ULP coprocessor is clocked from RTC_FAST_CLK, which is normally derived from the internal 8MHz oscillator. Applications which need to know exact ULP clock frequency can calibrate it against the main XTAL clock:: + + #include "soc/rtc.h" + + // calibrate 8M/256 clock against XTAL, get 8M/256 clock period + uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100); + uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period; + +ULP coprocessor needs 2 clock cycle to fetch each instuction (fetching is not pipelined), plus certain number of cycles to execute, depending on the instruction. See description of each instruction for details on the execution time. + +Note that when accessing RTC memories and RTC registers, ULP coprocessor has lower priority than the main CPUs. This means that ULP coprocessor execution may be suspended while the main CPUs access same memory region as the ULP. + + **NOP** - no operation ---------------------- -**Syntax:** +**Syntax** **NOP** -**Operands:** +**Operands** None -**Description:** +**Cycles** + 2 (fetch) + 1 (execute) +**Description** No operation is performed. Only the PC is incremented. **Example**:: @@ -80,20 +98,22 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **ADD** - Add to register ------------------------- -**Syntax:** +**Syntax** **ADD** *Rdst, Rsrc1, Rsrc2* **ADD** *Rdst, Rsrc1, imm* -**Operands:** +**Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *Imm* - 16-bit signed value +**Cycles** + 2 (fetch) + 2 (execute) -**Description:** +**Description** The instruction adds source register to another source register or to a 16-bit signed value and stores result to the destination register. **Examples**:: @@ -115,21 +135,24 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **SUB** - Subtract from register -------------------------------- -**Syntax:** +**Syntax** **SUB** *Rdst, Rsrc1, Rsrc2* **SUB** *Rdst, Rsrc1, imm* -**Operands:** +**Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *Imm* - 16-bit signed value -**Description:** +**Cycles** + 2 (fetch) + 2 (execute) + +**Description** The instruction subtracts the source register from another source register or subtracts 16-bit signed value from a source register, and stores result to the destination register. -**Examples:**:: +**Examples**:: 1: SUB R1, R2, R3 //R1 = R2 - R3 @@ -146,21 +169,24 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **AND** - Logical AND of two operands ------------------------------------- -**Syntax:** +**Syntax** **AND** *Rdst, Rsrc1, Rsrc2* **AND** *Rdst, Rsrc1, imm* -**Operands:** +**Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *Imm* - 16-bit signed value -**Description:** +**Cycles** + 2 (fetch) + 2 (execute) + +**Description** The instruction does logical AND of a source register and another source register or 16-bit signed value and stores result to the destination register. -**Example**:: +**Examples**:: 1: AND R1, R2, R3 //R1 = R2 & R3 @@ -183,12 +209,14 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **OR** *Rdst, Rsrc1, imm* - **Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *Imm* - 16-bit signed value + +**Cycles** + 2 (fetch) + 2 (execute) **Description** The instruction does logical OR of a source register and another source register or 16-bit signed value and stores result to the destination register. @@ -223,6 +251,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Rsrc2* - Register R[0..3] - *Imm* - 16-bit signed value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction does logical shift to left of source register to number of bits from another source register or 16-bit signed value and store result to the destination register. @@ -255,6 +286,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol *Rsrc2* - Register R[0..3] *Imm* - 16-bit signed value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction does logical shift to right of source register to number of bits from another source register or 16-bit signed value and store result to the destination register. @@ -286,6 +320,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Rsrc* – Register R[0..3] - *Imm* – 16-bit signed value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction move to destination register value from source register or 16-bit signed value. @@ -318,6 +355,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Rdst* – Register R[0..3], address of the destination, in 32-bit words - *Offset* – 10-bit signed value, offset in bytes +**Cycles** + 2 (fetch) + 4 (execute) + **Description** The instruction stores the 16-bit value of Rsrc to the lower half-word of memory with address Rdst+offset. The upper half-word is written with the current program counter (PC), expressed in words, shifted left by 5 bits:: @@ -352,6 +392,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol *Offset* – 10-bit signed value, offset in bytes +**Cycles** + 2 (fetch) + 4 (execute) + **Description** The instruction loads lower 16-bit half-word from memory with address Rsrc+offset into the destination register Rdst:: @@ -395,6 +438,8 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - EQ – jump if last ALU operation result was zero - OV – jump if last ALU has set overflow flag +**Cycles** + 2 (fetch) + 2 (execute) **Description** The instruction makes jump to the specified address. Jump can be either unconditional or based on an ALU flag. @@ -432,6 +477,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *LT* (less than) – jump if value in R0 < threshold +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of R0 register value and the threshold value. @@ -461,6 +509,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *LT* (less than) – jump if value in stage_cnt < threshold - *GT* (greater than) – jump if value in stage_cnt > threshold +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of count register value and threshold value. @@ -487,6 +538,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Description** The instruction sets the stage count register to 0 +**Cycles** + 2 (fetch) + 2 (execute) + **Examples**:: 1: STAGE_RST // Reset stage count register @@ -502,6 +556,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** - *Value* – 8 bits value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction increments stage count register by given value. @@ -525,6 +582,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** - *Value* – 8 bits value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction decrements stage count register by given value. @@ -548,17 +608,21 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** No operands + +**Cycles** + 2 (fetch) + 2 (execute) + **Description** - The instruction halt the processor to the power down mode + The instruction halts the ULP coprocessor and restarts ULP wakeup timer, if it is enabled. **Examples**:: - 1: HALT // Move chip to powerdown + 1: HALT // Halt the coprocessor -**WAKE** – wakeup the chip --------------------------- +**WAKE** – Wake up the chip +--------------------------- **Syntax** **WAKE** @@ -566,6 +630,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** No operands +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction sends an interrupt from ULP to RTC controller. @@ -598,6 +665,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** - *sleep_reg* – 0..4, selects one of ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers. +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction selects which of the ``SENS_ULP_CP_SLEEP_CYCx_REG`` (x = 0..4) register values is to be used by the ULP wakeup timer as wakeup period. By default, the value from ``SENS_ULP_CP_SLEEP_CYC0_REG`` is used. @@ -618,6 +688,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** - *Cycles* – number of cycles for wait +**Cycles** + 2 (fetch) + *Cycles* (execute) + **Description** The instruction delays for given number of cycles. @@ -641,6 +714,8 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Rdst* – Destination Register R[0..3], result will be stored to this register - *Wait_Delay* – number of cycles used to perform the measurement +**Cycles** + 2 (fetch) + *Wait_Delay* + 3 * TSENS_CLK **Description** The instruction performs measurement using TSENS and stores the result into a general purpose register. @@ -666,6 +741,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Sar_sel* – Select ADC: 0 = SARADC1, 1 = SARADC2 - *Mux* - selected PAD, SARADC Pad[Mux+1] is enabled +**Cycles** + 2 (fetch) + 21 + max(1, SAR_AMP_WAIT1) + max(1, SAR_AMP_WAIT2) + max(1, SAR_AMP_WAIT3) + SARx_SAMPLE_CYCLE + SARx_SAMPLE_BIT + **Description** The instruction makes measurements from ADC. @@ -684,6 +762,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *High*, *Low* — Define range of bits to read. Bits outside of [High, Low] range are masked. - *Slave_sel* - Index of I2C slave address to use. +**Cycles** + 2 (fetch) + I2C communication time + **Description** ``I2C_RD`` instruction reads one byte from I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``. 8 bits of read result is stored into `R0` register. @@ -705,6 +786,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *High*, *Low* — Define range of bits to write. Bits outside of [High, Low] range are masked. - *Slave_sel* - Index of I2C slave address to use. +**Cycles** + 2 (fetch) + I2C communication time + **Description** ``I2C_WR`` instruction writes one byte to I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``. @@ -724,6 +808,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *High* – High part of R0 - *Low* – Low part of R0 +**Cycles** + 2 (fetch) + 6 (execute) + **Description** The instruction reads up to 16 bits from a peripheral register into a general purpose register: ``R0 = REG[Addr][High:Low]``. @@ -749,6 +836,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Low* – Low part of R0 - *Data* – value to write, 8 bits +**Cycles** + 2 (fetch) + 10 (execute) + **Description** The instruction writes up to 8 bits from a general purpose register into a peripheral register. ``REG[Addr][High:Low] = data`` From 9b847b2903b5a69938e263b0b97f99f1fc26b9b3 Mon Sep 17 00:00:00 2001 From: krzychb Date: Thu, 7 Dec 2017 22:45:39 +0100 Subject: [PATCH 30/30] 1. Following https://esp32.com/viewtopic.php?f=14&t=3834 and https://github.com/espressif/esp-idf/issues/1351 updated information regarding selection of the main XTAL frequency. 2. Removed obsolete note about ': not a valid identifier...', as it does not show up anymore with the latests MSYS2 installation. --- docs/get-started/index.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index de1caa84c2..68c8f2ff0a 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -127,10 +127,6 @@ ESP-IDF will be downloaded into ``~/esp/esp-idf``. cd ~/esp/esp-idf git submodule update --init -.. note:: - - While cloning submodules on **Windows** platform, the ``git clone`` command may print some output starting ``': not a valid identifier...``. This is a `known issue `_ but the git clone still succeeds without any problems. - .. _get-started-setup-path: @@ -206,9 +202,6 @@ Here are couple of tips on navigation and use of ``menuconfig``: If you are **Arch Linux** user, navigate to ``SDK tool configuration`` and change the name of ``Python 2 interpreter`` from ``python`` to ``python2``. -.. note:: - - Most ESP32 development boards have a 40MHz crystal installed. However, some boards use a 26MHz crystal. If your board uses a 26MHz crystal, or you get garbage output from serial port after code upload, adjust the :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` option in menuconfig. .. _get-started-build-flash: @@ -283,7 +276,18 @@ Several lines below, after start up and diagnostic log, you should see "Hello wo Restarting in 8 seconds... Restarting in 7 seconds... -To exit monitor use shortcut ``Ctrl+]``. To execute ``make flash`` and ``make monitor`` in one shoot type ``make flash monitor``. Check section :doc:`IDF Monitor ` for handy shortcuts and more details on using this application. +To exit the monitor use shortcut ``Ctrl+]``. + +.. note:: + + If instead of the messages above, you see a random garbage similar to:: + + e���)(Xn@�y.!��(�PW+)��Hn9a؅/9�!�t5��P�~�k��e�ea�5�jA + ~zY��Y(1�,1�� e���)(Xn@�y.!Dr�zY(�jpi�|�+z5Ymvp + + or monitor fails shortly after upload, your board is likely using 26MHz crystal, while the ESP-IDF assumes default of 40MHz. Exit the monitor, go back to the :ref:`menuconfig `, change :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` to 26MHz, then :ref:`build and flash ` the application again. + +To execute ``make flash`` and ``make monitor`` in one go, type ``make flash monitor``. Check section :doc:`IDF Monitor ` for handy shortcuts and more details on using this application. That's all what you need to get started with ESP32!