This commit is contained in:
Luis Gustavo 2016-07-11 08:08:14 +00:00 committed by GitHub
commit 9303d75a30
26 changed files with 3906 additions and 1 deletions

View file

@ -221,6 +221,9 @@ size: $(PROGRAM_OUT)
test: flash test: flash
$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT) $(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
connect:
$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
# the rebuild target is written like this so it can be run in a parallel build # the rebuild target is written like this so it can be run in a parallel build
# environment without causing weird side effects # environment without causing weird side effects
rebuild: rebuild:

View file

@ -27,6 +27,7 @@
#include "espressif/esp_common.h" #include "espressif/esp_common.h"
#include "espressif/phy_info.h" #include "espressif/phy_info.h"
#include "sdk_internal.h" #include "sdk_internal.h"
#include "sysparam.h"
/* This is not declared in any header file (but arguably should be) */ /* This is not declared in any header file (but arguably should be) */
@ -140,6 +141,8 @@ void IRAM sdk_user_start(void) {
uint32_t cksum_len; uint32_t cksum_len;
uint32_t cksum_value; uint32_t cksum_value;
uint32_t ic_flash_addr; uint32_t ic_flash_addr;
uint32_t sysparam_addr;
sysparam_status_t status;
SPI(0).USER0 |= SPI_USER0_CS_SETUP; SPI(0).USER0 |= SPI_USER0_CS_SETUP;
sdk_SPIRead(0, buf32, 4); sdk_SPIRead(0, buf32, 4);
@ -205,6 +208,20 @@ void IRAM sdk_user_start(void) {
} }
memcpy(&sdk_g_ic.s, buf32, sizeof(struct sdk_g_ic_saved_st)); memcpy(&sdk_g_ic.s, buf32, sizeof(struct sdk_g_ic_saved_st));
// By default, put the sysparam region just below the config sectors at the
// top of the flash space
sysparam_addr = flash_size - (4 + DEFAULT_SYSPARAM_SECTORS) * sdk_flashchip.sector_size;
status = sysparam_init(sysparam_addr, flash_size);
if (status == SYSPARAM_NOTFOUND) {
status = sysparam_create_area(sysparam_addr, DEFAULT_SYSPARAM_SECTORS, false);
if (status == SYSPARAM_OK) {
status = sysparam_init(sysparam_addr, 0);
}
}
if (status != SYSPARAM_OK) {
printf("WARNING: Could not initialize sysparams (%d)!\n", status);
}
user_start_phase2(); user_start_phase2();
} }

View file

@ -3,6 +3,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
typedef volatile uint32_t *esp_reg_t; typedef volatile uint32_t *esp_reg_t;

413
core/include/sysparam.h Normal file
View file

@ -0,0 +1,413 @@
#ifndef _SYSPARAM_H_
#define _SYSPARAM_H_
#include <esp/types.h>
#ifndef DEFAULT_SYSPARAM_SECTORS
#define DEFAULT_SYSPARAM_SECTORS 4
#endif
/** @file sysparam.h
*
* Read/write "system parameters" to persistent flash.
*
* System parameters are stored as key/value pairs. Keys are string values
* between 1 and 255 characters long. Values can be any data up to 255 bytes
* in length (but are most commonly also text strings). Up to 126 key/value
* pairs can be stored at a time.
*
* Keys and values are stored in flash using a progressive list structure
* which allows space-efficient storage and minimizes flash erase cycles,
* improving write speed and increasing the lifespan of the flash memory.
*/
/** Status codes returned by all sysparam functions
*
* Error codes (`SYSPARAM_ERR_*`) all have values less than zero, and can be
* returned by any function. Values greater than zero are non-error status
* codes which may be returned by some functions to indicate various results.
*/
typedef enum {
SYSPARAM_OK = 0, ///< Success
SYSPARAM_NOTFOUND = 1, ///< Entry not found matching criteria
SYSPARAM_PARSEFAILED = 2, ///< Unable to parse retrieved value
SYSPARAM_ERR_NOINIT = -1, ///< sysparam_init() must be called first
SYSPARAM_ERR_BADVALUE = -2, ///< One or more arguments were invalid
SYSPARAM_ERR_FULL = -3, ///< No space left in sysparam area (or too many keys in use)
SYSPARAM_ERR_IO = -4, ///< I/O error reading/writing flash
SYSPARAM_ERR_CORRUPT = -5, ///< Sysparam region has bad/corrupted data
SYSPARAM_ERR_NOMEM = -6, ///< Unable to allocate memory
} sysparam_status_t;
/** Structure used by sysparam_iter_next() to keep track of its current state
* and return its results. This should be initialized by calling
* sysparam_iter_start() and cleaned up afterward by calling
* sysparam_iter_end().
*/
typedef struct {
char *key;
uint8_t *value;
size_t key_len;
size_t value_len;
bool binary;
size_t bufsize;
struct sysparam_context *ctx;
} sysparam_iter_t;
/** Initialize sysparam and set up the current area of flash to use.
*
* This must be called (and return successfully) before any other sysparam
* routines (except sysparam_create_area()) are called.
*
* This should normally be taken care of automatically on boot by the OS
* startup routines. It may be necessary to call it specially, however, if
* the normal initialization failed, or after calling sysparam_create_area()
* to reformat the current area.
*
* This routine will start at `base_addr` and scan all sectors up to
* `top_addr` looking for a valid sysparam area. If `top_addr` is zero (or
* equal to `base_addr`, then only the sector at `base_addr` will be checked.
*
* @param[in] base_addr The flash address to start looking for the start of
* the (already present) sysparam area
* @param[in] top_addr The flash address to stop looking for the sysparam
* area
*
* @retval ::SYSPARAM_OK Initialization successful.
* @retval ::SYSPARAM_NOTFOUND The specified address does not appear to
* contain a sysparam area. It may be
* necessary to call sysparam_create_area() to
* create one first.
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_init(uint32_t base_addr, uint32_t top_addr);
/** Create a new sysparam area in flash at the specified address.
*
* By default, this routine will scan the specified area to make sure it
* appears to be empty (i.e. all 0xFF bytes) before setting it up as a new
* sysparam area. If there appears to be other data already present, it will
* not overwrite it. Setting `force` to `true` will cause it to clobber any
* existing data instead.
*
* @param[in] base_addr The flash address at which it should start
* (must be a multiple of the sector size)
* @param[in] num_sectors The number of flash sectors to use for the sysparam
* area. This should be an even number >= 2. Note
* that the actual amount of useable parameter space
* will be roughly half this amount.
* @param[in] force Proceed even if the space does not appear to be empty
*
* @retval ::SYSPARAM_OK Area (re)created successfully.
* @retval ::SYSPARAM_NOTFOUND `force` was not specified, and the area at
* `base_addr` appears to have other data. No
* action taken.
* @retval ::SYSPARAM_ERR_BADVALUE The `num_sectors` value was not even (or
* was zero)
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*
* Note: This routine can create a sysparam area in another location than the
* one currently being used, but does not change which area is currently used
* (you will need to call sysparam_init() again if you want to do that). If
* you reformat the area currently being used, you will also need to call
* sysparam_init() again afterward before you will be able to continue using
* it.
*/
sysparam_status_t sysparam_create_area(uint32_t base_addr, uint16_t num_sectors, bool force);
/** Get the start address and size of the currently active sysparam area
*
* Fills in `base_addr` and `num_sectors` with the location and size of the
* currently active sysparam area. The returned values correspond to the
* arguments passed to the sysparam_create_area() call when the area was
* originally created.
*
* @param[out] base_addr The flash address at which the sysparam area starts
* @param[out] num_sectors The number of flash sectors used by the sysparam
* area
*
* @retval ::SYSPARAM_OK Completed successfully
* @retval ::SYSPARAM_ERR_NOINIT No current sysparam area is active
*/
sysparam_status_t sysparam_get_info(uint32_t *base_addr, uint32_t *num_sectors);
/** Get the value associated with a key
*
* This is the core "get value" function. It will retrieve the value for the
* specified key in a freshly malloc()'d buffer and return it. Raw values can
* contain any data (including zero bytes), so the `actual_length` parameter
* should be used to determine the length of the data in the buffer.
*
* It is up to the caller to free() the returned buffer when done using it.
*
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
* in `destptr` is not changed. This means it is possible to set a default
* value before calling this function which will be left as-is if a sysparam
* value could not be successfully read.
*
* @param[in] key Key name (zero-terminated string)
* @param[out] destptr Pointer to a location to hold the address of the
* returned data buffer
* @param[out] actual_length Pointer to a location to hold the length of the
* returned data buffer (may be NULL)
* @param[out] is_binary Pointer to a bool to hold whether the returned
* value is "binary" or not (may be NULL)
*
* @retval ::SYSPARAM_OK Value successfully retrieved.
* @retval ::SYSPARAM_NOTFOUND Key/value not found. No buffer returned.
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_get_data(const char *key, uint8_t **destptr, size_t *actual_length, bool *is_binary);
/** Get the value associate with a key (static buffers only)
*
* This performs the same function as sysparam_get_data() but without
* performing any memory allocations. It can thus be used before the heap has
* been configured or in other cases where using the heap would be a problem
* (i.e. in an OOM handler, etc). It requires that the caller pass in a
* suitably sized buffer for the value to be read (if the supplied buffer is
* not large enough, the returned value will be truncated and the full
* required length will be returned in `actual_length`).
*
* NOTE: In addition to being large enough for the value, the supplied buffer
* must also be at least as large as the length of the key being requested.
* If it is not, an error will be returned.
*
* @param[in] key Key name (zero-terminated string)
* @param[in] buffer Pointer to a buffer to hold the returned value
* @param[in] buffer_size Length of the supplied buffer in bytes
* @param[out] actual_length pointer to a location to hold the actual length
* of the data which was associated with the key
* (may be NULL).
* @param[out] is_binary Pointer to a bool to hold whether the returned
* value is "binary" or not (may be NULL)
*
* @retval ::SYSPARAM_OK Value successfully retrieved
* @retval ::SYSPARAM_NOTFOUND Key/value not found
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
* @retval ::SYSPARAM_ERR_NOMEM The supplied buffer is too small
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_get_data_static(const char *key, uint8_t *buffer, size_t buffer_size, size_t *actual_length, bool *is_binary);
/** Get the string value associated with a key
*
* This routine can be used if you know that the value in a key will (or at
* least should) be a string. It will return a zero-terminated char buffer
* containing the value retrieved.
*
* It is up to the caller to free() the returned buffer when done using it.
*
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
* in `destptr` is not changed. This means it is possible to set a default
* value before calling this function which will be left as-is if a sysparam
* value could not be successfully read.
*
* @param[in] key Key name (zero-terminated string)
* @param[out] destptr Pointer to a location to hold the address of the
* returned data buffer
*
* @retval ::SYSPARAM_OK Value successfully retrieved.
* @retval ::SYSPARAM_NOTFOUND Key/value not found.
* @retval ::SYSPARAM_PARSEFAILED The retrieved value was a binary value
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_get_string(const char *key, char **destptr);
/** Get the int32_t value associated with a key
*
* This routine can be used if you know that the value in a key will (or at
* least should) be an integer value. It will parse the stored data as a
* number (in standard decimal or "0x" hex notation) and return the result.
*
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
* in `result` is not changed. This means it is possible to set a default
* value before calling this function which will be left as-is if a sysparam
* value could not be successfully read.
*
* @param[in] key Key name (zero-terminated string)
* @param[out] result Pointer to a location to hold returned integer value
*
* @retval ::SYSPARAM_OK Value successfully retrieved.
* @retval ::SYSPARAM_NOTFOUND Key/value not found.
* @retval ::SYSPARAM_PARSEFAILED The retrieved value could not be parsed as
* an integer.
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_get_int(const char *key, int32_t *result);
/** Get the boolean value associated with a key
*
* This routine can be used if you know that the value in a key will (or at
* least should) be a boolean setting. It will read the specified value as a
* text string and attempt to parse it as a boolean value.
*
* It will recognize the following (case-insensitive) strings:
* * True: "yes", "y", "true", "t", "1"
* * False: "no", "n", "false", "f", "0"
*
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
* in `result` is not changed. This means it is possible to set a default
* value before calling this function which will be left as-is if a sysparam
* value could not be successfully read.
*
* @param[in] key Key name (zero-terminated string)
* @param[out] result Pointer to a location to hold returned boolean value
*
* @retval ::SYSPARAM_OK Value successfully retrieved.
* @retval ::SYSPARAM_NOTFOUND Key/value not found.
* @retval ::SYSPARAM_PARSEFAILED The retrieved value could not be parsed as a
* boolean setting.
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_get_bool(const char *key, bool *result);
/** Set the value associated with a key
*
* The supplied value can be any data, up to 255 bytes in length. If `value`
* is NULL or `value_len` is 0, this is treated as a request to delete any
* current entry matching `key`.
*
* If `binary` is true, the data will be considered binary (unprintable) data,
* and this will be annotated in the saved entry. This does not affect the
* saving or loading process in any way, but may be used by some applications
* to (for example) print binary data differently than text entries when
* printing parameter values.
*
* @param[in] key Key name (zero-terminated string)
* @param[in] value Pointer to a buffer containing the value data
* @param[in] value_len Length of the data in the buffer
* @param[in] binary Whether the data should be considered "binary"
* (unprintable) data
*
* @retval ::SYSPARAM_OK Value successfully set.
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
* @retval ::SYSPARAM_ERR_BADVALUE Either an empty key was provided or
* value_len is too large
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
* (or too many keys in use)
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_set_data(const char *key, const uint8_t *value, size_t value_len, bool binary);
/** Set a key's value from a string
*
* Performs the same function as sysparam_set_data(), but accepts a
* zero-terminated string value instead.
*
* @param[in] key Key name (zero-terminated string)
* @param[in] value Value to set (zero-terminated string)
*
* @retval ::SYSPARAM_OK Value successfully set.
* @retval ::SYSPARAM_ERR_BADVALUE Either an empty key was provided or the
* length of `value` is too large
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
* (or too many keys in use)
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_set_string(const char *key, const char *value);
/** Set a key's value as a number
*
* Converts an int32_t value to a decimal number and writes it to the
* specified key. This does the inverse of the sysparam_get_int()
* function.
*
* @param[in] key Key name (zero-terminated string)
* @param[in] value Value to set
*
* @retval ::SYSPARAM_OK Value successfully set.
* @retval ::SYSPARAM_ERR_BADVALUE An empty key was provided.
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
* (or too many keys in use)
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_set_int(const char *key, int32_t value);
/** Set a key's value as a boolean (yes/no) string
*
* Converts a bool value to a corresponding text string and writes it to the
* specified key. This does the inverse of the sysparam_get_bool()
* function.
*
* Note that if the key already contains a value which parses to the same
* boolean (true/false) value, it is left unchanged.
*
* @param[in] key Key name (zero-terminated string)
* @param[in] value Value to set
*
* @retval ::SYSPARAM_OK Value successfully set.
* @retval ::SYSPARAM_ERR_BADVALUE An empty key was provided.
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
* (or too many keys in use)
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_set_bool(const char *key, bool value);
/** Begin iterating through all key/value pairs
*
* This function initializes a sysparam_iter_t structure to prepare it for
* iterating through the list of key/value pairs using sysparam_iter_next().
* This does not fetch any items (the first successive call to
* sysparam_iter_next() will return the first key/value in the list).
*
* NOTE: When done, you must call sysparam_iter_end() to free the resources
* associated with `iter`, or you will leak memory.
*
* @param[in] iter A pointer to a sysparam_iter_t structure to initialize
*
* @retval ::SYSPARAM_OK Initialization successful
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
*/
sysparam_status_t sysparam_iter_start(sysparam_iter_t *iter);
/** Fetch the next key/value pair
*
* This will retrieve the next key and value from the sysparam area, placing
* them in `iter->key`, and `iter->value` (and updating `iter->key_len` and
* `iter->value_len`).
*
* NOTE: `iter->key` and `iter->value` are static buffers local to the `iter`
* structure, and will be overwritten with the next call to
* sysparam_iter_next() using the same `iter`. They should *not* be free()d
* after use.
*
* @param[in] iter The iterator structure to update
*
* @retval ::SYSPARAM_OK Next key/value retrieved
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
*/
sysparam_status_t sysparam_iter_next(sysparam_iter_t *iter);
/** Finish iterating through keys/values
*
* Cleans up and releases resources allocated by sysparam_iter_start() /
* sysparam_iter_next().
*/
void sysparam_iter_end(sysparam_iter_t *iter);
#endif /* _SYSPARAM_H_ */

1050
core/sysparam.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
PROGRAM=sysparam_editor
# Setting this to 1..3 will add extra debugging output to stdout
EXTRA_CFLAGS=-DSYSPARAM_DEBUG=0
include ../../common.mk
# `make dump-flash` can be used to view the current contents of the sysparam
# regions in flash.
dump-flash:
esptool.py read_flash 0x1f8000 8192 r1.bin
hexdump -C r1.bin
esptool.py read_flash 0x1fa000 8192 r2.bin
hexdump -C r2.bin

View file

@ -0,0 +1,233 @@
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysparam.h>
#include <espressif/spi_flash.h>
#define CMD_BUF_SIZE 5000
const int status_base = -6;
const char *status_messages[] = {
"SYSPARAM_ERR_NOMEM",
"SYSPARAM_ERR_CORRUPT",
"SYSPARAM_ERR_IO",
"SYSPARAM_ERR_FULL",
"SYSPARAM_ERR_BADVALUE",
"SYSPARAM_ERR_NOINIT",
"SYSPARAM_OK",
"SYSPARAM_NOTFOUND",
"SYSPARAM_PARSEFAILED",
};
void usage(void) {
printf(
"Available commands:\n"
" <key>? -- Query the value of <key>\n"
" <key>=<value> -- Set <key> to text <value>\n"
" <key>:<hexdata> -- Set <key> to binary value represented as hex\n"
" dump -- Show all currently set keys/values\n"
" reformat -- Reinitialize (clear) the sysparam area\n"
" help -- Show this help screen\n"
);
}
size_t tty_readline(char *buffer, size_t buf_size, bool echo) {
size_t i = 0;
int c;
while (true) {
c = getchar();
if (c == '\r') {
if (echo) putchar('\n');
break;
} else if (c == '\b' || c == 0x7f) {
if (i) {
if (echo) printf("\b \b");
i--;
}
} else if (c < 0x20) {
/* Ignore other control characters */
} else if (i >= buf_size - 1) {
if (echo) putchar('\a');
} else {
buffer[i++] = c;
if (echo) putchar(c);
}
}
buffer[i] = 0;
return i;
}
void print_text_value(char *key, char *value) {
printf(" '%s' = '%s'\n", key, value);
}
void print_binary_value(char *key, uint8_t *value, size_t len) {
size_t i;
printf(" %s:", key);
for (i = 0; i < len; i++) {
if (!(i & 0x0f)) {
printf("\n ");
}
printf(" %02x", value[i]);
}
printf("\n");
}
sysparam_status_t dump_params(void) {
sysparam_status_t status;
sysparam_iter_t iter;
status = sysparam_iter_start(&iter);
if (status < 0) return status;
while (true) {
status = sysparam_iter_next(&iter);
if (status != SYSPARAM_OK) break;
if (!iter.binary) {
print_text_value(iter.key, (char *)iter.value);
} else {
print_binary_value(iter.key, iter.value, iter.value_len);
}
}
sysparam_iter_end(&iter);
if (status == SYSPARAM_NOTFOUND) {
// This is the normal status when we've reached the end of all entries.
return SYSPARAM_OK;
} else {
// Something apparently went wrong
return status;
}
}
uint8_t *parse_hexdata(char *string, size_t *result_length) {
size_t string_len = strlen(string);
uint8_t *buf = malloc(string_len / 2);
uint8_t c;
int i, j;
bool digit = false;
j = 0;
for (i = 0; string[i]; i++) {
c = string[i];
if (c >= 0x30 && c <= 0x39) {
c &= 0x0f;
} else if (c >= 0x41 && c <= 0x46) {
c -= 0x37;
} else if (c >= 0x61 && c <= 0x66) {
c -= 0x57;
} else if (c == ' ') {
continue;
} else {
free(buf);
return NULL;
}
if (!digit) {
buf[j] = c << 4;
} else {
buf[j++] |= c;
}
digit = !digit;
}
if (digit) {
free(buf);
return NULL;
}
*result_length = j;
return buf;
}
void sysparam_editor_task(void *pvParameters) {
char *cmd_buffer = malloc(CMD_BUF_SIZE);
sysparam_status_t status;
char *value;
uint8_t *bin_value;
size_t len;
uint8_t *data;
uint32_t base_addr, num_sectors;
if (!cmd_buffer) {
printf("ERROR: Cannot allocate command buffer!\n");
return;
}
printf("\nWelcome to the system parameter editor! Enter 'help' for more information.\n\n");
status = sysparam_get_info(&base_addr, &num_sectors);
if (status == SYSPARAM_OK) {
printf("[current sysparam region is at 0x%08x (%d sectors)]\n", base_addr, num_sectors);
} else {
printf("[NOTE: No current sysparam region (initialization problem during boot?)]\n");
// Default to the same place/size as the normal system initialization
// stuff, so if the user uses this utility to reformat it, it will put
// it somewhere the system will find it later
num_sectors = DEFAULT_SYSPARAM_SECTORS;
base_addr = sdk_flashchip.chip_size - (4 + num_sectors) * sdk_flashchip.sector_size;
}
while (true) {
printf("==> ");
len = tty_readline(cmd_buffer, CMD_BUF_SIZE, true);
status = 0;
if (!len) continue;
if (cmd_buffer[len - 1] == '?') {
cmd_buffer[len - 1] = 0;
printf("Querying '%s'...\n", cmd_buffer);
status = sysparam_get_string(cmd_buffer, &value);
if (status == SYSPARAM_OK) {
print_text_value(cmd_buffer, value);
free(value);
} else if (status == SYSPARAM_PARSEFAILED) {
// This means it's actually a binary value
status = sysparam_get_data(cmd_buffer, &bin_value, &len, NULL);
if (status == SYSPARAM_OK) {
print_binary_value(cmd_buffer, bin_value, len);
free(value);
}
}
} else if ((value = strchr(cmd_buffer, '='))) {
*value++ = 0;
printf("Setting '%s' to '%s'...\n", cmd_buffer, value);
status = sysparam_set_string(cmd_buffer, value);
} else if ((value = strchr(cmd_buffer, ':'))) {
*value++ = 0;
data = parse_hexdata(value, &len);
if (value) {
printf("Setting '%s' to binary data...\n", cmd_buffer);
status = sysparam_set_data(cmd_buffer, data, len, true);
free(data);
} else {
printf("Error: Unable to parse hex data\n");
}
} else if (!strcmp(cmd_buffer, "dump")) {
printf("Dumping all params:\n");
status = dump_params();
} else if (!strcmp(cmd_buffer, "reformat")) {
printf("Re-initializing region...\n");
status = sysparam_create_area(base_addr, num_sectors, true);
if (status == SYSPARAM_OK) {
// We need to re-init after wiping out the region we've been
// using.
status = sysparam_init(base_addr, 0);
}
} else if (!strcmp(cmd_buffer, "help")) {
usage();
} else {
printf("Unrecognized command.\n\n");
usage();
}
if (status != SYSPARAM_OK) {
printf("! Operation returned status: %d (%s)\n", status, status_messages[status - status_base]);
}
}
}
void user_init(void)
{
xTaskCreate(sysparam_editor_task, (signed char *)"sysparam_editor_task", 512, NULL, 2, NULL);
}

View file

@ -0,0 +1,4 @@
PROGRAM=websocket_mbedtls
COMPONENTS = FreeRTOS lwip core extras/mbedtls
include ../../common.mk

View file

@ -0,0 +1,78 @@
/*
* Adler-32 checksum
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
/*
* Adler-32 algorithm taken from the zlib source, which is
* Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
*/
#include "tinf.h"
#define A32_BASE 65521
#define A32_NMAX 5552
unsigned int tinf_adler32(const void *data, unsigned int length)
{
const unsigned char *buf = (const unsigned char *)data;
unsigned int s1 = 1;
unsigned int s2 = 0;
while (length > 0)
{
int k = length < A32_NMAX ? length : A32_NMAX;
int i;
for (i = k / 16; i; --i, buf += 16)
{
s1 += buf[0]; s2 += s1; s1 += buf[1]; s2 += s1;
s1 += buf[2]; s2 += s1; s1 += buf[3]; s2 += s1;
s1 += buf[4]; s2 += s1; s1 += buf[5]; s2 += s1;
s1 += buf[6]; s2 += s1; s1 += buf[7]; s2 += s1;
s1 += buf[8]; s2 += s1; s1 += buf[9]; s2 += s1;
s1 += buf[10]; s2 += s1; s1 += buf[11]; s2 += s1;
s1 += buf[12]; s2 += s1; s1 += buf[13]; s2 += s1;
s1 += buf[14]; s2 += s1; s1 += buf[15]; s2 += s1;
}
for (i = k % 16; i; --i) { s1 += *buf++; s2 += s1; }
s1 %= A32_BASE;
s2 %= A32_BASE;
length -= k;
}
return (s2 << 16) | s1;
}

View file

@ -0,0 +1,44 @@
/* This is the CA certificate for the CA trust chain of
www.howsmyssl.com in PEM format, as dumped via:
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
The CA cert is the last cert in the chain output by the server.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/*
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
*/
const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n"
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
"-----END CERTIFICATE-----\r\n";

View file

@ -0,0 +1,229 @@
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "lwip/api.h"
#include "ssid_config.h"
/* mbedtls/config.h MUST appear before all other mbedtls headers, or
you'll get the default config.
(Although mostly that isn't a big problem, you just might get
errors at link time if functions don't exist.) */
#include "mbedtls/config.h"
#include "mbedtls/net.h"
#include "mbedtls/debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#include <stdio.h>
#include <string.h>
#include "conn.h"
/* SSL file descriptors */
#define SSL_HANDLES_SIZE 4
mbedtls_ssl_context* sslHandles[SSL_HANDLES_SIZE] = {NULL, NULL, NULL, NULL};
unsigned char ctxC = 0;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_context entropy;
/* Connect to a hostname and port using TLS 1.2 and
return a index for one SSL connection on the pool or -1 if error
*/
int ConnConnect(char *hostname, int port) {
int ret = 0;
mbedtls_ssl_context *sslFD;
mbedtls_net_context *socketFD;
int sslHandle = 0;
char s_port[10];
// configure mbedtls
if (!ctxC) {
mbedtls_ssl_config_init( &conf );
mbedtls_x509_crt_init( &cacert );
mbedtls_ctr_drbg_init( &ctr_drbg );
mbedtls_entropy_init( &entropy );
if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *) "ssl_client1",
strlen( "ssl_client1" ) ) ) != 0 )
{
printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret );
return -1;
}
ret = mbedtls_x509_crt_parse( &cacert, (const unsigned char *) mbedtls_test_cas_pem,
mbedtls_test_cas_pem_len );
if( ret < 0 )
{
printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret );
return -1;
}
if( ( ret = mbedtls_ssl_config_defaults( &conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 )
{
printf( " failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret );
return -1;
}
mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_OPTIONAL );
mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL );
mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg );
ctxC = 1;
}
// find a free slot at sslHandles
while(sslHandle < SSL_HANDLES_SIZE) {
if (sslHandles[sslHandle] == NULL) {
sslFD = malloc(sizeof(mbedtls_ssl_context));
socketFD = malloc(sizeof(mbedtls_net_context));
mbedtls_ssl_init( sslFD );
mbedtls_net_init( socketFD );
sslHandles[sslHandle] = sslFD;
break;
}
sslHandle++;
}
// no free slot at sslHandles
if (sslHandle == SSL_HANDLES_SIZE) {
return -1;
}
// connect mbedtls socket
memset(s_port, 0, sizeof(s_port));
sprintf(s_port, "%d", port);
ret = mbedtls_net_connect(socketFD, hostname, s_port, MBEDTLS_NET_PROTO_TCP);
if (ret != 0) {
ConnClose(sslHandle);
return -1;
}
if(( ret = mbedtls_ssl_setup( sslFD, &conf ) ) != 0 )
{
printf( " failed\n ! mbedtls_ssl_setup returned %d\n\n", ret );
ConnClose(sslHandle);
return -1;
}
if( ( ret = mbedtls_ssl_set_hostname( sslFD, "mbed TLS Server 1" ) ) != 0 )
{
printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret );
ConnClose(sslHandle);
return -1;
}
mbedtls_ssl_set_bio( sslFD, socketFD, mbedtls_net_send, mbedtls_net_recv, NULL );
while( ( ret = mbedtls_ssl_handshake( sslFD ) ) != 0 )
{
if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret );
ConnClose(sslHandle);
return -1;
}
}
return sslHandle;
}
int ConnReadBytesAvailable(int sslHandle) {
mbedtls_ssl_context* sslFD = NULL;
mbedtls_net_context* socketFD = NULL;
int count = 0;
if (sslHandle >= 0) sslFD = sslHandles[sslHandle];
else return -1;
if (sslFD) {
socketFD = (mbedtls_net_context *) sslFD->p_bio;
lwip_ioctl(socketFD->fd, FIONREAD, &count);
return count;
} else {
return -1;
}
}
/* Read bytes from a valid SSL connection on the pool. Blocking! */
int ConnRead(int sslHandle, void *buf, int num) {
mbedtls_ssl_context* sslFD = NULL;
int ret;
if (sslHandle >= 0) sslFD = sslHandles[sslHandle];
else return -1;
if (sslFD) {
if (num == 0) return 0;
ret = mbedtls_ssl_read(sslFD, buf, num);
return ret;
} else {
return -1;
}
}
/* Write bytes to a valid SSL connection on the pool */
int ConnWrite(int sslHandle, const void *buf, int num) {
mbedtls_ssl_context* sslFD = NULL;
int ret;
if (sslHandle >= 0) sslFD = sslHandles[sslHandle];
else return -1;
if (sslFD) {
if (num == 0) return 0;
ret = mbedtls_ssl_write(sslFD, buf, num);
return ret;
} else {
return -1;
}
}
/* Close a valid SSL connection on the pool, release the resources and
open the slot to another connection */
void ConnClose(int sslHandle) {
mbedtls_ssl_context *sslFD = NULL;
if (sslHandle >= 0) sslFD = sslHandles[sslHandle];
else return;
if (sslFD) {
mbedtls_ssl_close_notify( sslFD );
mbedtls_net_free( sslFD->p_bio );
free(sslFD->p_bio);
mbedtls_ssl_free( sslFD );
free(sslFD);
}
sslHandles[sslHandle] = NULL;
}
void sleep_ms(int milliseconds)
{
vTaskDelay(milliseconds / portTICK_RATE_MS);
}

View file

@ -0,0 +1,11 @@
#ifndef CONN
#define CONN
int ConnConnect(char *host, int port);
int ConnReadBytesAvailable(int sslHandle);
int ConnRead(int sslHandle, void *buf, int num);
int ConnWrite(int sslHandle, const void *buf, int num);
void ConnClose(int sslHandle);
void sleep_ms(int milliseconds);
#endif

View file

@ -0,0 +1,64 @@
/*
* CRC32 checksum
*
* Copyright (c) 1998-2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
/*
* CRC32 algorithm taken from the zlib source, which is
* Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
*/
#include "tinf.h"
static const unsigned int tinf_crc32tab[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190,
0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344,
0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278,
0xbdbdf21c
};
unsigned int tinf_crc32(const void *data, unsigned int length)
{
const unsigned char *buf = (const unsigned char *)data;
unsigned int crc = 0xffffffff;
unsigned int i;
if (length == 0) return 0;
for (i = 0; i < length; ++i)
{
crc ^= buf[i];
crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4);
crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4);
}
return crc ^ 0xffffffff;
}

View file

@ -0,0 +1,308 @@
/*
Routines in this file are based on:
Zlib (RFC1950 / RFC1951) compression for PuTTY.
PuTTY is copyright 1997-2014 Simon Tatham.
Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
Kuhn, Colin Watson, and CORE SDI S.A.
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 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 <stdlib.h>
#include <string.h>
#include <assert.h>
#include "defl_static.h"
#define snew(type) ( (type *) malloc(sizeof(type)) )
#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) )
#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) )
#define sfree(x) ( free((x)) )
#ifndef FALSE
#define FALSE 0
#define TRUE (!FALSE)
#endif
/* ----------------------------------------------------------------------
* Zlib compression. We always use the static Huffman tree option.
* Mostly this is because it's hard to scan a block in advance to
* work out better trees; dynamic trees are great when you're
* compressing a large file under no significant time constraint,
* but when you're compressing little bits in real time, things get
* hairier.
*
* I suppose it's possible that I could compute Huffman trees based
* on the frequencies in the _previous_ block, as a sort of
* heuristic, but I'm not confident that the gain would balance out
* having to transmit the trees.
*/
void outbits(struct Outbuf *out, unsigned long bits, int nbits)
{
assert(out->noutbits + nbits <= 32);
out->outbits |= bits << out->noutbits;
out->noutbits += nbits;
while (out->noutbits >= 8) {
if (out->outlen >= out->outsize) {
out->outsize = out->outlen + 64;
out->outbuf = sresize(out->outbuf, out->outsize, unsigned char);
}
out->outbuf[out->outlen++] = (unsigned char) (out->outbits & 0xFF);
out->outbits >>= 8;
out->noutbits -= 8;
}
}
static const unsigned char mirrorbytes[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
typedef struct {
short code, extrabits;
int min, max;
} coderecord;
static const coderecord lencodes[] = {
{257, 0, 3, 3},
{258, 0, 4, 4},
{259, 0, 5, 5},
{260, 0, 6, 6},
{261, 0, 7, 7},
{262, 0, 8, 8},
{263, 0, 9, 9},
{264, 0, 10, 10},
{265, 1, 11, 12},
{266, 1, 13, 14},
{267, 1, 15, 16},
{268, 1, 17, 18},
{269, 2, 19, 22},
{270, 2, 23, 26},
{271, 2, 27, 30},
{272, 2, 31, 34},
{273, 3, 35, 42},
{274, 3, 43, 50},
{275, 3, 51, 58},
{276, 3, 59, 66},
{277, 4, 67, 82},
{278, 4, 83, 98},
{279, 4, 99, 114},
{280, 4, 115, 130},
{281, 5, 131, 162},
{282, 5, 163, 194},
{283, 5, 195, 226},
{284, 5, 227, 257},
{285, 0, 258, 258},
};
static const coderecord distcodes[] = {
{0, 0, 1, 1},
{1, 0, 2, 2},
{2, 0, 3, 3},
{3, 0, 4, 4},
{4, 1, 5, 6},
{5, 1, 7, 8},
{6, 2, 9, 12},
{7, 2, 13, 16},
{8, 3, 17, 24},
{9, 3, 25, 32},
{10, 4, 33, 48},
{11, 4, 49, 64},
{12, 5, 65, 96},
{13, 5, 97, 128},
{14, 6, 129, 192},
{15, 6, 193, 256},
{16, 7, 257, 384},
{17, 7, 385, 512},
{18, 8, 513, 768},
{19, 8, 769, 1024},
{20, 9, 1025, 1536},
{21, 9, 1537, 2048},
{22, 10, 2049, 3072},
{23, 10, 3073, 4096},
{24, 11, 4097, 6144},
{25, 11, 6145, 8192},
{26, 12, 8193, 12288},
{27, 12, 12289, 16384},
{28, 13, 16385, 24576},
{29, 13, 24577, 32768},
};
void zlib_literal(struct Outbuf *out, unsigned char c)
{
if (out->comp_disabled) {
/*
* We're in an uncompressed block, so just output the byte.
*/
outbits(out, c, 8);
return;
}
if (c <= 143) {
/* 0 through 143 are 8 bits long starting at 00110000. */
outbits(out, mirrorbytes[0x30 + c], 8);
} else {
/* 144 through 255 are 9 bits long starting at 110010000. */
outbits(out, 1 + 2 * mirrorbytes[0x90 - 144 + c], 9);
}
}
void zlib_match(struct Outbuf *out, int distance, int len)
{
const coderecord *d, *l;
int i, j, k;
assert(!out->comp_disabled);
while (len > 0) {
int thislen;
/*
* We can transmit matches of lengths 3 through 258
* inclusive. So if len exceeds 258, we must transmit in
* several steps, with 258 or less in each step.
*
* Specifically: if len >= 261, we can transmit 258 and be
* sure of having at least 3 left for the next step. And if
* len <= 258, we can just transmit len. But if len == 259
* or 260, we must transmit len-3.
*/
thislen = (len > 260 ? 258 : len <= 258 ? len : len - 3);
len -= thislen;
/*
* Binary-search to find which length code we're
* transmitting.
*/
i = -1;
j = sizeof(lencodes) / sizeof(*lencodes);
while (1) {
assert(j - i >= 2);
k = (j + i) / 2;
if (thislen < lencodes[k].min)
j = k;
else if (thislen > lencodes[k].max)
i = k;
else {
l = &lencodes[k];
break; /* found it! */
}
}
/*
* Transmit the length code. 256-279 are seven bits
* starting at 0000000; 280-287 are eight bits starting at
* 11000000.
*/
if (l->code <= 279) {
outbits(out, mirrorbytes[(l->code - 256) * 2], 7);
} else {
outbits(out, mirrorbytes[0xc0 - 280 + l->code], 8);
}
/*
* Transmit the extra bits.
*/
if (l->extrabits)
outbits(out, thislen - l->min, l->extrabits);
/*
* Binary-search to find which distance code we're
* transmitting.
*/
i = -1;
j = sizeof(distcodes) / sizeof(*distcodes);
while (1) {
assert(j - i >= 2);
k = (j + i) / 2;
if (distance < distcodes[k].min)
j = k;
else if (distance > distcodes[k].max)
i = k;
else {
d = &distcodes[k];
break; /* found it! */
}
}
/*
* Transmit the distance code. Five bits starting at 00000.
*/
outbits(out, mirrorbytes[d->code * 8], 5);
/*
* Transmit the extra bits.
*/
if (d->extrabits)
outbits(out, distance - d->min, d->extrabits);
}
}
void zlib_start_block(struct Outbuf *out)
{
// outbits(out, 0x9C78, 16);
outbits(out, 1, 1); /* Final block */
outbits(out, 1, 2); /* Static huffman block */
}
void zlib_finish_block(struct Outbuf *out)
{
outbits(out, 0, 7); /* close block */
outbits(out, 0, 7); /* Make sure all bits are flushed */
}
void zlib_free_block(struct Outbuf *out) {
sfree(out->outbuf);
}

View file

@ -0,0 +1,14 @@
struct Outbuf {
unsigned char *outbuf;
int outlen, outsize;
unsigned long outbits;
int noutbits;
int comp_disabled;
};
void outbits(struct Outbuf *out, unsigned long bits, int nbits);
void zlib_start_block(struct Outbuf *ctx);
void zlib_finish_block(struct Outbuf *ctx);
void zlib_literal(struct Outbuf *ectx, unsigned char c);
void zlib_match(struct Outbuf *ectx, int distance, int len);
void zlib_free_block(struct Outbuf *out);

View file

@ -0,0 +1,116 @@
/*
* genlz77 - Generic LZ77 compressor
*
* Copyright (c) 2014 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "defl_static.h"
#define HASH_BITS 10
#define HASH_SIZE (1<<HASH_BITS)
/* Minimum and maximum length of matches to look for, inclusive */
#define MIN_MATCH 3
#define MAX_MATCH 258
/* Max offset of the match to look for, inclusive */
#define MAX_OFFSET 32768
/* Hash function can be defined as macro or as inline function */
/*#define HASH(p) (p[0] + p[1] + p[2])*/
/* This is hash function from liblzf */
static int HASH(const uint8_t *p) {
int v = (p[0] << 16) | (p[1] << 8) | p[2];
int hash = ((v >> (3*8 - HASH_BITS)) - v) & (HASH_SIZE - 1);
return hash;
}
#ifdef DUMP_LZTXT
/* Counter for approximate compressed length in LZTXT mode. */
/* Literal is counted as 1, copy as 2 bytes. */
unsigned approx_compressed_len;
void literal(void *data, uint8_t val)
{
printf("L%02x # %c\n", val, (val >= 0x20 && val <= 0x7e) ? val : '?');
approx_compressed_len++;
}
void copy(void *data, unsigned offset, unsigned len)
{
printf("C-%u,%u\n", offset, len);
approx_compressed_len += 2;
}
#else
static void literal(void *data, uint8_t val)
{
zlib_literal(data, val);
}
static void copy(void *data, unsigned offset, unsigned len)
{
zlib_match(data, offset, len);
}
#endif
void tinf_compress(void *data, const uint8_t *src, unsigned slen)
{
const uint8_t *hashtable[HASH_SIZE] = {0};
const uint8_t *top = src + slen - MIN_MATCH;
while (src < top) {
int h = HASH(src);
const uint8_t **bucket = &hashtable[h & (HASH_SIZE - 1)];
const uint8_t *subs = *bucket;
*bucket = src;
if (subs && src > subs && (src - subs) <= MAX_OFFSET && !memcmp(src, subs, MIN_MATCH)) {
src += MIN_MATCH;
const uint8_t *m = subs + MIN_MATCH;
int len = MIN_MATCH;
while (*src == *m && len < MAX_MATCH) {
src++; m++; len++;
}
copy(data, src - len - subs, len);
} else {
literal(data, *src++);
}
}
// Process buffer tail, which is less than MIN_MATCH
// (and so it doesn't make sense to look for matches there)
top += MIN_MATCH;
while (src < top) {
literal(data, *src++);
}
}

View file

@ -0,0 +1,27 @@
/* Special mbedTLS config file for http_get_mbedtls example,
overrides supported cipher suite list.
Overriding the set of cipher suites saves small amounts of ROM and
RAM, and is a good practice in general if you know what server(s)
you want to connect to.
However it's extra important here because the howsmyssl API sends
back the list of ciphers we send it as a JSON list in the, and we
only have a 4096kB receive buffer. If the server supported maximum
fragment length option then we wouldn't have this problem either,
but we do so this is a good workaround.
The ciphers chosen below are common ECDHE ciphers, the same ones
Firefox uses when connecting to a TLSv1.2 server.
*/
#ifndef MBEDTLS_CONFIG_H
/* include_next picks up default config from extras/mbedtls/include/mbedtls/config.h */
#include_next<mbedtls/config.h>
#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
/* uncomment next line to include debug output from example */
//#define MBEDTLS_DEBUG_C
#endif

View file

@ -0,0 +1,102 @@
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014 by Paul Sokolovsky
*/
#ifndef TINF_H_INCLUDED
#define TINF_H_INCLUDED
#include <stdint.h>
/* calling convention */
#ifndef TINFCC
#ifdef __WATCOMC__
#define TINFCC __cdecl
#else
#define TINFCC
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define TINF_OK 0
#define TINF_DATA_ERROR (-3)
#define TINF_DEST_OVERFLOW (-4)
/* data structures */
typedef struct {
unsigned short table[16]; /* table of code length counts */
unsigned short trans[288]; /* code -> symbol translation table */
} TINF_TREE;
struct TINF_DATA;
typedef struct TINF_DATA {
const unsigned char *source;
unsigned int tag;
unsigned int bitcount;
/* Buffer start */
unsigned char *destStart;
/* Buffer total size */
unsigned int destSize;
/* Current pointer in buffer */
unsigned char *dest;
/* Remaining bytes in buffer */
unsigned int destRemaining;
/* Argument is the allocation size which didn't fit into buffer. Note that
exact mimumum size to grow buffer by is lastAlloc - destRemaining. But
growing by this exact size is ineficient, as the next allocation will
fail again. */
int (*destGrow)(struct TINF_DATA *data, unsigned int lastAlloc);
TINF_TREE ltree; /* dynamic length/symbol tree */
TINF_TREE dtree; /* dynamic distance tree */
} TINF_DATA;
/* low-level API */
/* Step 1: Allocate TINF_DATA structure */
/* Step 2: Set destStart, destSize, and destGrow fields */
/* Step 3: Set source field */
/* Step 4: Call tinf_uncompress_dyn() */
/* Step 5: In response to destGrow callback, update destStart and destSize fields */
/* Step 6: When tinf_uncompress_dyn() returns, buf.dest points to a byte past last uncompressed byte */
int TINFCC tinf_uncompress_dyn(TINF_DATA *d);
int TINFCC tinf_zlib_uncompress_dyn(TINF_DATA *d, unsigned int sourceLen);
/* high-level API */
void TINFCC tinf_init(void);
int TINFCC tinf_uncompress(void *dest, unsigned int *destLen,
const void *source, unsigned int sourceLen);
int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen,
const void *source, unsigned int sourceLen);
int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen,
const void *source, unsigned int sourceLen);
unsigned int TINFCC tinf_adler32(const void *data, unsigned int length);
unsigned int TINFCC tinf_crc32(const void *data, unsigned int length);
/* compression API */
void TINFCC tinf_compress(void *data, const uint8_t *src, unsigned slen);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* TINF_H_INCLUDED */

View file

@ -0,0 +1,124 @@
/*
* tinfgzip - tiny gzip decompressor
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include "tinf.h"
#define FTEXT 1
#define FHCRC 2
#define FEXTRA 4
#define FNAME 8
#define FCOMMENT 16
int tinf_gzip_uncompress(void *dest, unsigned int *destLen,
const void *source, unsigned int sourceLen)
{
unsigned char *src = (unsigned char *)source;
unsigned char *dst = (unsigned char *)dest;
unsigned char *start;
unsigned int dlen, crc32;
int res;
unsigned char flg;
/* -- check format -- */
/* check id bytes */
if (src[0] != 0x1f || src[1] != 0x8b) return TINF_DATA_ERROR;
/* check method is deflate */
if (src[2] != 8) return TINF_DATA_ERROR;
/* get flag byte */
flg = src[3];
/* check that reserved bits are zero */
if (flg & 0xe0) return TINF_DATA_ERROR;
/* -- find start of compressed data -- */
/* skip base header of 10 bytes */
start = src + 10;
/* skip extra data if present */
if (flg & FEXTRA)
{
unsigned int xlen = start[1];
xlen = 256*xlen + start[0];
start += xlen + 2;
}
/* skip file name if present */
if (flg & FNAME) { while (*start) ++start; ++start; }
/* skip file comment if present */
if (flg & FCOMMENT) { while (*start) ++start; ++start; }
/* check header crc if present */
if (flg & FHCRC)
{
unsigned int hcrc = start[1];
hcrc = 256*hcrc + start[0];
if (hcrc != (tinf_crc32(src, start - src) & 0x0000ffff))
return TINF_DATA_ERROR;
start += 2;
}
/* -- get decompressed length -- */
dlen = src[sourceLen - 1];
dlen = 256*dlen + src[sourceLen - 2];
dlen = 256*dlen + src[sourceLen - 3];
dlen = 256*dlen + src[sourceLen - 4];
/* -- get crc32 of decompressed data -- */
crc32 = src[sourceLen - 5];
crc32 = 256*crc32 + src[sourceLen - 6];
crc32 = 256*crc32 + src[sourceLen - 7];
crc32 = 256*crc32 + src[sourceLen - 8];
/* -- decompress data -- */
res = tinf_uncompress(dst, destLen, start, src + sourceLen - start - 8);
if (res != TINF_OK) return TINF_DATA_ERROR;
if (*destLen != dlen) return TINF_DATA_ERROR;
/* -- check CRC32 checksum -- */
if (crc32 != tinf_crc32(dst, dlen)) return TINF_DATA_ERROR;
return TINF_OK;
}

View file

@ -0,0 +1,518 @@
/*
* tinflate - tiny inflate
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include "tinf.h"
/* --------------------------------------------------- *
* -- uninitialized global data (static structures) -- *
* --------------------------------------------------- */
#ifdef RUNTIME_BITS_TABLES
/* extra bits and base tables for length codes */
unsigned char length_bits[30];
unsigned short length_base[30];
/* extra bits and base tables for distance codes */
unsigned char dist_bits[30];
unsigned short dist_base[30];
#else
const unsigned char length_bits[30] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5
};
const unsigned short length_base[30] = {
3, 4, 5, 6, 7, 8, 9, 10,
11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115,
131, 163, 195, 227, 258
};
const unsigned char dist_bits[30] = {
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13
};
const unsigned short dist_base[30] = {
1, 2, 3, 4, 5, 7, 9, 13,
17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073,
4097, 6145, 8193, 12289, 16385, 24577
};
#endif
/* special ordering of code length codes */
const unsigned char clcidx[] = {
16, 17, 18, 0, 8, 7, 9, 6,
10, 5, 11, 4, 12, 3, 13, 2,
14, 1, 15
};
/* ----------------------- *
* -- utility functions -- *
* ----------------------- */
/* Execute callback to grow destination buffer */
static int tinf_grow_dest_buf(TINF_DATA *d, unsigned int lastAlloc)
{
unsigned int oldsize = d->dest - d->destStart;
/* This will update only destStart and destSize */
if (!d->destGrow)
{
return TINF_DEST_OVERFLOW;
}
d->destGrow(d, lastAlloc);
d->dest = d->destStart + oldsize;
d->destRemaining = d->destSize - oldsize;
return 0;
}
#ifdef RUNTIME_BITS_TABLES
/* build extra bits and base tables */
static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first)
{
int i, sum;
/* build bits table */
for (i = 0; i < delta; ++i) bits[i] = 0;
for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta;
/* build base table */
for (sum = first, i = 0; i < 30; ++i)
{
base[i] = sum;
sum += 1 << bits[i];
}
}
#endif
/* build the fixed huffman trees */
static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt)
{
int i;
/* build fixed length tree */
for (i = 0; i < 7; ++i) lt->table[i] = 0;
lt->table[7] = 24;
lt->table[8] = 152;
lt->table[9] = 112;
for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i;
for (i = 0; i < 144; ++i) lt->trans[24 + i] = i;
for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i;
for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i;
/* build fixed distance tree */
for (i = 0; i < 5; ++i) dt->table[i] = 0;
dt->table[5] = 32;
for (i = 0; i < 32; ++i) dt->trans[i] = i;
}
/* given an array of code lengths, build a tree */
static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num)
{
unsigned short offs[16];
unsigned int i, sum;
/* clear code length count table */
for (i = 0; i < 16; ++i) t->table[i] = 0;
/* scan symbol lengths, and sum code length counts */
for (i = 0; i < num; ++i) t->table[lengths[i]]++;
t->table[0] = 0;
/* compute offset table for distribution sort */
for (sum = 0, i = 0; i < 16; ++i)
{
offs[i] = sum;
sum += t->table[i];
}
/* create code->symbol translation table (symbols sorted by code) */
for (i = 0; i < num; ++i)
{
if (lengths[i]) t->trans[offs[lengths[i]]++] = i;
}
}
/* ---------------------- *
* -- decode functions -- *
* ---------------------- */
/* get one bit from source stream */
static int tinf_getbit(TINF_DATA *d)
{
unsigned int bit;
/* check if tag is empty */
if (!d->bitcount--)
{
/* load next tag */
d->tag = *d->source++;
d->bitcount = 7;
}
/* shift bit out of tag */
bit = d->tag & 0x01;
d->tag >>= 1;
return bit;
}
/* read a num bit value from a stream and add base */
static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base)
{
unsigned int val = 0;
/* read num bits */
if (num)
{
unsigned int limit = 1 << (num);
unsigned int mask;
for (mask = 1; mask < limit; mask *= 2)
if (tinf_getbit(d)) val += mask;
}
return val + base;
}
/* given a data stream and a tree, decode a symbol */
static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t)
{
int sum = 0, cur = 0, len = 0;
/* get more bits while code value is above sum */
do {
cur = 2*cur + tinf_getbit(d);
++len;
sum += t->table[len];
cur -= t->table[len];
} while (cur >= 0);
return t->trans[sum + cur];
}
/* given a data stream, decode dynamic trees from it */
static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
{
unsigned char lengths[288+32];
unsigned int hlit, hdist, hclen;
unsigned int i, num, length;
/* get 5 bits HLIT (257-286) */
hlit = tinf_read_bits(d, 5, 257);
/* get 5 bits HDIST (1-32) */
hdist = tinf_read_bits(d, 5, 1);
/* get 4 bits HCLEN (4-19) */
hclen = tinf_read_bits(d, 4, 4);
for (i = 0; i < 19; ++i) lengths[i] = 0;
/* read code lengths for code length alphabet */
for (i = 0; i < hclen; ++i)
{
/* get 3 bits code length (0-7) */
unsigned int clen = tinf_read_bits(d, 3, 0);
lengths[clcidx[i]] = clen;
}
/* build code length tree, temporarily use length tree */
tinf_build_tree(lt, lengths, 19);
/* decode code lengths for the dynamic trees */
for (num = 0; num < hlit + hdist; )
{
int sym = tinf_decode_symbol(d, lt);
switch (sym)
{
case 16:
/* copy previous code length 3-6 times (read 2 bits) */
{
unsigned char prev = lengths[num - 1];
for (length = tinf_read_bits(d, 2, 3); length; --length)
{
lengths[num++] = prev;
}
}
break;
case 17:
/* repeat code length 0 for 3-10 times (read 3 bits) */
for (length = tinf_read_bits(d, 3, 3); length; --length)
{
lengths[num++] = 0;
}
break;
case 18:
/* repeat code length 0 for 11-138 times (read 7 bits) */
for (length = tinf_read_bits(d, 7, 11); length; --length)
{
lengths[num++] = 0;
}
break;
default:
/* values 0-15 represent the actual code lengths */
lengths[num++] = sym;
break;
}
}
/* build dynamic trees */
tinf_build_tree(lt, lengths, hlit);
tinf_build_tree(dt, lengths + hlit, hdist);
}
/* ----------------------------- *
* -- block inflate functions -- *
* ----------------------------- */
/* given a stream and two trees, inflate a block of data */
static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
{
while (1)
{
int sym = tinf_decode_symbol(d, lt);
/* check for end of block */
if (sym == 256)
{
return TINF_OK;
}
if (sym < 256)
{
if (d->destRemaining == 0)
{
int res = tinf_grow_dest_buf(d, 1);
if (res) return res;
}
*d->dest++ = sym;
d->destRemaining--;
} else {
unsigned int length, offs, i;
int dist;
sym -= 257;
/* possibly get more bits from length code */
length = tinf_read_bits(d, length_bits[sym], length_base[sym]);
dist = tinf_decode_symbol(d, dt);
/* possibly get more bits from distance code */
offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]);
if (d->destRemaining < length)
{
int res = tinf_grow_dest_buf(d, length);
if (res) return res;
}
/* copy match */
for (i = 0; i < length; ++i)
{
d->dest[i] = d->dest[(int)(i - offs)];
}
d->dest += length;
d->destRemaining -= length;
}
}
}
/* inflate an uncompressed block of data */
static int tinf_inflate_uncompressed_block(TINF_DATA *d)
{
unsigned int length, invlength;
unsigned int i;
/* get length */
length = d->source[1];
length = 256*length + d->source[0];
/* get one's complement of length */
invlength = d->source[3];
invlength = 256*invlength + d->source[2];
/* check length */
if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR;
if (d->destRemaining < length)
{
int res = tinf_grow_dest_buf(d, length);
if (res) return res;
}
d->source += 4;
/* copy block */
for (i = length; i; --i) *d->dest++ = *d->source++;
d->destRemaining -= length;
/* make sure we start next block on a byte boundary */
d->bitcount = 0;
return TINF_OK;
}
/* inflate a block of data compressed with fixed huffman trees */
static int tinf_inflate_fixed_block(TINF_DATA *d)
{
/* build fixed huffman trees */
tinf_build_fixed_trees(&d->ltree, &d->dtree);
/* decode block using fixed trees */
return tinf_inflate_block_data(d, &d->ltree, &d->dtree);
}
/* inflate a block of data compressed with dynamic huffman trees */
static int tinf_inflate_dynamic_block(TINF_DATA *d)
{
/* decode trees from stream */
tinf_decode_trees(d, &d->ltree, &d->dtree);
/* decode block using decoded trees */
return tinf_inflate_block_data(d, &d->ltree, &d->dtree);
}
/* ---------------------- *
* -- public functions -- *
* ---------------------- */
/* initialize global (static) data */
void tinf_init(void)
{
#ifdef RUNTIME_BITS_TABLES
/* build extra bits and base tables */
tinf_build_bits_base(length_bits, length_base, 4, 3);
tinf_build_bits_base(dist_bits, dist_base, 2, 1);
/* fix a special case */
length_bits[28] = 0;
length_base[28] = 258;
#endif
}
/* inflate stream from source to dest */
int tinf_uncompress(void *dest, unsigned int *destLen,
const void *source, unsigned int sourceLen)
{
(void)sourceLen;
TINF_DATA d;
int res;
/* initialise data */
d.source = (const unsigned char *)source;
d.destStart = (unsigned char *)dest;
d.destRemaining = *destLen;
d.destSize = *destLen;
res = tinf_uncompress_dyn(&d);
*destLen = d.dest - d.destStart;
return res;
}
/* inflate stream from source to dest */
int tinf_uncompress_dyn(TINF_DATA *d)
{
int bfinal;
/* initialise data */
d->bitcount = 0;
d->dest = d->destStart;
d->destRemaining = d->destSize;
do {
unsigned int btype;
int res;
/* read final block flag */
bfinal = tinf_getbit(d);
/* read block type (2 bits) */
btype = tinf_read_bits(d, 2, 0);
/* decompress block */
switch (btype)
{
case 0:
/* decompress uncompressed block */
res = tinf_inflate_uncompressed_block(d);
break;
case 1:
/* decompress block with fixed huffman trees */
res = tinf_inflate_fixed_block(d);
break;
case 2:
/* decompress block with dynamic huffman trees */
res = tinf_inflate_dynamic_block(d);
break;
default:
return TINF_DATA_ERROR;
}
if (res != TINF_OK) return TINF_DATA_ERROR;
} while (!bfinal);
return TINF_OK;
}

View file

@ -0,0 +1,101 @@
/*
* tinfzlib - tiny zlib decompressor
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include "tinf.h"
int tinf_zlib_uncompress(void *dest, unsigned int *destLen,
const void *source, unsigned int sourceLen)
{
TINF_DATA d;
int res;
/* initialise data */
d.source = (const unsigned char *)source;
d.destStart = (unsigned char *)dest;
d.destRemaining = *destLen;
res = tinf_zlib_uncompress_dyn(&d, sourceLen);
*destLen = d.dest - d.destStart;
return res;
}
int tinf_zlib_uncompress_dyn(TINF_DATA *d, unsigned int sourceLen)
{
unsigned int a32;
int res;
unsigned char cmf, flg;
/* -- get header bytes -- */
cmf = d->source[0];
flg = d->source[1];
/* -- check format -- */
/* check checksum */
if ((256*cmf + flg) % 31) return TINF_DATA_ERROR;
/* check method is deflate */
if ((cmf & 0x0f) != 8) return TINF_DATA_ERROR;
/* check window size is valid */
if ((cmf >> 4) > 7) return TINF_DATA_ERROR;
/* check there is no preset dictionary */
if (flg & 0x20) return TINF_DATA_ERROR;
/* -- get adler32 checksum -- */
a32 = d->source[sourceLen - 4];
a32 = 256*a32 + d->source[sourceLen - 3];
a32 = 256*a32 + d->source[sourceLen - 2];
a32 = 256*a32 + d->source[sourceLen - 1];
d->source += 2;
/* -- inflate -- */
res = tinf_uncompress_dyn(d);
if (res != TINF_OK) return res;
/* -- check adler32 checksum -- */
if (a32 != tinf_adler32(d->destStart, d->dest - d->destStart)) return TINF_DATA_ERROR;
return TINF_OK;
}

View file

@ -0,0 +1,28 @@
// util
void hex_dump(const char *desc, const void *addr, const size_t len);
#define Int32 signed long
#define Uint32 unsigned long
#define Byte unsigned char
#define Word unsigned short
#define Bool char
#define true 1
#define false 0
#define MAKEWORD(a, b) ((Word)(((Byte)((a) & 0xff)) | ((Word)((Byte)((b) & 0xff))) << 8))
#define MAKELONG(low,high) ((Int32)(((Word)(low)) | (((Uint32)((Word)(high))) << 16)))
#define LOWORD(l) ((Word)((l) & 0xffff))
#define HIWORD(l) ((Word)((l) >> 16))
#define LOBYTE(w) ((Byte)((w) & 0xff))
#define HIBYTE(w) ((Byte)((w) >> 8))
#define HH(x) HIBYTE(HIWORD( x ))
#define HL(x) LOBYTE(HIWORD( x ))
#define LH(x) HIBYTE(LOWORD( x ))
#define LL(x) LOBYTE(LOWORD( x ))
#define LONIBLE(x) (((Byte)x) & 0x0F )
#define HINIBLE(x) ((((Byte)x) * 0xF0)>>4)
#define _SWAPS(x) ((unsigned short)( \
((((unsigned short) x) & 0x000000FF) << 8) | \
((((unsigned short) x) & 0x0000FF00) >> 8) \
))

View file

@ -0,0 +1,108 @@
/* websocket_mbedtls - Websocket example using mbed TLS.
*
* It creates a websocket with a server running on Heroku. It uses TLS v1.2.
* I already implemented support to the permessage-deflate extension that
* reduces the footprint of the websocket frames using the inflate deflate
* algorithm.
* The remaining memory when using the websocket on top of mbed TLS 1.2 is ~14Kb
*
* There is room for memory optimization. Feel free to improve the code.
*
* After get the esp8266 connected, go to ruby-websockets-chat.herokuapp.com
* and type some commands like break (to reconnect), turn led on and turn led off.
*
* If you wanna check the source code from the server:
* https://github.com/heroku-examples/ruby-websockets-chat-demo
*
* If you wanna work with the permessage-deflate extension, connect to the
* host serene-escarpment-15149.herokuapp.com and the path /echo . It is a websocket
* server echoing back your messages and supporting the permessage-deflate extension:
* https://github.com/luisbebop/websocket-echo-deflate
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "ssid_config.h"
#include "conn.h"
#include "ws.h"
#include "util.h"
const int gpio = 2;
void websocket_task(void *pvParameters)
{
//char * host = "serene-escarpment-15149.herokuapp.com";
//char * path = "/echo";
char *host = "ruby-websockets-chat.herokuapp.com";
char *path = "/";
int socket = 0, ret = 0;
int port = 443;
Bool compression = false, timeout = false;
char text[2048];
gpio_enable(gpio, GPIO_OUTPUT);
gpio_write(gpio, 1);
while(1) {
vTaskDelay(5000 / portTICK_RATE_MS);
printf("top of loop, free heap = %u\n", xPortGetFreeHeapSize());
socket = ConnConnect(host, port);
printf("\nConnConnect socket %d\n", socket);
ret = wsConnect(socket, host, path, &compression);
printf("wsConnect ret %d compression %d\n", ret, compression);
if ( ret < 0)
{
printf("websocket handshake error ret=%d\n", ret);
}
strcpy(text, "{\"handle\":\"websocket-c-client\", \"text\": \"hello from low level ~*~ esp8266\"}");
wsSendText(socket, text, compression);
while(1) {
printf("top of loop, free heap = %u\n", xPortGetFreeHeapSize());
memset(text, 0, sizeof(text));
wsReceiveText(socket, text, 2048, compression, &timeout, 15);
if (timeout) {
printf("wsReceiveText timeout\n");
} else {
printf("no timeout ...\n");
printf("%s\n", text);
if(strstr(text, "break") != 0) break;
if(strstr(text, "turn led on") != 0) gpio_write(gpio, 0);
if(strstr(text, "turn led off") != 0) gpio_write(gpio, 1);
}
}
printf("Before ConnClose top of loop, free heap = %u\n", xPortGetFreeHeapSize());
ConnClose(socket);
printf("After ConnClose top of loop, free heap = %u\n", xPortGetFreeHeapSize());
}
}
void user_init(void)
{
uart_set_baud(0, 115200);
printf("SDK version:%s\n", sdk_system_get_sdk_version());
struct sdk_station_config config = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
};
/* required to call wifi_set_opmode before station_set_config */
sdk_wifi_set_opmode(STATION_MODE);
sdk_wifi_station_set_config(&config);
xTaskCreate(&websocket_task, (signed char *)"websocket_task", 2048, NULL, 2, NULL);
}

View file

@ -0,0 +1,270 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "tinf.h"
#include "defl_static.h"
#include "conn.h"
#include "ws.h"
const static char http_get[] = "GET wss://%s%s HTTP/1.1\r\n";
const static char http_host[] = "Host: %s\r\n";
const static char http_origin[] = "Origin: https://%s\r\n";
const static char http_upgrade[] = "Upgrade: websocket\r\n";
const static char http_connection[] = "Connection: Upgrade\r\n";
const static char http_ws_key[] = "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Extensions: permessage-deflate\r\n";
const static char http_ws_version[] = "Sec-WebSocket-Version: 13\r\n";
#define WS_OPCODE_CONTINUATION 0x00
#define WS_OPCODE_TEXT 0x01
#define WS_OPCODE_BINARY 0x02
#define WS_OPCODE_CONECTION_CLOSE 0x08
#define WS_OPCODE_PING 0x09
#define WS_OPCODE_PONG 0x0A
int wsConnect(int socket, const char *host, const char *path, Bool *compression) {
char server_reply[1024];
char buffer[1024];
memset(server_reply, 0, sizeof(server_reply));
*compression = false;
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, http_get, host, path);
ConnWrite(socket, buffer, strlen(buffer));
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, http_host, host);
ConnWrite(socket, buffer, strlen(buffer));
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, http_origin, host);
ConnWrite(socket, buffer, strlen(buffer));
ConnWrite(socket, http_upgrade, strlen(http_upgrade));
ConnWrite(socket, http_connection, strlen(http_connection));
ConnWrite(socket, http_ws_key, strlen(http_ws_key));
ConnWrite(socket, http_ws_version, strlen(http_ws_version));
ConnWrite(socket, "\r\n", 2);
if (ConnRead(socket, server_reply, 300) <= 0) {
return -1;
}
if (strstr(server_reply, "HTTP/1.1 101 Switching Protocols") == NULL) {
return -1;
}
if (strstr(server_reply, "permessage-deflate") != 0) {
*compression = true;
}
return 1;
}
void wsInitFrame(WsFrame *frame) {
frame->finFlag = 0;
frame->maskingFlag = 0;
frame->opcode = 0;
frame->payloadLenght = 0;
frame->maskingMap = 0;
frame->payload = 0;
}
void wsCreateTextFrame(WsFrame *frame, const char *text) {
frame->finFlag = 1;
frame->maskingFlag = 1;
frame->opcode = WS_OPCODE_TEXT;
frame->payloadLenght = strlen(text);
frame->maskingMap = 0x00000000;
frame->payload = (uint8_t *)text;
}
void wsSendFrame(int socket, WsFrame *frame) {
uint8_t finOpcode = (frame->finFlag) ? frame->opcode | 0x80 : frame->opcode;
uint8_t maskPayloadLength = (frame->maskingFlag) ? frame->payloadLenght | 0x80 : frame->payloadLenght;
unsigned short sizePayload;
// todo support extended payloadLength
// The length of the "Payload data", in bytes: if 0-125, that is the
// payload length. If 126, the following 2 bytes interpreted as a
// 16-bit unsigned integer are the payload length. If 127, the
// following 8 bytes interpreted as a 64-bit unsigned integer (the
// most significant bit MUST be 0) are the payload length. Multibyte
// length quantities are expressed in network byte order. Note that
// in all cases, the minimal number of bytes MUST be used to encode
// the length, for example, the length of a 124-byte-long string
// can't be encoded as the sequence 126, 0, 124. The payload length
// is the length of the "Extension data" + the length of the
// "Application data". The length of the "Extension data" may be
// zero, in which case the payload length is the length of the
// "Application data".
// FE = 126
if (frame->payloadLenght > 125) {
maskPayloadLength = 0xfe;
sizePayload = MAKEWORD(LL(frame->payloadLenght), LH(frame->payloadLenght));
}
ConnWrite(socket, &finOpcode, 1);
ConnWrite(socket, &maskPayloadLength, 1);
if (frame->payloadLenght > 125) {
sizePayload = _SWAPS(sizePayload);
ConnWrite(socket, &sizePayload, 2);
}
ConnWrite(socket, &frame->maskingMap , 4);
ConnWrite(socket, frame->payload, frame->payloadLenght);
}
void wsReceiveFrame(int socket, WsFrame *frame, Bool *timeout, int seconds) {
unsigned char inBuffer[2048];
int receivedTotal = 0;
int receivedLength = 0;
int i = 0, ret = 0;
*timeout = false;
memset(inBuffer, 0, sizeof(inBuffer));
while((ret = ConnReadBytesAvailable(socket)) == 0) {
i++;
sleep_ms(10);
if ((i > (seconds * 100)) && ret == 0) {
*timeout = true;
return;
}
}
while((receivedLength = ConnRead(socket, inBuffer + receivedTotal, 2 - receivedTotal)) > 0 && receivedTotal < 2) {
receivedTotal += receivedLength;
}
frame->finFlag = (inBuffer[0] & 0x80) >> 7;
frame->opcode = inBuffer[0] & 0x0F;
frame->maskingFlag = inBuffer[1] & 0x80;
frame->payloadLenght = MAKEWORD(inBuffer[1], 0x00);
if (frame->payloadLenght > 125) {
receivedLength = ConnRead(socket, inBuffer, 2);
frame->payloadLenght = MAKEWORD(inBuffer[1], inBuffer[0]);
}
while((receivedLength = ConnRead(socket, inBuffer + receivedTotal, frame->payloadLenght + 2 - receivedTotal)) > 0 && receivedTotal < frame->payloadLenght) {
receivedTotal += receivedLength;
if ((receivedTotal - 2) == frame->payloadLenght) break;
}
memcpy(frame->payload, inBuffer + 2, receivedTotal - 2);
if (frame->opcode == WS_OPCODE_PING) {
wsSendPong(socket, frame);
}
}
unsigned char gzipBuffer[2048];
void wsInflateFrame(WsFrame *frame) {
unsigned int outlen = 2048;
memset(gzipBuffer, 0, sizeof(gzipBuffer));
// https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-28#page-22
// 7.2.2 Decompression
frame->payload[frame->payloadLenght-1] = 0x01;
frame->payload[frame->payloadLenght] = 0x00;
frame->payload[frame->payloadLenght+1] = 0x00;
frame->payload[frame->payloadLenght+2] = 0xff;
frame->payload[frame->payloadLenght+3] = 0xff;
tinf_uncompress(gzipBuffer, &outlen, frame->payload, frame->payloadLenght+4);
memset(frame->payload, 0, frame->payloadLenght+3);
memcpy(frame->payload, gzipBuffer, outlen);
}
void wsDeflateFrame(WsFrame *frame) {
struct Outbuf out = {0};
memset(gzipBuffer, 0, sizeof(gzipBuffer));
//Deflate payload
zlib_start_block(&out);
tinf_compress(&out, frame->payload, frame->payloadLenght);
zlib_finish_block(&out);
memcpy(gzipBuffer, out.outbuf, out.outlen);
frame->finFlag = 0;
frame->opcode = 0xc1;
frame->payloadLenght = out.outlen;
frame->payload = (uint8_t *)gzipBuffer;
zlib_free_block(&out);
}
void wsSendPong(int socket, WsFrame *frame) {
frame->maskingFlag = 1;
frame->opcode = WS_OPCODE_PONG;
frame->payloadLenght = 0;
wsSendFrame(socket, frame);
}
void wsSendText(int socket, const char *text, Bool compression) {
WsFrame frame;
wsInitFrame(&frame);
wsCreateTextFrame(&frame, text);
if (compression) wsDeflateFrame(&frame);
wsSendFrame(socket, &frame);
}
void wsReceiveText(int socket, char *buffer, int bufferSize, Bool compression, Bool *timeout, int seconds) {
WsFrame frame;
wsInitFrame(&frame);
memset(buffer, 0, bufferSize);
frame.payload = (uint8_t *)buffer;
wsReceiveFrame(socket, &frame, timeout, seconds);
if (frame.opcode == WS_OPCODE_TEXT && compression) wsInflateFrame(&frame);
if (frame.opcode == WS_OPCODE_PONG) strcpy(buffer, "OPCODE_PING");
}
void hex_dump(const char *desc, const void *addr, const size_t len) {
int i;
unsigned char buff[17];
unsigned char *pc = (unsigned char*)addr;
// Output description if given.
if (desc != NULL)
printf ("%s:\n", desc);
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of 16 means new line (with line offset).
if ((i % 16) == 0) {
// Just don't print ASCII for the zeroth line.
if (i != 0)
printf (" %s\n", buff);
// Output the offset.
printf (" %04x ", i);
}
// Now the hex code for the specific character.
printf (" %02x", pc[i]);
// And store a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
buff[i % 16] = '.';
else
buff[i % 16] = pc[i];
buff[(i % 16) + 1] = '\0';
}
// Pad out last line if not exactly 16 characters.
while ((i % 16) != 0) {
printf (" ");
i++;
}
// And print the final ASCII bit.
printf (" %s\n", buff);
}

View file

@ -0,0 +1,27 @@
#ifndef WEBSOCKETS
#define WEBSOCKETS
#include "util.h"
typedef struct WsFrame_ {
Byte finFlag;
Byte maskingFlag;
Byte opcode;
Uint32 payloadLenght;
Uint32 maskingMap;
Byte *payload;
} WsFrame;
int wsConnect(int socket, const char *host, const char *path, Bool *compression);
void wsInitFrame(WsFrame *frame);
void wsCreateTextFrame(WsFrame *frame, const char *text);
void wsSendFrame(int socket, WsFrame *frame);
void wsReceiveFrame(int socket, WsFrame *frame, Bool *timeout, int seconds);
void wsInflateFrame(WsFrame *frame);
void wsDeflateFrame(WsFrame *frame);
void wsSendPong(int socket, WsFrame *frame);
void wsSendText(int socket, const char *text, Bool compression);
void wsReceiveText(int socket, char *buffer, int bufferSize, Bool compression, Bool *timeout, int seconds);
void hex_dump(const char *desc, const void *addr, const size_t len);
#endif

View file

@ -345,7 +345,8 @@
/** /**
* LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing.
*/ */
#define LWIP_SO_RCVBUF 0 #define LWIP_SO_RCVBUF 1
#define INT_MAX __INT_MAX__
/** /**
* SO_REUSE==1: Enable SO_REUSEADDR option. * SO_REUSE==1: Enable SO_REUSEADDR option.