diff --git a/MicroPython_BUILD/.cproject b/MicroPython_BUILD/.cproject index 7594b094..dfa2a7bc 100644 --- a/MicroPython_BUILD/.cproject +++ b/MicroPython_BUILD/.cproject @@ -50,6 +50,7 @@ + diff --git a/MicroPython_BUILD/.gitignore b/MicroPython_BUILD/.gitignore index 9ff72e14..ee0285cf 100644 --- a/MicroPython_BUILD/.gitignore +++ b/MicroPython_BUILD/.gitignore @@ -22,6 +22,7 @@ build/ patches/ +qstrdefs.generated.h partitions_mpy.csv /sdkconfig sdkconfig.old @@ -35,3 +36,4 @@ sdkconfig.fw_psram_all make_firmwares.sh mncfg_exit.txt +updates.txt diff --git a/MicroPython_BUILD/BUILD.sh b/MicroPython_BUILD/BUILD.sh index a16bdf6e..aba28ff4 100755 --- a/MicroPython_BUILD/BUILD.sh +++ b/MicroPython_BUILD/BUILD.sh @@ -29,17 +29,19 @@ # Options: # -jN - make with multicore option, N should be the number of cores used -# -v | --verbose - enable verbose output, default: quiet output -# -f8 | --flashsize8 - declare the Flash size of 8 MB -# -f16 | --flashsize16 - declare the Flash size of 16 MB -# -fs | --fssize= - declare the size of Flash file system in KB +# -v | --verbose - enable verbose output, default: quiet output +# -f8 | --flashsize8 - declare the Flash size of 8 MB +# -f16 | --flashsize16 - declare the Flash size of 16 MB +# -fs | --fssize= - declare the size of Flash file system in KB # default: fit the Flash size -# -a | --appsize= - declare the size of application partition in KB +# -a | --appsize= - declare the size of application partition in KB # default: auto detect needed size # the actual size will be 128 KB smaller then the declared size +# -p | --port= - overwritte configured comm port, use the specified instead +# -b | --bdrate= - overwritte configured baud rate, use the specified instead # Note: -# Multiple commands can be given +# Multiple options and commands can be given # ################################################################# @@ -48,7 +50,7 @@ #======================= -TOOLS_VER=ver20180412.id +TOOLS_VER=ver20180510.id #======================= # ----------------------------- @@ -64,6 +66,8 @@ FORCE_3PART="no" POSITIONAL_ARGS=() BUILD_TYPE="" BUILD_BASE_DIR=${PWD} +BUILD_COMPORT="" +BUILD_BDRATE="" # --------------------------------------- # Include functions used in build process diff --git a/MicroPython_BUILD/build_func.sh b/MicroPython_BUILD/build_func.sh index 0b31fdf0..c74cf805 100755 --- a/MicroPython_BUILD/build_func.sh +++ b/MicroPython_BUILD/build_func.sh @@ -30,6 +30,20 @@ get_arguments() { ;; -fs|--fssize) FS_SIZE="$2" + shift # past argument + shift # past value + ;; + -p|--port) + BUILD_COMPORT="$2" + BUILD_COMPORT=" ESPPORT=${BUILD_COMPORT}" + + shift # past argument + shift # past value + ;; + -b|--bdrate) + BUILD_BDRATE="$2" + BUILD_BDRATE=" ESPBAUD=${BUILD_BDRATE}" + shift # past argument shift # past value ;; @@ -176,7 +190,7 @@ set_partitions() { fi echo "# -------------------------------------------------------" > partitions_mpy.csv - echo "# - Partition layout generaded by BUILD.sh script -" >> partitions_mpy.csv + echo "# - Partition layout generated by BUILD.sh script -" >> partitions_mpy.csv echo "# -------------------------------------------------------" >> partitions_mpy.csv echo "# Name, Type, SubType, Offset, Size, Flags" >> partitions_mpy.csv echo "# -------------------------------------------------------" >> partitions_mpy.csv @@ -219,7 +233,7 @@ set_partitions() { fi echo "# -------------------------------------------------------" > partitions_mpy.csv - echo "# - Partition layout generaded by BUILD.sh script -" >> partitions_mpy.csv + echo "# - Partition layout generated by BUILD.sh script -" >> partitions_mpy.csv echo "# -------------------------------------------------------" >> partitions_mpy.csv echo "# Name, Type, SubType, Offset, Size, Flags" >> partitions_mpy.csv echo "# -------------------------------------------------------" >> partitions_mpy.csv @@ -598,21 +612,21 @@ executeCommand() { echo "=========================================" echo "Flashing MicroPython firmware to ESP32..." echo "=========================================" - make ${J_OPTION} ${arg} 2>/dev/null + make ${J_OPTION} ${arg}${BUILD_COMPORT}${BUILD_BDRATE} 2>/dev/null # --------------------------------- elif [ "${arg}" == "erase" ]; then echo "======================" echo "Erasing ESP32 Flash..." echo "======================" - make erase_flash + make erase_flash${BUILD_COMPORT}${BUILD_BDRATE} # ---------------------------------- elif [ "${arg}" == "monitor" ]; then echo "============================" echo "Executing esp-idf monitor..." echo "============================" - make monitor + make monitor${BUILD_COMPORT} # --------------------------------- elif [ "${arg}" == "clean" ]; then diff --git a/MicroPython_BUILD/components/espmqtt/.gitignore b/MicroPython_BUILD/components/espmqtt/.gitignore index f805e810..2c4b72c7 100644 --- a/MicroPython_BUILD/components/espmqtt/.gitignore +++ b/MicroPython_BUILD/components/espmqtt/.gitignore @@ -31,3 +31,6 @@ # Debug files *.dSYM/ *.su +build +examples/**/build +examples/**/sdkconfig* diff --git a/MicroPython_BUILD/components/espmqtt/README.md b/MicroPython_BUILD/components/espmqtt/README.md index be5a1bbe..447c103b 100644 --- a/MicroPython_BUILD/components/espmqtt/README.md +++ b/MicroPython_BUILD/components/espmqtt/README.md @@ -1,5 +1,175 @@ +[![](https://travis-ci.org/tuanpmt/espmqtt.svg?branch=master)](https://travis-ci.org/tuanpmt/espmqtt) +[![](http://hits.dwyl.io/tuanpmt/espmqtt.svg)](http://hits.dwyl.io/tuanpmt/espmqtt) +[![Twitter Follow](https://img.shields.io/twitter/follow/tuanpmt.svg?style=social&label=Follow)](https://twitter.com/tuanpmt) +![GitHub contributors](https://img.shields.io/github/contributors/tuanpmt/espmqtt.svg) + # ESP32 MQTT Library -This is component based on ESP-IDF for ESP32 +## Features + +- Based on: https://github.com/tuanpmt/esp_mqtt +- Support MQTT over TCP, SSL with mbedtls, MQTT over Websocket, MQTT over Websocket Secure +- Easy to setup with URI +- Multiple instances (Multiple clients in one application) +- Support subscribing, publishing, authentication, will messages, keep alive pings and all 3 QoS levels (it should be a fully functional client). + +## How to use + +Clone this component to [ESP-IDF](https://github.com/espressif/esp-idf) project (as submodule): +``` +git submodule add https://github.com/tuanpmt/espmqtt.git components/espmqtt +``` + +Or run a sample (make sure you have installed the [toolchain](http://esp-idf.readthedocs.io/en/latest/get-started/index.html#setup-toolchain)): + +``` +git clone https://github.com/tuanpmt/espmqtt.git +cd espmqtt/examples/mqtt_tcp +make menuconfig +make flash monitor +``` + +## Documentation +### URI + +- Curently support `mqtt`, `mqtts`, `ws`, `wss` schemes +- MQTT over TCP samples: + + `mqtt://iot.eclipse.org`: MQTT over TCP, default port 1883: + + `mqtt://iot.eclipse.org:1884` MQTT over TCP, port 1884: + + `mqtt://username:password@iot.eclipse.org:1884` MQTT over TCP, port 1884, with username and password +- MQTT over SSL samples: + + `mqtts://iot.eclipse.org`: MQTT over SSL, port 8883 + + `mqtts://iot.eclipse.org:8884`: MQTT over SSL, port 8884 +- MQTT over Websocket samples: + + `ws://iot.eclipse.org:80/ws` +- MQTT over Websocket Secure samples: + + `wss://iot.eclipse.org:443/ws` +- Minimal configurations: + +```c +const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtt://iot.eclipse.org", + .event_handle = mqtt_event_handler, + // .user_context = (void *)your_context +}; +``` + +- If there are any options related to the URI in `esp_mqtt_client_config_t`, the option defined by the URI will be overridden. Sample: + +```c +const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtt://iot.eclipse.org:1234", + .event_handle = mqtt_event_handler, + .port = 4567, +}; +//MQTT client will connect to iot.eclipse.org using port 4567 +``` + + +### SSL + +- Get Certification from server, example: `iot.eclipse.org` `openssl s_client -showcerts -connect iot.eclipse.org:8883 /dev/null|openssl x509 -outform PEM >iot_eclipse_org.pem` +- Check the sample application: `examples/mqtt_ssl` +- Configuration: + +```cpp +const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtts://iot.eclipse.org:8883", + .event_handle = mqtt_event_handler, + .cert_pem = (const char *)iot_eclipse_org_pem_start, +}; +``` + + +### More options for `esp_mqtt_client_config_t` + +- `event_handle` for MQTT events +- `host`: MQTT server domain (ipv4 as string) +- `port`: MQTT server port +- `client_id`: default client id is `ESP32_%CHIPID%` +- `username`: MQTT username +- `password`: MQTT password +- `lwt_topic, lwt_msg, lwt_qos, lwt_retain, lwt_msg_len`: are mqtt lwt options, default NULL +- `disable_clean_session`: mqtt clean session, default clean_session is true +- `keepalive`: (value in seconds) mqtt keepalive, default is 120 seconds +- `disable_auto_reconnect`: this mqtt client will reconnect to server (when errors/disconnect). Set `disable_auto_reconnect=true` to disable +- `user_context` pass user context to this option, then can receive that context in `event->user_context` +- `task_prio, task_stack` for MQTT task, default priority is 5, and task_stack = 6144 bytes (or default task stack can be set via `make menucofig`). +- `buffer_size` for MQTT send/receive buffer, default is 1024 +- `cert_pem` pointer to CERT file for server verify (with SSL), default is NULL, not required to verify the server +- `transport`: override URI transport + + `MQTT_TRANSPORT_OVER_TCP`: MQTT over TCP, using scheme: `mqtt` + + `MQTT_TRANSPORT_OVER_SSL`: MQTT over SSL, using scheme: `mqtts` + + `MQTT_TRANSPORT_OVER_WS`: MQTT over Websocket, using scheme: `ws` + + `MQTT_TRANSPORT_OVER_WSS`: MQTT over Websocket Secure, using scheme: `wss` + +### Change settings in `menuconfig` + +``` +make menuconfig +-> Component config -> ESPMQTT Configuration +``` + +## Example + +Check `examples/mqtt_tcp` and `examples/mqtt_ssl` project. In Short: + +```cpp + +static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) +{ + esp_mqtt_client_handle_t client = event->client; + int msg_id; + // your_context_t *context = event->context; + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + } + return ESP_OK; +} +const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtt://iot.eclipse.org", + .event_handle = mqtt_event_handler, + // .user_context = (void *)your_context +}; + +esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); +esp_mqtt_client_start(client); +``` + +## License -Full documentation and sample project: https://github.com/tuanpmt/esp32-mqtt +[@tuanpmt](https://twitter.com/tuanpmt) +Apache License diff --git a/MicroPython_BUILD/components/espmqtt/component.mk b/MicroPython_BUILD/components/espmqtt/component.mk index 229049dd..79bcc62c 100644 --- a/MicroPython_BUILD/components/espmqtt/component.mk +++ b/MicroPython_BUILD/components/espmqtt/component.mk @@ -6,8 +6,5 @@ # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the SDK documents if you need to do this. # -COMPONENT_ADD_INCLUDEDIRS := include -#COMPONENT_PRIV_INCLUDEDIRS := - -#EXTRA_CFLAGS := -DICACHE_RODATA_ATTR -CFLAGS += -Wno-error=implicit-function-declaration -Wno-error=format= -DHAVE_CONFIG_H +COMPONENT_SRCDIRS := . lib +COMPONENT_PRIV_INCLUDEDIRS := lib/include diff --git a/MicroPython_BUILD/components/espmqtt/include/mqtt.h b/MicroPython_BUILD/components/espmqtt/include/mqtt.h deleted file mode 100644 index a6283ea6..00000000 --- a/MicroPython_BUILD/components/espmqtt/include/mqtt.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Boris Lovosevic (https://github.com/loboris) - * - * 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. - */ - -/* - * Mqtt Module using MQTT task. - * Based on ESP32 MQTT Library by Tuan PM, https://github.com/tuanpmt/espmqtt - * Adapted for MicroPython by Boris Lovosevic, https://github.com/loboris - * - */ - -#ifndef _MQTT_H_ -#define _MQTT_H_ - -#include "sdkconfig.h" - -#ifdef CONFIG_MICROPY_USE_MQTT - -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#include "freertos/task.h" - -#include "mqtt_msg.h" -#include "ringbuf.h" - -#include "openssl/ssl.h" - -// Constants not defined in menuconfig -#define CONFIG_MQTT_MAX_HOST_LEN 64 -#define CONFIG_MQTT_MAX_CLIENT_LEN 32 -#define CONFIG_MQTT_MAX_USERNAME_LEN 32 -#define CONFIG_MQTT_MAX_PASSWORD_LEN 32 -#define CONFIG_MQTT_MAX_LWT_TOPIC 32 -#define CONFIG_MQTT_MAX_LWT_MSG 32 -#define CONFIG_MQTT_MAX_TASKNAME_LEN 16 - -// Mqtt client status constants -#define MQTT_STATUS_DISCONNECTED 0 -#define MQTT_STATUS_CONNECTED 1 -#define MQTT_STATUS_STOPPING 2 -#define MQTT_STATUS_STOPPED 4 - -#define MQTT_SENDING_TYPE_NONE 0 -#define MQTT_SENDING_TYPE_PUBLISH 1 -#define MQTT_SENDING_TYPE_SUBSCRIBE 2 -#define MQTT_SENDING_TYPE_UNSUBSCRIBE 3 -#define MQTT_SENDING_TYPE_PING 4 - -typedef struct mqtt_client mqtt_client; -typedef struct mqtt_event_data_t mqtt_event_data_t; - -/** - * \return True on connect success, false on error - */ -typedef bool (* mqtt_connect_callback)(mqtt_client *client); -/** - */ -typedef void (* mqtt_disconnect_callback)(mqtt_client *client); -/** - * \param[out] buffer Pointer to buffer to fill - * \param[in] len Number of bytes to read - * \param[in] timeout_ms Time to wait for completion, or 0 for no timeout - * \return Number of bytes read, less than 0 on error - */ -typedef int (* mqtt_read_callback)(mqtt_client *client, void *buffer, int len, int timeout_ms); -/** - * \param[in] buffer Pointer to buffer to write - * \param[in] len Number of bytes to write - * \param[in] timeout_ms Time to wait for completion, or 0 for no timeout - * \return Number of bytes written, less than 0 on error - */ -typedef int (* mqtt_write_callback)(mqtt_client *client, const void *buffer, int len, int timeout_ms); -typedef void (* mqtt_event_callback)(mqtt_client *client, mqtt_event_data_t *event_data); - -typedef struct mqtt_settings { - mqtt_connect_callback connect_cb; - mqtt_disconnect_callback disconnect_cb; - - mqtt_read_callback read_cb; - mqtt_write_callback write_cb; - - mqtt_event_callback connected_cb; - mqtt_event_callback disconnected_cb; - - mqtt_event_callback subscribe_cb; - mqtt_event_callback unsubscribe_cb; - mqtt_event_callback publish_cb; - mqtt_event_callback data_cb; - - void *mpy_connected_cb; - void *mpy_disconnected_cb; - void *mpy_subscribed_cb; - void *mpy_unsubscribed_cb; - void *mpy_published_cb; - void *mpy_data_cb; - - char host[CONFIG_MQTT_MAX_HOST_LEN]; - uint16_t port; - char client_id[CONFIG_MQTT_MAX_CLIENT_LEN]; - char username[CONFIG_MQTT_MAX_USERNAME_LEN]; - char password[CONFIG_MQTT_MAX_PASSWORD_LEN]; - char lwt_topic[CONFIG_MQTT_MAX_LWT_TOPIC]; - char lwt_msg[CONFIG_MQTT_MAX_LWT_MSG]; - uint32_t lwt_msg_len; - uint32_t lwt_qos; - uint32_t lwt_retain; - uint32_t clean_session; - uint32_t keepalive; - bool auto_reconnect; - bool use_ssl; - TaskHandle_t xMqttTask; - TaskHandle_t xMqttSendingTask; - uint32_t xMqttTask_stacksize; - uint32_t xMqttSendingTask_stacksize; -} mqtt_settings; - -typedef struct mqtt_event_data_t -{ - uint8_t type; - const char* topic; - const char* data; - uint16_t topic_length; - uint16_t data_length; - uint32_t data_offset; - uint32_t data_total_length; -} mqtt_event_data_t; - -typedef struct mqtt_state_t -{ - uint16_t port; - int auto_reconnect; - mqtt_connect_info_t* connect_info; - uint8_t* in_buffer; - uint8_t* out_buffer; - int in_buffer_length; - int out_buffer_length; - uint16_t message_length; - uint16_t message_length_read; - mqtt_message_t* outbound_message; - mqtt_connection_t mqtt_connection; - uint16_t pending_msg_id; - int pending_msg_type; - int sending_msg_type; - int pending_publish_qos; -} mqtt_state_t; - -typedef struct mqtt_client { - int socket; - SSL_CTX *ctx; - SSL *ssl; - mqtt_settings *settings; - mqtt_state_t mqtt_state; - mqtt_connect_info_t connect_info; - QueueHandle_t xSendingQueue; - RINGBUF send_rb; - uint32_t keepalive_tick; - uint8_t status; - uint8_t subs_flag; - uint8_t unsubs_flag; - uint8_t *msgbuf; - uint8_t *topicbuf; - bool terminate_mqtt; - char *name; -} mqtt_client; - -const char *MQTT_TAG; - -int mqtt_start(mqtt_client *client); -void mqtt_stop(mqtt_client* client); -void mqtt_task(void *pvParameters); -void mqtt_subscribe(mqtt_client *client, const char *topic, uint8_t qos); -void mqtt_unsubscribe(mqtt_client *client, const char *topic); -int mqtt_publish(mqtt_client* client, const char *topic, const char *data, int len, int qos, int retain); -void mqtt_free(mqtt_client *client); - -#endif - -#endif diff --git a/MicroPython_BUILD/components/espmqtt/include/mqtt_client.h b/MicroPython_BUILD/components/espmqtt/include/mqtt_client.h new file mode 100755 index 00000000..1d9f7d9d --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/include/mqtt_client.h @@ -0,0 +1,167 @@ +/* + * This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo + * + * Apache License Version 2.0 + * + * Slightly modified mqtt library from https://github.com/tuanpmt/espmqtt + * + * Copyright (c) 2018 tuanpm (https://github.com/tuanpmt/espmqtt) + * Copyright (c) 2018 LoBo (https://github.com/loboris) +*/ + +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ + +#ifndef _MQTT_CLIENT_H_ +#define _MQTT_CLIENT_H_ + +#include +#include +#include +#include "esp_err.h" + +#include "platform.h" +#include "mqtt_config.h" +#include "mqtt_msg.h" +#include "transport.h" +#include "transport_tcp.h" +#include "transport_ssl.h" +#include "transport_ws.h" +#include "platform.h" +#include "mqtt_outbox.h" + +typedef struct esp_mqtt_client* esp_mqtt_client_handle_t; + +typedef enum { + MQTT_EVENT_ERROR = 0, + MQTT_EVENT_CONNECTED, + MQTT_EVENT_DISCONNECTED, + MQTT_EVENT_SUBSCRIBED, + MQTT_EVENT_UNSUBSCRIBED, + MQTT_EVENT_PUBLISHED, + MQTT_EVENT_DATA, +} esp_mqtt_event_id_t; + +typedef enum { + MQTT_TRANSPORT_UNKNOWN = 0x0, + MQTT_TRANSPORT_OVER_TCP, + MQTT_TRANSPORT_OVER_SSL, + MQTT_TRANSPORT_OVER_WS, + MQTT_TRANSPORT_OVER_WSS +} esp_mqtt_transport_t; + +typedef enum { + MQTT_STATE_ERROR = -1, + MQTT_STATE_UNKNOWN = 0, + MQTT_STATE_INIT, + MQTT_STATE_CONNECTED, + MQTT_STATE_WAIT_TIMEOUT, +} mqtt_client_state_t; + +typedef struct { + esp_mqtt_event_id_t event_id; + esp_mqtt_client_handle_t client; + void *user_context; + char *data; + int data_len; + int total_data_len; + int current_data_offset; + char *topic; + int topic_len; + int msg_id; + int type; +} esp_mqtt_event_t; + +typedef esp_mqtt_event_t* esp_mqtt_event_handle_t; + +typedef esp_err_t (* mqtt_event_callback_t)(esp_mqtt_event_handle_t event); + + +typedef struct { + mqtt_event_callback_t event_handle; + char host[MQTT_MAX_HOST_LEN]; + char uri[MQTT_MAX_HOST_LEN]; + uint32_t port; + char client_id[MQTT_MAX_CLIENT_LEN]; + char username[MQTT_MAX_USERNAME_LEN]; + char password[MQTT_MAX_PASSWORD_LEN]; + char lwt_topic[MQTT_MAX_LWT_TOPIC]; + char lwt_msg[MQTT_MAX_LWT_MSG]; + int lwt_qos; + int lwt_retain; + int lwt_msg_len; + int disable_clean_session; + int keepalive; + bool disable_auto_reconnect; + void *user_context; + int task_prio; + int task_stack; + int buffer_size; + const char *cert_pem; + esp_mqtt_transport_t transport; +} esp_mqtt_client_config_t; + +typedef struct mqtt_state +{ + mqtt_connect_info_t *connect_info; + uint8_t *in_buffer; + uint8_t *out_buffer; + int in_buffer_length; + int out_buffer_length; + uint16_t message_length; + uint16_t message_length_read; + mqtt_message_t *outbound_message; + mqtt_connection_t mqtt_connection; + uint16_t pending_msg_id; + int pending_msg_type; + int pending_publish_qos; + int pending_msg_count; +} mqtt_state_t; + +typedef struct { + mqtt_event_callback_t event_handle; + int task_stack; + int task_prio; + char *uri; + char *host; + char *path; + char *scheme; + int port; + bool auto_reconnect; + void *user_context; + int network_timeout_ms; +} mqtt_config_storage_t; + +struct esp_mqtt_client { + transport_list_handle_t transport_list; + transport_handle_t transport; + mqtt_config_storage_t *config; + mqtt_state_t mqtt_state; + mqtt_connect_info_t connect_info; + mqtt_client_state_t state; + long long keepalive_tick; + long long reconnect_tick; + int wait_timeout_ms; + int auto_reconnect; + esp_mqtt_event_t event; + bool run; + outbox_handle_t outbox; + EventGroupHandle_t status_bits; + void *mpy_mqtt_obj; +}; + +extern const char *MQTT_TAG; + +esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config); +esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *uri); +esp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client); +esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client); +esp_err_t esp_mqtt_client_subscribe(esp_mqtt_client_handle_t client, const char *topic, int qos); +esp_err_t esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *topic); +int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain); +esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client); + +#endif diff --git a/MicroPython_BUILD/components/espmqtt/include/mqtt_config.h b/MicroPython_BUILD/components/espmqtt/include/mqtt_config.h new file mode 100644 index 00000000..b10744ee --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/include/mqtt_config.h @@ -0,0 +1,68 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ +#ifndef _MQTT_CONFIG_H_ +#define _MQTT_CONFIG_H_ + +#include "sdkconfig.h" + +#define MQTT_PROTOCOL_311 CONFIG_MQTT_PROTOCOL_311 +#define MQTT_RECONNECT_TIMEOUT_MS (10*1000) + +#if CONFIG_MQTT_BUFFER_SIZE +#define MQTT_BUFFER_SIZE_BYTE CONFIG_MQTT_BUFFER_SIZE +#else +#define MQTT_BUFFER_SIZE_BYTE 1024 +#endif + +#define MQTT_MAX_HOST_LEN 64 +#define MQTT_MAX_CLIENT_LEN 32 +#define MQTT_MAX_USERNAME_LEN 32 +#define MQTT_MAX_PASSWORD_LEN 65 +#define MQTT_MAX_LWT_TOPIC 32 +#define MQTT_MAX_LWT_MSG 128 +#define MQTT_TASK_PRIORITY 5 + +#if CONFIG_MQTT_TASK_STACK_SIZE +#define MQTT_TASK_STACK CONFIG_MQTT_TASK_STACK_SIZE +#else +#define MQTT_TASK_STACK (6*1024) +#endif + +#define MQTT_KEEPALIVE_TICK (120) +#define MQTT_CMD_QUEUE_SIZE (10) +#define MQTT_NETWORK_TIMEOUT_MS (10000) + +#ifdef CONFIG_MQTT_TCP_DEFAULT_PORT +#define MQTT_TCP_DEFAULT_PORT CONFIG_MQTT_TCP_DEFAULT_PORT +#else +#define MQTT_TCP_DEFAULT_PORT 1883 +#endif + +#ifdef CONFIG_MQTT_SSL_DEFAULT_PORT +#define MQTT_SSL_DEFAULT_PORT CONFIG_MQTT_SSL_DEFAULT_PORT +#else +#define MQTT_SSL_DEFAULT_PORT 8883 +#endif + +#ifdef CONFIG_MQTT_WS_DEFAULT_PORT +#define MQTT_WS_DEFAULT_PORT CONFIG_MQTT_WS_DEFAULT_PORT +#else +#define MQTT_WS_DEFAULT_PORT 80 +#endif + +#ifdef MQTT_WSS_DEFAULT_PORT +#define MQTT_WSS_DEFAULT_PORT CONFIG_MQTT_WSS_DEFAULT_PORT +#else +#define MQTT_WSS_DEFAULT_PORT 443 +#endif + +#define MQTT_ENABLE_SSL CONFIG_MQTT_TRANSPORT_SSL +#define MQTT_ENABLE_WS CONFIG_MQTT_TRANSPORT_WEBSOCKET +#define MQTT_ENABLE_WSS CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE + +#define OUTBOX_EXPIRED_TIMEOUT_MS (30*1000) +#define OUTBOX_MAX_SIZE (4*1024) +#endif diff --git a/MicroPython_BUILD/components/espmqtt/include/ringbuf.h b/MicroPython_BUILD/components/espmqtt/include/ringbuf.h deleted file mode 100644 index 6444b234..00000000 --- a/MicroPython_BUILD/components/espmqtt/include/ringbuf.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _RING_BUF_H_ -#define _RING_BUF_H_ - -#include - - -typedef struct{ - uint8_t* p_o; /**< Original pointer */ - uint8_t* volatile p_r; /**< Read pointer */ - uint8_t* volatile p_w; /**< Write pointer */ - volatile int32_t fill_cnt; /**< Number of filled slots */ - int32_t size; /**< Buffer size */ - int32_t block_size; -}RINGBUF; - -int32_t rb_init(RINGBUF *r, uint8_t* buf, int32_t size, int32_t block_size); -int32_t rb_put(RINGBUF *r, uint8_t* c); -int32_t rb_get(RINGBUF *r, uint8_t* c); -int32_t rb_available(RINGBUF *r); -uint32_t rb_read(RINGBUF *r, uint8_t *buf, int len); -uint32_t rb_write(RINGBUF *r, uint8_t *buf, int len); - -#endif diff --git a/MicroPython_BUILD/components/espmqtt/include/mqtt_msg.h b/MicroPython_BUILD/components/espmqtt/lib/include/mqtt_msg.h similarity index 75% rename from MicroPython_BUILD/components/espmqtt/include/mqtt_msg.h rename to MicroPython_BUILD/components/espmqtt/lib/include/mqtt_msg.h index 9aceecb6..fd04cd0f 100644 --- a/MicroPython_BUILD/components/espmqtt/include/mqtt_msg.h +++ b/MicroPython_BUILD/components/espmqtt/lib/include/mqtt_msg.h @@ -1,6 +1,6 @@ #ifndef MQTT_MSG_H #define MQTT_MSG_H - +#include "mqtt_config.h" #ifdef __cplusplus extern "C" { #endif @@ -42,61 +42,61 @@ extern "C" { enum mqtt_message_type { - MQTT_MSG_TYPE_CONNECT = 1, - MQTT_MSG_TYPE_CONNACK = 2, - MQTT_MSG_TYPE_PUBLISH = 3, - MQTT_MSG_TYPE_PUBACK = 4, - MQTT_MSG_TYPE_PUBREC = 5, - MQTT_MSG_TYPE_PUBREL = 6, - MQTT_MSG_TYPE_PUBCOMP = 7, - MQTT_MSG_TYPE_SUBSCRIBE = 8, - MQTT_MSG_TYPE_SUBACK = 9, - MQTT_MSG_TYPE_UNSUBSCRIBE = 10, - MQTT_MSG_TYPE_UNSUBACK = 11, - MQTT_MSG_TYPE_PINGREQ = 12, - MQTT_MSG_TYPE_PINGRESP = 13, - MQTT_MSG_TYPE_DISCONNECT = 14 + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 }; enum mqtt_connect_return_code { - CONNECTION_ACCEPTED = 0, - CONNECTION_REFUSE_PROTOCOL, - CONNECTION_REFUSE_ID_REJECTED, - CONNECTION_REFUSE_SERVER_UNAVAILABLE, - CONNECTION_REFUSE_BAD_USERNAME, - CONNECTION_REFUSE_NOT_AUTHORIZED + CONNECTION_ACCEPTED = 0, + CONNECTION_REFUSE_PROTOCOL, + CONNECTION_REFUSE_ID_REJECTED, + CONNECTION_REFUSE_SERVER_UNAVAILABLE, + CONNECTION_REFUSE_BAD_USERNAME, + CONNECTION_REFUSE_NOT_AUTHORIZED }; typedef struct mqtt_message { - uint8_t* data; - uint16_t length; + uint8_t* data; + uint32_t length; } mqtt_message_t; typedef struct mqtt_connection { - mqtt_message_t message; + mqtt_message_t message; - uint16_t message_id; - uint8_t* buffer; - uint16_t buffer_length; + uint16_t message_id; + uint8_t* buffer; + uint16_t buffer_length; } mqtt_connection_t; typedef struct mqtt_connect_info { - char* client_id; - char* username; - char* password; - char* will_topic; - char* will_message; - int keepalive; - int will_length; - int will_qos; - int will_retain; - int clean_session; + char* client_id; + char* username; + char* password; + char* will_topic; + char* will_message; + int keepalive; + int will_length; + int will_qos; + int will_retain; + int clean_session; } mqtt_connect_info_t; @@ -108,9 +108,9 @@ static inline int mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1 static inline int mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); -int mqtt_get_total_length(uint8_t* buffer, uint16_t length); -const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); -const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); +uint32_t mqtt_get_total_length(uint8_t* buffer, uint16_t length); +const char* mqtt_get_publish_topic(uint8_t* buffer, uint32_t* length); +const char* mqtt_get_publish_data(uint8_t* buffer, uint32_t* length); uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length); mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); diff --git a/MicroPython_BUILD/components/espmqtt/lib/include/mqtt_outbox.h b/MicroPython_BUILD/components/espmqtt/lib/include/mqtt_outbox.h new file mode 100644 index 00000000..b94bd80f --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/include/mqtt_outbox.h @@ -0,0 +1,47 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ +#ifndef _MQTT_OUTOBX_H_ +#define _MQTT_OUTOBX_H_ +#include "platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct outbox_item { + char *buffer; + int len; + int msg_id; + int msg_type; + int tick; + int retry_count; + bool pending; + STAILQ_ENTRY(outbox_item) next; +} outbox_item_t; + +STAILQ_HEAD(outbox_list_t, outbox_item); + +typedef struct outbox_list_t * outbox_handle_t; +typedef outbox_item_t *outbox_item_handle_t; + +outbox_handle_t outbox_init(); +outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, uint8_t *data, int len, int msg_id, int msg_type, int tick); +outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox); +outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id); +esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type); +esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id); +esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type); +esp_err_t outbox_delete_expired(outbox_handle_t outbox, int current_tick, int timeout); + +esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id); +int outbox_get_size(outbox_handle_t outbox); +esp_err_t outbox_cleanup(outbox_handle_t outbox, int max_size); +void outbox_destroy(outbox_handle_t outbox); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/MicroPython_BUILD/components/espmqtt/lib/include/platform.h b/MicroPython_BUILD/components/espmqtt/lib/include/platform.h new file mode 100644 index 00000000..45eb3bf6 --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/include/platform.h @@ -0,0 +1,37 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ +#ifndef _PLATFORM_H__ +#define _PLATFORM_H__ + +//Support ESP32 +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" + +#include "rom/queue.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_system.h" + +char *platform_create_id_string(); +int platform_random(int max); +long long platform_tick_get_ms(); +void ms_to_timeval(int timeout_ms, struct timeval *tv); + +#define ESP_MEM_CHECK(TAG, a, action) if (!(a)) { \ + ESP_LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \ + action; \ + } + +#endif diff --git a/MicroPython_BUILD/components/espmqtt/lib/include/transport.h b/MicroPython_BUILD/components/espmqtt/lib/include/transport.h new file mode 100644 index 00000000..9dbd8809 --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/include/transport.h @@ -0,0 +1,242 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ +#ifndef _TRANSPORT_H_ +#define _TRANSPORT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct transport_list_t* transport_list_handle_t; +typedef struct transport_item_t* transport_handle_t; + +typedef int (*connect_func)(transport_handle_t t, const char *host, int port, int timeout_ms); +typedef int (*io_func)(transport_handle_t t, const char *buffer, int len, int timeout_ms); +typedef int (*io_read_func)(transport_handle_t t, char *buffer, int len, int timeout_ms); +typedef int (*trans_func)(transport_handle_t t); +typedef int (*poll_func)(transport_handle_t t, int timeout_ms); + +/** + * @brief Create transport list + * + * @return A handle can hold all transports + */ +transport_list_handle_t transport_list_init(); + +/** + * @brief Cleanup and free all transports, include itself, + * this function will invoke transport_destroy of every transport have added this the list + * + * @param[in] list The list + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t transport_list_destroy(transport_list_handle_t list); + +/** + * @brief Add a transport to the list, and define a scheme to indentify this transport in the list + * + * @param[in] list The list + * @param[in] t The Transport + * @param[in] scheme The scheme + * + * @return + * - ESP_OK + */ +esp_err_t transport_list_add(transport_list_handle_t list, transport_handle_t t, const char *scheme); + +/** + * @brief This function will remove all transport from the list, + * invoke transport_destroy of every transport have added this the list + * + * @param[in] list The list + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG + */ +esp_err_t transport_list_clean(transport_list_handle_t list); + +/** + * @brief Get the transport by scheme, which has been defined when calling function `transport_list_add` + * + * @param[in] list The list + * @param[in] tag The tag + * + * @return The transport handle + */ +transport_handle_t transport_list_get_transport(transport_list_handle_t list, const char *scheme); + +/** + * @brief Initialize a transport handle object + * + * @return The transport handle + */ +transport_handle_t transport_init(); + +/** + * @brief Cleanup and free memory the transport + * + * @param[in] t The transport handle + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t transport_destroy(transport_handle_t t); + +/** + * @brief Get default port number used by this transport + * + * @param[in] t The transport handle + * + * @return the port number + */ +int transport_get_default_port(transport_handle_t t); + +/** + * @brief Set default port number that can be used by this transport + * + * @param[in] t The transport handle + * @param[in] port The port number + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t transport_set_default_port(transport_handle_t t, int port); + +/** + * @brief Transport connection function, to make a connection to server + * + * @param t The transport handle + * @param[in] host Hostname + * @param[in] port Port + * @param[in] timeout_ms The timeout milliseconds + * + * @return + * - socket for will use by this transport + * - (-1) if there are any errors, should check errno + */ +int transport_connect(transport_handle_t t, const char *host, int port, int timeout_ms); + +/** + * @brief Transport read function + * + * @param t The transport handle + * @param buffer The buffer + * @param[in] len The length + * @param[in] timeout_ms The timeout milliseconds + * + * @return + * - Number of bytes was read + * - (-1) if there are any errors, should check errno + */ +int transport_read(transport_handle_t t, char *buffer, int len, int timeout_ms); + +/** + * @brief Poll the transport until readable or timeout + * + * @param[in] t The transport handle + * @param[in] timeout_ms The timeout milliseconds + * + * @return + * - 0 Timeout + * - (-1) If there are any errors, should check errno + * - other The transport can read + */ +int transport_poll_read(transport_handle_t t, int timeout_ms); + +/** + * @brief Transport write function + * + * @param t The transport handle + * @param buffer The buffer + * @param[in] len The length + * @param[in] timeout_ms The timeout milliseconds + * + * @return + * - Number of bytes was written + * - (-1) if there are any errors, should check errno + */ +int transport_write(transport_handle_t t, const char *buffer, int len, int timeout_ms); + +/** + * @brief Poll the transport until writeable or timeout + * + * @param[in] t The transport handle + * @param[in] timeout_ms The timeout milliseconds + * + * @return + * - 0 Timeout + * - (-1) If there are any errors, should check errno + * - other The transport can write + */ +int transport_poll_write(transport_handle_t t, int timeout_ms); + +/** + * @brief Transport close + * + * @param t The transport handle + * + * @return + * - 0 if ok + * - (-1) if there are any errors, should check errno + */ +int transport_close(transport_handle_t t); + +/** + * @brief Get user data context of this transport + * + * @param[in] t The transport handle + * + * @return The user data context + */ +void *transport_get_context_data(transport_handle_t t); + +/** + * @brief Set the user context data for this transport + * + * @param[in] t The transport handle + * @param data The user data context + * + * @return + * - ESP_OK + */ +esp_err_t transport_set_context_data(transport_handle_t t, void *data); + +/** + * @brief Set transport functions for the transport handle + * + * @param[in] t The transport handle + * @param[in] _connect The connect function pointer + * @param[in] _read The read function pointer + * @param[in] _write The write function pointer + * @param[in] _close The close function pointer + * @param[in] _poll_read The poll read function pointer + * @param[in] _poll_write The poll write function pointer + * @param[in] _destroy The destroy function pointer + * + * @return + * - ESP_OK + */ +esp_err_t transport_set_func(transport_handle_t t, + connect_func _connect, + io_read_func _read, + io_func _write, + trans_func _close, + poll_func _poll_read, + poll_func _poll_write, + trans_func _destroy); +#ifdef __cplusplus +} +#endif +#endif diff --git a/MicroPython_BUILD/components/espmqtt/lib/include/transport_ssl.h b/MicroPython_BUILD/components/espmqtt/lib/include/transport_ssl.h new file mode 100644 index 00000000..2469aa55 --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/include/transport_ssl.h @@ -0,0 +1,39 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ +#ifndef _TRANSPORT_SSL_H_ +#define _TRANSPORT_SSL_H_ + +#include "transport.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Create new SSL transport, the transport handle must be release transport_destroy callback + * + * @return the allocated transport_handle_t, or NULL if the handle can not be allocated + */ +transport_handle_t transport_ssl_init(); + +/** + * @brief Set SSL certificate data (as PEM format). + * Note that, this function stores the pointer to data, rather than making a copy. + * So we need to make sure to keep the data lifetime before cleanup the connection + * + * @param t ssl transport + * @param[in] data The pem data + * @param[in] len The length + */ +void transport_ssl_set_cert_data(transport_handle_t t, const char *data, int len); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/MicroPython_BUILD/components/espmqtt/lib/include/transport_tcp.h b/MicroPython_BUILD/components/espmqtt/lib/include/transport_tcp.h new file mode 100644 index 00000000..99160f30 --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/include/transport_tcp.h @@ -0,0 +1,27 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ +#ifndef _TRANSPORT_TCP_H_ +#define _TRANSPORT_TCP_H_ + +#include "transport.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create TCP transport, the transport handle must be release transport_destroy callback + * + * @return the allocated transport_handle_t, or NULL if the handle can not be allocated + */ +transport_handle_t transport_tcp_init(); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/MicroPython_BUILD/components/espmqtt/lib/include/transport_ws.h b/MicroPython_BUILD/components/espmqtt/lib/include/transport_ws.h new file mode 100644 index 00000000..7393088d --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/include/transport_ws.h @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ + +#ifndef _TRANSPORT_WS_H_ +#define _TRANSPORT_WS_H_ + +#include "transport.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WS_FIN 0x80 +#define WS_OPCODE_TEXT 0x01 +#define WS_OPCODE_BINARY 0x02 +#define WS_OPCODE_CLOSE 0x08 +#define WS_OPCODE_PING 0x09 +#define WS_OPCODE_PONG 0x0a +// Second byte +#define WS_MASK 0x80 +#define WS_SIZE16 126 +#define WS_SIZE64 127 +#define MAX_WEBSOCKET_HEADER_SIZE 10 +#define WS_RESPONSE_OK 101 + +/** + * @brief Create TCP transport + * + * @return + * - transport + * - NULL + */ +transport_handle_t transport_ws_init(transport_handle_t parent_handle); + +void transport_ws_set_path(transport_handle_t t, const char *path); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/MicroPython_BUILD/components/espmqtt/mqtt_msg.c b/MicroPython_BUILD/components/espmqtt/lib/mqtt_msg.c similarity index 97% rename from MicroPython_BUILD/components/espmqtt/mqtt_msg.c rename to MicroPython_BUILD/components/espmqtt/lib/mqtt_msg.c index 54f187db..eb978472 100644 --- a/MicroPython_BUILD/components/espmqtt/mqtt_msg.c +++ b/MicroPython_BUILD/components/espmqtt/lib/mqtt_msg.c @@ -31,6 +31,8 @@ #include #include #include "mqtt_msg.h" +#include "mqtt_config.h" +#include "platform.h" #define MQTT_MAX_FIXED_HEADER_SIZE 3 @@ -47,7 +49,7 @@ struct __attribute((__packed__)) mqtt_connect_variable_header { uint8_t lengthMsb; uint8_t lengthLsb; -#if defined(CONFIG_MQTT_PROTOCOL_311) +#if defined(MQTT_PROTOCOL_311) uint8_t magic[4]; #else uint8_t magic[6]; @@ -75,8 +77,9 @@ static uint16_t append_message_id(mqtt_connection_t* connection, uint16_t messag { // If message_id is zero then we should assign one, otherwise // we'll use the one supplied by the caller - while (message_id == 0) - message_id = ++connection->message_id; + while (message_id == 0) { + message_id = platform_random(65535); + } if (connection->message.length + 2 > connection->buffer_length) return 0; @@ -130,10 +133,10 @@ void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buff connection->buffer_length = buffer_length; } -int mqtt_get_total_length(uint8_t* buffer, uint16_t length) +uint32_t mqtt_get_total_length(uint8_t* buffer, uint16_t length) { int i; - int totlen = 0; + uint32_t totlen = 0; for (i = 1; i < length; ++i) { @@ -145,11 +148,11 @@ int mqtt_get_total_length(uint8_t* buffer, uint16_t length) } } totlen += i; - + return totlen; } -const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) +const char* mqtt_get_publish_topic(uint8_t* buffer, uint32_t* length) { int i; int totlen = 0; @@ -176,9 +179,9 @@ const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) *length = topiclen; return (const char*)(buffer + i); -} +} -const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) +const char* mqtt_get_publish_data(uint8_t* buffer, uint32_t* length) { int i; int totlen = 0; diff --git a/MicroPython_BUILD/components/espmqtt/lib/mqtt_outbox.c b/MicroPython_BUILD/components/espmqtt/lib/mqtt_outbox.c new file mode 100644 index 00000000..8175475f --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/mqtt_outbox.c @@ -0,0 +1,151 @@ +#include "mqtt_outbox.h" +#include +#include +#include "rom/queue.h" +#include "esp_log.h" + +static const char *TAG = "OUTBOX"; + +outbox_handle_t outbox_init() +{ + outbox_handle_t outbox = calloc(1, sizeof(struct outbox_list_t)); + ESP_MEM_CHECK(TAG, outbox, return NULL); + STAILQ_INIT(outbox); + return outbox; +} + +outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, uint8_t *data, int len, int msg_id, int msg_type, int tick) +{ + outbox_item_handle_t item = calloc(1, sizeof(outbox_item_t)); + ESP_MEM_CHECK(TAG, item, return NULL); + item->msg_id = msg_id; + item->msg_type = msg_type; + item->tick = tick; + item->len = len; + item->buffer = malloc(len); + ESP_MEM_CHECK(TAG, item->buffer, { + free(item); + return NULL; + }); + memcpy(item->buffer, data, len); + STAILQ_INSERT_TAIL(outbox, item, next); + ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%d", msg_id, msg_type, len, outbox_get_size(outbox)); + return item; +} + +outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id) +{ + outbox_item_handle_t item; + STAILQ_FOREACH(item, outbox, next) { + if (item->msg_id == msg_id) { + return item; + } + } + return NULL; +} + +outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox) +{ + outbox_item_handle_t item; + STAILQ_FOREACH(item, outbox, next) { + if (!item->pending) { + return item; + } + } + return NULL; +} +esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type) +{ + outbox_item_handle_t item, tmp; + STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { + if (item->msg_id == msg_id && item->msg_type == msg_type) { + STAILQ_REMOVE(outbox, item, outbox_item, next); + free(item->buffer); + free(item); + ESP_LOGD(TAG, "DELETED msgid=%d, msg_type=%d, remain size=%d", msg_id, msg_type, outbox_get_size(outbox)); + return ESP_OK; + } + + } + return ESP_FAIL; +} +esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id) +{ + outbox_item_handle_t item, tmp; + STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { + if (item->msg_id == msg_id) { + STAILQ_REMOVE(outbox, item, outbox_item, next); + free(item->buffer); + free(item); + } + + } + return ESP_OK; +} +esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id) +{ + outbox_item_handle_t item = outbox_get(outbox, msg_id); + if (item) { + item->pending = true; + return ESP_OK; + } + return ESP_FAIL; +} + +esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type) +{ + outbox_item_handle_t item, tmp; + STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { + if (item->msg_type == msg_type) { + STAILQ_REMOVE(outbox, item, outbox_item, next); + free(item->buffer); + free(item); + } + + } + return ESP_OK; +} + +esp_err_t outbox_delete_expired(outbox_handle_t outbox, int current_tick, int timeout) +{ + outbox_item_handle_t item, tmp; + STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { + if (current_tick - item->tick > timeout) { + STAILQ_REMOVE(outbox, item, outbox_item, next); + free(item->buffer); + free(item); + } + + } + return ESP_OK; +} + +int outbox_get_size(outbox_handle_t outbox) +{ + int siz = 0; + outbox_item_handle_t item; + STAILQ_FOREACH(item, outbox, next) { + siz += item->len; + } + return siz; +} + +esp_err_t outbox_cleanup(outbox_handle_t outbox, int max_size) +{ + while(outbox_get_size(outbox) > max_size) { + outbox_item_handle_t item = outbox_dequeue(outbox); + if (item == NULL) { + return ESP_FAIL; + } + STAILQ_REMOVE(outbox, item, outbox_item, next); + free(item->buffer); + free(item); + } + return ESP_OK; +} + +void outbox_destroy(outbox_handle_t outbox) +{ + outbox_cleanup(outbox, 0); + free(outbox); +} diff --git a/MicroPython_BUILD/components/espmqtt/lib/platform.c b/MicroPython_BUILD/components/espmqtt/lib/platform.c new file mode 100644 index 00000000..73247f0b --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/platform.c @@ -0,0 +1,36 @@ +#include "platform.h" + +#include "esp_system.h" +#include + +#define MAX_ID_STRING (32) + +char *platform_create_id_string() +{ + uint8_t mac[6]; + char *id_string = calloc(1, MAX_ID_STRING); + ESP_MEM_CHECK("MQTT_CLIENT", id_string, return NULL); + esp_read_mac(mac, ESP_MAC_WIFI_STA); + sprintf(id_string, "ESP32_%02x%02X%02X", mac[3], mac[4], mac[5]); + return id_string; +} + +int platform_random(int max) +{ + return esp_random()%max; +} + +long long platform_tick_get_ms() +{ + struct timeval te; + gettimeofday(&te, NULL); // get current time + long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds + // printf("milliseconds: %lld\n", milliseconds); + return milliseconds; +} + +void ms_to_timeval(int timeout_ms, struct timeval *tv) +{ + tv->tv_sec = timeout_ms / 1000; + tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000; +} diff --git a/MicroPython_BUILD/components/espmqtt/lib/transport.c b/MicroPython_BUILD/components/espmqtt/lib/transport.c new file mode 100644 index 00000000..04f3939e --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/transport.c @@ -0,0 +1,218 @@ +#include +#include + +#include "rom/queue.h" +#include "esp_log.h" + +#include "transport.h" +#include "platform.h" + + +static const char *TAG = "TRANSPORT"; + +/** + * Transport layer structure, which will provide functions, basic properties for transport types + */ +struct transport_item_t { + int port; + int socket; /*!< Socket to use in this transport */ + char *scheme; /*!< Tag name */ + void *context; /*!< Context data */ + void *data; /*!< Additional transport data */ + connect_func _connect; /*!< Connect function of this transport */ + io_read_func _read; /*!< Read */ + io_func _write; /*!< Write */ + trans_func _close; /*!< Close */ + poll_func _poll_read; /*!< Poll and read */ + poll_func _poll_write; /*!< Poll and write */ + trans_func _destroy; /*!< Destroy and free transport */ + STAILQ_ENTRY(transport_item_t) next; +}; + + +/** + * This list will hold all transport available + */ +STAILQ_HEAD(transport_list_t, transport_item_t); + + +transport_list_handle_t transport_list_init() +{ + transport_list_handle_t list = calloc(1, sizeof(struct transport_list_t)); + ESP_MEM_CHECK(TAG, list, return NULL); + STAILQ_INIT(list); + return list; +} + +esp_err_t transport_list_add(transport_list_handle_t list, transport_handle_t t, const char *scheme) +{ + if (list == NULL || t == NULL) { + return ESP_ERR_INVALID_ARG; + } + t->scheme = calloc(1, strlen(scheme) + 1); + ESP_MEM_CHECK(TAG, t->scheme, return ESP_ERR_NO_MEM); + strcpy(t->scheme, scheme); + STAILQ_INSERT_TAIL(list, t, next); + return ESP_OK; +} + +transport_handle_t transport_list_get_transport(transport_list_handle_t list, const char *scheme) +{ + if (!list) { + return NULL; + } + if (scheme == NULL) { + return STAILQ_FIRST(list); + } + transport_handle_t item; + STAILQ_FOREACH(item, list, next) { + if (strcasecmp(item->scheme, scheme) == 0) { + return item; + } + } + return NULL; +} + +esp_err_t transport_list_destroy(transport_list_handle_t list) +{ + transport_list_clean(list); + free(list); + return ESP_OK; +} + +esp_err_t transport_list_clean(transport_list_handle_t list) +{ + transport_handle_t item = STAILQ_FIRST(list); + transport_handle_t tmp; + while (item != NULL) { + tmp = STAILQ_NEXT(item, next); + if (item->_destroy) { + item->_destroy(item); + } + transport_destroy(item); + item = tmp; + } + STAILQ_INIT(list); + return ESP_OK; +} + +transport_handle_t transport_init() +{ + transport_handle_t t = calloc(1, sizeof(struct transport_item_t)); + ESP_MEM_CHECK(TAG, t, return NULL); + return t; +} + +esp_err_t transport_destroy(transport_handle_t t) +{ + if (t->scheme) { + free(t->scheme); + } + free(t); + return ESP_OK; +} + +int transport_connect(transport_handle_t t, const char *host, int port, int timeout_ms) +{ + int ret = -1; + if (t && t->_connect) { + return t->_connect(t, host, port, timeout_ms); + } + return ret; +} + +int transport_read(transport_handle_t t, char *buffer, int len, int timeout_ms) +{ + if (t && t->_read) { + return t->_read(t, buffer, len, timeout_ms); + } + return -1; +} + +int transport_write(transport_handle_t t, const char *buffer, int len, int timeout_ms) +{ + if (t && t->_write) { + return t->_write(t, buffer, len, timeout_ms); + } + return -1; +} + +int transport_poll_read(transport_handle_t t, int timeout_ms) +{ + if (t && t->_poll_read) { + return t->_poll_read(t, timeout_ms); + } + return -1; +} + +int transport_poll_write(transport_handle_t t, int timeout_ms) +{ + if (t && t->_poll_write) { + return t->_poll_write(t, timeout_ms); + } + return -1; +} + +int transport_close(transport_handle_t t) +{ + if (t && t->_close) { + return t->_close(t); + } + return 0; +} + +void *transport_get_context_data(transport_handle_t t) +{ + if (t) { + return t->data; + } + return NULL; +} + +esp_err_t transport_set_context_data(transport_handle_t t, void *data) +{ + if (t) { + t->data = data; + return ESP_OK; + } + return ESP_FAIL; +} + +esp_err_t transport_set_func(transport_handle_t t, + connect_func _connect, + io_read_func _read, + io_func _write, + trans_func _close, + poll_func _poll_read, + poll_func _poll_write, + trans_func _destroy) +{ + if (t == NULL) { + return ESP_FAIL; + } + t->_connect = _connect; + t->_read = _read; + t->_write = _write; + t->_close = _close; + t->_poll_read = _poll_read; + t->_poll_write = _poll_write; + t->_destroy = _destroy; + return ESP_OK; +} + +int transport_get_default_port(transport_handle_t t) +{ + if (t == NULL) { + return -1; + } + return t->port; +} + +esp_err_t transport_set_default_port(transport_handle_t t, int port) +{ + if (t == NULL) { + return ESP_FAIL; + } + t->port = port; + return ESP_OK; +} diff --git a/MicroPython_BUILD/components/espmqtt/lib/transport_ssl.c b/MicroPython_BUILD/components/espmqtt/lib/transport_ssl.c new file mode 100644 index 00000000..da0a1794 --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/transport_ssl.c @@ -0,0 +1,253 @@ +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" + +#include "mbedtls/platform.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/esp_debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" + + +#include "esp_log.h" +#include "esp_system.h" +#include "platform.h" + +#include "transport.h" +#include "transport_ssl.h" + +static const char *TAG = "TRANS_SSL"; +/** + * mbedtls specific transport data + */ +typedef struct { + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ctx; + mbedtls_x509_crt cacert; + mbedtls_ssl_config conf; + mbedtls_net_context client_fd; + void *cert_pem_data; + int cert_pem_len; + bool ssl_initialized; + bool verify_server; +} transport_ssl_t; + +static int ssl_close(transport_handle_t t); + +static int ssl_connect(transport_handle_t t, const char *host, int port, int timeout_ms) +{ + int ret = -1, flags; + struct timeval tv; + transport_ssl_t *ssl = transport_get_context_data(t); + + if (!ssl) { + return -1; + } + ssl->ssl_initialized = true; + mbedtls_ssl_init(&ssl->ctx); + mbedtls_ctr_drbg_init(&ssl->ctr_drbg); + mbedtls_ssl_config_init(&ssl->conf); + mbedtls_entropy_init(&ssl->entropy); + + if ((ret = mbedtls_ssl_config_defaults(&ssl->conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret); + goto exit; + } + + if ((ret = mbedtls_ctr_drbg_seed(&ssl->ctr_drbg, mbedtls_entropy_func, &ssl->entropy, NULL, 0)) != 0) { + ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret); + goto exit; + } + + if (ssl->cert_pem_data) { + mbedtls_x509_crt_init(&ssl->cacert); + ssl->verify_server = true; + if ((ret = mbedtls_x509_crt_parse(&ssl->cacert, ssl->cert_pem_data, ssl->cert_pem_len + 1)) < 0) { + ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\nDATA=%s,len=%d", -ret, (char*)ssl->cert_pem_data, ssl->cert_pem_len); + goto exit; + } + mbedtls_ssl_conf_ca_chain(&ssl->conf, &ssl->cacert, NULL); + mbedtls_ssl_conf_authmode(&ssl->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + + if ((ret = mbedtls_ssl_set_hostname(&ssl->ctx, host)) != 0) { + ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret); + goto exit; + } + } else { + mbedtls_ssl_conf_authmode(&ssl->conf, MBEDTLS_SSL_VERIFY_NONE); + } + + + mbedtls_ssl_conf_rng(&ssl->conf, mbedtls_ctr_drbg_random, &ssl->ctr_drbg); + +#ifdef CONFIG_MBEDTLS_DEBUG + mbedtls_esp_enable_debug_log(&ssl->conf, 4); +#endif + + if ((ret = mbedtls_ssl_setup(&ssl->ctx, &ssl->conf)) != 0) { + ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret); + goto exit; + } + + mbedtls_net_init(&ssl->client_fd); + + ms_to_timeval(timeout_ms, &tv); + + setsockopt(ssl->client_fd.fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + ESP_LOGD(TAG, "Connect to %s:%d", host, port); + char port_str[8] = {0}; + sprintf(port_str, "%d", port); + if ((ret = mbedtls_net_connect(&ssl->client_fd, host, port_str, MBEDTLS_NET_PROTO_TCP)) != 0) { + ESP_LOGE(TAG, "mbedtls_net_connect returned -%x", -ret); + goto exit; + } + + mbedtls_ssl_set_bio(&ssl->ctx, &ssl->client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + if((ret = mbedtls_ssl_set_hostname(&ssl->ctx, host)) != 0) { + ESP_LOGE(TAG, " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); + goto exit; + } + + ESP_LOGD(TAG, "Performing the SSL/TLS handshake..."); + + while ((ret = mbedtls_ssl_handshake(&ssl->ctx)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret); + goto exit; + } + } + + ESP_LOGD(TAG, "Verifying peer X.509 certificate..."); + + if ((flags = mbedtls_ssl_get_verify_result(&ssl->ctx)) != 0) { + /* In real life, we probably want to close connection if ret != 0 */ + ESP_LOGW(TAG, "Failed to verify peer certificate!"); + if (ssl->cert_pem_data) { + goto exit; + } + } else { + ESP_LOGD(TAG, "Certificate verified."); + } + + ESP_LOGD(TAG, "Cipher suite is %s", mbedtls_ssl_get_ciphersuite(&ssl->ctx)); + return ret; +exit: + ssl_close(t); + return ret; +} + +static int ssl_poll_read(transport_handle_t t, int timeout_ms) +{ + transport_ssl_t *ssl = transport_get_context_data(t); + fd_set readset; + FD_ZERO(&readset); + FD_SET(ssl->client_fd.fd, &readset); + struct timeval timeout; + ms_to_timeval(timeout_ms, &timeout); + + return select(ssl->client_fd.fd + 1, &readset, NULL, NULL, &timeout); +} + +static int ssl_poll_write(transport_handle_t t, int timeout_ms) +{ + transport_ssl_t *ssl = transport_get_context_data(t); + fd_set writeset; + FD_ZERO(&writeset); + FD_SET(ssl->client_fd.fd, &writeset); + struct timeval timeout; + ms_to_timeval(timeout_ms, &timeout); + return select(ssl->client_fd.fd + 1, NULL, &writeset, NULL, &timeout); +} + +static int ssl_write(transport_handle_t t, const char *buffer, int len, int timeout_ms) +{ + int poll, ret; + transport_ssl_t *ssl = transport_get_context_data(t); + + if ((poll = transport_poll_write(t, timeout_ms)) <= 0) { + ESP_LOGW(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d", strerror(errno), ssl->client_fd.fd, timeout_ms); + return poll; + } + ret = mbedtls_ssl_write(&ssl->ctx, (const unsigned char *) buffer, len); + if (ret <= 0) { + ESP_LOGE(TAG, "mbedtls_ssl_write error, errno=%s", strerror(errno)); + } + return ret; +} + +static int ssl_read(transport_handle_t t, char *buffer, int len, int timeout_ms) +{ + int ret; + transport_ssl_t *ssl = transport_get_context_data(t); + ret = mbedtls_ssl_read(&ssl->ctx, (unsigned char *)buffer, len); + if (ret == 0) { + return -1; + } + return ret; +} + +static int ssl_close(transport_handle_t t) +{ + int ret = -1; + transport_ssl_t *ssl = transport_get_context_data(t); + if (ssl->ssl_initialized) { + ESP_LOGD(TAG, "Cleanup mbedtls"); + mbedtls_ssl_close_notify(&ssl->ctx); + mbedtls_ssl_session_reset(&ssl->ctx); + mbedtls_net_free(&ssl->client_fd); + mbedtls_ssl_config_free(&ssl->conf); + if (ssl->verify_server) { + mbedtls_x509_crt_free(&ssl->cacert); + } + mbedtls_ctr_drbg_free(&ssl->ctr_drbg); + mbedtls_entropy_free(&ssl->entropy); + mbedtls_ssl_free(&ssl->ctx); + ssl->ssl_initialized = false; + ssl->verify_server = false; + } + return ret; +} + +static int ssl_destroy(transport_handle_t t) +{ + transport_ssl_t *ssl = transport_get_context_data(t); + transport_close(t); + free(ssl); + return 0; +} + +void transport_ssl_set_cert_data(transport_handle_t t, const char *data, int len) +{ + transport_ssl_t *ssl = transport_get_context_data(t); + if (t && ssl) { + ssl->cert_pem_data = (void *)data; + ssl->cert_pem_len = len; + } +} + +transport_handle_t transport_ssl_init() +{ + transport_handle_t t = transport_init(); + transport_ssl_t *ssl = calloc(1, sizeof(transport_ssl_t)); + ESP_MEM_CHECK(TAG, ssl, return NULL); + mbedtls_net_init(&ssl->client_fd); + transport_set_context_data(t, ssl); + transport_set_func(t, ssl_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy); + return t; +} + diff --git a/MicroPython_BUILD/components/espmqtt/lib/transport_tcp.c b/MicroPython_BUILD/components/espmqtt/lib/transport_tcp.c new file mode 100644 index 00000000..e98ce939 --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/transport_tcp.c @@ -0,0 +1,152 @@ +#include +#include + +#include "lwip/sockets.h" +#include "lwip/dns.h" +#include "lwip/netdb.h" + +#include "esp_log.h" +#include "esp_system.h" +#include "esp_err.h" + +#include "platform.h" +#include "transport.h" + +static const char *TAG = "TRANS_TCP"; + +typedef struct { + int sock; +} transport_tcp_t; + +static int resolve_dns(const char *host, struct sockaddr_in *ip) { + + struct hostent *he; + struct in_addr **addr_list; + he = gethostbyname(host); + if (he == NULL) { + return ESP_FAIL; + } + addr_list = (struct in_addr **)he->h_addr_list; + if (addr_list[0] == NULL) { + return ESP_FAIL; + } + ip->sin_family = AF_INET; + memcpy(&ip->sin_addr, addr_list[0], sizeof(ip->sin_addr)); + return ESP_OK; +} + +static int tcp_connect(transport_handle_t t, const char *host, int port, int timeout_ms) +{ + struct sockaddr_in remote_ip; + struct timeval tv; + transport_tcp_t *tcp = transport_get_context_data(t); + + bzero(&remote_ip, sizeof(struct sockaddr_in)); + + //if stream_host is not ip address, resolve it AF_INET,servername,&serveraddr.sin_addr + if (inet_pton(AF_INET, host, &remote_ip.sin_addr) != 1) { + if (resolve_dns(host, &remote_ip) < 0) { + return -1; + } + } + + tcp->sock = socket(PF_INET, SOCK_STREAM, 0); + + if (tcp->sock < 0) { + ESP_LOGE(TAG, "Error create socket"); + return -1; + } + + remote_ip.sin_family = AF_INET; + remote_ip.sin_port = htons(port); + + ms_to_timeval(timeout_ms, &tv); + + setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + ESP_LOGD(TAG, "[sock=%d],connecting to server IP:%s,Port:%d...", + tcp->sock, ipaddr_ntoa((const ip_addr_t*)&remote_ip.sin_addr.s_addr), port); + if (connect(tcp->sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) != 0) { + close(tcp->sock); + tcp->sock = -1; + return -1; + } + return tcp->sock; +} + +static int tcp_write(transport_handle_t t, const char *buffer, int len, int timeout_ms) +{ + int poll; + transport_tcp_t *tcp = transport_get_context_data(t); + if ((poll = transport_poll_write(t, timeout_ms)) <= 0) { + return poll; + } + return write(tcp->sock, buffer, len); +} + +static int tcp_read(transport_handle_t t, char *buffer, int len, int timeout_ms) +{ + transport_tcp_t *tcp = transport_get_context_data(t); + int poll = -1; + if ((poll = transport_poll_read(t, timeout_ms)) <= 0) { + return poll; + } + int read_len = read(tcp->sock, buffer, len); + if (read_len == 0) { + return -1; + } + return read_len; +} + +static int tcp_poll_read(transport_handle_t t, int timeout_ms) +{ + transport_tcp_t *tcp = transport_get_context_data(t); + fd_set readset; + FD_ZERO(&readset); + FD_SET(tcp->sock, &readset); + struct timeval timeout; + ms_to_timeval(timeout_ms, &timeout); + return select(tcp->sock + 1, &readset, NULL, NULL, &timeout); +} + +static int tcp_poll_write(transport_handle_t t, int timeout_ms) +{ + transport_tcp_t *tcp = transport_get_context_data(t); + fd_set writeset; + FD_ZERO(&writeset); + FD_SET(tcp->sock, &writeset); + struct timeval timeout; + ms_to_timeval(timeout_ms, &timeout); + return select(tcp->sock + 1, NULL, &writeset, NULL, &timeout); +} + +static int tcp_close(transport_handle_t t) +{ + transport_tcp_t *tcp = transport_get_context_data(t); + int ret = -1; + if (tcp->sock >= 0) { + ret = close(tcp->sock); + tcp->sock = -1; + } + return ret; +} + +static esp_err_t tcp_destroy(transport_handle_t t) +{ + transport_tcp_t *tcp = transport_get_context_data(t); + transport_close(t); + free(tcp); + return 0; +} + +transport_handle_t transport_tcp_init() +{ + transport_handle_t t = transport_init(); + transport_tcp_t *tcp = calloc(1, sizeof(transport_tcp_t)); + ESP_MEM_CHECK(TAG, tcp, return NULL); + tcp->sock = -1; + transport_set_func(t, tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy); + transport_set_context_data(t, tcp); + + return t; +} diff --git a/MicroPython_BUILD/components/espmqtt/lib/transport_ws.c b/MicroPython_BUILD/components/espmqtt/lib/transport_ws.c new file mode 100644 index 00000000..df0ae1b7 --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/lib/transport_ws.c @@ -0,0 +1,251 @@ +#include +#include +#include + +#include "platform.h" +#include "transport.h" +#include "transport_tcp.h" +#include "transport_ws.h" +#include "mbedtls/base64.h" +#include "mbedtls/sha1.h" + +static const char *TAG = "TRANSPORT_WS"; + +#define DEFAULT_WS_BUFFER (1024) + +typedef struct { + char *path; + char *buffer; + transport_handle_t parent; +} transport_ws_t; + +static char *trimwhitespace(const char *str) +{ + char *end; + + // Trim leading space + while (isspace((unsigned char)*str)) str++; + + if (*str == 0) { + return (char *)str; + } + + // Trim trailing space + end = (char *)(str + strlen(str) - 1); + while (end > str && isspace((unsigned char)*end)) end--; + + // Write new null terminator + *(end + 1) = 0; + + return (char *)str; +} + + +static char *get_http_header(const char *buffer, const char *key) +{ + char *found = strstr(buffer, key); + if (found) { + found += strlen(key); + char *found_end = strstr(found, "\r\n"); + if (found_end) { + found_end[0] = 0;//terminal string + + return trimwhitespace(found); + } + } + return NULL; +} + +static int ws_connect(transport_handle_t t, const char *host, int port, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + if (transport_connect(ws->parent, host, port, timeout_ms) < 0) { + ESP_LOGE(TAG, "Error connect to ther server"); + } + unsigned char random_key[16] = { 0 }, client_key[32] = {0}; + int i; + for (i = 0; i < sizeof(random_key); i++) { + random_key[i] = rand() & 0xFF; + } + size_t outlen = 0; + mbedtls_base64_encode(client_key, 32, &outlen, random_key, 16); + int len = snprintf(ws->buffer, DEFAULT_WS_BUFFER, + "GET %s HTTP/1.1\r\n" + "Connection: Upgrade\r\n" + "Host: %s:%d\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Protocol: mqtt\r\n" + "Sec-WebSocket-Key: %s\r\n" + "User-Agent: ESP32 MQTT Client\r\n\r\n", + ws->path, + host, port, + client_key); + ESP_LOGD(TAG, "Write upgrate request\r\n%s", ws->buffer); + if (transport_write(ws->parent, ws->buffer, len, timeout_ms) <= 0) { + ESP_LOGE(TAG, "Error write Upgrade header %s", ws->buffer); + return -1; + } + if ((len = transport_read(ws->parent, ws->buffer, DEFAULT_WS_BUFFER, timeout_ms)) <= 0) { + ESP_LOGE(TAG, "Error read response for Upgrade header %s", ws->buffer); + return -1; + } + char *server_key = get_http_header(ws->buffer, "Sec-WebSocket-Accept:"); + if (server_key == NULL) { + ESP_LOGE(TAG, "Sec-WebSocket-Accept not found"); + return -1; + } + + unsigned char client_key_b64[64], valid_client_key[20], accept_key[32] = {0}; + int key_len = sprintf((char*)client_key_b64, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", (char*)client_key); + mbedtls_sha1(client_key_b64, (size_t)key_len, valid_client_key); + mbedtls_base64_encode(accept_key, 32, &outlen, valid_client_key, 20); + accept_key[outlen] = 0; + ESP_LOGD(TAG, "server key=%s, send_key=%s, accept_key=%s", (char *)server_key, (char*)client_key, accept_key); + if (strcmp((char*)accept_key, (char*)server_key) != 0) { + ESP_LOGE(TAG, "Invalid websocket key"); + return -1; + } + return 0; +} + +static int ws_write(transport_handle_t t, const char *buff, int len, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + char ws_header[MAX_WEBSOCKET_HEADER_SIZE]; + char *mask; + int header_len = 0, i; + char *buffer = (char *)buff; + int poll_write; + if ((poll_write = transport_poll_write(ws->parent, timeout_ms)) <= 0) { + return poll_write; + } + + ws_header[header_len++] = WS_OPCODE_BINARY | WS_FIN; + + // NOTE: no support for > 16-bit sized messages + if (len > 125) { + ws_header[header_len++] = WS_SIZE16 | WS_MASK; + ws_header[header_len++] = (uint8_t)(len >> 8); + ws_header[header_len++] = (uint8_t)(len & 0xFF); + } else { + ws_header[header_len++] = (uint8_t)(len | WS_MASK); + } + mask = &ws_header[header_len]; + ws_header[header_len++] = rand() & 0xFF; + ws_header[header_len++] = rand() & 0xFF; + ws_header[header_len++] = rand() & 0xFF; + ws_header[header_len++] = rand() & 0xFF; + + for (i = 0; i < len; ++i) { + buffer[i] = (buffer[i] ^ mask[i % 4]); + } + if (transport_write(ws->parent, ws_header, header_len, timeout_ms) != header_len) { + ESP_LOGE(TAG, "Error write header"); + return -1; + } + return transport_write(ws->parent, buffer, len, timeout_ms); +} + +static int ws_read(transport_handle_t t, char *buffer, int len, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + int payload_len; + char *data_ptr = buffer, opcode, mask, *mask_key = NULL; + int rlen; + int poll_read; + if ((poll_read = transport_poll_read(ws->parent, timeout_ms)) <= 0) { + return poll_read; + } + if ((rlen = transport_read(ws->parent, buffer, len, timeout_ms)) <= 0) { + ESP_LOGE(TAG, "Error read data"); + return rlen; + } + + opcode = (*data_ptr & 0x0F); + data_ptr ++; + mask = ((*data_ptr >> 7) & 0x01); + payload_len = (*data_ptr & 0x7F); + data_ptr++; + ESP_LOGD(TAG, "Opcode: %d, mask: %d, len: %d\r\n", opcode, mask, payload_len); + if (payload_len == 126) { + // headerLen += 2; + payload_len = data_ptr[0] << 8 | data_ptr[1]; + data_ptr += 2; + } else if (payload_len == 127) { + // headerLen += 8; + + if (data_ptr[0] != 0 || data_ptr[1] != 0 || data_ptr[2] != 0 || data_ptr[3] != 0) { + // really too big! + payload_len = 0xFFFFFFFF; + } else { + payload_len = data_ptr[4] << 24 | data_ptr[5] << 16 | data_ptr[6] << 8 | data_ptr[7]; + } + data_ptr += 8; + } + + if (mask) { + mask_key = data_ptr; + data_ptr += 4; + for (int i = 0; i < payload_len; i++) { + buffer[i] = (data_ptr[i] ^ mask_key[i % 4]); + } + } else { + memmove(buffer, data_ptr, payload_len); + } + return payload_len; +} + +static int ws_poll_read(transport_handle_t t, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + return transport_poll_read(ws->parent, timeout_ms); +} + +static int ws_poll_write(transport_handle_t t, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + return transport_poll_write(ws->parent, timeout_ms);; +} + +static int ws_close(transport_handle_t t) +{ + transport_ws_t *ws = transport_get_context_data(t); + return transport_close(ws->parent); +} + +static esp_err_t ws_destroy(transport_handle_t t) +{ + transport_ws_t *ws = transport_get_context_data(t); + free(ws->buffer); + free(ws->path); + free(ws); + return 0; +} +void transport_ws_set_path(transport_handle_t t, const char *path) +{ + transport_ws_t *ws = transport_get_context_data(t); + ws->path = realloc(ws->path, strlen(path) + 1); + strcpy(ws->path, path); +} +transport_handle_t transport_ws_init(transport_handle_t parent_handle) +{ + transport_handle_t t = transport_init(); + transport_ws_t *ws = calloc(1, sizeof(transport_ws_t)); + ESP_MEM_CHECK(TAG, ws, return NULL); + ws->parent = parent_handle; + + ws->path = strdup("/"); + ESP_MEM_CHECK(TAG, ws->path, return NULL); + ws->buffer = malloc(DEFAULT_WS_BUFFER); + ESP_MEM_CHECK(TAG, ws->buffer, { + free(ws->path); + free(ws); + return NULL; + }); + + transport_set_func(t, ws_connect, ws_read, ws_write, ws_close, ws_poll_read, ws_poll_write, ws_destroy); + transport_set_context_data(t, ws); + return t; +} + diff --git a/MicroPython_BUILD/components/espmqtt/mqtt.c b/MicroPython_BUILD/components/espmqtt/mqtt.c deleted file mode 100644 index fa8cb13a..00000000 --- a/MicroPython_BUILD/components/espmqtt/mqtt.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Boris Lovosevic (https://github.com/loboris) - * - * 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. - */ - -/* - * Mqtt Module using MQTT task. - * Based on ESP32 MQTT Library by Tuan PM, https://github.com/tuanpmt/espmqtt - * Adapted for MicroPython by Boris Lovosevic, https://github.com/loboris - * - */ - -#include "sdkconfig.h" - -#ifdef CONFIG_MICROPY_USE_MQTT - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "esp_log.h" - -#include "lwip/sockets.h" -#include "lwip/dns.h" -#include "lwip/netdb.h" -#include "ringbuf.h" -#include "mqtt.h" - -#include "esp_wifi_types.h" -#include "tcpip_adapter.h" -#include "libs/libGSM.h" - -const char *MQTT_TAG = "[Mqtt client]"; -static char *subs_last_topic = NULL; -static char *unsubs_last_topic = NULL; - -//---------------------------------------------------------------- -static int resolve_dns(const char *host, struct sockaddr_in *ip) { - struct hostent *he; - struct in_addr **addr_list; - he = gethostbyname(host); - if (he == NULL) return 0; - addr_list = (struct in_addr **)he->h_addr_list; - if (addr_list[0] == NULL) return 0; - ip->sin_family = AF_INET; - memcpy(&ip->sin_addr, addr_list[0], sizeof(ip->sin_addr)); - return 1; -} - -//----------------------------------------- -static void mqtt_queue(mqtt_client *client) -{ - int msg_len; - while (rb_available(&client->send_rb) < client->mqtt_state.outbound_message->length) { - xQueueReceive(client->xSendingQueue, &msg_len, 1000 / portTICK_RATE_MS); - rb_read(&client->send_rb, client->mqtt_state.out_buffer, msg_len); - } - rb_write(&client->send_rb, - client->mqtt_state.outbound_message->data, - client->mqtt_state.outbound_message->length); - xQueueSend(client->xSendingQueue, &client->mqtt_state.outbound_message->length, 0); -} - -//--------------------------------------------- -static bool client_connect(mqtt_client *client) -{ - struct sockaddr_in remote_ip; - - client->status = MQTT_STATUS_DISCONNECTED; - while (1) { - bzero(&remote_ip, sizeof(struct sockaddr_in)); - remote_ip.sin_family = AF_INET; - remote_ip.sin_port = htons(client->settings->port); - - //if host is not ip address, resolve it - if (inet_aton( client->settings->host, &(remote_ip.sin_addr)) == 0) { - ESP_LOGI(MQTT_TAG, "Resolve dns for domain: %s", client->settings->host); - - if (!resolve_dns(client->settings->host, &remote_ip)) { - ESP_LOGE(MQTT_TAG, "Resolve dns for domain: %s failed", client->settings->host); - return false; - } - } - - if (client->settings->use_ssl) { - client->ctx = NULL; - client->ssl = NULL; - - client->ctx = SSL_CTX_new(TLSv1_2_client_method()); - if (!client->ctx) { - ESP_LOGE(MQTT_TAG, "Failed to create SSL CTX"); - goto failed1; - } - } - - client->socket = socket(PF_INET, SOCK_STREAM, 0); - if (client->socket == -1) { - ESP_LOGE(MQTT_TAG, "Failed to create socket"); - goto failed2; - } - - ESP_LOGI(MQTT_TAG, "Connecting to server %s:%d,%d", inet_ntoa((remote_ip.sin_addr)), client->settings->port, remote_ip.sin_port); - - if (connect(client->socket, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) != 00) { - ESP_LOGE(MQTT_TAG, "Connect failed"); - goto failed3; - } - - if (client->settings->use_ssl) { - ESP_LOGI(MQTT_TAG, "Creating SSL object..."); - client->ssl = SSL_new(client->ctx); - if (!client->ssl) { - ESP_LOGE(MQTT_TAG, "Unable to create new SSL object"); - goto failed3; - } - - if (!SSL_set_fd(client->ssl, client->socket)) { - ESP_LOGE(MQTT_TAG, "SSL set_fd failed"); - goto failed3; - } - - ESP_LOGI(MQTT_TAG, "Start SSL connect.."); - if (!SSL_connect(client->ssl)) { - ESP_LOGE(MQTT_TAG, "SSL Connect FAILED"); - goto failed4; - } - } - ESP_LOGI(MQTT_TAG, "Connected!"); - - client->status = MQTT_STATUS_CONNECTED; - return true; - - //failed5: - // SSL_shutdown(client->ssl); - -failed4: // SSL_CTX_new failed - if (client->settings->use_ssl) { - SSL_free(client->ssl); - client->ssl = NULL; - } - -failed3: // Connect failed - close(client->socket); - client->socket = -1; - -failed2: // Failed to create socket - if (client->settings->use_ssl) { - SSL_CTX_free(client->ctx); - } - -failed1: - if (client->settings->use_ssl) { - client->ctx = NULL; - } - return false; - } -} - - -// Close client socket -// including SSL objects if enabled -//----------------------------------- -void closeclient(mqtt_client *client) -{ - if (client->socket != -1) { - close(client->socket); - client->socket = -1; - ESP_LOGI(MQTT_TAG, "Closing client socket"); - } - - if (client->settings->use_ssl) { - if (client->ssl != NULL) { - SSL_shutdown(client->ssl); - SSL_free(client->ssl); - client->ssl = NULL; - } - - if (client->ctx != NULL) { - SSL_CTX_free(client->ctx); - client->ctx = NULL; - } - } - client->status = MQTT_STATUS_DISCONNECTED; -} - -//----------------------------------------------------------------------- -int mqtt_read(mqtt_client *client, void *buffer, int len, int timeout_ms) -{ - int result; - struct timeval tv; - if (timeout_ms > 0) { - tv.tv_sec = 0; - tv.tv_usec = timeout_ms * 1000; - while (tv.tv_usec > 1000 * 1000) { - tv.tv_usec -= 1000 * 1000; - tv.tv_sec++; - } - setsockopt(client->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - } - - if (client->settings->use_ssl) result = SSL_read(client->ssl, buffer, len); - else result = read(client->socket, buffer, len); - - if (timeout_ms > 0) { - tv.tv_sec = 0; - tv.tv_usec = 0; - setsockopt(client->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - } - - return result; -} - -//------------------------------------------------------------------------------ -int mqtt_write(mqtt_client *client, const void *buffer, int len, int timeout_ms) -{ - int result; - struct timeval tv; - if (timeout_ms > 0) { - tv.tv_sec = 0; - tv.tv_usec = timeout_ms * 1000; - while (tv.tv_usec > 1000 * 1000) { - tv.tv_usec -= 1000 * 1000; - tv.tv_sec++; - } - setsockopt(client->socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - } - - if (client->settings->use_ssl) result = SSL_write(client->ssl, buffer, len); - else result = write(client->socket, buffer, len); - - if (timeout_ms > 0) { - tv.tv_sec = 0; - tv.tv_usec = 0; - setsockopt(client->socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - } - - return result; -} - -/* - * mqtt_connect - * input - client - * return 1: success, 0: fail - */ -//------------------------------------------- -static bool mqtt_connect(mqtt_client *client) -{ - int write_len, read_len, connect_rsp_code; - - mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); - client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); - client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); - client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - - ESP_LOGI(MQTT_TAG, "Sending MQTT CONNECT message, type: %d, id: %04X", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); - - write_len = client->settings->write_cb(client, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length, 0); - if(write_len < 0) { - ESP_LOGE(MQTT_TAG, "Writing failed: %d", errno); - return false; - } - - ESP_LOGI(MQTT_TAG, "Reading MQTT CONNECT response message"); - - read_len = client->settings->read_cb(client, client->mqtt_state.in_buffer, CONFIG_MQTT_BUFFER_SIZE_BYTE, 10 * 1000); - - if (read_len < 0) { - ESP_LOGE(MQTT_TAG, "Error network response"); - return false; - } - if (mqtt_get_type(client->mqtt_state.in_buffer) != MQTT_MSG_TYPE_CONNACK) { - ESP_LOGE(MQTT_TAG, "Invalid MSG_TYPE response: %d, read_len: %d", mqtt_get_type(client->mqtt_state.in_buffer), read_len); - return false; - } - connect_rsp_code = mqtt_get_connect_return_code(client->mqtt_state.in_buffer); - switch (connect_rsp_code) { - case CONNECTION_ACCEPTED: - ESP_LOGI(MQTT_TAG, "Connected"); - return true; - case CONNECTION_REFUSE_PROTOCOL: - ESP_LOGW(MQTT_TAG, "Connection refused, bad protocol"); - return false; - case CONNECTION_REFUSE_SERVER_UNAVAILABLE: - ESP_LOGW(MQTT_TAG, "Connection refused, server unavailable"); - return false; - case CONNECTION_REFUSE_BAD_USERNAME: - ESP_LOGW(MQTT_TAG, "Connection refused, bad username or password"); - return false; - case CONNECTION_REFUSE_NOT_AUTHORIZED: - ESP_LOGW(MQTT_TAG, "Connection refused, not authorized"); - return false; - default: - ESP_LOGW(MQTT_TAG, "Connection refused, Unknown reason"); - return false; - } - return false; -} - -//======================================== -void mqtt_sending_task(void *pvParameters) -{ - mqtt_client *client = (mqtt_client *)pvParameters; - uint32_t msg_len; - int send_len, sent_len = 0; - bool connected = true; - ESP_LOGI(MQTT_TAG, "Sending task started"); - - while (connected) { - if (client->terminate_mqtt) { - ESP_LOGI(MQTT_TAG, "Terminate, sending task exit."); - client->status = MQTT_STATUS_STOPPING; - break; - } - if (xQueueReceive(client->xSendingQueue, &msg_len, 1000 / portTICK_RATE_MS)) { - //queue available - while (msg_len > 0) { - send_len = msg_len; - if (send_len > CONFIG_MQTT_BUFFER_SIZE_BYTE) send_len = CONFIG_MQTT_BUFFER_SIZE_BYTE; - ESP_LOGD(MQTT_TAG, "Sending %d bytes", send_len); - - rb_read(&client->send_rb, client->mqtt_state.out_buffer, send_len); - client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.out_buffer); - client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.out_buffer, send_len); - send_len = client->settings->write_cb(client, client->mqtt_state.out_buffer, send_len, 5 * 1000); - if (send_len <= 0) { - ESP_LOGE(MQTT_TAG, "Write error: %d", errno); - connected = false; - break; - } - - //TODO: Check sending type, to callback publish message - msg_len -= send_len; - sent_len += send_len; - } - //invalidate keepalive timer - client->keepalive_tick = client->settings->keepalive / 2; - if (client->mqtt_state.sending_msg_type == MQTT_SENDING_TYPE_PUBLISH) { - client->mqtt_state.sending_msg_type = MQTT_SENDING_TYPE_NONE; - ESP_LOGI(MQTT_TAG, "Published %d bytes", sent_len); - if (client->settings->publish_cb) { - client->settings->publish_cb(client, (void *)"Sent"); - } - } - } - else { - if (client->keepalive_tick > 0) client->keepalive_tick --; - else { - client->keepalive_tick = client->settings->keepalive / 2; - client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); - client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); - client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, - client->mqtt_state.outbound_message->length); - ESP_LOGD(MQTT_TAG, "Sending ping request"); - send_len = client->settings->write_cb(client, - client->mqtt_state.outbound_message->data, - client->mqtt_state.outbound_message->length, 0); - if(send_len <= 0) { - ESP_LOGE(MQTT_TAG, "Write error: %d", errno); - connected = false; - break; - } - } - } - } - closeclient(client); - client->settings->xMqttSendingTask = NULL; - vTaskDelete(NULL); -} - -//--------------------------------------------------------------------- -void deliver_publish(mqtt_client *client, uint8_t *message, int length) -{ - mqtt_event_data_t event_data; - int len_read, total_mqtt_len = 0, mqtt_len = 0, mqtt_offset = 0; - uint8_t do_cb = (client->settings->data_cb != NULL); - do - { - if(total_mqtt_len == 0){ - event_data.topic_length = length; - event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); - event_data.data_length = length; - event_data.data = mqtt_get_publish_data(message, &event_data.data_length); - total_mqtt_len = client->mqtt_state.message_length - client->mqtt_state.message_length_read + event_data.data_length; - if (total_mqtt_len > CONFIG_MQTT_MAX_PAYLOAD_SIZE) event_data.data_total_length = CONFIG_MQTT_MAX_PAYLOAD_SIZE; - else event_data.data_total_length = total_mqtt_len; - mqtt_len = event_data.data_length; - } else { - mqtt_len = len_read; - event_data.data = (const char*)client->mqtt_state.in_buffer; - } - - event_data.data_offset = mqtt_offset; - event_data.data_length = mqtt_len; - - ESP_LOGD(MQTT_TAG, "Data received: %d/%d bytes ", mqtt_len, total_mqtt_len); - if (do_cb) { - if ((mqtt_offset+mqtt_len) > CONFIG_MQTT_MAX_PAYLOAD_SIZE) { - event_data.data_length = CONFIG_MQTT_MAX_PAYLOAD_SIZE - mqtt_offset; - do_cb = 0; - } - client->settings->data_cb(client, &event_data); - } - mqtt_offset += mqtt_len; - if (client->mqtt_state.message_length_read >= client->mqtt_state.message_length) - break; - - len_read = client->settings->read_cb(client, client->mqtt_state.in_buffer, CONFIG_MQTT_BUFFER_SIZE_BYTE, 0); - if(len_read < 0) { - ESP_LOGE(MQTT_TAG, "Read error: %d", errno); - break; - } - client->mqtt_state.message_length_read += len_read; - } while (1); - -} - -//--------------------------------------------------- -void mqtt_start_receive_schedule(mqtt_client *client) -{ - int read_len; - uint8_t msg_type; - uint8_t msg_qos; - uint16_t msg_id; - - while (1) { - if (client->terminate_mqtt) { - ESP_LOGI(MQTT_TAG, "Terminate, receive schedule exit."); - client->status = MQTT_STATUS_STOPPING; - break; - } - if (client->settings->xMqttSendingTask == NULL) break; - - read_len = client->settings->read_cb(client, client->mqtt_state.in_buffer, CONFIG_MQTT_BUFFER_SIZE_BYTE, 0); - - ESP_LOGD(MQTT_TAG, "Read length %d", read_len); - if (read_len <= 0) { - if (client->terminate_mqtt) { - ESP_LOGI(MQTT_TAG, "Terminate, receive schedule exit."); - client->status = MQTT_STATUS_STOPPING; - } - else { - ESP_LOGE(MQTT_TAG, "Socket error (%d), exit Receive schedule", errno); - } - break; - } - - msg_type = mqtt_get_type(client->mqtt_state.in_buffer); - msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); - msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); - // ESP_LOGI(MQTT_TAG, "msg_type %d, msg_id: %d, pending_id: %d", msg_type, msg_id, client->mqtt_state.pending_msg_type); - switch (msg_type) - { - case MQTT_MSG_TYPE_SUBACK: - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) { - ESP_LOGI(MQTT_TAG, "Subscribe successful"); - client->subs_flag = 1; - if (client->settings->subscribe_cb) { - client->settings->subscribe_cb(client, (void *)subs_last_topic); - } - } - break; - case MQTT_MSG_TYPE_UNSUBACK: - //if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) { - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE) { - ESP_LOGI(MQTT_TAG, "UnSubscribe successful"); - client->unsubs_flag = 1; - client->subs_flag = 1; - if (client->settings->unsubscribe_cb) { - client->settings->unsubscribe_cb(client, (void *)unsubs_last_topic); - } - } - break; - case MQTT_MSG_TYPE_PUBLISH: - if (msg_qos == 1) - client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); - else if (msg_qos == 2) - client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); - - if (msg_qos == 1 || msg_qos == 2) { - ESP_LOGI(MQTT_TAG, "Queue response QoS: %d", msg_qos); - mqtt_queue(client); - // if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - // ESP_LOGI(MQTT_TAG, "MQTT: Queue full"); - // } - } - client->mqtt_state.message_length_read = read_len; - client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); - ESP_LOGI(MQTT_TAG, "deliver_publish"); - - deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); - break; - case MQTT_MSG_TYPE_PUBACK: - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { - ESP_LOGI(MQTT_TAG, "received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish"); - if (client->settings->publish_cb) { - client->settings->publish_cb(client, (void *)"QoS1 acknowledged"); - } - } - break; - case MQTT_MSG_TYPE_PUBREC: - client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); - mqtt_queue(client); - break; - case MQTT_MSG_TYPE_PUBREL: - client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); - mqtt_queue(client); - break; - case MQTT_MSG_TYPE_PUBCOMP: - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBREL && client->mqtt_state.pending_msg_id == msg_id) { - ESP_LOGI(MQTT_TAG, "Receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish"); - if (client->settings->publish_cb) { - client->settings->publish_cb(client, (void *)"QoS2 acknowledged"); - } - } - break; - case MQTT_MSG_TYPE_PINGREQ: - client->mqtt_state.sending_msg_type = MQTT_SENDING_TYPE_PING; - client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); - mqtt_queue(client); - break; - case MQTT_MSG_TYPE_PINGRESP: - ESP_LOGD(MQTT_TAG, "MQTT_MSG_TYPE_PINGRESP"); - // Ignore - break; - } - } -} - -//--------------------------------- -void mqtt_free(mqtt_client *client) -{ - if (client == NULL) return; - - vQueueDelete(client->xSendingQueue); - - free(client->mqtt_state.in_buffer); - free(client->mqtt_state.out_buffer); - free(client->send_rb.p_o); - - ESP_LOGI(MQTT_TAG, "Client freed"); -} - -//================================ -void mqtt_task(void *pvParameters) -{ - ESP_LOGI(MQTT_TAG, "Starting Mqtt task"); - - mqtt_client *client = (mqtt_client *)pvParameters; - - while (1) { - if (client->terminate_mqtt) { - client->status = MQTT_STATUS_STOPPING; - break; - } - - if (client->settings->connect_cb(client) == false) { - ESP_LOGE(MQTT_TAG, "Connection to server %s:%d failed!", client->settings->host, client->settings->port); - client->status = MQTT_STATUS_STOPPING; - break; - } - - ESP_LOGI(MQTT_TAG, "Connected to server %s:%d", client->settings->host, client->settings->port); - if (!mqtt_connect(client)) { - client->settings->disconnect_cb(client); - - if (client->settings->disconnected_cb) { - client->settings->disconnected_cb(client, NULL); - } - - if (!client->settings->auto_reconnect) { - client->status = MQTT_STATUS_STOPPING; - break; - } - else continue; - } - - ESP_LOGI(MQTT_TAG, "Connected to MQTT broker, creating sending thread before calling connected callback"); - xTaskCreate(&mqtt_sending_task, "mqtt_sending_task", client->settings->xMqttSendingTask_stacksize, client, CONFIG_MQTT_PRIORITY + 1, &(client->settings->xMqttSendingTask)); - if (client->settings->xMqttSendingTask == NULL) break; - if (client->settings->connected_cb) { - client->settings->connected_cb(client, NULL); - } - - ESP_LOGI(MQTT_TAG, "mqtt_start_receive_schedule"); - mqtt_start_receive_schedule(client); - - client->settings->disconnect_cb(client); - if (client->settings->disconnected_cb) { - client->settings->disconnected_cb(client, NULL); - } - - if (client->settings->xMqttSendingTask != NULL) { - vTaskDelete(client->settings->xMqttSendingTask); - } - if (!client->settings->auto_reconnect) { - client->status = MQTT_STATUS_STOPPING; - break; - } - vTaskDelay(1000 / portTICK_RATE_MS); - - } - - mqtt_free(client); - client->settings->xMqttTask = NULL; - client->status = MQTT_STATUS_STOPPED; - vTaskDelete(NULL); -} - -// ================================= -int mqtt_start(mqtt_client *client) -{ - // ==== Check for Internet connection first ==== - tcpip_adapter_ip_info_t info; - tcpip_adapter_get_ip_info(WIFI_IF_STA, &info); - if (info.ip.addr == 0) { - #ifdef CONFIG_MICROPY_USE_GSM - if (ppposStatus() != GSM_STATE_CONNECTED) { - return -9; - } - #else - return -9; - #endif - } - // ============================================= - - client->terminate_mqtt = false; - - uint8_t *rb_buf; - if (client->settings->xMqttTask != NULL) return -1; - - client->status = MQTT_STATUS_DISCONNECTED; - client->settings->xMqttSendingTask = NULL; - client->settings->xMqttSendingTask_stacksize = 2048; - client->settings->xMqttTask_stacksize = 2048; - - client->connect_info.client_id = client->settings->client_id; - client->connect_info.username = client->settings->username; - client->connect_info.password = client->settings->password; - client->connect_info.will_topic = client->settings->lwt_topic; - client->connect_info.will_message = client->settings->lwt_msg; - client->connect_info.will_qos = client->settings->lwt_qos; - client->connect_info.will_retain = client->settings->lwt_retain; - client->connect_info.will_length = client->settings->lwt_msg_len; - - client->keepalive_tick = client->settings->keepalive / 2; - - client->connect_info.keepalive = client->settings->keepalive; - client->connect_info.clean_session = client->settings->clean_session; - - client->mqtt_state.in_buffer = (uint8_t *)malloc(CONFIG_MQTT_BUFFER_SIZE_BYTE); - client->mqtt_state.in_buffer_length = CONFIG_MQTT_BUFFER_SIZE_BYTE; - client->mqtt_state.out_buffer = (uint8_t *)malloc(CONFIG_MQTT_BUFFER_SIZE_BYTE); - client->mqtt_state.out_buffer_length = CONFIG_MQTT_BUFFER_SIZE_BYTE; - client->mqtt_state.connect_info = &client->connect_info; - - client->socket = -1; - - if (!client->settings->connect_cb) - client->settings->connect_cb = client_connect; - if (!client->settings->disconnect_cb) - client->settings->disconnect_cb = closeclient; - if (!client->settings->read_cb) - client->settings->read_cb = mqtt_read; - if (!client->settings->write_cb) - client->settings->write_cb = mqtt_write; - - client->ctx = NULL; - client->ssl = NULL; - if (client->settings->use_ssl) client->settings->xMqttTask_stacksize = 10240; // Need more stack to handle SSL handshake - - /* Create a queue capable of containing 64 unsigned long values. */ - client->xSendingQueue = xQueueCreate(64, sizeof( uint32_t )); - if (client->xSendingQueue == 0) return -3; - - rb_buf = (uint8_t*) malloc(CONFIG_MQTT_BUFFER_SIZE_BYTE * 4); - - if (rb_buf == NULL) { - ESP_LOGE(MQTT_TAG, "Error allocating ring buffer"); - return -2; - } - - rb_init(&client->send_rb, rb_buf, CONFIG_MQTT_BUFFER_SIZE_BYTE * 4, 1); - - mqtt_msg_init(&client->mqtt_state.mqtt_connection, - client->mqtt_state.out_buffer, - client->mqtt_state.out_buffer_length); - - xTaskCreate(&mqtt_task, "mqtt_task", client->settings->xMqttTask_stacksize, client, CONFIG_MQTT_PRIORITY, &client->settings->xMqttTask); - if (client->settings->xMqttTask == NULL) return -4; - - return 0; -} - -//---------------------------------------------------------------------- -void mqtt_subscribe(mqtt_client *client, const char *topic, uint8_t qos) -{ - if (subs_last_topic) free(subs_last_topic); - subs_last_topic = malloc(strlen(topic)+1); - if (subs_last_topic) strcpy(subs_last_topic, topic); - - client->subs_flag = 0; - client->mqtt_state.sending_msg_type = MQTT_SENDING_TYPE_SUBSCRIBE; - client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, - topic, qos, - &client->mqtt_state.pending_msg_id); - ESP_LOGI(MQTT_TAG, "Queue subscribe, topic \"%s\", id: %d", topic, client->mqtt_state.pending_msg_id); - mqtt_queue(client); -} - -//----------------------------------------------------------- -void mqtt_unsubscribe(mqtt_client *client, const char *topic) -{ - if (unsubs_last_topic) free(unsubs_last_topic); - unsubs_last_topic = malloc(strlen(topic)+1); - if (unsubs_last_topic) strcpy(unsubs_last_topic, topic); - - client->unsubs_flag = 0; - client->mqtt_state.sending_msg_type = MQTT_SENDING_TYPE_UNSUBSCRIBE; - client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection, - topic, - &client->mqtt_state.pending_msg_id); - ESP_LOGI(MQTT_TAG, "Queue unsubscribe, topic \"%s\", id: %d", topic, client->mqtt_state.pending_msg_id); - mqtt_queue(client); -} - -//------------------------------------------------------------------------------------------------------ -int mqtt_publish(mqtt_client* client, const char *topic, const char *data, int len, int qos, int retain) -{ - client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, - topic, data, len, - qos, retain, - &client->mqtt_state.pending_msg_id); - if (client->mqtt_state.outbound_message->length == 0) return -1; - - client->mqtt_state.sending_msg_type = MQTT_SENDING_TYPE_PUBLISH; - mqtt_queue(client); - ESP_LOGI(MQTT_TAG, "Queuing publish, length: %d, queue size(%d/%d)", - client->mqtt_state.outbound_message->length, - client->send_rb.fill_cnt, - client->send_rb.size); - return 0; -} - -//--------------------------------- -void mqtt_stop(mqtt_client* client) -{ - client->terminate_mqtt = true; - client->status = MQTT_STATUS_STOPPING; -} - -#endif diff --git a/MicroPython_BUILD/components/espmqtt/mqtt_client.c b/MicroPython_BUILD/components/espmqtt/mqtt_client.c new file mode 100644 index 00000000..9783e5da --- /dev/null +++ b/MicroPython_BUILD/components/espmqtt/mqtt_client.c @@ -0,0 +1,820 @@ +/* + * This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo + * + * Apache License Version 2.0 + * + * Slightly modified mqtt library from https://github.com/tuanpmt/espmqtt + * + * Copyright (c) 2018 tuanpm (https://github.com/tuanpmt/espmqtt) + * Copyright (c) 2018 LoBo (https://github.com/loboris) +*/ + +#include + +#include "mqtt_client.h" + +/* using uri parser */ +#include "http_parser.h" +#include "sdkconfig.h" + +const char *MQTT_TAG = "MQTT_CLIENT"; + +const static int STOPPED_BIT = BIT0; + +extern int MainTaskCore; + +static esp_err_t esp_mqtt_dispatch_event(esp_mqtt_client_handle_t client); +static esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_client_config_t *config); +static esp_err_t esp_mqtt_destroy_config(esp_mqtt_client_handle_t client); +static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_ms); +static esp_err_t esp_mqtt_abort_connection(esp_mqtt_client_handle_t client); +static esp_err_t esp_mqtt_client_ping(esp_mqtt_client_handle_t client); +static char *create_string(const char *ptr, int len); + +static esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_client_config_t *config) +{ + //Copy user configurations to client context + esp_err_t err = ESP_OK; + mqtt_config_storage_t *cfg = calloc(1, sizeof(mqtt_config_storage_t)); + ESP_MEM_CHECK(MQTT_TAG, cfg, return ESP_ERR_NO_MEM); + + client->config = cfg; + + cfg->task_prio = config->task_prio; + if (cfg->task_prio <= 0) { + cfg->task_prio = MQTT_TASK_PRIORITY; + } + + cfg->task_stack = config->task_stack; + if (cfg->task_stack == 0) { + cfg->task_stack = MQTT_TASK_STACK; + } + + err = ESP_ERR_NO_MEM; + if (config->host[0]) { + cfg->host = strdup(config->host); + ESP_MEM_CHECK(MQTT_TAG, cfg->host, goto _mqtt_set_config_failed); + } + cfg->port = config->port; + + if (config->username[0]) { + client->connect_info.username = strdup(config->username); + ESP_MEM_CHECK(MQTT_TAG, client->connect_info.username, goto _mqtt_set_config_failed); + } + + if (config->password[0]) { + client->connect_info.password = strdup(config->password); + ESP_MEM_CHECK(MQTT_TAG, client->connect_info.password, goto _mqtt_set_config_failed); + } + + if (config->client_id[0]) { + client->connect_info.client_id = strdup(config->client_id); + } else { + client->connect_info.client_id = platform_create_id_string(); + } + ESP_MEM_CHECK(MQTT_TAG, client->connect_info.client_id, goto _mqtt_set_config_failed); + ESP_LOGD(MQTT_TAG, "MQTT client_id=%s", client->connect_info.client_id); + + if (config->uri[0]) { + cfg->uri = strdup(config->uri); + ESP_MEM_CHECK(MQTT_TAG, cfg->uri, goto _mqtt_set_config_failed); + } + + if (config->lwt_topic[0]) { + client->connect_info.will_topic = strdup(config->lwt_topic); + ESP_MEM_CHECK(MQTT_TAG, client->connect_info.will_topic, goto _mqtt_set_config_failed); + } + + if (config->lwt_msg_len) { + client->connect_info.will_message = malloc(config->lwt_msg_len); + ESP_MEM_CHECK(MQTT_TAG, client->connect_info.will_message, goto _mqtt_set_config_failed); + memcpy(client->connect_info.will_message, config->lwt_msg, config->lwt_msg_len); + client->connect_info.will_length = config->lwt_msg_len; + } else if (config->lwt_msg[0]) { + client->connect_info.will_message = strdup(config->lwt_msg); + ESP_MEM_CHECK(MQTT_TAG, client->connect_info.will_message, goto _mqtt_set_config_failed); + client->connect_info.will_length = strlen(config->lwt_msg); + } + + client->connect_info.will_qos = config->lwt_qos; + client->connect_info.will_retain = config->lwt_retain; + + client->connect_info.clean_session = 1; + if (config->disable_clean_session) { + client->connect_info.clean_session = false; + } + client->connect_info.keepalive = config->keepalive; + if (client->connect_info.keepalive == 0) { + client->connect_info.keepalive = MQTT_KEEPALIVE_TICK; + } + cfg->network_timeout_ms = MQTT_NETWORK_TIMEOUT_MS; + cfg->user_context = config->user_context; + cfg->event_handle = config->event_handle; + cfg->auto_reconnect = true; + if (config->disable_auto_reconnect) { + cfg->auto_reconnect = false; + } + + return ESP_OK; + +_mqtt_set_config_failed: + esp_mqtt_destroy_config(client); + return err; +} + +static esp_err_t esp_mqtt_destroy_config(esp_mqtt_client_handle_t client) +{ + mqtt_config_storage_t *cfg = client->config; + if (cfg->host) free(cfg->host); + if (cfg->uri) free(cfg->uri); + if (cfg->path) free(cfg->path); + if (cfg->scheme) free(cfg->scheme); + if (client->connect_info.will_topic) free(client->connect_info.will_topic); + if (client->connect_info.will_message) free(client->connect_info.will_message); + if (client->connect_info.client_id) free(client->connect_info.client_id); + if (client->connect_info.username) free(client->connect_info.username); + if (client->connect_info.password) free(client->connect_info.password); + free(client->config); + return ESP_OK; +} + +static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_ms) +{ + int write_len, read_len, connect_rsp_code; + mqtt_msg_init(&client->mqtt_state.mqtt_connection, + client->mqtt_state.out_buffer, + client->mqtt_state.out_buffer_length); + client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, + client->mqtt_state.connect_info); + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, + client->mqtt_state.outbound_message->length); + ESP_LOGI(MQTT_TAG, "Sending MQTT CONNECT message, type: %d, id: %04X", + client->mqtt_state.pending_msg_type, + client->mqtt_state.pending_msg_id); + + write_len = transport_write(client->transport, + (char *)client->mqtt_state.outbound_message->data, + client->mqtt_state.outbound_message->length, + client->config->network_timeout_ms); + if (write_len < 0) { + ESP_LOGE(MQTT_TAG, "Writing failed, errno= %d", errno); + return ESP_FAIL; + } + read_len = transport_read(client->transport, + (char *)client->mqtt_state.in_buffer, + client->mqtt_state.outbound_message->length, + client->config->network_timeout_ms); + if (read_len < 0) { + ESP_LOGE(MQTT_TAG, "Error network response"); + return ESP_FAIL; + } + + if (mqtt_get_type(client->mqtt_state.in_buffer) != MQTT_MSG_TYPE_CONNACK) { + ESP_LOGE(MQTT_TAG, "Invalid MSG_TYPE response: %d, read_len: %d", mqtt_get_type(client->mqtt_state.in_buffer), read_len); + return ESP_FAIL; + } + connect_rsp_code = mqtt_get_connect_return_code(client->mqtt_state.in_buffer); + switch (connect_rsp_code) { + case CONNECTION_ACCEPTED: + ESP_LOGD(MQTT_TAG, "Connected"); + return ESP_OK; + case CONNECTION_REFUSE_PROTOCOL: + ESP_LOGW(MQTT_TAG, "Connection refused, bad protocol"); + return ESP_FAIL; + case CONNECTION_REFUSE_SERVER_UNAVAILABLE: + ESP_LOGW(MQTT_TAG, "Connection refused, server unavailable"); + return ESP_FAIL; + case CONNECTION_REFUSE_BAD_USERNAME: + ESP_LOGW(MQTT_TAG, "Connection refused, bad username or password"); + return ESP_FAIL; + case CONNECTION_REFUSE_NOT_AUTHORIZED: + ESP_LOGW(MQTT_TAG, "Connection refused, not authorized"); + return ESP_FAIL; + default: + ESP_LOGW(MQTT_TAG, "Connection refused, Unknow reason"); + return ESP_FAIL; + } + return ESP_OK; +} + +static esp_err_t esp_mqtt_abort_connection(esp_mqtt_client_handle_t client) +{ + transport_close(client->transport); + client->wait_timeout_ms = MQTT_RECONNECT_TIMEOUT_MS; + client->reconnect_tick = platform_tick_get_ms(); + client->state = MQTT_STATE_WAIT_TIMEOUT; + ESP_LOGI(MQTT_TAG, "Reconnect after %d ms", client->wait_timeout_ms); + client->event.event_id = MQTT_EVENT_DISCONNECTED; + esp_mqtt_dispatch_event(client); + return ESP_OK; +} + +esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config) +{ + esp_mqtt_client_handle_t client = calloc(1, sizeof(struct esp_mqtt_client)); + ESP_MEM_CHECK(MQTT_TAG, client, return NULL); + + if (esp_mqtt_set_config(client, config) != ESP_OK) { + ESP_LOGE(MQTT_TAG, "Setting configuration failed"); + return NULL; + } + + client->transport_list = transport_list_init(); + ESP_MEM_CHECK(MQTT_TAG, client->transport_list, goto _mqtt_init_failed); + + transport_handle_t tcp = transport_tcp_init(); + ESP_MEM_CHECK(MQTT_TAG, tcp, goto _mqtt_init_failed); + transport_set_default_port(tcp, MQTT_TCP_DEFAULT_PORT); + transport_list_add(client->transport_list, tcp, "mqtt"); + if (config->transport == MQTT_TRANSPORT_OVER_TCP) { + client->config->scheme = create_string("mqtt", 4); + ESP_MEM_CHECK(MQTT_TAG, client->config->scheme, goto _mqtt_init_failed); + } + +#if MQTT_ENABLE_WS + transport_handle_t ws = transport_ws_init(tcp); + ESP_MEM_CHECK(MQTT_TAG, ws, goto _mqtt_init_failed); + transport_set_default_port(ws, MQTT_WS_DEFAULT_PORT); + transport_list_add(client->transport_list, ws, "ws"); + if (config->transport == MQTT_TRANSPORT_OVER_WS) { + client->config->scheme = create_string("ws", 2); + ESP_MEM_CHECK(MQTT_TAG, client->config->scheme, goto _mqtt_init_failed); + } +#endif + +#if MQTT_ENABLE_SSL + transport_handle_t ssl = transport_ssl_init(); + ESP_MEM_CHECK(MQTT_TAG, ssl, goto _mqtt_init_failed); + transport_set_default_port(ssl, MQTT_SSL_DEFAULT_PORT); + if (config->cert_pem) { + transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem)); + } + transport_list_add(client->transport_list, ssl, "mqtts"); + if (config->transport == MQTT_TRANSPORT_OVER_SSL) { + client->config->scheme = create_string("mqtts", 5); + ESP_MEM_CHECK(MQTT_TAG, client->config->scheme, goto _mqtt_init_failed); + } +#endif + +#if MQTT_ENABLE_WSS + transport_handle_t wss = transport_ws_init(ssl); + ESP_MEM_CHECK(MQTT_TAG, wss, goto _mqtt_init_failed); + transport_set_default_port(wss, MQTT_WSS_DEFAULT_PORT); + transport_list_add(client->transport_list, wss, "wss"); + if (config->transport == MQTT_TRANSPORT_OVER_WSS) { + client->config->scheme = create_string("wss", 3); + ESP_MEM_CHECK(MQTT_TAG, client->config->scheme, goto _mqtt_init_failed); + } +#endif + if (client->config->uri) { + if (esp_mqtt_client_set_uri(client, client->config->uri) != ESP_OK) { + goto _mqtt_init_failed; + } + } + + if (client->config->scheme == NULL) { + client->config->scheme = create_string("mqtt", 4); + ESP_MEM_CHECK(MQTT_TAG, client->config->scheme, goto _mqtt_init_failed); + } + + client->keepalive_tick = platform_tick_get_ms(); + client->reconnect_tick = platform_tick_get_ms(); + + int buffer_size = config->buffer_size; + if (buffer_size <= 0) { + buffer_size = MQTT_BUFFER_SIZE_BYTE; + } + + client->mqtt_state.in_buffer = (uint8_t *)malloc(buffer_size); + ESP_MEM_CHECK(MQTT_TAG, client->mqtt_state.in_buffer, goto _mqtt_init_failed); + client->mqtt_state.in_buffer_length = buffer_size; + client->mqtt_state.out_buffer = (uint8_t *)malloc(buffer_size); + ESP_MEM_CHECK(MQTT_TAG, client->mqtt_state.out_buffer, goto _mqtt_init_failed); + + client->mqtt_state.out_buffer_length = buffer_size; + client->mqtt_state.connect_info = &client->connect_info; + client->outbox = outbox_init(); + ESP_MEM_CHECK(MQTT_TAG, client->outbox, goto _mqtt_init_failed); + client->status_bits = xEventGroupCreate(); + ESP_MEM_CHECK(MQTT_TAG, client->status_bits, goto _mqtt_init_failed); + return client; + +_mqtt_init_failed: + esp_mqtt_client_destroy(client); + return NULL; +} + +esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client) +{ + esp_mqtt_client_stop(client); + esp_mqtt_destroy_config(client); + transport_list_destroy(client->transport_list); + outbox_destroy(client->outbox); + vEventGroupDelete(client->status_bits); + free(client->mqtt_state.in_buffer); + free(client->mqtt_state.out_buffer); + free(client); + return ESP_OK; +} + +static char *create_string(const char *ptr, int len) +{ + char *ret; + if (len <= 0) { + return NULL; + } + ret = calloc(1, len + 1); + ESP_MEM_CHECK(MQTT_TAG, ret, return NULL); + memcpy(ret, ptr, len); + return ret; +} + +esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *uri) +{ + struct http_parser_url puri; + http_parser_url_init(&puri); + int parser_status = http_parser_parse_url(uri, strlen(uri), 0, &puri); + if (parser_status != 0) { + ESP_LOGE(MQTT_TAG, "Error parse uri = %s", uri); + return ESP_FAIL; + } + + if (client->config->scheme == NULL) { + client->config->scheme = create_string(uri + puri.field_data[UF_SCHEMA].off, puri.field_data[UF_SCHEMA].len); + } + + if (client->config->host == NULL) { + client->config->host = create_string(uri + puri.field_data[UF_HOST].off, puri.field_data[UF_HOST].len); + } + + if (client->config->path == NULL) { + client->config->path = create_string(uri + puri.field_data[UF_PATH].off, puri.field_data[UF_PATH].len); + } + if (client->config->path) { + transport_handle_t trans = transport_list_get_transport(client->transport_list, "ws"); + if (trans) { + transport_ws_set_path(trans, client->config->path); + } + trans = transport_list_get_transport(client->transport_list, "wss"); + if (trans) { + transport_ws_set_path(trans, client->config->path); + } + } + + if (puri.field_data[UF_PORT].len) { + client->config->port = strtol((const char*)(uri + puri.field_data[UF_PORT].off), NULL, 10); + } + + char *user_info = create_string(uri + puri.field_data[UF_USERINFO].off, puri.field_data[UF_USERINFO].len); + if (user_info) { + char *pass = strchr(user_info, ':'); + if (pass) { + pass[0] = 0; //terminal username + pass ++; + client->connect_info.password = strdup(pass); + } + client->connect_info.username = strdup(user_info); + + free(user_info); + } + + return ESP_OK; +} + +static esp_err_t mqtt_write_data(esp_mqtt_client_handle_t client) +{ + int write_len = transport_write(client->transport, + (char *)client->mqtt_state.outbound_message->data, + client->mqtt_state.outbound_message->length, + client->config->network_timeout_ms); + // client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + if (write_len <= 0) { + ESP_LOGE(MQTT_TAG, "Error write data or timeout, written len = %d", write_len); + return ESP_FAIL; + } + return ESP_OK; +} + +static esp_err_t esp_mqtt_dispatch_event(esp_mqtt_client_handle_t client) +{ + client->event.msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); + client->event.user_context = client->config->user_context; + client->event.client = client; + + if (client->config->event_handle) { + return client->config->event_handle(&client->event); + } + return ESP_FAIL; +} + + + +static void deliver_publish(esp_mqtt_client_handle_t client, uint8_t *message, int length) +{ + const char *mqtt_topic, *mqtt_data; + uint32_t mqtt_topic_length, mqtt_data_length; + uint32_t mqtt_len, mqtt_offset = 0, total_mqtt_len = 0; + int len_read; + + do + { + mqtt_topic_length = length; + mqtt_topic = mqtt_get_publish_topic(message, &mqtt_topic_length); + mqtt_data_length = length; + mqtt_data = mqtt_get_publish_data(message, &mqtt_data_length); + + if(total_mqtt_len == 0){ + mqtt_topic_length = length; + mqtt_topic = mqtt_get_publish_topic(message, &mqtt_topic_length); + mqtt_data_length = length; + mqtt_data = mqtt_get_publish_data(message, &mqtt_data_length); + total_mqtt_len = client->mqtt_state.message_length - client->mqtt_state.message_length_read + mqtt_data_length; + mqtt_len = mqtt_data_length; + } else { + mqtt_len = len_read; + mqtt_data = (const char*)client->mqtt_state.in_buffer; + } + + ESP_LOGD(MQTT_TAG, "Get data len= %d, topic len=%d", mqtt_data_length, mqtt_topic_length); + client->event.event_id = MQTT_EVENT_DATA; + client->event.data = (char *)mqtt_data; + client->event.data_len = mqtt_len; + client->event.total_data_len = total_mqtt_len; + client->event.current_data_offset = mqtt_offset; + client->event.topic = (char *)mqtt_topic; + client->event.topic_len = mqtt_topic_length; + esp_mqtt_dispatch_event(client); + + mqtt_offset += mqtt_len; + if (client->mqtt_state.message_length_read >= client->mqtt_state.message_length) { + break; + } + + /*len_read = transport_read(client->transport, + (char *)client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_length, + client->config->network_timeout_ms);*/ + len_read = transport_read(client->transport, + (char *)client->mqtt_state.in_buffer, + client->mqtt_state.message_length - client->mqtt_state.message_length_read > client->mqtt_state.in_buffer_length ? + client->mqtt_state.in_buffer_length : client->mqtt_state.message_length - client->mqtt_state.message_length_read, + client->config->network_timeout_ms); + if (len_read <= 0) { + ESP_LOGE(MQTT_TAG, "Read error or timeout: %d", errno); + break; + } + client->mqtt_state.message_length_read += len_read; + } while (1); + + +} + +static bool is_valid_mqtt_msg(esp_mqtt_client_handle_t client, int msg_type, int msg_id) +{ + ESP_LOGD(MQTT_TAG, "pending_id=%d, pending_msg_count = %d", client->mqtt_state.pending_msg_id, client->mqtt_state.pending_msg_count); + if (client->mqtt_state.pending_msg_count == 0) { + return false; + } + if (outbox_delete(client->outbox, msg_id, msg_type) == ESP_OK) { + client->mqtt_state.pending_msg_count --; + return true; + } + if (client->mqtt_state.pending_msg_type == msg_type && client->mqtt_state.pending_msg_id == msg_id) { + client->mqtt_state.pending_msg_count --; + return true; + } + + return false; +} + +static void mqtt_enqueue(esp_mqtt_client_handle_t client) +{ + ESP_LOGD(MQTT_TAG, "mqtt_enqueue id: %d, type=%d successful", + client->mqtt_state.pending_msg_id, client->mqtt_state.pending_msg_type); + //lock mutex + if (client->mqtt_state.pending_msg_count > 0) { + //Copy to queue buffer + outbox_enqueue(client->outbox, + client->mqtt_state.outbound_message->data, + client->mqtt_state.outbound_message->length, + client->mqtt_state.pending_msg_id, + client->mqtt_state.pending_msg_type, + platform_tick_get_ms()); + } + //unlock +} + +static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) +{ + int read_len; + uint8_t msg_type; + uint8_t msg_qos; + uint16_t msg_id; + + read_len = transport_read(client->transport, (char *)client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length, 1000); + + if (read_len < 0) { + ESP_LOGE(MQTT_TAG, "Read error or end of stream"); + return ESP_FAIL; + } + + if (read_len == 0) { + return ESP_OK; + } + + msg_type = mqtt_get_type(client->mqtt_state.in_buffer); + msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); + msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); + + ESP_LOGD(MQTT_TAG, "msg_type=%d, msg_id=%d", msg_type, msg_id); + switch (msg_type) + { + case MQTT_MSG_TYPE_SUBACK: + if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_SUBSCRIBE, msg_id)) { + ESP_LOGD(MQTT_TAG, "Subscribe successful"); + client->event.event_id = MQTT_EVENT_SUBSCRIBED; + client->event.type = MQTT_MSG_TYPE_SUBSCRIBE; + esp_mqtt_dispatch_event(client); + } + break; + case MQTT_MSG_TYPE_UNSUBACK: + if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_UNSUBSCRIBE, msg_id)) { + ESP_LOGD(MQTT_TAG, "UnSubscribe successful"); + client->event.event_id = MQTT_EVENT_UNSUBSCRIBED; + client->event.type = MQTT_MSG_TYPE_UNSUBSCRIBE; + esp_mqtt_dispatch_event(client); + } + break; + case MQTT_MSG_TYPE_PUBLISH: + if (msg_qos == 1) { + client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); + } + else if (msg_qos == 2) { + client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); + } + + if (msg_qos == 1 || msg_qos == 2) { + ESP_LOGD(MQTT_TAG, "Queue response QoS: %d", msg_qos); + + if (mqtt_write_data(client) != ESP_OK) { + ESP_LOGE(MQTT_TAG, "Error write qos msg repsonse, qos = %d", msg_qos); + // TODO: Shoule reconnect? + // return ESP_FAIL; + } + } + client->mqtt_state.message_length_read = read_len; + client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + ESP_LOGI(MQTT_TAG, "deliver_publish, message_length_read=%d, message_length=%d", read_len, client->mqtt_state.message_length); + + deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + break; + case MQTT_MSG_TYPE_PUBACK: + if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_PUBLISH, msg_id)) { + ESP_LOGD(MQTT_TAG, "received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish"); + client->event.event_id = MQTT_EVENT_PUBLISHED; + client->event.type = MQTT_MSG_TYPE_PUBACK; + esp_mqtt_dispatch_event(client); + } + + break; + case MQTT_MSG_TYPE_PUBREC: + ESP_LOGD(MQTT_TAG, "received MQTT_MSG_TYPE_PUBREC"); + client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); + mqtt_write_data(client); + break; + case MQTT_MSG_TYPE_PUBREL: + ESP_LOGD(MQTT_TAG, "received MQTT_MSG_TYPE_PUBREL"); + client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); + mqtt_write_data(client); + + break; + case MQTT_MSG_TYPE_PUBCOMP: + ESP_LOGD(MQTT_TAG, "received MQTT_MSG_TYPE_PUBCOMP"); + if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_PUBREL, msg_id)) { + ESP_LOGD(MQTT_TAG, "Receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish"); + client->event.event_id = MQTT_EVENT_PUBLISHED; + client->event.type = MQTT_MSG_TYPE_PUBCOMP; + esp_mqtt_dispatch_event(client); + } + break; + case MQTT_MSG_TYPE_PINGREQ: + client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); + mqtt_write_data(client); + break; + case MQTT_MSG_TYPE_PINGRESP: + ESP_LOGD(MQTT_TAG, "MQTT_MSG_TYPE_PINGRESP"); + // Ignore + break; + } + + return ESP_OK; +} + +static void esp_mqtt_task(void *pv) +{ + esp_mqtt_client_handle_t client = (esp_mqtt_client_handle_t) pv; + client->run = true; + + //get transport by scheme + client->transport = transport_list_get_transport(client->transport_list, client->config->scheme); + + if (client->transport == NULL) { + ESP_LOGE(MQTT_TAG, "There are no transports valid, stop mqtt client, config scheme = %s", client->config->scheme); + client->run = false; + } + //default port + if (client->config->port == 0) { + client->config->port = transport_get_default_port(client->transport); + } + + client->state = MQTT_STATE_INIT; + xEventGroupClearBits(client->status_bits, STOPPED_BIT); + while (client->run) { + + switch ((int)client->state) { + case MQTT_STATE_INIT: + if (client->transport == NULL) { + ESP_LOGE(MQTT_TAG, "There are no transport"); + client->run = false; + } + + if (transport_connect(client->transport, + client->config->host, + client->config->port, + client->config->network_timeout_ms) < 0) { + ESP_LOGE(MQTT_TAG, "Error transport connect"); + esp_mqtt_abort_connection(client); + break; + } + ESP_LOGD(MQTT_TAG, "Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port); + if (esp_mqtt_connect(client, client->config->network_timeout_ms) != ESP_OK) { + ESP_LOGI(MQTT_TAG, "Error MQTT Connected"); + esp_mqtt_abort_connection(client); + break; + } + client->event.event_id = MQTT_EVENT_CONNECTED; + client->state = MQTT_STATE_CONNECTED; + esp_mqtt_dispatch_event(client); + + break; + case MQTT_STATE_CONNECTED: + // receive and process data + if (mqtt_process_receive(client) == ESP_FAIL) { + esp_mqtt_abort_connection(client); + break; + } + + if (platform_tick_get_ms() - client->keepalive_tick > client->connect_info.keepalive * 1000 / 2) { + if (esp_mqtt_client_ping(client) == ESP_FAIL) { + esp_mqtt_abort_connection(client); + break; + } + client->keepalive_tick = platform_tick_get_ms(); + } + + //Delete mesaage after 30 senconds + outbox_delete_expired(client->outbox, platform_tick_get_ms(), OUTBOX_EXPIRED_TIMEOUT_MS); + // + outbox_cleanup(client->outbox, OUTBOX_MAX_SIZE); + break; + case MQTT_STATE_WAIT_TIMEOUT: + + if (!client->config->auto_reconnect) { + client->run = false; + break; + } + if (platform_tick_get_ms() - client->reconnect_tick > client->wait_timeout_ms) { + client->state = MQTT_STATE_INIT; + client->reconnect_tick = platform_tick_get_ms(); + ESP_LOGD(MQTT_TAG, "Reconnecting..."); + } + vTaskDelay(client->wait_timeout_ms / 2 / portTICK_RATE_MS); + break; + } + } + transport_close(client->transport); + xEventGroupSetBits(client->status_bits, STOPPED_BIT); + + vTaskDelete(NULL); +} + +esp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client) +{ + if (client->state >= MQTT_STATE_INIT) { + ESP_LOGE(MQTT_TAG, "Client has started"); + return ESP_FAIL; + } + #if CONFIG_MICROPY_USE_BOTH_CORES + int tres = xTaskCreate(esp_mqtt_task, "mqtt_task", client->config->task_stack, client, client->config->task_prio, NULL); + #else + int tres = xTaskCreatePinnedToCore(esp_mqtt_task, "mqtt_task", client->config->task_stack, client, client->config->task_prio, NULL, MainTaskCore); + #endif + if (tres != pdTRUE) { + ESP_LOGE(MQTT_TAG, "Error creating mqtt task"); + return ESP_FAIL; + } + xEventGroupClearBits(client->status_bits, STOPPED_BIT); + return ESP_OK; +} + + +esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client) +{ + client->run = false; + xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY); + client->state = MQTT_STATE_UNKNOWN; + return ESP_OK; +} + +static esp_err_t esp_mqtt_client_ping(esp_mqtt_client_handle_t client) +{ + client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); + + if (mqtt_write_data(client) != ESP_OK) { + ESP_LOGE(MQTT_TAG, "Error sending ping"); + return ESP_FAIL; + } + ESP_LOGD(MQTT_TAG, "Sent PING successful"); + return ESP_OK; +} + +int esp_mqtt_client_subscribe(esp_mqtt_client_handle_t client, const char *topic, int qos) +{ + if (client->state != MQTT_STATE_CONNECTED) { + ESP_LOGE(MQTT_TAG, "Client has not connected"); + return -1; + } + mqtt_enqueue(client); //move pending msg to outbox (if have) + client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, + topic, qos, + &client->mqtt_state.pending_msg_id); + + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_count ++; + + if (mqtt_write_data(client) != ESP_OK) { + ESP_LOGE(MQTT_TAG, "Error to subscribe topic=%s, qos=%d", topic, qos); + return -1; + } + + ESP_LOGD(MQTT_TAG, "Sent subscribe topic=%s, id: %d, type=%d successful", topic, client->mqtt_state.pending_msg_id, client->mqtt_state.pending_msg_type); + return client->mqtt_state.pending_msg_id; +} + +int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *topic) +{ + if (client->state != MQTT_STATE_CONNECTED) { + ESP_LOGE(MQTT_TAG, "Client has not connected"); + return -1; + } + mqtt_enqueue(client); + client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection, + topic, + &client->mqtt_state.pending_msg_id); + ESP_LOGD(MQTT_TAG, "unsubscribe, topic\"%s\", id: %d", topic, client->mqtt_state.pending_msg_id); + + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_count ++; + + if (mqtt_write_data(client) != ESP_OK) { + ESP_LOGE(MQTT_TAG, "Error to unsubscribe topic=%s", topic); + return -1; + } + + ESP_LOGD(MQTT_TAG, "Sent Unsubscribe topic=%s, id: %d, successful", topic, client->mqtt_state.pending_msg_id); + return client->mqtt_state.pending_msg_id; +} + +int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain) +{ + uint16_t pending_msg_id = 0; + if (client->state != MQTT_STATE_CONNECTED) { + ESP_LOGE(MQTT_TAG, "Client has not connected"); + return -1; + } + if (len <= 0) { + len = strlen(data); + } + if (qos > 0) { + mqtt_enqueue(client); + } + + client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, + topic, data, len, + qos, retain, + &pending_msg_id); + if (qos > 0) { + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = pending_msg_id; + client->mqtt_state.pending_msg_count ++; + } + + if (mqtt_write_data(client) != ESP_OK) { + ESP_LOGE(MQTT_TAG, "Error publishing data to topic=%s, qos=%d", topic, qos); + return -1; + } + return pending_msg_id; +} + + diff --git a/MicroPython_BUILD/components/espmqtt/ringbuf.c b/MicroPython_BUILD/components/espmqtt/ringbuf.c deleted file mode 100644 index 5bb4adbe..00000000 --- a/MicroPython_BUILD/components/espmqtt/ringbuf.c +++ /dev/null @@ -1,106 +0,0 @@ -/** -* \file -* Ring Buffer library -*/ -#include -#include -#include "ringbuf.h" - -/** -* \brief init a RINGBUF object -* \param r pointer to a RINGBUF object -* \param buf pointer to a byte array -* \param size size of buf -* \param block_size is size of data as block -* \return 0 if successfull, otherwise failed -*/ -int32_t rb_init(RINGBUF *r, uint8_t* buf, int32_t size, int32_t block_size) -{ - if (r == 0 || buf == 0 || size < 2) return -1; - - if (size % block_size != 0) return -1; - - r->p_o = r->p_r = r->p_w = buf; - r->fill_cnt = 0; - r->size = size; - r->block_size = block_size; - return 0; -} -/** -* \brief put a character into ring buffer -* \param r pointer to a ringbuf object -* \param c character to be put -* \return 0 if successfull, otherwise failed -*/ -int32_t rb_put(RINGBUF *r, uint8_t *c) -{ - int32_t i; - uint8_t *data = c; - if (r->fill_cnt >= r->size) - return -1; // ring buffer is full, this should be atomic operation - - - r->fill_cnt += r->block_size; // increase filled slots count, this should be atomic operation - - for (i = 0; i < r->block_size; i++) { - *r->p_w = *data; // put character into buffer - - r->p_w ++; - data ++; - } - - if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass - r->p_w = r->p_o; // the physical boundary - - return 0; -} -/** -* \brief get a character from ring buffer -* \param r pointer to a ringbuf object -* \param c read character -* \return 0 if successfull, otherwise failed -*/ -int32_t rb_get(RINGBUF *r, uint8_t *c) -{ - int32_t i; - uint8_t *data = c; - if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation - - r->fill_cnt -= r->block_size; // decrease filled slots count - - for (i = 0; i < r->block_size; i++) - *data++ = *r->p_r++; // get the character out - - if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass - r->p_r = r->p_o; // the physical boundary - - return 0; -} - -int32_t rb_available(RINGBUF *r) -{ - return (r->size - r->fill_cnt); -} - -uint32_t rb_read(RINGBUF *r, uint8_t *buf, int len) -{ - int n = 0; - uint8_t data; - while (len > 0) { - while (rb_get(r, &data) != 0); - *buf++ = data; - n ++; - len --; - } - - return n; -} - -uint32_t rb_write(RINGBUF *r, uint8_t *buf, int len) -{ - uint32_t wi; - for (wi = 0; wi < len; wi++) { - while (rb_put(r, &buf[wi]) != 0); - } - return 0; -} diff --git a/MicroPython_BUILD/components/libnmea/.gitignore b/MicroPython_BUILD/components/libnmea/.gitignore new file mode 100644 index 00000000..bbf313b2 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/.gitignore @@ -0,0 +1,32 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ diff --git a/MicroPython_BUILD/components/libnmea/LICENSE b/MicroPython_BUILD/components/libnmea/LICENSE new file mode 100644 index 00000000..8e4bbc83 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jack Engqvist Johansson + +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. diff --git a/MicroPython_BUILD/components/libnmea/README.md b/MicroPython_BUILD/components/libnmea/README.md new file mode 100644 index 00000000..9d5868a0 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/README.md @@ -0,0 +1,9 @@ +C Library for Parsing NMEA 0183 Sentences +========================================= + +### This library was modified by LoBo (https://github.com/loboris) + +as part of the [MicroPython ESP32 project](https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo) + +from the original [libnmea](https://github.com/jacketizer/libnmea) GitHub repository. + diff --git a/MicroPython_BUILD/components/libnmea/component.mk b/MicroPython_BUILD/components/libnmea/component.mk new file mode 100644 index 00000000..9fbaf871 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/component.mk @@ -0,0 +1,39 @@ +COMPONENT_SUBMODULES := src +COMPONENT_SRCDIRS := src/nmea src/parsers +COMPONENT_ADD_INCLUDEDIRS := src/nmea src/parsers +PARSER_OBJS := $(addprefix src/parsers/,\ + gpgga.o \ + gpgll.o \ + gprmc.o \ + gpgst.o \ + gpvtg.o \ + ) + +COMPONENT_OBJS := $(addprefix src/,\ + nmea/nmea.o \ + nmea/parser_static.o \ + parsers/parse.o \ + ) \ + $(PARSER_OBJS) + +define RENAME_SYMBOLS + $(OBJCOPY) \ + --redefine-sym init=nmea_$(1)_init \ + --redefine-sym parse=nmea_$(1)_parse \ + --redefine-sym set_default=nmea_$(1)_set_default \ + --redefine-sym allocate_data=nmea_$(1)_allocate_data \ + --redefine-sym free_data=nmea_$(1)_free_data \ + src/parsers/$(1).o +endef + +$(COMPONENT_LIBRARY): | rename_symbols + +.PHONY: rename_symbols + +rename_symbols: | $(PARSER_OBJS) + $(call RENAME_SYMBOLS,gpgga) + $(call RENAME_SYMBOLS,gpgll) + $(call RENAME_SYMBOLS,gprmc) + $(call RENAME_SYMBOLS,gpgst) + $(call RENAME_SYMBOLS,gpvtg) + diff --git a/MicroPython_BUILD/components/libnmea/src/nmea/nmea.c b/MicroPython_BUILD/components/libnmea/src/nmea/nmea.c new file mode 100644 index 00000000..b47c2fb6 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/nmea/nmea.c @@ -0,0 +1,303 @@ +#include "nmea.h" +#include "parser.h" +#include "parser_types.h" +#include "esp_log.h" + +#define ARRAY_LENGTH(a) (sizeof a / sizeof (a[0])) + +const char *NMEA_TAG = "NMEA"; + +/** + * Check if a value is not NULL and not empty. + * + * Returns 0 if set, otherwise -1. + */ +//----------------------------------------- +static int _is_value_set(const char *value) +{ + if (NULL == value || '\0' == *value) { + return -1; + } + + return 0; +} + +/** + * Crop a sentence from the type word and checksum. + * + * The type word at the beginning along with the dollar sign ($) will be + * removed. If there is a checksum, it will also be removed. The two end + * characters (usually ) will not be included in the new string. + * + * sentence is a validated NMEA sentence string. + * length is the char length of the sentence string. + * + * Returns pointer (char *) to the new string. + */ +//-------------------------------------------------------- +static char *_crop_sentence(char *sentence, size_t length) +{ + /* Skip type word, 7 characters (including $ and ,) */ + sentence += NMEA_PREFIX_LENGTH + NMEA_ID_LENGTH + 2; + + /* Null terminate before end of line/sentence, 2 characters */ + sentence[length - 9] = '\0'; + + /* Remove checksum, if there is one */ + if ('*' == sentence[length - 12]) { + sentence[length - 12] = '\0'; + } + + return sentence; +} + +/** + * Splits a string by comma. + * + * string is the string to split, will be manipulated. Needs to be + * null-terminated. + * values is a char pointer array that will be filled with pointers to the + * splitted values in the string. + * max_values is the maximum number of values to be parsed. + * + * Returns the number of values found in string. + */ +//---------------------------------------------------------------------------- +static int _split_string_by_comma(char *string, char **values, int max_values) +{ + int i = 0; + + values[i++] = string; + while (i < max_values && NULL != (string = strchr(string, ','))) { + *string = '\0'; + values[i++] = ++string; + } + + return i; +} + +/** + * Initiate the NMEA library and load the parser modules. + * + * This function will be called before the main() function. + */ +void __attribute__ ((constructor)) nmea_init(void); +//-------------- +void nmea_init() +{ + nmea_load_parsers(); +} + +/** + * Unload the parser modules. + * + * This function will be called after the exit() function. + */ +void __attribute__ ((destructor)) nmea_cleanup(void); +//----------------- +void nmea_cleanup() +{ + nmea_unload_parsers(); +} + +//---------------------------------------- +nmea_t nmea_get_type(const char *sentence) +{ + nmea_parser_module_s *parser = nmea_get_parser_by_sentence(sentence); + if (NULL == parser) { + return NMEA_UNKNOWN; + } + + return parser->parser.type; +} + +//--------------------------------------------- +uint8_t nmea_get_checksum(const char *sentence) +{ + const char *n = sentence + 1; + uint8_t chk = 0; + + /* While current char isn't '*' or sentence ending (newline) */ + while ('*' != *n && NMEA_END_CHAR_1 != *n && '\0' != *n) { + chk ^= (uint8_t) *n; + n++; + } + + return chk; +} + +//-------------------------------------------------------- +int nmea_has_checksum(const char *sentence, size_t length) +{ + if ('*' == sentence[length - 5]) { + return 0; + } + + return -1; +} + +//------------------------------------------------------------------------ +int nmea_validate(const char *sentence, size_t length, int check_checksum) +{ + const char *n; + + /* should have atleast 9 characters */ + if (9 > length) { + ESP_LOGD(NMEA_TAG, "Sentence too short (%d)", length); + return -1; + } + + /* should be less or equal to NMEA_MAX_LENGTH characters */ + if (NMEA_MAX_LENGTH < length) { + ESP_LOGD(NMEA_TAG, "Sentence too long (%d)", length); + return -2; + } + + /* should start with $ */ + if ('$' != *sentence) { + ESP_LOGD(NMEA_TAG, "Sentence not starting with '$'"); + return -3; + } + + /* should end with \r\n, or other... */ + if (NMEA_END_CHAR_2 != sentence[length - 1] || NMEA_END_CHAR_1 != sentence[length - 2]) { + ESP_LOGD(NMEA_TAG, "Sentence does not end with 'CRLF'"); + return -4; + } + + /* should have a 5 letter, upper case word */ + n = sentence; + while (++n < sentence + 6) { + if (*n < 'A' || *n > 'Z') { + /* not upper case letter */ + ESP_LOGD(NMEA_TAG, "Wrong sentence header"); + return -5; + } + } + + /* should have a comma after the type word */ + if (',' != sentence[6]) { + ESP_LOGD(NMEA_TAG, "Wrong sentence header, no comma"); + return -6; + } + + /* test for not allowed characters */ + n = sentence+6; + while (++n < (sentence + length - 2)) { + if ((*n < ' ') || (*n > 'z') || (*n == '$')) { + /* not allowed character */ + ESP_LOGD(NMEA_TAG, "Sentence contains invalid characters"); + return -7; + } + } + + /* check for checksum */ + if (1 == check_checksum && 0 == nmea_has_checksum(sentence, length)) { + uint8_t actual_chk; + uint8_t expected_chk; + char checksum[3]; + + checksum[0] = sentence[length - 4]; + checksum[1] = sentence[length - 3]; + checksum[2] = '\0'; + actual_chk = nmea_get_checksum(sentence); + expected_chk = (uint8_t) strtol(checksum, NULL, 16); + if (expected_chk != actual_chk) { + ESP_LOGD(NMEA_TAG, "Wrong sentence checksum"); + return -8; + } + } + + return 0; +} + +//-------------------------- +void nmea_free(nmea_s *data) +{ + nmea_parser_module_s *parser; + + if (NULL == data) { + return; + } + + parser = nmea_get_parser_by_type(data->type); + if (NULL == parser) { + return; + } + + parser->free_data(data); +} + +//------------------------------------------------------------------- +nmea_s *nmea_parse(char *sentence, size_t length, int check_checksum) +{ + unsigned int n_vals, val_index; + char *value, *val_string; + char *values[255]; + nmea_parser_module_s *parser; + nmea_t type; + int res; + + /* Validate sentence string */ + res = nmea_validate(sentence, length, check_checksum); + if (res < 0) { + ESP_LOGD(NMEA_TAG, "Validate error (%d)", res); + return (nmea_s *) NULL; + } + + type = nmea_get_type(sentence); + if (NMEA_UNKNOWN == type) { + ESP_LOGD(NMEA_TAG, "Get type error"); + return (nmea_s *) NULL; + } + + /* Crop sentence from type word and checksum */ + val_string = _crop_sentence(sentence, length); + if (NULL == val_string) { + ESP_LOGD(NMEA_TAG, "Sentence crop error"); + return (nmea_s *) NULL; + } + + /* Split the sentence into values */ + n_vals = _split_string_by_comma(val_string, values, ARRAY_LENGTH(values)); + if (0 == n_vals) { + ESP_LOGD(NMEA_TAG, "Sentence split error"); + return (nmea_s *) NULL; + } + + /* Get the right parser */ + parser = nmea_get_parser_by_type(type); + if (NULL == parser) { + ESP_LOGD(NMEA_TAG, "Get parser error"); + return (nmea_s *) NULL; + } + + /* Allocate memory for parsed data */ + parser->allocate_data((nmea_parser_s *) parser); + if (NULL == parser->parser.data) { + ESP_LOGD(NMEA_TAG, "Error allocating parser data"); + return (nmea_s *) NULL; + } + + /* Set default values */ + parser->set_default((nmea_parser_s *) parser); + parser->errors = 0; + + /* Loop through the values and parse them... */ + for (val_index = 0; val_index < n_vals; val_index++) { + value = values[val_index]; + if (-1 == _is_value_set(value)) { + continue; + } + + if (-1 == parser->parse((nmea_parser_s *) parser, value, val_index)) { + parser->errors++; + ESP_LOGD(NMEA_TAG, "Parser error at index %d",val_index); + } + } + + parser->parser.data->type = type; + parser->parser.data->errors = parser->errors; + + return parser->parser.data; +} diff --git a/MicroPython_BUILD/components/libnmea/src/nmea/nmea.h b/MicroPython_BUILD/components/libnmea/src/nmea/nmea.h new file mode 100644 index 00000000..4995d220 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/nmea/nmea.h @@ -0,0 +1,127 @@ +#ifndef INC_NMEA_H +#define INC_NMEA_H + +#include +#include +#include + +/* NMEA sentence types */ +typedef enum { + NMEA_UNKNOWN, + NMEA_GGA, + NMEA_GLL, + NMEA_RMC, + NMEA_GST, + NMEA_VTG +} nmea_t; + +/* NMEA cardinal direction types */ +typedef char nmea_cardinal_t; +#define NMEA_CARDINAL_DIR_NORTH (nmea_cardinal_t) 'N' +#define NMEA_CARDINAL_DIR_EAST (nmea_cardinal_t) 'E' +#define NMEA_CARDINAL_DIR_SOUTH (nmea_cardinal_t) 'S' +#define NMEA_CARDINAL_DIR_WEST (nmea_cardinal_t) 'W' +#define NMEA_CARDINAL_DIR_UNKNOWN (nmea_cardinal_t) '\0' + +extern const char *NMEA_TAG; + +/** + * NMEA data base struct + * + * This struct will be extended by the parser data structs (ex: nmea_gpgll_s). + */ +typedef struct { + nmea_t type; + int errors; +} nmea_s; + +/* GPS position struct */ +typedef struct { + double minutes; + int degrees; + nmea_cardinal_t cardinal; +} nmea_position; + +/* NMEA sentence max length, including \r\n (chars) */ +#define NMEA_MAX_LENGTH 83 + +/* NMEA sentence endings, should be \r\n according the NMEA 0183 standard */ +#define NMEA_END_CHAR_1 '\r' +#define NMEA_END_CHAR_2 '\n' + +/* NMEA sentence prefix length (num chars), Ex: GPGLL */ +#define NMEA_PREFIX_LENGTH 3 +#define NMEA_IDS_LENGTH 6 +#define NMEA_ID_LENGTH 2 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get the sentence type. + * + * sentence needs to be a validated NMEA sentence string. + * + * Returns nmea_t (int). + */ +extern nmea_t nmea_get_type(const char *sentence); + +/** + * Calculate the checksum of the sentence. + * + * sentence needs to be a validated NMEA sentence string. + * + * Returns the calculated checksum (uint8_t). + */ +extern uint8_t nmea_get_checksum(const char *sentence); + +/** + * Check if the sentence contains a precalculated checksum. + * + * sentence needs to be a validated NMEA sentence string. + * length is the character length of the sentence string. + * + * Return 0 if checksum exists, otherwise -1. + */ +extern int nmea_has_checksum(const char *sentence, size_t length); + +/** + * Validate the sentence according to NMEA 0183. + * + * Criterias: + * - Should be between the correct length. + * - Should start with a dollar sign. + * - The next five characters should be uppercase letters. + * - If it has a checksum, check it. + * - Ends with the correct 2 characters. + * + * length is the character length of the sentence string. + * + * Returns 0 if sentence is valid, otherwise error code (< 0). + */ +extern int nmea_validate(const char *sentence, size_t length, int check_checksum); + +/** + * Free an nmea data struct. + * + * data should be a pointer to a struct of type nmea_s. + */ +extern void nmea_free(nmea_s *data); + +/** + * Parse an NMEA sentence string to a struct. + * + * sentence needs to be a validated NMEA sentence string. + * length is the character length of the sentence string. + * check_checksum, if 1 and there is a checksum, validate it. + * + * Returns a pointer to an NMEA data struct, or (nmea_s *) NULL if an error occurs. + */ +extern nmea_s *nmea_parse(char *sentence, size_t length, int check_checksum); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_NMEA_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/nmea/parser.h b/MicroPython_BUILD/components/libnmea/src/nmea/parser.h new file mode 100644 index 00000000..0b42c096 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/nmea/parser.h @@ -0,0 +1,68 @@ +#ifndef INC_NMEA_PARSER_H +#define INC_NMEA_PARSER_H + +#include +#include +#include "nmea.h" +#include "parser_types.h" + +typedef int (*allocate_data_f) (nmea_parser_s *); +typedef int (*set_default_f) (nmea_parser_s *); +typedef int (*free_data_f) (nmea_s *); +typedef int (*parse_f) (nmea_parser_s *, char *, int); +typedef int (*init_f) (nmea_parser_s *); + +typedef struct { + nmea_parser_s parser; + int errors; + void *handle; + + /* Functions */ + allocate_data_f allocate_data; + set_default_f set_default; + free_data_f free_data; + parse_f parse; +} nmea_parser_module_s; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Load the parser libs into array. + * + * Returns 0 on success, or -1 if an error occurs. + */ +int nmea_load_parsers(); + +/** + * Unload all the parser libs. + */ +void nmea_unload_parsers(); + +/** + * Initiate a parser. + * + * Returns a sentence parser struct, or (nmea_parser_module_s *) NULL if an error occurs. + */ +nmea_parser_module_s * nmea_init_parser(const char *filename); + +/** + * Get a parser for a sentence type. + * + * Returns the sentence parser struct, should be checked for NULL. + */ +nmea_parser_module_s * nmea_get_parser_by_type(nmea_t type); + +/** + * Get a parser for a sentence type by a sentence string. + * + * Returns the sentence parser struct, should be checked for NULL. + */ +nmea_parser_module_s * nmea_get_parser_by_sentence(const char *sentence); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_NMEA_PARSER_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/nmea/parser_static.c b/MicroPython_BUILD/components/libnmea/src/nmea/parser_static.c new file mode 100644 index 00000000..686ba2e4 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/nmea/parser_static.c @@ -0,0 +1,93 @@ +#include "nmea.h" +#include "parser.h" + +#define PARSER_COUNT 5 + +#define DECLARE_PARSER_API(modname) \ + extern int nmea_##modname##_init(nmea_parser_s *parser); \ + extern int nmea_##modname##_allocate_data(nmea_parser_s *parser); \ + extern int nmea_##modname##_set_default(nmea_parser_s *parser); \ + extern int nmea_##modname##_free_data(nmea_s *data); \ + extern int nmea_##modname##_parse(nmea_parser_s *parser, char *value, int val_index); + +#define PARSER_LOAD(modname) \ + parser = &(parsers[i]); \ + parser->handle = NULL; \ + parser->allocate_data = nmea_##modname##_allocate_data; \ + parser->set_default = nmea_##modname##_set_default; \ + parser->free_data = nmea_##modname##_free_data; \ + parser->parse = nmea_##modname##_parse; \ + if (-1 == nmea_##modname##_init((nmea_parser_s *) parser)) { \ + return -1; \ + } \ + i++; + +DECLARE_PARSER_API(gpgll) +DECLARE_PARSER_API(gpgga) +DECLARE_PARSER_API(gprmc) +DECLARE_PARSER_API(gpgst) +DECLARE_PARSER_API(gpvtg) + +nmea_parser_module_s parsers[PARSER_COUNT]; + +//---------------------------------------------------------- +nmea_parser_module_s *nmea_init_parser(const char *filename) +{ + /* This function intentionally returns NULL */ + return NULL; +} + +//--------------------- +int nmea_load_parsers() +{ + int i = 0; + nmea_parser_module_s *parser; + + PARSER_LOAD(gpgll); + PARSER_LOAD(gpgga); + PARSER_LOAD(gprmc); + PARSER_LOAD(gpgst); + PARSER_LOAD(gpvtg); + + return PARSER_COUNT; +} + +//------------------------ +void nmea_unload_parsers() +{ + /* This function body is intentionally left empty, + because there is no dynamic memory allocations. */ +} + +//-------------------------------------------------------- +nmea_parser_module_s *nmea_get_parser_by_type(nmea_t type) +{ + int i; + + for (i = 0; i < PARSER_COUNT; i++) { + if (type == parsers[i].parser.type) { + return &(parsers[i]); + } + } + + return (nmea_parser_module_s *) NULL; +} + +//--------------------------------------------------------------------- +nmea_parser_module_s *nmea_get_parser_by_sentence(const char *sentence) +{ + int i; + + char type_prefix[NMEA_ID_LENGTH+1] = {'\0'}; + memcpy(type_prefix, sentence+1, NMEA_ID_LENGTH); + for (i = 0; i < PARSER_COUNT; i++) { + if (strstr(parsers[i].parser.type_prefixes, type_prefix) == NULL) { + continue; + } + if (0 == strncmp(sentence + NMEA_ID_LENGTH + 1, parsers[i].parser.type_word, NMEA_PREFIX_LENGTH)) { + return &(parsers[i]); + } + } + + return (nmea_parser_module_s *) NULL; +} diff --git a/MicroPython_BUILD/components/libnmea/src/nmea/parser_types.h b/MicroPython_BUILD/components/libnmea/src/nmea/parser_types.h new file mode 100644 index 00000000..19196b0b --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/nmea/parser_types.h @@ -0,0 +1,17 @@ +#ifndef INC_NMEA_PARSER_TYPES_H +#define INC_NMEA_PARSER_TYPES_H + +#include "nmea.h" + +typedef struct { + nmea_t type; + char type_word[4]; + char type_prefixes[7]; + nmea_s *data; +} nmea_parser_s; + +#define NMEA_PARSER_PREFIX(parser, type_prefix) strncpy(parser->type_word, type_prefix, NMEA_PREFIX_LENGTH) +#define NMEA_PARSER_IDS(parser, type_ids) strncpy(parser->type_prefixes, type_ids, NMEA_IDS_LENGTH) +#define NMEA_PARSER_TYPE(parser, nmea_type) parser->type = nmea_type + +#endif /* INC_NMEA_PARSER_TYPES_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpgga.c b/MicroPython_BUILD/components/libnmea/src/parsers/gpgga.c new file mode 100644 index 00000000..192c5525 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpgga.c @@ -0,0 +1,148 @@ +/* ----------------------------- GGA Data Struct ------------------------------ */ + +//GGA - essential fix data which provide 3D location and accuracy data. +// +// $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 +// +//Where: +// GGA Global Positioning System Fix Data +// 123519 Fix taken at 12:35:19 UTC +// 4807.038,N Latitude 48 deg 07.038' N +// 01131.000,E Longitude 11 deg 31.000' E +// 1 Fix quality: 0 = invalid +// 1 = GPS fix (SPS) +// 2 = DGPS fix +// 3 = PPS fix +// 4 = Real Time Kinematic +// 5 = Float RTK +// 6 = estimated (dead reckoning) (2.3 feature) +// 7 = Manual input mode +// 8 = Simulation mode +// 08 Number of satellites being tracked +// 0.9 Horizontal dilution of position +// 545.4,M Altitude, Meters, above mean sea level +// 46.9,M Height of geoid (mean sea level) above WGS84 +// ellipsoid +// (empty field) time in seconds since last DGPS update +// (empty field) DGPS station ID number +// *47 the checksum data, always begins with * +// +// If the height of geoid is missing then the altitude should be suspect. Some non-standard +// implementations report altitude with respect to the ellipsoid rather than geoid altitude. +// Some units do not report negative altitudes at alUART driver interface. This is the only +// sentence that reports altitude. + +#include "../nmea/parser_types.h" +#include "gpgga.h" +#include "parse.h" + +//----------------------------- +int init(nmea_parser_s *parser) +{ + /* Declare what sentence type to parse */ + NMEA_PARSER_TYPE(parser, NMEA_GGA); + NMEA_PARSER_PREFIX(parser, "GGA"); + NMEA_PARSER_IDS(parser, "GPGNGL"); + return 0; +} + +//-------------------------------------- +int allocate_data(nmea_parser_s *parser) +{ + parser->data = malloc(sizeof (nmea_gpgga_s)); + if (NULL == parser->data) { + return -1; + } + + return 0; +} + +//------------------------------------ +int set_default(nmea_parser_s *parser) +{ + memset(parser->data, 0, sizeof (nmea_gpgga_s)); + return 0; +} + +//------------------------- +int free_data(nmea_s *data) +{ + free(data); + return 0; +} + +// Parse Global Positioning System Fix Data sentence +//---------------------------------------------------------- +int parse(nmea_parser_s *parser, char *value, int val_index) +{ + nmea_gpgga_s *data = (nmea_gpgga_s *) parser->data; + + switch (val_index) { + case NMEA_GPGGA_TIME: + /* Parse time */ + if (-1 == nmea_time_parse(value, &data->time)) { + return -1; + } + break; + + case NMEA_GPGGA_LATITUDE: + /* Parse latitude */ + if (-1 == nmea_position_parse(value, &data->latitude)) { + return -1; + } + break; + + case NMEA_GPGGA_LATITUDE_CARDINAL: + /* Parse cardinal direction */ + data->latitude.cardinal = nmea_cardinal_direction_parse(value); + if (NMEA_CARDINAL_DIR_UNKNOWN == data->latitude.cardinal) { + return -1; + } + break; + + case NMEA_GPGGA_LONGITUDE: + /* Parse longitude */ + if (-1 == nmea_position_parse(value, &data->longitude)) { + return -1; + } + break; + + case NMEA_GPGGA_LONGITUDE_CARDINAL: + /* Parse cardinal direction */ + data->longitude.cardinal = nmea_cardinal_direction_parse(value); + if (NMEA_CARDINAL_DIR_UNKNOWN == data->longitude.cardinal) { + return -1; + } + break; + + case NMEA_GPGGA_N_SATELLITES: + /* Parse number of satellies */ + data->n_satellites = atoi(value); + break; + + case NMEA_GPGGA_ALTITUDE: + /* Parse altitude */ + data->altitude = strtof(value, NULL); + break; + + case NMEA_GPGGA_ALTITUDE_UNIT: + /* Parse altitude unit */ + data->altitude_unit = *value; + break; + + case NMEA_GPGGA_QUALITY: + /* GPS Quality Indicator */ + data->quality = (int)strtol(value, NULL, 0); + break; + + case NMEA_GPGGA_DOP: + /* Horizontal Dilution of precision */ + data->dop = strtof(value, NULL); + break; + + default: + break; + } + + return 0; +} diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpgga.h b/MicroPython_BUILD/components/libnmea/src/parsers/gpgga.h new file mode 100644 index 00000000..ef9d8fbf --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpgga.h @@ -0,0 +1,33 @@ +#ifndef INC_NMEA_GPGGA_H +#define INC_NMEA_GPGGA_H + +#include +#include +#include +#include + +typedef struct { + nmea_s base; + struct tm time; + nmea_position longitude; + nmea_position latitude; + int n_satellites; + float altitude; + char altitude_unit; + int quality; + float dop; +} nmea_gpgga_s; + +/* Value indexes */ +#define NMEA_GPGGA_TIME 0 +#define NMEA_GPGGA_LATITUDE 1 +#define NMEA_GPGGA_LATITUDE_CARDINAL 2 +#define NMEA_GPGGA_LONGITUDE 3 +#define NMEA_GPGGA_LONGITUDE_CARDINAL 4 +#define NMEA_GPGGA_QUALITY 5 +#define NMEA_GPGGA_N_SATELLITES 6 +#define NMEA_GPGGA_DOP 7 +#define NMEA_GPGGA_ALTITUDE 8 +#define NMEA_GPGGA_ALTITUDE_UNIT 9 + +#endif /* INC_NMEA_GPGGA_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpgll.c b/MicroPython_BUILD/components/libnmea/src/parsers/gpgll.c new file mode 100644 index 00000000..55177311 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpgll.c @@ -0,0 +1,94 @@ +#include "../nmea/parser_types.h" +#include "gpgll.h" +#include "parse.h" + +//----------------------------- +int init(nmea_parser_s *parser) +{ + /* Declare what sentence type to parse */ + NMEA_PARSER_TYPE(parser, NMEA_GLL); + NMEA_PARSER_PREFIX(parser, "GLL"); + NMEA_PARSER_IDS(parser, "GPGNGL"); + return 0; +} + +//-------------------------------------- +int allocate_data(nmea_parser_s *parser) +{ + parser->data = malloc(sizeof (nmea_gpgll_s)); + if (NULL == parser->data) { + return -1; + } + + return 0; +} + +//------------------------------------ +int set_default(nmea_parser_s *parser) +{ + memset(parser->data, 0, sizeof (nmea_gpgll_s)); + return 0; +} + +//------------------------- +int free_data(nmea_s *data) +{ + free(data); + return 0; +} + +// Parse Geographic Position – Latitude/Longitude sentence +//---------------------------------------------------------- +int parse(nmea_parser_s *parser, char *value, int val_index) +{ + nmea_gpgll_s *data = (nmea_gpgll_s *) parser->data; + + switch (val_index) { + case NMEA_GPGLL_TIME: + /* Parse time */ + if (-1 == nmea_time_parse(value, &data->time)) { + return -1; + } + break; + + case NMEA_GPGLL_LATITUDE: + /* Parse latitude */ + if (-1 == nmea_position_parse(value, &data->latitude)) { + return -1; + } + break; + + case NMEA_GPGLL_LATITUDE_CARDINAL: + /* Parse cardinal direction */ + data->latitude.cardinal = nmea_cardinal_direction_parse(value); + if (NMEA_CARDINAL_DIR_UNKNOWN == data->latitude.cardinal) { + return -1; + } + break; + + case NMEA_GPGLL_LONGITUDE: + /* Parse longitude */ + if (-1 == nmea_position_parse(value, &data->longitude)) { + return -1; + } + break; + + case NMEA_GPGLL_LONGITUDE_CARDINAL: + /* Parse cardinal direction */ + data->longitude.cardinal = nmea_cardinal_direction_parse(value); + if (NMEA_CARDINAL_DIR_UNKNOWN == data->longitude.cardinal) { + return -1; + } + break; + + case NMEA_GPGLL_VALID: + /* Status A - Data Valid, V - Data Invalid */ + data->valid = (strcmp(value, "A") == 0); + break; + + default: + break; + } + + return 0; +} diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpgll.h b/MicroPython_BUILD/components/libnmea/src/parsers/gpgll.h new file mode 100644 index 00000000..fd484683 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpgll.h @@ -0,0 +1,25 @@ +#ifndef INC_NMEA_GPGLL_H +#define INC_NMEA_GPGLL_H + +#include +#include +#include +#include + +typedef struct { + nmea_s base; + nmea_position longitude; + nmea_position latitude; + struct tm time; + uint8_t valid; +} nmea_gpgll_s; + +/* Value indexes */ +#define NMEA_GPGLL_LATITUDE 0 +#define NMEA_GPGLL_LATITUDE_CARDINAL 1 +#define NMEA_GPGLL_LONGITUDE 2 +#define NMEA_GPGLL_LONGITUDE_CARDINAL 3 +#define NMEA_GPGLL_TIME 4 +#define NMEA_GPGLL_VALID 5 + +#endif /* INC_NMEA_GPGLL_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpgst.c b/MicroPython_BUILD/components/libnmea/src/parsers/gpgst.c new file mode 100644 index 00000000..5c3e5caf --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpgst.c @@ -0,0 +1,94 @@ +#include "../nmea/parser_types.h" +#include "gpgst.h" +#include "parse.h" + +//----------------------------- +int init(nmea_parser_s *parser) +{ + /* Declare what sentence type to parse */ + NMEA_PARSER_TYPE(parser, NMEA_GST); + NMEA_PARSER_PREFIX(parser, "GST"); + NMEA_PARSER_IDS(parser, "GPGNGL"); + return 0; +} + +//-------------------------------------- +int allocate_data(nmea_parser_s *parser) +{ + parser->data = malloc(sizeof (nmea_gpgst_s)); + if (NULL == parser->data) { + return -1; + } + + return 0; +} + +//------------------------------------ +int set_default(nmea_parser_s *parser) +{ + memset(parser->data, 0, sizeof (nmea_gpgst_s)); + return 0; +} + +//------------------------- +int free_data(nmea_s *data) +{ + free(data); + return 0; +} + +// Parse Global Positioning System Fix Data sentence +//---------------------------------------------------------- +int parse(nmea_parser_s *parser, char *value, int val_index) +{ + nmea_gpgst_s *data = (nmea_gpgst_s *) parser->data; + + switch (val_index) { + case NMEA_GPGST_TIME: + /* Parse time */ + if (-1 == nmea_time_parse(value, &data->time)) { + return -1; + } + break; + + case NMEA_GPGST_RMSSD: + /* Parse RMS value of the standard deviation of the range inputs */ + data->rmssd = strtof(value, NULL); + break; + + case NMEA_GPGST_SDMAJ: + /* Parse Standard deviation of semi-major axis of error ellipse */ + data->sdmaj = strtof(value, NULL); + break; + + case NMEA_GPGST_SDMIN: + /* Parse Standard deviation of semi-minor axis of error ellipse */ + data->sdmin = strtof(value, NULL); + break; + + case NMEA_GPGST_ORI: + /* Parse Orientation of semi-major axis of error ellipse */ + data->ori = strtof(value, NULL); + break; + + case NMEA_GPGST_LATSD: + /* Parse Standard deviation of latitude error, in meters */ + data->latsd = strtof(value, NULL); + break; + + case NMEA_GPGST_LONSD: + /* Parse Standard deviation of longitude error, in meters */ + data->lonsd = strtof(value, NULL); + break; + + case NMEA_GPGST_ALTSD: + /* Parse Standard deviation of altitude error, in meters */ + data->altsd = strtof(value, NULL); + break; + + default: + break; + } + + return 0; +} diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpgst.h b/MicroPython_BUILD/components/libnmea/src/parsers/gpgst.h new file mode 100644 index 00000000..880be8c2 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpgst.h @@ -0,0 +1,31 @@ +#ifndef INC_NMEA_GPGST_H +#define INC_NMEA_GPGST_H + +#include +#include +#include +#include + +typedef struct { + nmea_s base; + struct tm time; + float rmssd; + float sdmaj; + float sdmin; + float ori; + float latsd; + float lonsd; + float altsd; +} nmea_gpgst_s; + +/* Value indexes */ +#define NMEA_GPGST_TIME 0 +#define NMEA_GPGST_RMSSD 1 +#define NMEA_GPGST_SDMAJ 2 +#define NMEA_GPGST_SDMIN 3 +#define NMEA_GPGST_ORI 4 +#define NMEA_GPGST_LATSD 5 +#define NMEA_GPGST_LONSD 6 +#define NMEA_GPGST_ALTSD 7 + +#endif /* INC_NMEA_GPGGA_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gprmc.c b/MicroPython_BUILD/components/libnmea/src/parsers/gprmc.c new file mode 100644 index 00000000..6add622c --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gprmc.c @@ -0,0 +1,129 @@ +/* ----------------------------- RMC Data Struct ------------------------------ */ + +//RMC - NMEA has its own version of essential gps pvt (position, velocity, time) data. +//It is called RMC, The Recommended Minimum, which will look similar to: +// +//$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A +// +//Where: +// RMC Recommended Minimum sentence C +// 123519 Fix taken at 12:35:19 UTC +// A Status A=active or V=Void. +// 4807.038,N Latitude 48 deg 07.038' N +// 01131.000,E Longitude 11 deg 31.000' E +// 022.4 Speed over the ground in knots +// 084.4 Track angle in degrees True +// 230394 Date - 23rd of March 1994 +// 003.1,W Magnetic Variation +// *6A The checksum data, always begins with * + +#include "../nmea/parser_types.h" +#include "gprmc.h" +#include "parse.h" + +//----------------------------- +int init(nmea_parser_s *parser) +{ + /* Declare what sentence type to parse */ + NMEA_PARSER_TYPE(parser, NMEA_RMC); + NMEA_PARSER_PREFIX(parser, "RMC"); + NMEA_PARSER_IDS(parser, "GPGNGL"); + return 0; +} + +//-------------------------------------- +int allocate_data(nmea_parser_s *parser) +{ + parser->data = malloc(sizeof (nmea_gprmc_s)); + if (NULL == parser->data) { + return -1; + } + + return 0; +} + +//------------------------------------ +int set_default(nmea_parser_s *parser) +{ + memset(parser->data, 0, sizeof (nmea_gprmc_s)); + return 0; +} + +//------------------------- +int free_data(nmea_s *data) +{ + free(data); + return 0; +} + +// Parse Recommended Minimum Navigation Information sentence +//---------------------------------------------------------- +int parse(nmea_parser_s *parser, char *value, int val_index) +{ + nmea_gprmc_s *data = (nmea_gprmc_s *) parser->data; + switch (val_index) { + case NMEA_GPRMC_TIME: + /* Parse time */ + if (-1 == nmea_time_parse(value, &data->time)) { + return -1; + } + break; + + case NMEA_GPRMC_LATITUDE: + /* Parse latitude */ + if (-1 == nmea_position_parse(value, &data->latitude)) { + return -1; + } + break; + + case NMEA_GPRMC_LATITUDE_CARDINAL: + /* Parse cardinal direction */ + data->latitude.cardinal = nmea_cardinal_direction_parse(value); + if (NMEA_CARDINAL_DIR_UNKNOWN == data->latitude.cardinal) { + return -1; + } + break; + + case NMEA_GPRMC_LONGITUDE: + /* Parse longitude */ + if (-1 == nmea_position_parse(value, &data->longitude)) { + return -1; + } + break; + + case NMEA_GPRMC_LONGITUDE_CARDINAL: + /* Parse cardinal direction */ + data->longitude.cardinal = nmea_cardinal_direction_parse(value); + if (NMEA_CARDINAL_DIR_UNKNOWN == data->longitude.cardinal) { + return -1; + } + break; + + case NMEA_GPRMC_DATE: + /* Parse date */ + if (-1 == nmea_date_parse(value, &data->time)) { + return -1; + } + break; + + case NMEA_GPRMC_VALID: + /* Status, V = Navigation receiver warning */ + data->valid = (strcmp(value, "A") == 0); + break; + + case NMEA_GPRMC_SPEED: + /* Speed over ground, knots */ + data->speed = strtof(value, NULL); + break; + + case NMEA_GPRMC_COURSE: + /* Track made good, degrees true */ + data->course = strtof(value, NULL); + break; + + default: + break; + } + + return 0; +} diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gprmc.h b/MicroPython_BUILD/components/libnmea/src/parsers/gprmc.h new file mode 100644 index 00000000..e4804728 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gprmc.h @@ -0,0 +1,30 @@ +#ifndef INC_NMEA_GPRMC_H +#define INC_NMEA_GPRMC_H + +#include +#include +#include +#include + +typedef struct { + nmea_s base; + nmea_position longitude; + nmea_position latitude; + struct tm time; + float speed; + float course; + uint8_t valid; +} nmea_gprmc_s; + +/* Value indexes */ +#define NMEA_GPRMC_TIME 0 +#define NMEA_GPRMC_VALID 1 +#define NMEA_GPRMC_LATITUDE 2 +#define NMEA_GPRMC_LATITUDE_CARDINAL 3 +#define NMEA_GPRMC_LONGITUDE 4 +#define NMEA_GPRMC_LONGITUDE_CARDINAL 5 +#define NMEA_GPRMC_SPEED 6 +#define NMEA_GPRMC_COURSE 7 +#define NMEA_GPRMC_DATE 8 + +#endif /* INC_NMEA_GPRMC_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpvtg.c b/MicroPython_BUILD/components/libnmea/src/parsers/gpvtg.c new file mode 100644 index 00000000..4202fe16 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpvtg.c @@ -0,0 +1,100 @@ +/* ----------------------------- VTG Data Struct ------------------------------ */ + +/* + Track Made Good and Ground Speed. + + eg1. $GPVTG,360.0,T,348.7,M,000.0,N,000.0,K*43 + eg2. $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K + + + 054.7,T True track made good + 034.4,M Magnetic track made good + 005.5,N Ground speed, knots + 010.2,K Ground speed, Kilometers per hour + + + eg3. $GPVTG,t,T,,,s.ss,N,s.ss,K*hh + 1 = Track made good + 2 = Fixed text 'T' indicates that track made good is relative to true north + 3 = not used + 4 = not used + 5 = Speed over ground in knots + 6 = Fixed text 'N' indicates that speed over ground in in knots + 7 = Speed over ground in kilometers/hour + 8 = Fixed text 'K' indicates that speed over ground is in kilometers/hour + 9 = Checksum + The actual track made good and speed relative to the ground. + + $--VTG,x.x,T,x.x,M,x.x,N,x.x,K + x.x,T = Track, degrees True + x.x,M = Track, degrees Magnetic + x.x,N = Speed, knots + x.x,K = Speed, Km/hr + */ + +#include "../nmea/parser_types.h" +#include "gpvtg.h" +#include "parse.h" + +//----------------------------- +int init(nmea_parser_s *parser) +{ + /* Declare what sentence type to parse */ + NMEA_PARSER_TYPE(parser, NMEA_VTG); + NMEA_PARSER_PREFIX(parser, "VTG"); + NMEA_PARSER_IDS(parser, "GPGNGL"); + return 0; +} + +//-------------------------------------- +int allocate_data(nmea_parser_s *parser) +{ + parser->data = malloc(sizeof (nmea_gpvtg_s)); + if (NULL == parser->data) { + return -1; + } + + return 0; +} + +//------------------------------------ +int set_default(nmea_parser_s *parser) +{ + memset(parser->data, 0, sizeof (nmea_gpvtg_s)); + return 0; +} + +//------------------------- +int free_data(nmea_s *data) +{ + free(data); + return 0; +} + +// Parse Recommended Minimum Navigation Information sentence +//---------------------------------------------------------- +int parse(nmea_parser_s *parser, char *value, int val_index) +{ + nmea_gpvtg_s *data = (nmea_gpvtg_s *) parser->data; + switch (val_index) { + case NMEA_GPVTG_COURSE: + /* Track made good */ + data->course = strtof(value, NULL); + break; + + case NMEA_GPVTG_SPEED_KNOTS: + /* Speed over ground in knots */ + data->speed_kn = strtof(value, NULL); + break; + + case NMEA_GPVTG_SPEED_KMH: + /* Speed over ground in km/h */ + data->speed_kmh = strtof(value, NULL); + break; + + default: + break; + } + + return 0; +} diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/gpvtg.h b/MicroPython_BUILD/components/libnmea/src/parsers/gpvtg.h new file mode 100644 index 00000000..ebf99db1 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/gpvtg.h @@ -0,0 +1,21 @@ +#ifndef INC_NMEA_GPVTG_H +#define INC_NMEA_GPVTG_H + +#include +#include +#include +#include + +typedef struct { + nmea_s base; + float course; + float speed_kn; + float speed_kmh; +} nmea_gpvtg_s; + +/* Value indexes */ +#define NMEA_GPVTG_COURSE 0 +#define NMEA_GPVTG_SPEED_KNOTS 4 +#define NMEA_GPVTG_SPEED_KMH 6 + +#endif /* INC_NMEA_GPVTG_H */ diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/parse.c b/MicroPython_BUILD/components/libnmea/src/parsers/parse.c new file mode 100644 index 00000000..0b119160 --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/parse.c @@ -0,0 +1,96 @@ +#include "parse.h" +#include + +//-------------------------------------------------- +int nmea_position_parse(char *s, nmea_position *pos) +{ + char *cursor; + + pos->degrees = 0; + pos->minutes = 0; + + if (s == NULL || *s == '\0') { + return -1; + } + + /* decimal minutes */ + if (NULL == (cursor = strchr(s, '.'))) { + return -1; + } + + /* minutes starts 2 digits before dot */ + cursor -= 2; + pos->minutes = atof(cursor); + *cursor = '\0'; + + /* integer degrees */ + cursor = s; + pos->degrees = atoi(cursor); + + return 0; +} + +//---------------------------------------------------- +nmea_cardinal_t nmea_cardinal_direction_parse(char *s) +{ + if (NULL == s || '\0'== *s) { + return NMEA_CARDINAL_DIR_UNKNOWN; + } + + switch (*s) { + case NMEA_CARDINAL_DIR_NORTH: + return NMEA_CARDINAL_DIR_NORTH; + case NMEA_CARDINAL_DIR_EAST: + return NMEA_CARDINAL_DIR_EAST; + case NMEA_CARDINAL_DIR_SOUTH: + return NMEA_CARDINAL_DIR_SOUTH; + case NMEA_CARDINAL_DIR_WEST: + return NMEA_CARDINAL_DIR_WEST; + default: + break; + } + + return NMEA_CARDINAL_DIR_UNKNOWN; +} + +//------------------------------------------- +int nmea_time_parse(char *s, struct tm *time) +{ + char *rv; + + memset(time, 0, sizeof (struct tm)); + + if (s == NULL || *s == '\0') { + return -1; + } + + char ss[NMEA_TIME_FORMAT_LEN+3]; + sprintf(ss, "%.2s:%.2s:%.2s", s, s+2, s+4); + rv = strptime(ss, "%H:%M:%S", time); + if (NULL == rv || (int) (rv - ss) != NMEA_TIME_FORMAT_LEN+2) { + return -1; + } + return 0; +} + +//------------------------------------------- +int nmea_date_parse(char *s, struct tm *time) +{ + char *rv; + + // Assume it has been already cleared + // memset(time, 0, sizeof (struct tm)); + + if (s == NULL || *s == '\0') { + return -1; + } + + char ss[NMEA_DATE_FORMAT_LEN+3]; + sprintf(ss, "%.2s/%.2s/%.2s", s, s+2, s+4); + rv = strptime(ss, "%d/%m/%y", time); + if (NULL == rv || (int) (rv - ss) != NMEA_DATE_FORMAT_LEN+2) { + return -1; + } + + return 0; +} diff --git a/MicroPython_BUILD/components/libnmea/src/parsers/parse.h b/MicroPython_BUILD/components/libnmea/src/parsers/parse.h new file mode 100644 index 00000000..f36037cb --- /dev/null +++ b/MicroPython_BUILD/components/libnmea/src/parsers/parse.h @@ -0,0 +1,65 @@ +#ifndef INC_NMEA_PARSE_H +#define INC_NMEA_PARSE_H + +#define _XOPEN_SOURCE /* glibc2 needs this */ +#include +#include +#include +#include "../nmea/nmea.h" + +#define NMEA_TIME_FORMAT "%H%M%S" +#define NMEA_TIME_FORMAT_LEN 6 + +#define NMEA_DATE_FORMAT "%d%m%y" +#define NMEA_DATE_FORMAT_LEN 6 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Parse GPS position longitude or latitude + * + * s string containing the position. Ex: "4712.55", 47 degrees and + * 12.55 minutes. Will be modified. + * pos is a pointer to a nmea_position struct where the result should be stored. + * + * Returns 0 on success, otherwise -1. + */ +int nmea_position_parse(char *s, nmea_position *pos); + +/** + * Parse cardinal direction + * + * s is a string containing the letter representing the cardinal direction. + * + * Returns the cardinal direction (nmea_cardinal_t). On failure, + * NMEA_CARDINAL_DIR_UNKNOWN is returned. + */ +nmea_cardinal_t nmea_cardinal_direction_parse(char *s); + +/** + * Parse time from a string + * + * s is a string containing the time in format "HHMMSS". + * time is a pointer to a tm struct where the parser time will be stored. + * + * Returns 0 on success, otherwise -1. + */ +int nmea_time_parse(char *s, struct tm *time); + +/** + * Parse date from a string + * + * s is a string containing the time in format "DDMMYY". + * time is a pointer to a tm struct where the parser date will be stored. + * + * Returns 0 on success, otherwise -1. + */ +int nmea_date_parse(char *s, struct tm *time); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_NMEA_PARSE_H */ diff --git a/MicroPython_BUILD/components/micropython/Kconfig.projbuild b/MicroPython_BUILD/components/micropython/Kconfig.projbuild index 072be2b4..e74a1c60 100644 --- a/MicroPython_BUILD/components/micropython/Kconfig.projbuild +++ b/MicroPython_BUILD/components/micropython/Kconfig.projbuild @@ -100,6 +100,14 @@ menu "MicroPython" bool "Verbose" endchoice + config MICROPY_USE_UNICODE + bool "Enable UNICODE support in MicroPython" + default y + help + Enable UNICODE support in MicroPython + Running without UNICODE support may solve some issues, + also the string operations may be faster + config MICROPY_USE_THREADED_REPL bool "Start REPL in separate thread" default n @@ -304,7 +312,21 @@ menu "MicroPython" default n help Include CURL module into build - Using CURLmodule will add ~230 KB to your flash code size + Using CURL module will add ~230 KB to your flash code size + + config MICROPY_USE_GPS + bool "Use GPS module" + default y + help + Include GPS module into build + + config MICROPY_GPS_SERVICE_STACK + int "GPS service stack size" + depends on MICROPY_USE_GPS + default 3072 + range 3072 6144 + help + Set the stack size of GPS service task config MICROPY_USE_CURL_TLS bool "Enable TLS in Curl module" @@ -339,39 +361,84 @@ menu "MicroPython" menu "MQTT Configuration" depends on MICROPY_USE_MQTT - config MQTT_PROTOCOL_311 - bool "Use protocol version 3.1.1" - default y - help - Use protocol version 3.1.1 - If not set, protocol version 3.1.0 is used - - config MQTT_PRIORITY - int "MQTT task priority" - default MICROPY_TASK_PRIORITY - range 1 20 - help - FreeRTOS task priority of the Mqtt task - Default is MicroPython task priority - - config MQTT_BUFFER_SIZE_BYTE - int "MQTT send/receive buffer size" - default 256 - range 256 2048 - help - Send/Receive buffer size in bytes - More than buffer size bytes can be received... - Publish payload size is limited to this size. - Keep in mind that 4*CONFIG_MQTT_BUFFER_SIZE_BYTE queue buffer will also be created. - - config MQTT_MAX_PAYLOAD_SIZE - int "MQTT max payload size" - default 2048 - range MQTT_BUFFER_SIZE_BYTE 16384 - help - Maximum payload size which can be received - If the payload size is larger, it will be truncated - + config MQTT_PROTOCOL_311 + bool "Enable MQTT protocol 3.1.1" + default y + help + If not, this library will use MQTT protocol 3.1 + + config MQTT_TRANSPORT_SSL + bool "Enable MQTT over SSL" + default y + help + Enable MQTT transport over SSL with mbedtls + + config MQTT_TRANSPORT_WEBSOCKET + bool "Enable MQTT over Websocket" + default y + help + Enable MQTT transport over Websocket. + + config MQTT_TRANSPORT_WEBSOCKET_SECURE + bool "Enable MQTT over Websocket Secure" + default y + depends on MQTT_TRANSPORT_WEBSOCKET + depends on MQTT_TRANSPORT_SSL + help + Enable MQTT transport over Websocket Secure. + + config MQTT_USE_CUSTOM_CONFIG + bool "MQTT Using custom configurations" + default n + help + Custom MQTT configurations. + + config MQTT_TCP_DEFAULT_PORT + int "Default MQTT over TCP port" + default 1883 + depends on MQTT_USE_CUSTOM_CONFIG + help + Default MQTT over TCP port + + config MQTT_SSL_DEFAULT_PORT + int "Default MQTT over SSL port" + default 8883 + depends on MQTT_USE_CUSTOM_CONFIG + depends on MQTT_TRANSPORT_SSL + help + Default MQTT over SSL port + + config MQTT_WS_DEFAULT_PORT + int "Default MQTT over Websocket port" + default 80 + depends on MQTT_USE_CUSTOM_CONFIG + depends on MQTT_TRANSPORT_WEBSOCKET + help + Default MQTT over Websocket port + + config MQTT_WSS_DEFAULT_PORT + int "Default MQTT over Websocket Secure port" + default 443 + depends on MQTT_USE_CUSTOM_CONFIG + depends on MQTT_TRANSPORT_WEBSOCKET + depends on MQTT_TRANSPORT_WEBSOCKET_SECURE + help + Default MQTT over Websocket Secure port + + config MQTT_BUFFER_SIZE + int "Default MQTT Buffer Size" + default 1024 + depends on MQTT_USE_CUSTOM_CONFIG + help + This buffer size using for both transmit and receive + + config MQTT_TASK_STACK_SIZE + int "MQTT task stack size" + default 6144 + depends on MQTT_USE_CUSTOM_CONFIG + help + MQTT task stack size + config MQTT_LOG_LEVEL int default 0 if MQTT_LOG_LEVEL0 diff --git a/MicroPython_BUILD/components/micropython/component.mk b/MicroPython_BUILD/components/micropython/component.mk index 4ddc605e..cb83152e 100644 --- a/MicroPython_BUILD/components/micropython/component.mk +++ b/MicroPython_BUILD/components/micropython/component.mk @@ -7,7 +7,7 @@ COMPONENT_ADD_INCLUDEDIRS := . genhdr py esp32 lib lib/utils lib/mp-readline extmod extmod/crypto-algorithms lib/netutils drivers/dht \ lib/timeutils lib/berkeley-db-1.xx/include lib/berkeley-db-1.xx/btree \ lib/berkeley-db-1.xx/db lib/berkeley-db-1.xx/hash lib/berkeley-db-1.xx/man lib/berkeley-db-1.xx/mpool lib/berkeley-db-1.xx/recno \ - ../curl/include ../curl/lib ../zlib ../libssh2/include ../espmqtt/include ../littlefs + ../curl/include ../curl/lib ../zlib ../libssh2/include ../espmqtt/include ../espmqtt/lib/include ../littlefs COMPONENT_PRIV_INCLUDEDIRS := . genhdr py esp32 lib @@ -49,6 +49,7 @@ MP_EXTRA_INC += -I$(PROJECT_PATH)/components/curl/include MP_EXTRA_INC += -I$(PROJECT_PATH)/components/libssh2/include MP_EXTRA_INC += -I$(PROJECT_PATH)/components/zlib MP_EXTRA_INC += -I$(PROJECT_PATH)/components/espmqtt/include +MP_EXTRA_INC += -I$(PROJECT_PATH)/components/espmqtt/lib/include MP_EXTRA_INC += -I$(PROJECT_PATH)/components/littlefs MP_EXTRA_INC += -I$(COMPONENT_PATH)/py MP_EXTRA_INC += -I$(COMPONENT_PATH)/lib/mp-readline @@ -106,6 +107,11 @@ MP_EXTRA_INC += -I$(ESPCOMP)/bt/include MP_EXTRA_INC += -I$(ESPCOMP)/bt/bluedroid/api/include endif +ifdef CONFIG_MICROPY_USE_GPS +MP_EXTRA_INC += -I$(PROJECT_PATH)/components/libnmea/src/nmea +MP_EXTRA_INC += -I$(PROJECT_PATH)/components/libnmea/src/parsers +endif + # CPP macro # ------------ CPP = $(CC) -E @@ -177,6 +183,10 @@ ifdef CONFIG_MICROPY_USE_CURL SRC_C += esp32/modcurl.c endif +ifdef CONFIG_MICROPY_USE_GPS +SRC_C += esp32/machine_gps.c +endif + ifdef CONFIG_MICROPY_USE_SSH SRC_C += esp32/modssh.c endif @@ -211,24 +221,6 @@ EXTMOD_SRC_C = $(addprefix extmod/,\ ) LIB_SRC_C = $(addprefix lib/,\ - libm/math.c \ - libm/fmodf.c \ - libm/roundf.c \ - libm/ef_sqrt.c \ - libm/kf_rem_pio2.c \ - libm/kf_sin.c \ - libm/kf_cos.c \ - libm/kf_tan.c \ - libm/ef_rem_pio2.c \ - libm/sf_sin.c \ - libm/sf_cos.c \ - libm/sf_tan.c \ - libm/sf_frexp.c \ - libm/sf_modf.c \ - libm/sf_ldexp.c \ - libm/asinfacosf.c \ - libm/atanf.c \ - libm/atan2f.c \ mp-readline/readline.c \ netutils/netutils.c \ timeutils/timeutils.c \ diff --git a/MicroPython_BUILD/components/micropython/esp32/help.c b/MicroPython_BUILD/components/micropython/esp32/help.c index 3ed1cfd0..e7fe4067 100644 --- a/MicroPython_BUILD/components/micropython/esp32/help.c +++ b/MicroPython_BUILD/components/micropython/esp32/help.c @@ -28,36 +28,24 @@ */ #include "py/builtin.h" -#include "sdkconfig.h" const char esp32_help_text[] = -#if CONFIG_SPIRAM_SUPPORT -"Welcome to LoBo MicroPython on the ESP32 with psRAM!\n" -#else -"Welcome to LoBo MicroPython on the ESP32!\n" -#endif +"Welcome to LoBo MicroPython for the ESP32\n" "\n" -"For online documentation please visit:\nhttps://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki\n" +"For online documentation please visit the Wiki pages:\n" +"https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki\n" "\n" -"For access to the hardware use the 'machine' module:\n" +"Based on official MicroPython, this port brings many new features:\n" "\n" -"import machine\n" -"pin12 = machine.Pin(12, machine.Pin.OUT)\n" -"pin12.value(1)\n" -"pin13 = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)\n" -"print(pin13.value())\n" -"i2c = machine.I2C(scl=machine.Pin(21), sda=machine.Pin(22))\n" -"i2c.scan()\n" -"i2c.writeto(addr, b'1234')\n" -"i2c.readfrom(addr, 4)\n" -"\n" -"Basic WiFi configuration:\n" -"\n" -"import network\n" -"sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" -"sta_if.scan() # Scan for available access points\n" -"sta_if.connect(\"\", \"\") # Connect to an AP\n" -"sta_if.isconnected() # Check for successful connection\n" +" - support for two cores and 4MB SPIRAM (psRAM)\n" +" - improved 'network' module\n" +" - greatly improved thread support\n" +" - support for 3 different internal file systems on top of ESP32 VFS\n" +" - ESP32 native support for SD Card\n" +" - built-in FTP & Telnet servers\n" +" - support for OTA updates\n" +" - many new and improved hardware access modules implemented in C\n" +" and many more...\n" "\n" "Control commands:\n" " CTRL-A -- on a blank line, enter raw REPL mode\n" diff --git a/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.c b/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.c index 46aac88f..09e1cb3c 100644 --- a/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.c +++ b/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.c @@ -46,9 +46,11 @@ #include "esp_wifi_types.h" #include "tcpip_adapter.h" +#include "modmachine.h" #include "py/mpthread.h" #include "py/nlr.h" + //-------------------- void checkConnection() { @@ -65,6 +67,7 @@ void checkConnection() } } + #ifdef CONFIG_MICROPY_USE_CURL const char *CURL_TAG = "[Curl]"; @@ -77,14 +80,6 @@ uint32_t curl_maxbytes = 300000; // limit download length uint8_t curl_initialized = 0; uint8_t curl_nodecode = 0; // if set to 1, do not use compression in http transfers -#if CONFIG_SPIRAM_SUPPORT -int hdr_maxlen = 1024; -int body_maxlen = 4096; -#else -int hdr_maxlen = 512; -int body_maxlen = 1024; -#endif - static uint8_t curl_sim_fs = 0; struct curl_Transfer { @@ -651,13 +646,6 @@ uint16_t ssh2_session_trace = 0; uint8_t ssh2_session_timeout = 8; uint32_t ssh2_maxbytes = 300000; // limit download length -#if CONFIG_SPIRAM_SUPPORT -int ssh2_hdr_maxlen = 1024; -int ssh2_body_maxlen = 4096; -#else -int ssh2_hdr_maxlen = 512; -int ssh2_body_maxlen = 1024; -#endif // ==== LIBSSH2 functions ==== diff --git a/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.h b/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.h index 0cafabd9..9de8f4b3 100644 --- a/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.h +++ b/MicroPython_BUILD/components/micropython/esp32/libs/espcurl.h @@ -58,8 +58,6 @@ extern uint16_t curl_timeout; // curl operations timeout in seconds extern uint32_t curl_maxbytes; // limit download length extern uint8_t curl_initialized; extern uint8_t curl_nodecode; -extern int hdr_maxlen; -extern int body_maxlen; /* * ---------------------------------------------------------- diff --git a/MicroPython_BUILD/components/micropython/esp32/libs/libGSM.c b/MicroPython_BUILD/components/micropython/esp32/libs/libGSM.c index b34603a7..e380c40d 100644 --- a/MicroPython_BUILD/components/micropython/esp32/libs/libGSM.c +++ b/MicroPython_BUILD/components/micropython/esp32/libs/libGSM.c @@ -936,7 +936,7 @@ int ppposInit(int tx, int rx, int rts, int cts, int bdr, char *user, char *pass, tcpip_adapter_initialized = 1; } #if CONFIG_MICROPY_USE_BOTH_CORES - xTaskCreate(&pppos_client_task, "GSM_PPPoS", PPPOS_CLIENT_STACK_SIZE, NULL, CONFIG_MICROPY_TASK_PRIORITY+1, &PPPoSTaskHandle); + xTaskCreate(&pppos_client_task, "GSM_PPPoS", PPPOS_CLIENT_STACK_SIZE, NULL, CONFIG_MICROPY_TASK_PRIORITY, &PPPoSTaskHandle); #else // Select GSM task core int task_core = MainTaskCore; @@ -945,7 +945,7 @@ int ppposInit(int tx, int rx, int rts, int cts, int bdr, char *user, char *pass, else task_core = 0; #endif - xTaskCreatePinnedToCore(&pppos_client_task, "GSM_PPPoS", PPPOS_CLIENT_STACK_SIZE, NULL, CONFIG_MICROPY_TASK_PRIORITY+1, &PPPoSTaskHandle, task_core); + xTaskCreatePinnedToCore(&pppos_client_task, "GSM_PPPoS", PPPOS_CLIENT_STACK_SIZE, NULL, CONFIG_MICROPY_TASK_PRIORITY, &PPPoSTaskHandle, task_core); #endif if (PPPoSTaskHandle == NULL) return -2; diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_gps.c b/MicroPython_BUILD/components/micropython/esp32/machine_gps.c new file mode 100644 index 00000000..066b6d49 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/machine_gps.c @@ -0,0 +1,756 @@ +/* + * This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo + * + * The MIT License (MIT) + * + * Copyright (c) 2018 LoBo (https://github.com/loboris) + * + * 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. + */ + +/* GPS module based on nmea parsing library from + * 'https://github.com/jacketizer/libnmea', modified by LoBo + */ + +#include "sdkconfig.h" + +#ifdef CONFIG_MICROPY_USE_GPS + +#include +#include +#include +#include +#include +#include "esp_log.h" +#include "driver/uart.h" + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" + +#include "machine_uart.h" +#include "modmachine.h" +#include "nmea.h" +#include "gpgll.h" +#include "gpgga.h" +#include "gprmc.h" +#include "gpgst.h" +#include "gpvtg.h" + +#define EARTH_RADIUS_KM 6371.0 + + +const char *GPS_TAG = "MODGPS"; + +typedef struct { + struct tm datetime; + float latitude; + float longitude; + float altitude; + float speed; + float course; + float dop; + uint8_t quality; + uint8_t nsat; +} gps_data_t; + +typedef struct { + void *cb_func; + uint8_t type; + uint8_t compare; + nmea_position position; +} cb_func_coord_t; + +typedef struct { + void *cb_func; + uint8_t compare; + float value; +} cb_func_float_t; + +typedef struct { + void *cb_func; + uint8_t compare; + int value; +} cb_func_int_t; + +//--------------------------------- +typedef struct _machine_gps_obj_t { + mp_obj_base_t base; + mp_obj_t uart; + int timeout; + bool use_crc; + bool task_running; + bool task_stop; + uint32_t sent_read; + gps_data_t gps_data; + gps_data_t last_gps_data; + cb_func_coord_t cb_latitude; + cb_func_coord_t cb_longitude; +} machine_gps_obj_t; + +static const char* const known_parsers[] = { + "RMC", + "GGA", + "GGL", + "GST", + "VTG", +}; + +static const char* const known_talkers[] = { + "GP", + "GN", + "GL", +}; + +extern int MainTaskCore; + +static QueueHandle_t gps_mutex = NULL; + +const mp_obj_type_t machine_gps_type; + +//--------------------------------------------------- +static float coord_to_degrees(nmea_position position) +{ + double sign = 1.0; + if ((position.cardinal == NMEA_CARDINAL_DIR_SOUTH) || (position.cardinal == NMEA_CARDINAL_DIR_WEST)) sign = -1.0; + return (float)(((double)position.degrees + (position.minutes / 60.0)) * sign); +} + +//--------------------------------------------------- +static float coord_to_radians(nmea_position position) +{ + return (coord_to_degrees(position) * M_PI / 180.0); +} + +//------------------------------------------------------------------------------- +static float distance(float lat_from, float lat_to, float lon_from, float lon_to) +{ + float dLat = (lat_to - lat_from) * M_PI / 180.0; + float dLon = (lon_to - lon_from) * M_PI / 180.0; + + float lat1 = lat_from * M_PI / 180.0; + float lat2 = lat_to * M_PI / 180.0; + + float a = sin(dLat/2) * sin(dLat/2) + sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2); + float c = 2 * atan2(sqrt(a), sqrt(1-a)); + + return (EARTH_RADIUS_KM * c); +} + +//------------------------------------------ +static mp_obj_t _getTime(struct tm *tm_info) +{ + mp_obj_t tuple[8] = { + mp_obj_new_int(tm_info->tm_year + 1900), + mp_obj_new_int(tm_info->tm_mon + 1), + mp_obj_new_int(tm_info->tm_mday), + mp_obj_new_int(tm_info->tm_hour), + mp_obj_new_int(tm_info->tm_min), + mp_obj_new_int(tm_info->tm_sec), + mp_obj_new_int(tm_info->tm_wday + 1), + mp_obj_new_int(tm_info->tm_yday + 1) + }; + + return mp_obj_new_tuple(8, tuple); +} + +//------------------------------- +static long _currTime(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec*1000) + (tv.tv_usec / 1000); +} + +//-------------------------------------------------------------------------------------------- +static nmea_s *get_nmea_data(uart_port_t uart_num, char *sent_type, int timeout, bool use_crc) +{ + long end_time = _currTime() + timeout; + + nmea_s *data; + char *sentence = NULL;; + + while (_currTime() < end_time) { + sentence = _uart_read(uart_num, timeout, "\r\n", "$G"); + if (sentence) { + if (strstr(sentence, sent_type) == sentence) { + data = nmea_parse((char *)sentence, strlen(sentence), use_crc); + free(sentence); + if (data == NULL) continue; + return data; + } + else { + // not expected sentence type + free(sentence); + continue; + } + } + } + return NULL; // no date received, timeout +} + +//----------------------------------------------------------------------------------------------------- +static mp_obj_t nmea_data(nmea_s *data, bool settuple, gps_data_t *gps_data, gps_data_t *gps_last_data) +{ + mp_obj_t res_tuple = mp_const_none; + + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + if (data->errors != 0) { + if (settuple) { + mp_obj_t tuple[2] = { + mp_obj_new_str("ERRORS", 6), + mp_obj_new_int(data->errors) + }; + + res_tuple = mp_obj_new_tuple(2, tuple); + } + if (gps_mutex) xSemaphoreGive(gps_mutex); + return res_tuple; + } + + if ((gps_data) && (gps_last_data)) { + memcpy(gps_last_data, gps_data, sizeof(gps_data_t)); + } + + if (NMEA_GGA == data->type) { + nmea_gpgga_s *gpgga = (nmea_gpgga_s *) data; + if (gps_data) { + gps_data->nsat = gpgga->n_satellites; + gps_data->quality = (uint8_t)gpgga->quality; + if ((gpgga->n_satellites > 0) && (gpgga->quality > 0)) { + gps_data->altitude = gpgga->altitude; + gps_data->dop = gpgga->dop; + gps_data->latitude = coord_to_degrees(gpgga->latitude); + gps_data->longitude = coord_to_degrees(gpgga->longitude); + gps_data->datetime.tm_hour = gpgga->time.tm_hour; + gps_data->datetime.tm_min = gpgga->time.tm_min; + gps_data->datetime.tm_sec = gpgga->time.tm_sec; + } + } + if (settuple) { + if ((gpgga->n_satellites > 0) && (gpgga->quality > 0)) { + mp_obj_t tuple[8] = { + mp_obj_new_str("GGA", 3), + _getTime(&gpgga->time), + mp_obj_new_float(coord_to_degrees(gpgga->latitude)), + mp_obj_new_float(coord_to_degrees(gpgga->longitude)), + mp_obj_new_float(gpgga->altitude), + mp_obj_new_int(gpgga->n_satellites), + mp_obj_new_int(gpgga->quality), + mp_obj_new_float(gpgga->dop) + }; + res_tuple = mp_obj_new_tuple(8, tuple); + } + else { + mp_obj_t tuple[3] = { + mp_obj_new_str("GGA", 3), + mp_obj_new_int(gpgga->n_satellites), + mp_obj_new_int(gpgga->quality) + }; + res_tuple = mp_obj_new_tuple(3, tuple); + } + } + } + else if (NMEA_GLL == data->type) { + nmea_gpgll_s *gpgll = (nmea_gpgll_s *) data; + if ((gps_data) && (gpgll->valid)) { + gps_data->latitude = coord_to_degrees(gpgll->latitude); + gps_data->longitude = coord_to_degrees(gpgll->longitude); + gps_data->datetime.tm_hour = gpgll->time.tm_hour; + gps_data->datetime.tm_min = gpgll->time.tm_min; + gps_data->datetime.tm_sec = gpgll->time.tm_sec; + } + if (settuple) { + if (gpgll->valid) { + mp_obj_t tuple[5] = { + mp_obj_new_str("GLL", 3), + mp_obj_new_bool(gpgll->valid), + _getTime(&gpgll->time), + mp_obj_new_float(coord_to_degrees(gpgll->latitude)), + mp_obj_new_float(coord_to_degrees(gpgll->longitude)) + }; + res_tuple = mp_obj_new_tuple(5, tuple); + } + else { + mp_obj_t tuple[2] = { + mp_obj_new_str("GLL", 3), + mp_obj_new_bool(gpgll->valid) + }; + res_tuple = mp_obj_new_tuple(2, tuple); + } + } + } + else if (NMEA_RMC == data->type) { + nmea_gprmc_s *gprmc = (nmea_gprmc_s *) data; + if ((gps_data) && (gprmc->valid)) { + gps_data->speed = gprmc->speed * 1.85200; // knots -> km/h + gps_data->course = gprmc->course; + gps_data->latitude =coord_to_degrees(gprmc->latitude); + gps_data->longitude = coord_to_degrees(gprmc->longitude); + memcpy(&gps_data->datetime, &gprmc->time, sizeof(struct tm)); + } + if (settuple) { + if (gprmc->valid) { + mp_obj_t tuple[7] = { + mp_obj_new_str("RMC", 3), + mp_obj_new_bool(gprmc->valid), + _getTime(&gprmc->time), + mp_obj_new_float(coord_to_degrees(gprmc->latitude)), + mp_obj_new_float(coord_to_degrees(gprmc->longitude)), + mp_obj_new_float(gprmc->speed * 1.85200), // knots -> km/h + mp_obj_new_float(gprmc->course) + }; + res_tuple = mp_obj_new_tuple(7, tuple); + } + else { + mp_obj_t tuple[2] = { + mp_obj_new_str("RMC", 3), + mp_obj_new_bool(gprmc->valid) + }; + res_tuple = mp_obj_new_tuple(2, tuple); + } + } + } + else if (NMEA_VTG == data->type) { + nmea_gpvtg_s *gpvtg = (nmea_gpvtg_s *) data; + if (gps_data) { + gps_data->speed = gpvtg->speed_kmh; + gps_data->course = gpvtg->course; + } + if (settuple) { + mp_obj_t tuple[4] = { + mp_obj_new_str("VTG", 3), + mp_obj_new_float(gpvtg->speed_kmh), + mp_obj_new_float(gpvtg->speed_kn), + mp_obj_new_float(gpvtg->course) + }; + res_tuple = mp_obj_new_tuple(4, tuple); + } + } + else if (NMEA_GST == data->type) { + if (settuple) { + nmea_gpgst_s *gpgst = (nmea_gpgst_s *) data; + mp_obj_t tuple[9] = { + mp_obj_new_str("GST", 3), + _getTime(&gpgst->time), + mp_obj_new_float(gpgst->rmssd), + mp_obj_new_float(gpgst->sdmaj), + mp_obj_new_float(gpgst->sdmin), + mp_obj_new_float(gpgst->ori), + mp_obj_new_float(gpgst->latsd), + mp_obj_new_float(gpgst->lonsd), + mp_obj_new_float(gpgst->altsd) + }; + res_tuple = mp_obj_new_tuple(9, tuple); + } + } + if (gps_mutex) xSemaphoreGive(gps_mutex); + return res_tuple; +} + +//------------------------------------------ +static char *_get_sent_type(const char *sent) +{ + char *sent_type = calloc(8, 1); + if (sent_type == NULL) return NULL; + + if (sent[0] != '$') snprintf(sent_type, 7, "$%s", sent); + else snprintf(sent_type, 7, "%s", sent); + + bool f = false; + if (strcmp(sent_type, "$G") == 0) { + f = true; + } + else { + for (int i=0; itask_running = true; + if (gps_mutex) xSemaphoreGive(gps_mutex); + machine_uart_obj_t *uart = (machine_uart_obj_t *)gps_obj->uart; + + nmea_s *data; + char *sentence; + + while (true) { + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + if (gps_obj->task_stop) { + gps_obj->task_stop = false; + if (gps_mutex) xSemaphoreGive(gps_mutex); + break; + } + if (gps_mutex) xSemaphoreGive(gps_mutex); + sentence = _uart_read(uart->uart_num, 2000, "\r\n", "$G"); + if (sentence) { + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + data = nmea_parse(sentence, strlen(sentence), gps_obj->use_crc); + if (data != NULL) gps_obj->sent_read++; + if (gps_mutex) xSemaphoreGive(gps_mutex); + if (data != NULL) { + // store to gps_data only + nmea_data(data, false, &gps_obj->gps_data, &gps_obj->last_gps_data); + nmea_free(data); + } + free(sentence); + // Check callbacks + if (gps_obj->cb_latitude.cb_func) { + + } + } + } + + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + gps_obj->task_running = false; + if (gps_mutex) xSemaphoreGive(gps_mutex); + + esp_log_level_set(GPS_TAG, CONFIG_MICRO_PY_LOG_LEVEL); + ESP_LOGI(GPS_TAG, "GPS task ended, min free stack: %d", uxTaskGetStackHighWaterMark(NULL)); + + vTaskDelete(NULL); +} + +//---------------------------------------------------------- +static bool _check_task(machine_gps_obj_t *self, bool start) +{ + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + bool res = self->task_running; + if (gps_mutex) xSemaphoreGive(gps_mutex); + + if ((!res) && (start)) { + esp_log_level_set(GPS_TAG, ESP_LOG_ERROR); + esp_log_level_set(NMEA_TAG, ESP_LOG_ERROR); + self->sent_read = 0; + #if CONFIG_MICROPY_USE_BOTH_CORES + int tres = xTaskCreate(gps_task, "gps_task", CONFIG_MICROPY_GPS_SERVICE_STACK, self, CONFIG_MICROPY_TASK_PRIORITY, NULL); + #else + int tres = xTaskCreatePinnedToCore(gps_task, "gps_task", CONFIG_MICROPY_GPS_SERVICE_STACK, self, CONFIG_MICROPY_TASK_PRIORITY, NULL, MainTaskCore); + #endif + if (tres != pdTRUE) { + ESP_LOGE(GPS_TAG, "Error creating GPS task"); + res = false; + } + else res = true; + } + return res; +} + +/******************************************************************************/ +// MicroPython bindings for GPS + +//-------------------------------------------------------------------------------------------- +STATIC void machine_gps_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + bool task_running = self->task_running; + uint32_t sent_read = self->sent_read; + if (gps_mutex) xSemaphoreGive(gps_mutex); + + mp_printf(print, "GPS(default_timeout=%u, use_crc=%s, task_running=%s, read_sentences=%u)", + self->timeout, self->use_crc ? "True" : "False", task_running ? "True" : "False", sent_read); +} + +//-------------------------------------- +static const mp_arg_t allowed_args[] = { + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_crc, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_service, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, +}; + +enum { ARG_timeout, ARG_crc, ARG_service }; + +//------------------------------------------------------------------------------------------------------------------------ +STATIC void machine_gps_init_helper(machine_gps_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (args[ARG_timeout].u_int > 0) self->timeout = args[ARG_timeout].u_int; + if (args[ARG_crc].u_int >= 0) self->use_crc = (args[ARG_crc].u_int != 0); + if (args[ARG_service].u_bool) { + _check_task(self, true); + } +} + +//----------------------------------------------------------------------------------------------------------------- +STATIC mp_obj_t machine_gps_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + machine_gps_obj_t *self = m_new_obj(machine_gps_obj_t); + memset(self, 0, sizeof(machine_gps_obj_t)); + + self->base.type = &machine_gps_type; + + if (!MP_OBJ_IS_TYPE(args[0], &machine_uart_type)) { + mp_raise_ValueError("uart object expected as 1st argument"); + } + self->uart = args[0]; + self->timeout = 1500; + self->use_crc = true; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + + mp_arg_val_t kargs[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args-1, args+1, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, kargs); + + machine_gps_init_helper(self, n_args - 1, args + 1, &kw_args); + + if (gps_mutex == NULL) { + gps_mutex = xSemaphoreCreateMutex(); + } + + self->gps_data.datetime.tm_mday = 1; + + return MP_OBJ_FROM_PTR(self); +} + +//-------------------------------------------------------------------------------------- +STATIC mp_obj_t machine_gps_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + machine_gps_init_helper(args[0], n_args - 1, args + 1, kw_args); + if (gps_mutex) xSemaphoreGive(gps_mutex); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_gps_init_obj, 1, machine_gps_init); + +//--------------------------------------------------------------------------- +STATIC mp_obj_t machine_gps_readsentence(size_t n_args, const mp_obj_t *args) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (_check_task(self, false)) { + mp_raise_ValueError("GPS task running"); + } + machine_uart_obj_t *uart = (machine_uart_obj_t *)self->uart; + + char *sentence = NULL; + char *sent_type = NULL; + int timeout = 0; + if (n_args > 1) timeout = mp_obj_get_int(args[1]); + if (n_args > 2) { + const char *sent = mp_obj_str_get_str(args[2]); + sent_type = _get_sent_type(sent); + if (sent_type == NULL) { + mp_raise_ValueError("Invalid sentence type"); + } + MP_THREAD_GIL_EXIT(); + sentence = _uart_read(uart->uart_num, timeout, "\r\n", sent_type); + MP_THREAD_GIL_ENTER(); + free(sent_type); + } + else { + MP_THREAD_GIL_EXIT(); + sentence = _uart_read(uart->uart_num, timeout, "\r\n", "$G"); + MP_THREAD_GIL_ENTER(); + } + + + if (sentence == NULL) return mp_obj_new_str("", 0); + + mp_obj_t res_str = mp_obj_new_str((const char *)sentence, strlen(sentence)); + + if (sentence != NULL) free(sentence); + return res_str; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_gps_readsentence_obj, 1, 3, machine_gps_readsentence); + +//------------------------------------------------------------------- +STATIC mp_obj_t machine_gps_parse(mp_obj_t self_in, mp_obj_t sent_in) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(self_in); + + const char *sentence = mp_obj_str_get_str(sent_in); + mp_obj_t res = mp_const_none; + + char *sent = strdup(sentence); + if (sent) { + nmea_s *data = nmea_parse(sent, strlen(sent), self->use_crc); + if (data != NULL) { + // store to dict only + res = nmea_data(data, true, NULL, NULL); + nmea_free(data); + } + free(sent); + } + + return res; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_gps_parse_obj, machine_gps_parse); + +//------------------------------------------------------------------------- +STATIC mp_obj_t machine_gps_read_parse(size_t n_args, const mp_obj_t *args) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (_check_task(self, false)) { + mp_raise_ValueError("GPS task running"); + } + + machine_uart_obj_t *uart = (machine_uart_obj_t *)self->uart; + const char *sent = mp_obj_str_get_str(args[1]); + char *sent_type = NULL; + + sent_type = _get_sent_type(sent); + if (sent_type == NULL) { + mp_raise_ValueError("Invalid sentence type"); + } + + int timeout = self->timeout; + if (n_args > 2) { + timeout = mp_obj_get_int(args[2]); + } + + if (timeout < 1200) timeout = 1200; + mp_obj_t res = mp_const_none; + + MP_THREAD_GIL_EXIT(); + nmea_s *data = get_nmea_data(uart->uart_num, sent_type, timeout, self->use_crc); + MP_THREAD_GIL_ENTER(); + free(sent_type); + + if (data != NULL) { + // store to dict and gps_data + res = nmea_data(data, true, &self->gps_data, NULL); + nmea_free(data); + } + + return res; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_gps_read_parse_obj, 2, 3, machine_gps_read_parse); + +//--------------------------------------------------- +STATIC mp_obj_t machine_gps_getdata(mp_obj_t self_in) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + mp_obj_t tuple[9] = { + _getTime(&self->gps_data.datetime), + mp_obj_new_float(self->gps_data.latitude), + mp_obj_new_float(self->gps_data.longitude), + mp_obj_new_float(self->gps_data.altitude), + mp_obj_new_int(self->gps_data.nsat), + mp_obj_new_int(self->gps_data.quality), + mp_obj_new_float(self->gps_data.speed), + mp_obj_new_float(self->gps_data.course), + mp_obj_new_float(self->gps_data.dop) + }; + if (gps_mutex) xSemaphoreGive(gps_mutex); + + return mp_obj_new_tuple(9, tuple);; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_gps_getdata_obj, machine_gps_getdata); + +//-------------------------------------------------------- +STATIC mp_obj_t machine_gps_startservice(mp_obj_t self_in) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (_check_task(self, true)) return mp_const_true; + return mp_const_true; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_gps_startservice_obj, machine_gps_startservice); + +//------------------------------------------------------- +STATIC mp_obj_t machine_gps_stopservice(mp_obj_t self_in) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + if (self->task_running) { + self->task_stop = true; + } + if (gps_mutex) xSemaphoreGive(gps_mutex); + return mp_const_true; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_gps_stopservice_obj, machine_gps_stopservice); + +//------------------------------------------------------- +STATIC mp_obj_t machine_gps_taskrunning(mp_obj_t self_in) +{ + machine_gps_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (gps_mutex) xSemaphoreTake(gps_mutex, 200 / portTICK_PERIOD_MS); + bool res = self->task_running; + if (gps_mutex) xSemaphoreGive(gps_mutex); + + if (res) return mp_const_true; + return mp_const_false; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_gps_taskrunning_obj, machine_gps_taskrunning); + +//----------------------------------------------------------------------- +STATIC mp_obj_t machine_gps_distance(size_t n_args, const mp_obj_t *args) +{ + float lat1 = mp_obj_get_float(args[1]); + float lon1 = mp_obj_get_float(args[2]); + float lat2 = mp_obj_get_float(args[3]); + float lon2 = mp_obj_get_float(args[4]); + + return mp_obj_new_float(distance(lat1, lat2, lon1, lon2)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_gps_distance_obj, 5, 5, machine_gps_distance); + + +//================================================================ +STATIC const mp_rom_map_elem_t machine_gps_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_gps_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_parse), MP_ROM_PTR(&machine_gps_parse_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_gps_readsentence_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_parse), MP_ROM_PTR(&machine_gps_read_parse_obj) }, + { MP_ROM_QSTR(MP_QSTR_getdata), MP_ROM_PTR(&machine_gps_getdata_obj) }, + { MP_ROM_QSTR(MP_QSTR_startservice), MP_ROM_PTR(&machine_gps_startservice_obj) }, + { MP_ROM_QSTR(MP_QSTR_stopservice), MP_ROM_PTR(&machine_gps_stopservice_obj) }, + { MP_ROM_QSTR(MP_QSTR_service), MP_ROM_PTR(&machine_gps_taskrunning_obj) }, + { MP_ROM_QSTR(MP_QSTR_distance), MP_ROM_PTR(&machine_gps_distance_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_gps_locals_dict, machine_gps_locals_dict_table); + + +//====================================== +const mp_obj_type_t machine_gps_type = { + { &mp_type_type }, + .name = MP_QSTR_GPS, + .print = machine_gps_print, + .make_new = machine_gps_make_new, + .locals_dict = (mp_obj_dict_t*)&machine_gps_locals_dict, +}; + + +#endif + diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_hw_i2c.c b/MicroPython_BUILD/components/micropython/esp32/machine_hw_i2c.c index 30a8b710..518db154 100644 --- a/MicroPython_BUILD/components/micropython/esp32/machine_hw_i2c.c +++ b/MicroPython_BUILD/components/micropython/esp32/machine_hw_i2c.c @@ -66,6 +66,8 @@ typedef struct _mp_machine_i2c_obj_t { } mp_machine_i2c_obj_t; +extern int MainTaskCore; + const mp_obj_type_t machine_hw_i2c_type; static int i2c_used[I2C_MODE_MAX] = { -1, -1 }; @@ -479,7 +481,11 @@ mp_obj_t mp_machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_ } if (i2c_slave_task_handle[self->bus_id] == NULL) { - xTaskCreate(i2c_slave_task, "i2c_slave_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY+4, &i2c_slave_task_handle[self->bus_id]); + #if CONFIG_MICROPY_USE_BOTH_CORES + xTaskCreate(i2c_slave_task, "i2c_slave_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &i2c_slave_task_handle[self->bus_id]); + #else + xTaskCreatePinnedToCore(i2c_slave_task, "i2c_slave_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &i2c_slave_task_handle[self->bus_id], MainTaskCore); + #endif } } @@ -577,7 +583,11 @@ STATIC mp_obj_t mp_machine_i2c_init(mp_uint_t n_args, const mp_obj_t *pos_args, nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error installing I2C driver")); } if (i2c_slave_task_handle[self->bus_id] == NULL) { - xTaskCreate(i2c_slave_task, "i2c_slave_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY+4, &i2c_slave_task_handle[self->bus_id]); + #if CONFIG_MICROPY_USE_BOTH_CORES + xTaskCreate(i2c_slave_task, "i2c_slave_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &i2c_slave_task_handle[self->bus_id]); + #else + xTaskCreatePinnedToCore(i2c_slave_task, "i2c_slave_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &i2c_slave_task_handle[self->bus_id], MainTaskCore); + #endif } } i2c_used[bus_id] = mode; diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_pin.c b/MicroPython_BUILD/components/micropython/esp32/machine_pin.c index b10b4b9d..06c1dee5 100644 --- a/MicroPython_BUILD/components/micropython/esp32/machine_pin.c +++ b/MicroPython_BUILD/components/micropython/esp32/machine_pin.c @@ -37,6 +37,8 @@ #include "extmod/virtpin.h" #include "machine_pin.h" +extern bool mpy_use_spiram; + STATIC const machine_pin_obj_t machine_pin_obj[] = { {{&machine_pin_type}, GPIO_NUM_0}, {{&machine_pin_type}, GPIO_NUM_1}, @@ -157,11 +159,11 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - #if CONFIG_SPIRAM_SUPPORT - if ((self->id == 16) || (self->id == 17)) { - mp_raise_ValueError("Pins 16&17 cannot be used if SPIRAM is used"); + if (mpy_use_spiram) { + if ((self->id == 16) || (self->id == 17)) { + mp_raise_ValueError("Pins 16&17 cannot be used if SPIRAM is used"); + } } - #endif // configure the pin for gpio if (rtc_gpio_is_valid_gpio(self->id)) rtc_gpio_deinit(self->id); diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_rtc.c b/MicroPython_BUILD/components/micropython/esp32/machine_rtc.c index f4da3112..96eb3d6b 100644 --- a/MicroPython_BUILD/components/micropython/esp32/machine_rtc.c +++ b/MicroPython_BUILD/components/micropython/esp32/machine_rtc.c @@ -45,11 +45,14 @@ #include "mphalport.h" #include "machine_pin.h" +#include "mpsleep.h" #define RTC_MEM_INT_SIZE 64 #define RTC_MEM_STR_SIZE 2048 +extern int MainTaskCore; + char mpy_time_zone[64] = {'\0'}; static int RTC_DATA_ATTR rtc_mem_int[RTC_MEM_INT_SIZE] = { 0 }; @@ -75,14 +78,6 @@ static RTC_DATA_ATTR uint64_t seconds_at_boot; static mach_rtc_obj_t mach_rtc_obj; const mp_obj_type_t mach_rtc_type; -//------------------------------------------------------------ -static void mach_rtc_set_seconds_since_epoch(uint64_t nowus) { - struct timeval tv; - - // store the packet timestamp - gettimeofday(&tv, NULL); - seconds_at_boot = tv.tv_sec; -} //------------------------ static void rtc_init_mem() @@ -95,8 +90,12 @@ static void rtc_init_mem() //-------------------- void rtc_init0(void) { - mach_rtc_set_seconds_since_epoch(0); - rtc_init_mem(); + mpsleep_reset_cause_t rstc = mpsleep_get_reset_cause(); + if ((rstc != MPSLEEP_DEEPSLEEP_RESET) && (rstc != MPSLEEP_SOFT_RESET) && (rstc != MPSLEEP_SOFT_CPU_RESET)) { + seconds_at_boot = 0; + setTicks_base(0); + rtc_init_mem(); + } } // Set system date time @@ -148,7 +147,7 @@ STATIC mp_obj_t mach_rtc_datetime(const mp_obj_t *args) { // Set new base for ticks counting setTicks_base((((uint64_t)now.tv_sec * 1000000) - ticks_us)); - mach_rtc_set_seconds_since_epoch(seconds); + seconds_at_boot = seconds; return mp_const_none; } @@ -330,12 +329,12 @@ STATIC mp_obj_t mach_rtc_ntp_sync(size_t n_args, const mp_obj_t *pos_args, mp_ma if (args[2].u_obj != mp_const_none) { // get TZ argument const char *tzs = mp_obj_str_get_str(args[2].u_obj); - if ((strlen(tzs) < 2) && (strlen(tzs) < 64)) { + if ((strlen(tzs) > 2) && (strlen(tzs) < 64)) { sprintf(mpy_time_zone, "%s", tzs); tz_fromto_NVS(NULL, mpy_time_zone); } else { - mp_raise_ValueError("tz string length must be 3 - 64"); + mp_raise_ValueError("tz string length must be 3 - 63"); } } setenv("TZ", mpy_time_zone, 1); @@ -361,7 +360,12 @@ STATIC mp_obj_t mach_rtc_ntp_sync(size_t n_args, const mp_obj_t *pos_args, mp_ma if (sntp_handle == NULL) { // Create and start sntp task - if (xTaskCreate(&sntp_task, "SNTP_TASK", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY+1, &sntp_handle) != pdPASS) { + #if CONFIG_MICROPY_USE_BOTH_CORES + int tres = xTaskCreate(&sntp_task, "SNTP_TASK", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &sntp_handle); + #else + int tres = xTaskCreatePinnedToCore(&sntp_task, "SNTP_TASK", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &sntp_handle, MainTaskCore); + #endif + if (tres != pdTRUE) { mp_raise_msg(&mp_type_OSError, "Error creating SNTP task"); } } diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_uart.c b/MicroPython_BUILD/components/micropython/esp32/machine_uart.c index 8d6e8b6a..9100102c 100644 --- a/MicroPython_BUILD/components/micropython/esp32/machine_uart.c +++ b/MicroPython_BUILD/components/micropython/esp32/machine_uart.c @@ -32,54 +32,21 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" -#include "driver/uart.h" - -#include "py/runtime.h" +#include "machine_uart.h" #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" #include "modmachine.h" +#include "sdkconfig.h" + -#define UART_CB_TYPE_DATA 1 -#define UART_CB_TYPE_PATTERN 2 -#define UART_CB_TYPE_ERROR 3 -#define UART_BUFF_SIZE 256 - -typedef struct _machine_uart_obj_t { - mp_obj_base_t base; - uart_port_t uart_num; - int8_t bits; - int8_t parity; - int8_t stop; - int8_t tx; - int8_t rx; - int8_t rts; - int8_t cts; - int data_cb_size; - uint8_t pattern[16]; - uint8_t pattern_len; - uint16_t timeout; // timeout waiting for first char (in ms) - uint16_t buffer_size; - uint32_t *data_cb; - uint32_t *pattern_cb; - uint32_t *error_cb; - uint32_t inverted; - uint8_t end_task; - uint8_t lineend[3]; -} machine_uart_obj_t; - -typedef struct _uart_ringbuf_t { - uint8_t *buf; - uint16_t size; - uint16_t iget; - uint16_t iput; -} uart_ringbuf_t; +extern int MainTaskCore; static const char *_parity_name[] = {"None", "None", "Even", "Odd"}; static const char *_stopbits_name[] = {"?", "1", "1.5", "2"}; static QueueHandle_t UART_QUEUE[2] = {NULL}; static QueueHandle_t uart_mutex = NULL; -TaskHandle_t task_id[2] = {NULL}; +static TaskHandle_t task_id[2] = {NULL}; static uart_ringbuf_t uart_buffer[2]; static uart_ringbuf_t *uart_buf[2] = {NULL}; @@ -94,8 +61,9 @@ static void uart_ringbuf_alloc(uint8_t uart_num, uint16_t sz) uart_buf[uart_num] = &uart_buffer[uart_num]; } -//----------------------------------------------------------------------- -static int uart_buf_get(uart_ringbuf_t *r, uint8_t *dest, uint16_t len) { +//-------------------------------------------------------------- +int uart_buf_get(uart_ringbuf_t *r, uint8_t *dest, uint16_t len) +{ if (r->iget == r->iput) return -1; // input buffer empty int res = 0; @@ -112,8 +80,9 @@ static int uart_buf_get(uart_ringbuf_t *r, uint8_t *dest, uint16_t len) { return res; } -//------------------------------------------------------------------------- -static int uart_buf_put(uart_ringbuf_t *r, uint8_t *source, uint16_t len) { +//---------------------------------------------------------------- +int uart_buf_put(uart_ringbuf_t *r, uint8_t *source, uint16_t len) +{ int res = 0; for (int i=0; iiput >= r->size) return 1; // overflow @@ -122,8 +91,8 @@ static int uart_buf_put(uart_ringbuf_t *r, uint8_t *source, uint16_t len) { return res; } -//--------------------------------------------------------------------------------------------- -static int match_pattern(uint8_t *text, int text_length, uint8_t *pattern, int pattern_length) +//------------------------------------------------------------------------------------- +int match_pattern(uint8_t *text, int text_length, uint8_t *pattern, int pattern_length) { int c, d, e, position = -1; @@ -265,6 +234,143 @@ static void uart_event_task(void *pvParameters) vTaskDelete(NULL); } +//----------------------------------------------------------------------------- +char *_uart_read(uart_port_t uart_num, int timeout, char *lnend, char *lnstart) +{ + char *rdstr = NULL; + int rdlen = -1; + int minlen = strlen(lnend); + if (lnstart) minlen += strlen(lnstart); + + if (timeout == 0) { + if (uart_mutex) { + if (xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) { + return NULL; + } + } + // check for minimal length + if (uart_buf[uart_num]->iput < minlen) { + if (uart_mutex) xSemaphoreGive(uart_mutex); + return NULL; + } + while (1) { + rdlen = match_pattern(uart_buf[uart_num]->buf, uart_buf[uart_num]->iput, (uint8_t *)lnend, strlen(lnend)); + if (rdlen >= 0) { + // found, pull data, including pattern from buffer + rdlen += 2; + rdstr = calloc(rdlen+1, 1); + if (rdstr) { + uart_buf_get(uart_buf[uart_num], (uint8_t *)rdstr, rdlen); + rdstr[rdlen] = 0; + if (lnstart) { + // Match beginning string + char *start_ptr = strstr(rdstr, lnstart); + if (start_ptr) { + if (start_ptr != rdstr) { + char *new_rdstr = strdup(start_ptr); + free(rdstr); + rdstr = new_rdstr; + } + break; + } + else { + free(rdstr); + rdstr = NULL; + rdlen = -1; + break; + } + } + else break; + } + else { + rdlen = -1; + break; + } + } + else break; + } + if (uart_mutex) xSemaphoreGive(uart_mutex); + if (rdlen < 0) return NULL; + } + else { + // wait until lnend received or timeout + int wait = timeout; + int buflen = 0; + mp_hal_set_wdt_tmo(); + while (wait > 0) { + if (uart_mutex) { + if (xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) { + vTaskDelay(10 / portTICK_PERIOD_MS); + wait -= 10; + mp_hal_reset_wdt(); + continue; + } + } + if (buflen < uart_buf[uart_num]->iput) { + // ** new data received, reset timeout + buflen = uart_buf[uart_num]->iput; + wait = timeout; + } + if (uart_buf[uart_num]->iput < minlen) { + // ** too few characters received + if (uart_mutex) xSemaphoreGive(uart_mutex); + vTaskDelay(10 / portTICK_PERIOD_MS); + wait -= 10; + mp_hal_reset_wdt(); + continue; + } + + while (1) { + // * Check if lineend pattern is received + rdlen = match_pattern(uart_buf[uart_num]->buf, uart_buf[uart_num]->iput, (uint8_t *)lnend, strlen(lnend)); + if (rdlen >= 0) { + rdlen += 2; + // * found, pull data, including pattern from buffer + rdstr = calloc(rdlen+1, 1); + if (rdstr) { + uart_buf_get(uart_buf[uart_num], (uint8_t *)rdstr, rdlen); + rdstr[rdlen] = 0; + if (lnstart) { + // * Find beginning of the sentence + char *start_ptr = strstr(rdstr, lnstart); + if (start_ptr) { + // === received string ending with lnend and starting with lnstart + if (start_ptr != rdstr) { + char *new_rdstr = strdup(start_ptr); + free(rdstr); + rdstr = new_rdstr; + } + break; + } + else { + free(rdstr); + rdstr = NULL; + break; + } + } + else break; // === received string ending with lineend + } + else { + // error allocating buffer, finish + wait = 0; + break; + } + } + else break; + } + if (uart_mutex) xSemaphoreGive(uart_mutex); + + if (rdstr) break; + if (wait > 0) { + vTaskDelay(10 / portTICK_PERIOD_MS); + wait -= 10; + mp_hal_reset_wdt(); + } + } + } + return rdstr; +} + /******************************************************************************/ // MicroPython bindings for UART @@ -584,7 +690,7 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, uart_param_config(uart_num, &uartcfg); // RX ring buffer size is set to UART_BUFF_SIZE (256), TX buffer is disabled. - esp_err_t res = uart_driver_install(uart_num, UART_BUFF_SIZE, 0, 10, &UART_QUEUE[self->uart_num], 0); + esp_err_t res = uart_driver_install(uart_num, UART_BUFF_SIZE, 0, 20, &UART_QUEUE[self->uart_num], 0); if (res != ESP_OK) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) Error installing driver", uart_num)); } @@ -598,7 +704,11 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, uart_disable_pattern_det_intr(uart_num); //Create a task to handle UART event from ISR - if (task_id[self->uart_num] == NULL) xTaskCreate(uart_event_task, "uart_event_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY+4, &task_id[self->uart_num]); + #if CONFIG_MICROPY_USE_BOTH_CORES + if (task_id[self->uart_num] == NULL) xTaskCreate(uart_event_task, "uart_event_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &task_id[self->uart_num]); + #else + if (task_id[self->uart_num] == NULL) xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 1024, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, &task_id[self->uart_num], MainTaskCore); + #endif return MP_OBJ_FROM_PTR(self); } @@ -636,83 +746,26 @@ STATIC mp_obj_t machine_uart_flush(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_flush_obj, machine_uart_flush); -//------------------------------------------------------------------------ -STATIC mp_obj_t machine_uart_readln(size_t n_args, const mp_obj_t *args) { +//----------------------------------------------------------------- +mp_obj_t machine_uart_readln(size_t n_args, const mp_obj_t *args) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(args[0]); - uint8_t *rdstr = NULL; - int rdlen = -1; - int lnendlen = strlen((char *)self->lineend); - if (lnendlen == 0) return mp_const_none; - int timeout = self->timeout; - if (n_args == 2) timeout = mp_obj_get_int(args[1]); + if (n_args > 1) timeout = mp_obj_get_int(args[1]); + + const char *startstr = NULL; + if (n_args > 2) startstr = mp_obj_str_get_str(args[2]); + + MP_THREAD_GIL_EXIT(); + char *rdstr = _uart_read(self->uart_num, timeout, (char *)self->lineend, (char *)startstr); + MP_THREAD_GIL_ENTER(); - if (timeout == 0) { - if (uart_mutex) xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS); - // just return the buffer content if line end was found - if (uart_buf[self->uart_num]->iput < lnendlen) { - if (uart_mutex) xSemaphoreGive(uart_mutex); - return mp_const_none; - } - rdlen = match_pattern(uart_buf[self->uart_num]->buf, uart_buf[self->uart_num]->iput, self->lineend, lnendlen); - if (rdlen >= 0) { - // found, pull data, including pattern from buffer - rdlen += lnendlen; - rdstr = calloc(rdlen+1, 1); - if (rdstr) { - uart_buf_get(uart_buf[self->uart_num], rdstr, rdlen); - rdstr[rdlen] = 0; - } - } - if (uart_mutex) xSemaphoreGive(uart_mutex); - if (rdlen < 0) return mp_const_none; - } - else { - // wait until line end received or timeout - int wait = timeout; - int buflen = 0; - mp_hal_set_wdt_tmo(); - MP_THREAD_GIL_EXIT(); - while (wait > 0) { - if (uart_mutex) xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS); - if (buflen < uart_buf[self->uart_num]->iput) { - buflen = uart_buf[self->uart_num]->iput; - wait = timeout; // new data received, reset timeout - } - if (uart_buf[self->uart_num]->iput < lnendlen) { - if (uart_mutex) xSemaphoreGive(uart_mutex); - vTaskDelay(10 / portTICK_PERIOD_MS); - wait -= 10; - mp_hal_reset_wdt(); - continue; - } - rdlen = match_pattern(uart_buf[self->uart_num]->buf, uart_buf[self->uart_num]->iput, self->lineend, lnendlen); - if (rdlen >= 0) { - rdlen += lnendlen; - // found, pull data, including pattern from buffer - rdstr = calloc(rdlen+1, 1); - if (rdstr) { - uart_buf_get(uart_buf[self->uart_num], rdstr, rdlen); - rdstr[rdlen] = 0; - } - if (uart_mutex) xSemaphoreGive(uart_mutex); - break; - } - if (uart_mutex) xSemaphoreGive(uart_mutex); - vTaskDelay(10 / portTICK_PERIOD_MS); - wait -= 10; - mp_hal_reset_wdt(); - } - MP_THREAD_GIL_ENTER(); - if (rdlen < 0) return mp_const_none; - } if (rdstr == NULL) return mp_const_none; - mp_obj_t res_str = mp_obj_new_str((const char *)rdstr, rdlen); - free(rdstr); + mp_obj_t res_str = mp_obj_new_str((const char *)rdstr, strlen(rdstr)); + if (rdstr != NULL) free(rdstr); return res_str; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_uart_readln_obj, 1, 2, machine_uart_readln); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_uart_readln_obj, 1, 3, machine_uart_readln); //----------------------------------------------------------------------------------------------- @@ -878,7 +931,11 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz int bytes_read = 0; if (self->timeout == 0) { // just return the buffer content - if (uart_mutex) xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS); + if (uart_mutex) { + if (xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) { + return 0; + } + } bytes_read = uart_buf_get(uart_buf[self->uart_num], (uint8_t *)buf_in, size); if (uart_mutex) xSemaphoreGive(uart_mutex); if (bytes_read < 0) bytes_read = 0; @@ -889,7 +946,14 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz int wait = self->timeout; MP_THREAD_GIL_EXIT(); while (wait > 0) { - if (uart_mutex) xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS); + if (uart_mutex) { + if (xSemaphoreTake(uart_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) { + vTaskDelay(2 / portTICK_PERIOD_MS); + wait -= 2; + mp_hal_reset_wdt(); + continue; + } + } if (uart_buf[self->uart_num]->iput < size) { if (uart_mutex) xSemaphoreGive(uart_mutex); vTaskDelay(2 / portTICK_PERIOD_MS); diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_uart.h b/MicroPython_BUILD/components/micropython/esp32/machine_uart.h new file mode 100644 index 00000000..504b8123 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/machine_uart.h @@ -0,0 +1,48 @@ +#ifndef INC_MACHINE_UART_H +#define INC_MACHINE_UART_H + +#include "driver/uart.h" +#include "py/runtime.h" + +#define UART_CB_TYPE_DATA 1 +#define UART_CB_TYPE_PATTERN 2 +#define UART_CB_TYPE_ERROR 3 +#define UART_BUFF_SIZE 256 + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + uart_port_t uart_num; + int8_t bits; + int8_t parity; + int8_t stop; + int8_t tx; + int8_t rx; + int8_t rts; + int8_t cts; + int data_cb_size; + uint8_t pattern[16]; + uint8_t pattern_len; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t buffer_size; + uint32_t *data_cb; + uint32_t *pattern_cb; + uint32_t *error_cb; + uint32_t inverted; + uint8_t end_task; + uint8_t lineend[3]; +} machine_uart_obj_t; + +typedef struct _uart_ringbuf_t { + uint8_t *buf; + uint16_t size; + uint16_t iget; + uint16_t iput; +} uart_ringbuf_t; + + +char *_uart_read(uart_port_t uart_num, int timeout, char *lnend, char *lnstart); +int match_pattern(uint8_t *text, int text_length, uint8_t *pattern, int pattern_length); +int uart_buf_get(uart_ringbuf_t *r, uint8_t *dest, uint16_t len); +int uart_buf_put(uart_ringbuf_t *r, uint8_t *source, uint16_t len); + +#endif diff --git a/MicroPython_BUILD/components/micropython/esp32/main.c b/MicroPython_BUILD/components/micropython/esp32/main.c index 7d586eac..8b7a59fa 100644 --- a/MicroPython_BUILD/components/micropython/esp32/main.c +++ b/MicroPython_BUILD/components/micropython/esp32/main.c @@ -60,64 +60,42 @@ #ifdef CONFIG_MICROPY_USE_FTPSERVER #include "libs/ftp.h" #endif -#ifdef CONFIG_MICROPY_USE_MQTT -#include "mqtt.h" -#endif #include "driver/uart.h" #include "rom/uart.h" #include "sdkconfig.h" -// ========================================= -// MicroPython runs as a task under FreeRTOS -// ========================================= +// ================================================ +// MicroPython runs as a STATIC task under FreeRTOS +// ================================================ #define NVS_NAMESPACE "MPY_NVM" -#define MP_TASK_PRIORITY CONFIG_MICROPY_TASK_PRIORITY -#define MP_TASK_STACK_LEN 3072 +#define MP_TASK_STACK_LEN 4096 + +#if CONFIG_SPIRAM_IGNORE_NOTFOUND +extern bool s_spiram_okay; +#endif static StaticTask_t DRAM_ATTR mp_task_tcb; static StackType_t *mp_task_stack; -static uint8_t *mp_task_heap; -static int mp_task_stack_len = 4092; +static StackType_t *mp_task_stack_end; +static int mp_task_stack_len = 4096; +static uint8_t *mp_task_heap = NULL; -int MainTaskCore = 0; +//STATIC StackType_t mp_task_stack[MP_TASK_STACK_LEN] __attribute__((aligned (8))); +int MainTaskCore = 0; //=============================== void mp_task(void *pvParameter) { - volatile uint32_t sp = (uint32_t)get_sp(); - - uart_config_t uartcfg = { - .baud_rate = 115200, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - .rx_flow_ctrl_thresh = 0, - .use_ref_tick = true - }; - uart_param_config(UART_NUM_0, &uartcfg); - uart_set_baudrate(UART_NUM_0, CONFIG_CONSOLE_UART_BAUDRATE); - /* - // ---- esp-idf PM bug! ---------------------------------------------------------------------------------------- - #if defined(CONFIG_PM_ENABLE) && !defined(CONFIG_PM_DFS_INIT_AUTO) && defined(CONFIG_ESP32_DEFAULT_CPU_FREQ_240) - esp_pm_config_esp32_t pm_config; - pm_config.max_cpu_freq = RTC_CPU_FREQ_160M; - pm_config.min_cpu_freq = RTC_CPU_FREQ_XTAL; - pm_config.light_sleep_enable = false; - esp_pm_configure(&pm_config); - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_160M); - uart_set_baudrate(UART_NUM_0, CONFIG_CONSOLE_UART_BAUDRATE); - pm_config.max_cpu_freq = RTC_CPU_FREQ_240M; - esp_pm_configure(&pm_config); - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M); - uart_set_baudrate(UART_NUM_0, CONFIG_CONSOLE_UART_BAUDRATE); + #ifdef CONFIG_MICROPY_USE_TASK_WDT + // Enable watchdog for MicroPython main task + esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, false); + esp_task_wdt_add(MainTaskHandle); + esp_task_wdt_reset(); #endif - // ------------------------------------------------------------------------------------------------------------- - */ uart_init(); @@ -130,12 +108,13 @@ void mp_task(void *pvParameter) { // Get and print reset & wakeup reasons mpsleep_init0(); - if (mpsleep_get_reset_cause() != MPSLEEP_DEEPSLEEP_RESET) rtc_init0(); + rtc_init0(); - mp_thread_preinit(&mp_task_stack[0], mp_task_stack_len); + volatile uint32_t sp = (uint32_t)get_sp(); + mp_task_stack_len -= ((uint32_t)mp_task_stack_end - sp); - // Thread init - mp_thread_init(); + // === Main MicroPython thread init === + mp_thread_preinit(mp_task_stack, mp_task_stack_len); // Initialize the stack pointer for the main thread mp_stack_set_top((void *)sp); @@ -145,9 +124,10 @@ void mp_task(void *pvParameter) { mp_stack_set_limit(mp_task_stack_len - 1024); #endif - // initialize the mp heap + // Initialize the MicroPython heap gc_init(mp_task_heap, mp_task_heap + mpy_heap_size); + // Initialize MicroPython environment mp_init(); mp_obj_list_init(mp_sys_path, 0); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); @@ -159,6 +139,8 @@ void mp_task(void *pvParameter) { // Initialize peripherals machine_pins_init(); + ESP_LOGI("MicroPython", "[=== MicroPython FreeRTOS task started (sp=%08x) ===]\n", sp); + // === Mount internal flash file system === int res = mount_vfs(VFS_NATIVE_TYPE_SPIFLASH, VFS_NATIVE_INTERNAL_MP); @@ -181,19 +163,22 @@ void mp_task(void *pvParameter) { char sbuff[24] = { 0 }; gc_info_t info; gc_info(&info); - // set gc.threshold to 80% of usable heap - MP_STATE_MEM(gc_alloc_threshold) = ((info.total / 10) * 8) / MICROPY_BYTES_PER_GC_BLOCK; + // -------------------------------------- + // set gc.threshold to 60% of usable heap + // -------------------------------------- + MP_STATE_MEM(gc_alloc_threshold) = ((info.total / 10) * 6) / MICROPY_BYTES_PER_GC_BLOCK; #if CONFIG_FREERTOS_UNICORE - printf("\nFreeRTOS running only on FIRST CORE.\n"); + printf("\nFreeRTOS and MicroPython running only on FIRST CORE.\n"); #else #if CONFIG_MICROPY_USE_BOTH_CORES printf("\nFreeRTOS running on BOTH CORES, MicroPython task running on both cores.\n"); #else - printf("\nFreeRTOS running on BOTH CORES, MicroPython task started on App Core.\n"); + printf("\nFreeRTOS running on BOTH CORES, MicroPython task started on App Core (1).\n"); #endif #endif + #ifdef CONFIG_MICROPY_USE_OTA // Print partition info const esp_partition_t *running_partition = esp_ota_get_running_partition(); if (running_partition != NULL) { @@ -204,6 +189,7 @@ void mp_task(void *pvParameter) { printf("Running from %s%spartition starting at 0x%X, [%s].\n", ((running_partition->encrypted) ? "encrypted " : ""), sbuff, running_partition->address, running_partition->label); } + #endif mpsleep_get_reset_desc(sbuff); if (mpsleep_get_wake_reason() != MPSLEEP_NONE_WAKE) printf(" "); @@ -216,47 +202,34 @@ void mp_task(void *pvParameter) { #ifdef CONFIG_MICROPY_USE_THREADED_REPL printf(" REPL stack: %d bytes\n", mpy_repl_stack_size); #else - printf(" uPY stack: %d bytes\n", mp_task_stack_len); + printf(" uPY stack: %d bytes\n", mp_task_stack_len - 1024); #endif - #if CONFIG_SPIRAM_SUPPORT + if (mpy_use_spiram) { // ## USING SPI RAM FOR HEAP ## #if CONFIG_SPIRAM_USE_CAPS_ALLOC printf(" uPY heap: %u/%u/%u bytes (in SPIRAM using heap_caps_malloc)\n\n", info.total, info.used, info.free); - #elif SPIRAM_USE_MEMMAP + #elif CONFIG_SPIRAM_USE_MEMMAP printf(" uPY heap: %u/%u/%u bytes (in SPIRAM using MEMMAP)\n\n", info.total, info.used, info.free); #else printf(" uPY heap: %u/%u/%u bytes (in SPIRAM using malloc)\n\n", info.total, info.used, info.free); #endif - #else + } + else { // ## USING DRAM FOR HEAP ## printf(" uPY heap: %u/%u/%u bytes\n\n", info.total, info.used, info.free); - #endif + } // === Main loop ================================== - MP_THREAD_GIL_EXIT(); - - #ifdef CONFIG_MICROPY_USE_TASK_WDT - // Enable watchdog for MicroPython main task - esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, false); - esp_task_wdt_add(MainTaskHandle); - esp_task_wdt_reset(); - #endif + MP_THREAD_GIL_EXIT(); #ifdef CONFIG_MICROPY_USE_THREADED_REPL - // === Start REPL in separate thread === + // === Start REPL in separate thread ============== pyexec_frozen_module("modREPL.py"); - #ifdef CONFIG_MICROPY_USE_TASK_WDT - if (ReplTaskHandle) { - // Enable watchdog for MicroPython main task - esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, false); - esp_task_wdt_add(ReplTaskHandle); - esp_task_wdt_reset(); - } - #endif if (!ReplTaskHandle) { printf("!! Error starting threaded REPL, Halted. !!\n"); } + while (1) { // Main task just waits doing nothing vTaskDelay(CONFIG_TASK_WDT_TIMEOUT_S * 500 / portTICK_RATE_MS); @@ -264,8 +237,9 @@ void mp_task(void *pvParameter) { esp_task_wdt_reset(); #endif } - #else - // === Start REPL in main task === + #else // CONFIG_MICROPY_USE_THREADED_REPL + + // === Start REPL in main task ==================== ReplTaskHandle = MainTaskHandle; for (;;) { if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { @@ -291,50 +265,105 @@ void mp_task(void *pvParameter) { //============================ void micropython_entry(void) { - // === Set esp32 log levels while running MicroPython === - if (CONFIG_MICRO_PY_LOG_LEVEL < CONFIG_LOG_DEFAULT_LEVEL) esp_log_level_set("*", CONFIG_MICRO_PY_LOG_LEVEL); - if ((CONFIG_LOG_DEFAULT_LEVEL > ESP_LOG_WARN) && (CONFIG_MICRO_PY_LOG_LEVEL > ESP_LOG_WARN)){ - esp_log_level_set("wifi", ESP_LOG_WARN); - esp_log_level_set("rmt", ESP_LOG_WARN); - esp_log_level_set("tcpip_adapter", ESP_LOG_WARN); - esp_log_level_set("event", ESP_LOG_WARN); - esp_log_level_set("nvs", ESP_LOG_WARN); - esp_log_level_set("phy_init", ESP_LOG_WARN); - esp_log_level_set("wl_flash", ESP_LOG_WARN); - esp_log_level_set("RTC_MODULE", ESP_LOG_WARN); - } - #ifdef CONFIG_MICROPY_USE_OTA - if (CONFIG_LOG_DEFAULT_LEVEL >= ESP_LOG_DEBUG) esp_log_level_set("OTA_UPDATE", ESP_LOG_DEBUG); - else esp_log_level_set("OTA_UPDATE", CONFIG_LOG_DEFAULT_LEVEL); - #endif + ESP_LOGD("MicroPython","Entry"); + vTaskDelay(1000); + // Configure UART for power management usage + uart_config_t uartcfg = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0, + .use_ref_tick = true + }; + uart_param_config(UART_NUM_0, &uartcfg); + uart_set_baudrate(UART_NUM_0, CONFIG_CONSOLE_UART_BAUDRATE); - #ifdef CONFIG_MICROPY_USE_MQTT - esp_log_level_set(MQTT_TAG, CONFIG_MQTT_LOG_LEVEL); + // === Check SPIRAM support ================= + MPY_DEFAULT_STACK_SIZE = CONFIG_MICROPY_STACK_SIZE * 1024; + MPY_DEFAULT_HEAP_SIZE = CONFIG_MICROPY_HEAP_SIZE * 1024; + #if CONFIG_SPIRAM_SUPPORT + #if CONFIG_SPIRAM_IGNORE_NOTFOUND + if (s_spiram_okay) mpy_use_spiram = true; + else { + mpy_use_spiram = false; + MPY_DEFAULT_STACK_SIZE = 16 * 1024; + MPY_DEFAULT_HEAP_SIZE = 80 * 1024; + ESP_LOGW("MicroPython","SPIRAM support enabled but SPIRAM not detected"); + } + #else + mpy_use_spiram = true; + #endif + #else + mpy_use_spiram = false; #endif - #ifdef CONFIG_MICROPY_USE_FTPSERVER - esp_log_level_set(FTP_TAG, CONFIG_FTPSERVER_LOG_LEVEL); + ESP_LOGD("MicroPython","SPIRAM: %s", mpy_use_spiram ? "Enabled" : "Disabled"); + // ========================================== + + // Initialize some global variables depending on SPIRAM support + if (mpy_use_spiram) { + MPY_MAX_STACK_SIZE = 64*1024; + MPY_MIN_HEAP_SIZE = 128*1024; + MPY_MAX_HEAP_SIZE = 3584*1024; + hdr_maxlen = 1024; + body_maxlen = 4096; + ssh2_hdr_maxlen = 1024; + ssh2_body_maxlen = 4096; + } + else { + MPY_MAX_STACK_SIZE = 32*1024; + MPY_MIN_HEAP_SIZE = 48*1024; + #if defined(CONFIG_MICROPY_USE_CURL) && defined(CONFIG_MICROPY_USE_CURL_TLS) + MPY_MAX_HEAP_SIZE = 74*1024; + #else + MPY_MAX_HEAP_SIZE = 96*1024; + #endif + hdr_maxlen = 512; + body_maxlen = 1024; + ssh2_hdr_maxlen = 512; + ssh2_body_maxlen = 1024; + } + + /* + // ---- esp-idf PM bug! ---------------------------------------------------------------------------------------- + #if defined(CONFIG_PM_ENABLE) && !defined(CONFIG_PM_DFS_INIT_AUTO) && defined(CONFIG_ESP32_DEFAULT_CPU_FREQ_240) + esp_pm_config_esp32_t pm_config; + pm_config.max_cpu_freq = RTC_CPU_FREQ_160M; + pm_config.min_cpu_freq = RTC_CPU_FREQ_XTAL; + pm_config.light_sleep_enable = false; + esp_pm_configure(&pm_config); + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_160M); + uart_set_baudrate(UART_NUM_0, CONFIG_CONSOLE_UART_BAUDRATE); + pm_config.max_cpu_freq = RTC_CPU_FREQ_240M; + esp_pm_configure(&pm_config); + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M); + uart_set_baudrate(UART_NUM_0, CONFIG_CONSOLE_UART_BAUDRATE); #endif + // ------------------------------------------------------------------------------------------------------------- + */ nvs_flash_init(); + // ================================ // === Check and allocate stack === - // Open NVS name space + ESP_LOGD("MicroPython","Configure stack"); + mpy_repl_stack_size = MPY_DEFAULT_STACK_SIZE; + + // Open NVS name space if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &mpy_nvs_handle) != ESP_OK) { mpy_nvs_handle = 0; ESP_LOGE("MicroPython","Error while opening MicroPython NVS name space"); } if (mpy_nvs_handle != 0) { // Get stack size from NVS - if (ESP_ERR_NVS_NOT_FOUND == nvs_get_i32(mpy_nvs_handle, "MPY_StackSize", &mpy_repl_stack_size)) { - mpy_repl_stack_size = CONFIG_MICROPY_STACK_SIZE * 1024; - } - else { + if (ESP_ERR_NVS_NOT_FOUND != nvs_get_i32(mpy_nvs_handle, "MPY_StackSize", &mpy_repl_stack_size)) { if ((mpy_repl_stack_size < MPY_MIN_STACK_SIZE) || (mpy_repl_stack_size > MPY_MAX_STACK_SIZE)) { - mpy_repl_stack_size = CONFIG_MICROPY_STACK_SIZE * 1024; - ESP_LOGE("MicroPython","Wrong Stack size set in NVS: %d (set to configured: %d)", mpy_heap_size, CONFIG_MICROPY_HEAP_SIZE * 1024); + mpy_repl_stack_size = MPY_DEFAULT_STACK_SIZE; + ESP_LOGW("MicroPython","Wrong Stack size set in NVS: %d (set to configured: %d)", mpy_repl_stack_size, MPY_DEFAULT_STACK_SIZE); } else { - ESP_LOGW("MicroPython","Stack size set from NVS: %d (configured: %d)", mpy_repl_stack_size, CONFIG_MICROPY_STACK_SIZE * 1024); + ESP_LOGI("MicroPython","Stack size set from NVS: %d (configured: %d)", mpy_repl_stack_size, MPY_DEFAULT_STACK_SIZE); } } // restore time zone @@ -344,78 +373,130 @@ void micropython_entry(void) tzset(); } } - mpy_repl_stack_size &= 0x7FFFFFFC; + mpy_repl_stack_size &= 0x7FFFFFF8; #ifdef CONFIG_MICROPY_USE_THREADED_REPL mp_task_stack_len = MP_TASK_STACK_LEN; #else - mp_task_stack_len = mpy_repl_stack_size; + mp_task_stack_len = mpy_repl_stack_size / sizeof(StackType_t); #endif - mp_task_stack = heap_caps_malloc(mp_task_stack_len, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + + //if (mpy_use_spiram) mp_task_stack = heap_caps_malloc((mp_task_stack_len * sizeof(StackType_t))+8, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + //else mp_task_stack = heap_caps_malloc((mp_task_stack_len * sizeof(StackType_t))+8, MALLOC_CAP_DMA | MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + mp_task_stack = malloc((mp_task_stack_len * sizeof(StackType_t))+8); if (mp_task_stack == NULL) { - printf("Error allocating stack, Halted.\n"); + ESP_LOGE("MicroPython", "Error allocating stack, HALTED."); return; } + mp_task_stack_end = mp_task_stack + ((mp_task_stack_len * sizeof(StackType_t)) + 8); + ESP_LOGD("MicroPython", "MPy stack: %p - %p (%d)", mp_task_stack, mp_task_stack_end, mp_task_stack_len+8); - // ==== Allocate heap memory ==== + // ========================================== + // ==== Allocate MicroPython HEAP memory ==== + ESP_LOGD("MicroPython","Configure heap"); + mpy_heap_size = MPY_DEFAULT_HEAP_SIZE; if (mpy_nvs_handle != 0) { // Get heap size from NVS - if (ESP_ERR_NVS_NOT_FOUND == nvs_get_i32(mpy_nvs_handle, "MPY_HeapSize", &mpy_heap_size)) { - mpy_heap_size = CONFIG_MICROPY_HEAP_SIZE * 1024; - } - else { + if (ESP_ERR_NVS_NOT_FOUND != nvs_get_i32(mpy_nvs_handle, "MPY_HeapSize", &mpy_heap_size)) { if ((mpy_heap_size < MPY_MIN_HEAP_SIZE) || (mpy_heap_size > MPY_MAX_HEAP_SIZE)) { - mpy_heap_size = CONFIG_MICROPY_HEAP_SIZE * 1024; - ESP_LOGE("MicroPython","Wrong Heap size set in NVS: %d (set to configured: %d)", mpy_heap_size, CONFIG_MICROPY_HEAP_SIZE * 1024); + mpy_heap_size = MPY_DEFAULT_HEAP_SIZE; + ESP_LOGW("MicroPython", "Wrong Heap size set in NVS: %d (set to configured: %d)", mpy_heap_size, MPY_DEFAULT_HEAP_SIZE); } else { - ESP_LOGW("MicroPython","Heap size set from NVS: %d (configured: %d)", mpy_heap_size, CONFIG_MICROPY_HEAP_SIZE * 1024); + ESP_LOGI("MicroPython", "Heap size set from NVS: %d (configured: %d)", mpy_heap_size, MPY_DEFAULT_HEAP_SIZE); } } } - mpy_heap_size &= 0x7FFFFFFC; - #if CONFIG_SPIRAM_SUPPORT + mpy_heap_size &= 0x7FFFFFF0; + + if (mpy_use_spiram) { // ## USING SPI RAM FOR HEAP ## #if CONFIG_SPIRAM_USE_CAPS_ALLOC - mp_task_heap = heap_caps_malloc(mpy_heap_size, MALLOC_CAP_SPIRAM); - #elif SPIRAM_USE_MEMMAP + mp_task_heap = heap_caps_malloc(mpy_heap_size+16, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + #elif CONFIG_SPIRAM_USE_MEMMAP mp_task_heap = (uint8_t *)0x3f800000; + #elif CONFIG_SPIRAM_USE_MALLOC + mp_task_heap = malloc(mpy_heap_size+16); + //mp_task_heap = heap_caps_malloc(mpy_heap_size+16, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); #else - mp_task_heap = malloc(mpy_heap_size); + ESP_LOGE("MicroPython", "Unknown SPIRAM configuration, HALTED."); + return; #endif - #else + } + else { // ## USING DRAM FOR HEAP ## - mp_task_heap = malloc(mpy_heap_size); - #endif + mp_task_heap = malloc(mpy_heap_size+16); + //mp_task_heap = heap_caps_malloc(mpy_heap_size+16, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + } if (mp_task_heap == NULL) { - printf("Error allocating heap, Halted.\n"); + ESP_LOGE("MicroPython", "Error allocating heap, HALTED."); return; } + ESP_LOGD("MicroPython", "MPy heap: %p - %p", mp_task_heap, mp_task_heap+mpy_heap_size+64); // Workaround for possible bug in i2c driver !? + //ToDo: Is it still needed? periph_module_disable(PERIPH_I2C0_MODULE); periph_module_enable(PERIPH_I2C0_MODULE); - printf("\n[=== MicroPython started ===]\n"); - // ==== Create and start main MicroPython task ==== + // === Set esp32 log levels while running MicroPython === + if (CONFIG_MICRO_PY_LOG_LEVEL < CONFIG_LOG_DEFAULT_LEVEL) esp_log_level_set("*", CONFIG_MICRO_PY_LOG_LEVEL); + if ((CONFIG_LOG_DEFAULT_LEVEL > ESP_LOG_WARN) && (CONFIG_MICRO_PY_LOG_LEVEL > ESP_LOG_WARN)){ + esp_log_level_set("wifi", ESP_LOG_WARN); + esp_log_level_set("rmt", ESP_LOG_WARN); + esp_log_level_set("tcpip_adapter", ESP_LOG_WARN); + esp_log_level_set("event", ESP_LOG_WARN); + esp_log_level_set("nvs", ESP_LOG_WARN); + esp_log_level_set("phy_init", ESP_LOG_WARN); + esp_log_level_set("wl_flash", ESP_LOG_WARN); + esp_log_level_set("RTC_MODULE", ESP_LOG_WARN); + } + #ifdef CONFIG_MICROPY_USE_OTA + if (CONFIG_LOG_DEFAULT_LEVEL >= ESP_LOG_DEBUG) esp_log_level_set("OTA_UPDATE", ESP_LOG_DEBUG); + else esp_log_level_set("OTA_UPDATE", CONFIG_LOG_DEFAULT_LEVEL); + #endif + + #ifdef CONFIG_MICROPY_USE_MQTT + esp_log_level_set("MQTT_CLIENT", CONFIG_MQTT_LOG_LEVEL); + #endif + #ifdef CONFIG_MICROPY_USE_FTPSERVER + esp_log_level_set(FTP_TAG, CONFIG_FTPSERVER_LOG_LEVEL); + #endif + esp_log_level_set("MicroPython", CONFIG_LOG_DEFAULT_LEVEL); + + // ================================================ + // ==== Create and start main MicroPython task ==== + // ================================================ #if CONFIG_FREERTOS_UNICORE MainTaskCore = 0; - MainTaskHandle = xTaskCreateStaticPinnedToCore(&mp_task, "mp_task", mp_task_stack_len, NULL, MP_TASK_PRIORITY, &mp_task_stack[0], &mp_task_tcb, 0); + MainTaskHandle = xTaskCreateStaticPinnedToCore(&mp_task, "mp_task", mp_task_stack_len, NULL, CONFIG_MICROPY_TASK_PRIORITY, mp_task_stack, &mp_task_tcb, 0); #else MainTaskCore = 1; #if CONFIG_MICROPY_USE_BOTH_CORES - MainTaskHandle = xTaskCreateStatic(&mp_task, "mp_task", mp_task_stack_len, NULL, MP_TASK_PRIORITY, &mp_task_stack[0], &mp_task_tcb); + MainTaskHandle = xTaskCreateStatic(&mp_task, "mp_task", mp_task_stack_len, NULL, CONFIG_MICROPY_TASK_PRIORITY, mp_task_stack, &mp_task_tcb); #else - MainTaskHandle = xTaskCreateStaticPinnedToCore(&mp_task, "mp_task", mp_task_stack_len, NULL, MP_TASK_PRIORITY, &mp_task_stack[0], &mp_task_tcb, 1); + MainTaskHandle = xTaskCreateStaticPinnedToCore(&mp_task, "mp_task", mp_task_stack_len, NULL, CONFIG_MICROPY_TASK_PRIORITY, mp_task_stack, &mp_task_tcb, 1); #endif #endif + + if (!MainTaskHandle) { + ESP_LOGE("MicroPython", "Error creating MicroPython task, HALTED."); + return; + } + + ESP_LOGD("MicroPython", "Main task min stack: %d", uxTaskGetStackHighWaterMark(NULL)); + /* + while (1) { + vTaskDelay(60000 / portTICK_PERIOD_MS); + } + */ } //----------------------------- void nlr_jump_fail(void *val) { - printf("RESET: NLR jump failed, val=%p\n", val); - prepareSleepReset(1, NULL); + //printf("RESET: NLR jump failed, val=%p\n", val); + //prepareSleepReset(1, NULL); esp_restart(); } diff --git a/MicroPython_BUILD/components/micropython/esp32/modcurl.c b/MicroPython_BUILD/components/micropython/esp32/modcurl.c index 48ceb347..82611e10 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modcurl.c +++ b/MicroPython_BUILD/components/micropython/esp32/modcurl.c @@ -41,7 +41,7 @@ #include "py/obj.h" #include "py/runtime.h" - +#include "modmachine.h" #include "libs/espcurl.h" #include "libs/curl_mail.h" #include "extmod/vfs_native.h" diff --git a/MicroPython_BUILD/components/micropython/esp32/moddisplay.c b/MicroPython_BUILD/components/micropython/esp32/moddisplay.c index 91728c50..88378005 100644 --- a/MicroPython_BUILD/components/micropython/esp32/moddisplay.c +++ b/MicroPython_BUILD/components/micropython/esp32/moddisplay.c @@ -1484,6 +1484,22 @@ STATIC mp_obj_t display_tft_set_fg(mp_obj_t self_in, mp_obj_t color_in) } STATIC MP_DEFINE_CONST_FUN_OBJ_2(display_tft_set_fg_obj, display_tft_set_fg); +//------------------------------------------------- +STATIC mp_obj_t display_tft_get_X(mp_obj_t self_in) +{ + int x = TFT_X; + return mp_obj_new_int(x); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(display_tft_get_X_obj, display_tft_get_X); + +//------------------------------------------------- +STATIC mp_obj_t display_tft_get_Y(mp_obj_t self_in) +{ + int y = TFT_Y; + return mp_obj_new_int(y); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(display_tft_get_Y_obj, display_tft_get_Y); + //================================================================ STATIC const mp_rom_map_elem_t display_tft_locals_dict_table[] = { @@ -1528,6 +1544,8 @@ STATIC const mp_rom_map_elem_t display_tft_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_get_bg), MP_ROM_PTR(&display_tft_get_bg_obj) }, { MP_ROM_QSTR(MP_QSTR_set_fg), MP_ROM_PTR(&display_tft_set_fg_obj) }, { MP_ROM_QSTR(MP_QSTR_set_bg), MP_ROM_PTR(&display_tft_set_bg_obj) }, + { MP_ROM_QSTR(MP_QSTR_text_x), MP_ROM_PTR(&display_tft_get_X_obj) }, + { MP_ROM_QSTR(MP_QSTR_text_y), MP_ROM_PTR(&display_tft_get_Y_obj) }, { MP_ROM_QSTR(MP_QSTR_tft_setspeed), MP_ROM_PTR(&display_tft_set_speed_obj) }, { MP_ROM_QSTR(MP_QSTR_tft_select), MP_ROM_PTR(&display_tft_select_obj) }, diff --git a/MicroPython_BUILD/components/micropython/esp32/modmachine.c b/MicroPython_BUILD/components/micropython/esp32/modmachine.c index bcdae4b0..e959926f 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modmachine.c +++ b/MicroPython_BUILD/components/micropython/esp32/modmachine.c @@ -69,12 +69,22 @@ #if MICROPY_PY_MACHINE -nvs_handle mpy_nvs_handle = 0; +// === Global variables === +bool mpy_use_spiram = false; +nvs_handle mpy_nvs_handle = 0; machine_rtc_config_t RTC_DATA_ATTR machine_rtc_config = {0}; int mpy_repl_stack_size = CONFIG_MICROPY_STACK_SIZE * 1024; int mpy_heap_size = CONFIG_MICROPY_HEAP_SIZE * 1024; - +int MPY_DEFAULT_STACK_SIZE = 16*1024; +int MPY_MAX_STACK_SIZE = 32*1024; +int MPY_DEFAULT_HEAP_SIZE = 80*1024; +int MPY_MIN_HEAP_SIZE = 48*1024; +int MPY_MAX_HEAP_SIZE = 96*1024; +int hdr_maxlen = 512; +int body_maxlen = 1024; +int ssh2_hdr_maxlen = 512; +int ssh2_body_maxlen = 1024; // === Variables stored in RTC_SLOW_MEM === static uint64_t RTC_DATA_ATTR s_t_wake; @@ -396,17 +406,18 @@ STATIC mp_obj_t machine_heap_info(void) { heap_caps_get_info(&info, MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT | MALLOC_CAP_8BIT | MALLOC_CAP_DMA); print_heap_info(&info); - #if CONFIG_SPIRAM_SUPPORT - #if SPIRAM_USE_MEMMAP + if (mpy_use_spiram) { + #if CONFIG_SPIRAM_USE_MEMMAP mp_printf(&mp_plat_print, "\nSPIRAM info (MEMMAP used):\n--------------------------\n"); - mp_printf(&mp_plat_print, "Total: %u\n", CONFIG_SPIRAM_SIZE); - mp_printf(&mp_plat_print, " Free: %u\n", CONFIG_SPIRAM_SIZE - (CONFIG_MICROPY_HEAP_SIZE * 1024); + mp_printf(&mp_plat_print, " Total: %u\n", CONFIG_SPIRAM_SIZE); + mp_printf(&mp_plat_print, "Used for MPy heap: %u\n", mpy_heap_size); + mp_printf(&mp_plat_print, " Free (not used): %u\n", CONFIG_SPIRAM_SIZE - mpy_heap_size); #else mp_printf(&mp_plat_print, "\nSPIRAM info:\n------------\n"); heap_caps_get_info(&info, MALLOC_CAP_SPIRAM); print_heap_info(&info); #endif - #endif + } return mp_const_none; } @@ -964,7 +975,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_Neopixel), MP_ROM_PTR(&machine_neopixel_type) }, { MP_OBJ_NEW_QSTR(MP_QSTR_DHT), MP_ROM_PTR(&machine_dht_type) }, { MP_OBJ_NEW_QSTR(MP_QSTR_Onewire), MP_ROM_PTR(&machine_onewire_type) }, - + #ifdef CONFIG_MICROPY_USE_GPS + { MP_OBJ_NEW_QSTR(MP_QSTR_GPS), MP_ROM_PTR(&machine_gps_type) }, + #endif // Constants { MP_ROM_QSTR(MP_QSTR_LOG_NONE), MP_ROM_INT(ESP_LOG_NONE) }, { MP_ROM_QSTR(MP_QSTR_LOG_ERROR), MP_ROM_INT(ESP_LOG_ERROR) }, diff --git a/MicroPython_BUILD/components/micropython/esp32/modmachine.h b/MicroPython_BUILD/components/micropython/esp32/modmachine.h index 8015b770..daab67aa 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modmachine.h +++ b/MicroPython_BUILD/components/micropython/esp32/modmachine.h @@ -38,20 +38,6 @@ #include "driver/rtc_io.h" #define MPY_MIN_STACK_SIZE (6*1024) -#if CONFIG_SPIRAM_SUPPORT -#define MPY_MAX_STACK_SIZE (64*1024) -#define MPY_MIN_HEAP_SIZE (128*1024) -#define MPY_MAX_HEAP_SIZE (3584*1024) -#else -#define MPY_MAX_STACK_SIZE (32*1024) -#define MPY_MIN_HEAP_SIZE (48*1024) -#if defined(CONFIG_MICROPY_USE_CURL) && defined(CONFIG_MICROPY_USE_CURL_TLS) -#define MPY_MAX_HEAP_SIZE (72*1024) -#else -#define MPY_MAX_HEAP_SIZE (96*1024) -#endif -#endif - #define EXT1_WAKEUP_ALL_HIGH 2 //!< Wake the chip when all selected GPIOs go high #define EXT1_WAKEUP_MAX_PINS 4 @@ -75,6 +61,17 @@ typedef struct { uint8_t stub_outpin_level; } machine_rtc_config_t; +extern bool mpy_use_spiram; +extern int MPY_DEFAULT_STACK_SIZE; +extern int MPY_MAX_STACK_SIZE; +extern int MPY_DEFAULT_HEAP_SIZE; +extern int MPY_MIN_HEAP_SIZE; +extern int MPY_MAX_HEAP_SIZE; +extern int hdr_maxlen; +extern int body_maxlen; +extern int ssh2_hdr_maxlen; +extern int ssh2_body_maxlen; + extern machine_rtc_config_t machine_rtc_config; extern const mp_obj_type_t machine_timer_type; @@ -90,7 +87,9 @@ extern const mp_obj_type_t machine_neopixel_type; extern const mp_obj_type_t machine_dht_type; extern const mp_obj_type_t machine_onewire_type; extern const mp_obj_type_t machine_ds18x20_type; - +#ifdef CONFIG_MICROPY_USE_GPS +extern const mp_obj_type_t machine_gps_type; +#endif extern nvs_handle mpy_nvs_handle; extern int mpy_repl_stack_size; extern int mpy_heap_size; diff --git a/MicroPython_BUILD/components/micropython/esp32/modmqtt.c b/MicroPython_BUILD/components/micropython/esp32/modmqtt.c index dadcbc70..2893f7b6 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modmqtt.c +++ b/MicroPython_BUILD/components/micropython/esp32/modmqtt.c @@ -39,162 +39,153 @@ #include #include -#include "mqtt.h" +#include "mqtt_client.h" +#include "http_parser.h" #include "py/nlr.h" #include "py/runtime.h" #include "modmachine.h" #include "mphalport.h" +#include "extmod/vfs_native.h" +#define CONFIG_MQTT_MAX_TASKNAME_LEN 16 typedef struct _mqtt_obj_t { mp_obj_base_t base; - mqtt_client *client; + esp_mqtt_client_handle_t client; + esp_mqtt_client_config_t mqtt_cfg; char name[CONFIG_MQTT_MAX_TASKNAME_LEN]; + void *mpy_connected_cb; + void *mpy_disconnected_cb; + void *mpy_subscribed_cb; + void *mpy_unsubscribed_cb; + void *mpy_published_cb; + void *mpy_data_cb; + uint8_t *msgbuf; + uint8_t *topicbuf; + char *certbuf; + uint8_t subs_flag; + uint8_t unsubs_flag; + uint8_t publish_flag; } mqtt_obj_t; const mp_obj_type_t mqtt_type; -//-------------------------------------- -STATIC int checkClient(mqtt_obj_t *self) +//------------------------------------------ +STATIC int checkClient(mqtt_obj_t *mqtt_obj) { - if (self->client == NULL) { + if (mqtt_obj->client == NULL) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Mqtt client destroyed")); } - if (self->client->status == MQTT_STATUS_DISCONNECTED) { - //nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Mqtt client disconnected")); - return 1; - } - if (self->client->status == MQTT_STATUS_STOPPING) { - //nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Mqtt client stopping")); - return 2; - } - if (self->client->status == MQTT_STATUS_STOPPED) { - //nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Mqtt client stopped")); - return 3; - } - return 0; + return mqtt_obj->client->state; } -//------------------------------------------------ -STATIC void connected_cb(void *self, void *params) +//---------------------------------------- +STATIC void connected_cb(mqtt_obj_t *self) { - mqtt_client *client = (mqtt_client *)self; - - if (client->settings->mpy_connected_cb) { + if (self->mpy_connected_cb) { mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_SINGLE); if (!carg) return; - if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(client->name), (const uint8_t *)client->name, NULL)) return; - mp_sched_schedule(client->settings->mpy_connected_cb, mp_const_none, carg); + if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(self->name), (const uint8_t *)self->name, NULL)) return; + mp_sched_schedule(self->mpy_connected_cb, mp_const_none, carg); } } -//--------------------------------------------------- -STATIC void disconnected_cb(void *self, void *params) +//------------------------------------------- +STATIC void disconnected_cb(mqtt_obj_t *self) { - mqtt_client *client = (mqtt_client *)self; - - if (client->settings->mpy_disconnected_cb) { + if (self->mpy_disconnected_cb) { mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_SINGLE); if (!carg) return; - if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(client->name), (const uint8_t *)client->name, NULL)) return; - mp_sched_schedule(client->settings->mpy_disconnected_cb, mp_const_none, carg); + if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(self->name), (const uint8_t *)self->name, NULL)) return; + mp_sched_schedule(self->mpy_disconnected_cb, mp_const_none, carg); } } -//------------------------------------------------- -STATIC void subscribed_cb(void *self, void *params) +//------------------------------------------------------------ +STATIC void subscribed_cb(mqtt_obj_t *self, const char *topic) { - mqtt_client *client = (mqtt_client *)self; - const char *topic = (const char *)params; - - if (client->settings->mpy_subscribed_cb) { + if (self->mpy_subscribed_cb) { mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_TUPLE); if (carg == NULL) return; - if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(client->name), (const uint8_t *)client->name, NULL)) return; + if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(self->name), (const uint8_t *)self->name, NULL)) return; if (topic) { if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, strlen(topic), (const uint8_t *)topic, NULL)) return; } else { if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, 1, (const uint8_t *)"?", NULL)) return; } - mp_sched_schedule(client->settings->mpy_subscribed_cb, mp_const_none, carg); + mp_sched_schedule(self->mpy_subscribed_cb, mp_const_none, carg); } } -//--------------------------------------------------- -STATIC void unsubscribed_cb(void *self, void *params) +//-------------------------------------------------------------- +STATIC void unsubscribed_cb(mqtt_obj_t *self, const char *topic) { - mqtt_client *client = (mqtt_client *)self; - const char *topic = (const char *)params; - - if (client->settings->mpy_unsubscribed_cb) { + if (self->mpy_unsubscribed_cb) { mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_TUPLE); if (carg == NULL) return; - if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(client->name), (const uint8_t *)client->name, NULL)) return; + if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(self->name), (const uint8_t *)self->name, NULL)) return; if (topic) { if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, strlen(topic), (const uint8_t *)topic, NULL)) return; } else { if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, 1, (const uint8_t *)"?", NULL)) return; } - mp_sched_schedule(client->settings->mpy_unsubscribed_cb, mp_const_none, carg); + mp_sched_schedule(self->mpy_unsubscribed_cb, mp_const_none, carg); } } -//------------------------------------------------ -STATIC void published_cb(void *self, void *params) +//--------------------------------------------------------------------- +STATIC void published_cb(mqtt_obj_t *self, const char *topic, int type) { - mqtt_client *client = (mqtt_client *)self; - const char *type = (const char *)params; - - if (client->settings->mpy_published_cb) { + if (self->mpy_published_cb) { mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_TUPLE); if (carg == NULL) return; - if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(client->name), (const uint8_t *)client->name, NULL)) return; - if (type) { - if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, strlen(type), (const uint8_t *)type, NULL)) return; + if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(self->name), (const uint8_t *)self->name, NULL)) return; + if (topic) { + if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, strlen(topic), (const uint8_t *)topic, NULL)) return; } else { if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, 1, (const uint8_t *)"?", NULL)) return; } - mp_sched_schedule(client->settings->mpy_published_cb, mp_const_none, carg); + if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_INT, type, NULL, NULL)) return; + mp_sched_schedule(self->mpy_published_cb, mp_const_none, carg); } } -//------------------------------------------- -STATIC void data_cb(void *self, void *params) +//------------------------------------------------- +STATIC void data_cb(mqtt_obj_t *self, void *params) { - mqtt_client *client = (mqtt_client *)self; - if (!client->settings->mpy_data_cb) return; + if (!self->mpy_data_cb) return; - mqtt_event_data_t *event_data = (mqtt_event_data_t *)params; + esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)params; - if (event_data->data_offset == 0) { + if (event->current_data_offset == 0) { // *** First block of data - if (client->msgbuf != NULL) free(client->msgbuf); - if (client->topicbuf != NULL) free(client->topicbuf); - client->msgbuf = NULL; - client->topicbuf = NULL; - if (event_data->data_length < event_data->data_total_length) { + if (self->msgbuf != NULL) free(self->msgbuf); + if (self->topicbuf != NULL) free(self->topicbuf); + self->msgbuf = NULL; + self->topicbuf = NULL; + if (event->data_len < event->total_data_len) { // === more data will follow, allocate the data buffer and copy the first part === - client->topicbuf = malloc(event_data->topic_length + 1); - if (client->topicbuf) { - memcpy(client->topicbuf, event_data->topic, event_data->topic_length); - client->topicbuf[event_data->topic_length] = 0; - - int buf_len = event_data->data_total_length + 1; - client->msgbuf = malloc(buf_len + 1); - if (client->msgbuf) { - memcpy(client->msgbuf, event_data->data, event_data->data_length); - client->msgbuf[event_data->data_length] = 0; + self->topicbuf = malloc(event->topic_len + 1); + if (self->topicbuf) { + memcpy(self->topicbuf, event->topic, event->topic_len); + self->topicbuf[event->topic_len] = 0; + + int buf_len = event->total_data_len + 1; + self->msgbuf = malloc(buf_len + 1); + if (self->msgbuf) { + memcpy(self->msgbuf, event->data, event->data_len); + self->msgbuf[event->data_len] = 0; } else { - free(client->topicbuf); - client->msgbuf = NULL; - client->topicbuf = NULL; + free(self->topicbuf); + self->msgbuf = NULL; + self->topicbuf = NULL; } } } @@ -202,44 +193,111 @@ STATIC void data_cb(void *self, void *params) // === all data received, we can schedule the callback function now === mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_TUPLE); if (!carg) return; - if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(client->name), (const uint8_t *)client->name, NULL)) return; - if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, event_data->topic_length, (const uint8_t *)event_data->topic, NULL)) return; - if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_STR, event_data->data_length, (const uint8_t *)event_data->data, NULL)) return; - mp_sched_schedule(client->settings->mpy_data_cb, mp_const_none, carg); + if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(self->name), (const uint8_t *)self->name, NULL)) return; + if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, event->topic_len, (const uint8_t *)event->topic, NULL)) return; + if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_STR, event->data_len, (const uint8_t *)event->data, NULL)) return; + mp_sched_schedule(self->mpy_data_cb, mp_const_none, carg); } } else { - if ((client->topicbuf) && (client->msgbuf)) { + if ((self->topicbuf) && (self->msgbuf)) { // === more payload data arrived, add to buffer === - int new_len = event_data->data_offset + event_data->data_length; - memcpy(client->msgbuf + event_data->data_offset, event_data->data, event_data->data_length); - client->msgbuf[new_len] = 0; - if (new_len >= event_data->data_total_length) { + int new_len = event->current_data_offset + event->data_len; + memcpy(self->msgbuf + event->current_data_offset, event->data, event->data_len); + self->msgbuf[new_len] = 0; + if (new_len >= event->total_data_len) { // === all data received, we can schedule the callback function now === mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_TUPLE); if (!carg) goto freebufs; - if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(client->name), (const uint8_t *)client->name, NULL)) goto freebufs; - if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, strlen((const char *)client->topicbuf), client->topicbuf, NULL)) goto freebufs; - if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_STR, event_data->data_total_length, client->msgbuf, NULL)) goto freebufs; - mp_sched_schedule(client->settings->mpy_data_cb, mp_const_none, carg); + if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_STR, strlen(self->name), (const uint8_t *)self->name, NULL)) goto freebufs; + if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_STR, strlen((const char *)self->topicbuf), self->topicbuf, NULL)) goto freebufs; + if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_STR, event->total_data_len, self->msgbuf, NULL)) goto freebufs; + mp_sched_schedule(self->mpy_data_cb, mp_const_none, carg); freebufs: // Free the buffers - free(client->msgbuf); - free(client->topicbuf); - client->msgbuf = NULL; - client->topicbuf = NULL; + free(self->msgbuf); + free(self->topicbuf); + self->msgbuf = NULL; + self->topicbuf = NULL; } } else { // more payload data arrived, but there is no data buffers allocated (!?) - if (client->msgbuf != NULL) free(client->msgbuf); - if (client->topicbuf != NULL) free(client->topicbuf); - client->msgbuf = NULL; - client->topicbuf = NULL; + if (self->msgbuf != NULL) free(self->msgbuf); + if (self->topicbuf != NULL) free(self->topicbuf); + self->msgbuf = NULL; + self->topicbuf = NULL; } } } +//---------------------------------------------------------------- +static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) +{ + esp_mqtt_client_handle_t client = event->client; + mqtt_obj_t *mpy_client = (mqtt_obj_t *)client->mpy_mqtt_obj; + const char *topic = NULL; + // your_context_t *context = event->context; + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(MQTT_TAG, "Connected"); + connected_cb(mpy_client); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(MQTT_TAG, "Disconnected"); + disconnected_cb(mpy_client); + break; + + case MQTT_EVENT_SUBSCRIBED: + if (client->config->user_context) { + topic = (const char *)client->config->user_context; + ESP_LOGI(MQTT_TAG, "Subscribed to '%s'", topic); + } + else { + ESP_LOGI(MQTT_TAG, "Subscribed"); + } + subscribed_cb(mpy_client, topic); + mpy_client->subs_flag = 1; + break; + case MQTT_EVENT_UNSUBSCRIBED: + if (client->config->user_context) { + topic = (const char *)client->config->user_context; + ESP_LOGI(MQTT_TAG, "Unsubscribed from '%s'", topic); + } + else { + ESP_LOGI(MQTT_TAG, "Unsubscribed"); + } + unsubscribed_cb(mpy_client, topic); + mpy_client->unsubs_flag = 1; + break; + case MQTT_EVENT_PUBLISHED: + if (client->config->user_context) { + topic = (const char *)client->config->user_context; + ESP_LOGI(MQTT_TAG, "Published to '%s'", topic); + } + else { + ESP_LOGI(MQTT_TAG, "Published"); + } + published_cb(mpy_client, topic, event->type); + mpy_client->publish_flag = 1; + break; + case MQTT_EVENT_DATA: + if (mpy_client->mpy_data_cb == NULL) { + ESP_LOGI(MQTT_TAG, "TOPIC: %.*s\r\n", event->topic_len, event->topic); + ESP_LOGI(MQTT_TAG, " DATA: %.*s\r\n", event->data_len, event->data); + } + else { + ESP_LOGI(MQTT_TAG, "Data received"); + } + data_cb(mpy_client, event); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(MQTT_TAG, "Mqtt Error"); + break; + } + return ESP_OK; +} + //------------------------------------------------------------------------------------- STATIC void mqtt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) @@ -251,23 +309,33 @@ STATIC void mqtt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ return; } char sstat[16]; - if (self->client->status == MQTT_STATUS_CONNECTED) sprintf(sstat, "Connected"); - else if (self->client->status == MQTT_STATUS_DISCONNECTED) sprintf(sstat, "Disconnected"); - else if (self->client->status == MQTT_STATUS_STOPPING) sprintf(sstat, "Stopping"); - else if (self->client->status == MQTT_STATUS_STOPPED) sprintf(sstat, "Stopped"); - else sprintf(sstat, "Unknown"); - - mp_printf(print, "Mqtt[%s](Server: %s:%u, Status: %s\n", self->name, self->client->settings->host, self->client->settings->port, sstat); - if ((self->client->status != MQTT_STATUS_STOPPING) && (self->client->status != MQTT_STATUS_STOPPED)) { - mp_printf(print, " Client ID: %s, Clean session=%s, Keepalive=%d sec, QoS=%d, Retain=%s, Secure=%s\n", - self->client->settings->client_id, (self->client->settings->clean_session ? "True" : "False"), self->client->settings->keepalive, self->client->settings->lwt_qos, - (self->client->settings->lwt_retain ? "True" : "False"), (self->client->settings->use_ssl ? "True" : "False")); + if (self->client->state == MQTT_STATE_CONNECTED) sprintf(sstat, "Connected"); + else if (self->client->state == MQTT_STATE_INIT) sprintf(sstat, "Initialized"); + else if (self->client->state == MQTT_STATE_WAIT_TIMEOUT) sprintf(sstat, "Wait timeout"); + else if (self->client->state == MQTT_STATE_UNKNOWN) sprintf(sstat, "Unknown"); + else sprintf(sstat, "Error"); + + const char *server_uri = "Unknown"; + if (self->client->config->uri) server_uri = self->client->config->uri; + else if (self->client->config->host) server_uri = self->client->config->host; + + mp_printf(print, "Mqtt[%s]\n (Server: %s:%u, Status: %s\n", self->name, server_uri, self->client->config->port, sstat); + if ((self->client->state == MQTT_STATE_CONNECTED) || (self->client->state == MQTT_STATE_INIT)) { + mp_printf(print, " Client ID: %s, Clean session=%s, Keepalive=%ds\n LWT(", + self->client->connect_info.client_id, (self->client->connect_info.clean_session ? "True" : "False"), self->client->connect_info.keepalive); + if (self->client->connect_info.will_topic) { + mp_printf(print, "QoS=%d, Retain=%s, Topic='%s', Msg='%s')\n", + self->client->connect_info.will_qos, (self->client->connect_info.will_retain ? "True" : "False"), self->client->connect_info.will_topic, self->client->connect_info.will_message); + } + else mp_printf(print, "not set)\n"); } + /* if ((self->client->settings->xMqttTask) && (self->client->settings->xMqttSendingTask)) { mp_printf(print, " Used stack: %u/%u + %u/%u\n", self->client->settings->xMqttTask_stacksize - uxTaskGetStackHighWaterMark(self->client->settings->xMqttTask), self->client->settings->xMqttTask_stacksize, self->client->settings->xMqttSendingTask_stacksize - uxTaskGetStackHighWaterMark(self->client->settings->xMqttSendingTask), self->client->settings->xMqttSendingTask_stacksize); } + */ mp_printf(print, " )\n"); } @@ -275,8 +343,8 @@ STATIC void mqtt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ //------------------------------------------------------------------------------------------------------------ STATIC mp_obj_t mqtt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_name, ARG_host, ARG_user, ARG_pass, ARG_port, ARG_reconnect, ARG_clientid, ARG_cleansess, ARG_keepalive, ARG_qos, ARG_retain, ARG_secure, - ARG_datacb, ARG_connected, ARG_disconnected, ARG_subscribed, ARG_unsubscribed, ARG_published }; + enum { ARG_name, ARG_server, ARG_user, ARG_pass, ARG_port, ARG_reconnect, ARG_clientid, ARG_cleansess, ARG_keepalive, ARG_cert, + ARG_lwt_topic, ARG_lwt_msg, ARG_lwt_qos, ARG_lwt_retain, ARG_datacb, ARG_connected, ARG_disconnected, ARG_subscribed, ARG_unsubscribed, ARG_published }; const mp_arg_t mqtt_init_allowed_args[] = { { MP_QSTR_name, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -287,10 +355,12 @@ STATIC mp_obj_t mqtt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n { MP_QSTR_autoreconnect, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_clientid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_cleansession, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_keepalive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 120} }, - { MP_QSTR_qos, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_retain, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_secure, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_keepalive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MQTT_KEEPALIVE_TICK} }, + { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_lwt_topic, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_lwt_msg, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_lwt_qos, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_lwt_retain, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_data_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_connected_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_disconnected_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -301,93 +371,165 @@ STATIC mp_obj_t mqtt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_arg_val_t args[MP_ARRAY_SIZE(mqtt_init_allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(mqtt_init_allowed_args), mqtt_init_allowed_args, args); - // Setup the mqtt object + const char *tstr = NULL; + + // Create the mqtt object mqtt_obj_t *self = m_new_obj(mqtt_obj_t ); + memset(self, 0 , sizeof(mqtt_obj_t)); - // === allocate client memory === - self->client = malloc(sizeof(mqtt_client)); - if (self->client == NULL) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "Error allocating client memory")); - } - memset(self->client, 0, sizeof(mqtt_client)); + // Populate settings + esp_mqtt_client_config_t mqtt_cfg = {0}; - self->client->settings = malloc(sizeof(mqtt_settings)); - if (self->client->settings == NULL) { - free(self->client); - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "Error allocating client memory")); - } - memset(self->client->settings, 0, sizeof(mqtt_settings)); + // Event handle + mqtt_cfg.event_handle = mqtt_event_handler; - // Populate settings - self->client->settings->use_ssl = args[ARG_secure].u_bool; + // Object name + tstr = mp_obj_str_get_str(args[ARG_name].u_obj); + if (strlen(tstr) >= CONFIG_MQTT_MAX_TASKNAME_LEN) { + mp_raise_ValueError("Name too long"); + } + sprintf(self->name, "%s", tstr); - snprintf(self->name, CONFIG_MQTT_MAX_TASKNAME_LEN, mp_obj_str_get_str(args[ARG_name].u_obj)); - self->client->name = self->name; - snprintf(self->client->settings->host, CONFIG_MQTT_MAX_HOST_LEN, mp_obj_str_get_str(args[ARG_host].u_obj)); + // Port + if (args[ARG_port].u_int > 0) mqtt_cfg.port = args[ARG_port].u_int; - if (args[ARG_port].u_int > 0) self->client->settings->port = args[ARG_port].u_int; - else if (args[ARG_secure].u_bool) self->client->settings->port = 8883; - else self->client->settings->port = 1883; + // Get URI or server domain + tstr = mp_obj_str_get_str(args[ARG_server].u_obj); + if (strlen(tstr) >= MQTT_MAX_HOST_LEN) { + mp_raise_ValueError("URI too long"); + } + struct http_parser_url puri; + http_parser_url_init(&puri); + int parser_status = http_parser_parse_url(tstr, strlen(tstr), 0, &puri); + if (parser_status != 0) { + sprintf(mqtt_cfg.host, "%s", tstr); + } + else sprintf(mqtt_cfg.uri, "%s", tstr); + // User name if (MP_OBJ_IS_STR(args[ARG_user].u_obj)) { - snprintf(self->client->settings->username, CONFIG_MQTT_MAX_USERNAME_LEN, mp_obj_str_get_str(args[ARG_user].u_obj)); + tstr = mp_obj_str_get_str(args[ARG_user].u_obj); + if (strlen(tstr) >= MQTT_MAX_USERNAME_LEN) { + mp_raise_ValueError("User name too long"); + } + sprintf(mqtt_cfg.username, "%s", tstr); } + // Password if (MP_OBJ_IS_STR(args[ARG_pass].u_obj)) { - snprintf(self->client->settings->password, CONFIG_MQTT_MAX_PASSWORD_LEN, mp_obj_str_get_str(args[ARG_pass].u_obj)); + tstr = mp_obj_str_get_str(args[ARG_pass].u_obj); + if (strlen(tstr) >= MQTT_MAX_PASSWORD_LEN) { + mp_raise_ValueError("Password too long"); + } + sprintf(mqtt_cfg.password, "%s", tstr); } + // Client ID if (MP_OBJ_IS_STR(args[ARG_clientid].u_obj)) { - snprintf(self->client->settings->client_id, CONFIG_MQTT_MAX_CLIENT_LEN, mp_obj_str_get_str(args[ARG_clientid].u_obj)); + tstr = mp_obj_str_get_str(args[ARG_clientid].u_obj); + if (strlen(tstr) >= MQTT_MAX_CLIENT_LEN) { + mp_raise_ValueError("Client ID too long"); + } + sprintf(mqtt_cfg.client_id, "%s", tstr); + } + else sprintf(mqtt_cfg.client_id, "mpy_mqtt_client"); + + mqtt_cfg.disable_auto_reconnect = args[ARG_reconnect].u_int ? false : true; + mqtt_cfg.keepalive = args[ARG_keepalive].u_int; + mqtt_cfg.disable_clean_session = args[ARG_cleansess].u_int ? false : true; + + // LWT options + if (MP_OBJ_IS_STR(args[ARG_lwt_topic].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_lwt_topic].u_obj); + if (strlen(tstr) >= MQTT_MAX_LWT_TOPIC) { + mp_raise_ValueError("LWT topic too long"); + } + sprintf(mqtt_cfg.lwt_topic, "%s", tstr); + if (MP_OBJ_IS_STR(args[ARG_lwt_msg].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_lwt_msg].u_obj); + if (strlen(tstr) >= MQTT_MAX_LWT_MSG) { + mp_raise_ValueError("LWT message too long"); + } + sprintf(mqtt_cfg.lwt_msg, "%s", tstr); + } + else sprintf(mqtt_cfg.lwt_msg, "offline"); + mqtt_cfg.lwt_qos = args[ARG_lwt_qos].u_int; + mqtt_cfg.lwt_retain = args[ARG_lwt_retain].u_int; } - else sprintf(self->client->settings->client_id, "mpy_mqtt_client"); - self->client->settings->auto_reconnect = args[ARG_reconnect].u_int; - self->client->settings->keepalive = args[ARG_keepalive].u_int; - self->client->settings->clean_session = args[ARG_cleansess].u_int; - sprintf(self->client->settings->lwt_topic, "/lwt"); - sprintf(self->client->settings->lwt_msg, "offline"); - self->client->settings->lwt_qos = args[ARG_qos].u_int; - self->client->settings->lwt_retain = args[ARG_retain].u_int; + // Get Certificate from file + if (MP_OBJ_IS_STR(args[ARG_cert].u_obj)) { + char *fname = NULL; + char fullname[128] = {'\0'}; + + fname = (char *)mp_obj_str_get_str(args[ARG_cert].u_obj); + + int res = physicalPath(fname, fullname); + if ((res != 0) || (strlen(fullname) == 0)) { + mp_raise_ValueError("Certificate file not found"); + } + struct stat sb; + if (stat(fullname, &sb) != 0) { + mp_raise_ValueError("Error opening certificate file"); + } + int size = sb.st_size; + FILE *fhndl = fopen(fullname, "rb"); + if (fhndl != NULL) { + if (self->certbuf) { + free(self->certbuf); + self->certbuf = NULL; + } + self->certbuf = malloc(size); + if (self->certbuf == NULL) { + fclose(fhndl); + mp_raise_ValueError("Error allocating certification buffer"); + } + int len = fread(self->certbuf, 1, size, fhndl); // read cert file + fclose(fhndl); + if (len != size) { + free(self->certbuf); + self->certbuf = NULL; + mp_raise_ValueError("Error reading certificate file"); + } + mqtt_cfg.cert_pem = (const char *)self->certbuf; + } + else { + mp_raise_ValueError("Error opening certificate file"); + } + } - // set callbacks + // Set callbacks if ((MP_OBJ_IS_FUN(args[ARG_datacb].u_obj)) || (MP_OBJ_IS_METH(args[ARG_datacb].u_obj))) { - self->client->settings->data_cb = (void*)data_cb; - self->client->settings->mpy_data_cb = args[ARG_datacb].u_obj; + self->mpy_data_cb = args[ARG_datacb].u_obj; } if ((MP_OBJ_IS_FUN(args[ARG_connected].u_obj)) || (MP_OBJ_IS_METH(args[ARG_connected].u_obj))) { - self->client->settings->connected_cb = (void*)connected_cb; - self->client->settings->mpy_connected_cb = args[ARG_connected].u_obj; + self->mpy_connected_cb = args[ARG_connected].u_obj; } if ((MP_OBJ_IS_FUN(args[ARG_disconnected].u_obj)) || (MP_OBJ_IS_METH(args[ARG_disconnected].u_obj))) { - self->client->settings->disconnected_cb = (void*)disconnected_cb; - self->client->settings->mpy_disconnected_cb = args[ARG_disconnected].u_obj; + self->mpy_disconnected_cb = args[ARG_disconnected].u_obj; } if ((MP_OBJ_IS_FUN(args[ARG_subscribed].u_obj)) || (MP_OBJ_IS_METH(args[ARG_subscribed].u_obj))) { - self->client->settings->subscribe_cb = (void*)subscribed_cb; - self->client->settings->mpy_subscribed_cb = args[ARG_subscribed].u_obj; + self->mpy_subscribed_cb = args[ARG_subscribed].u_obj; } if ((MP_OBJ_IS_FUN(args[ARG_unsubscribed].u_obj)) || (MP_OBJ_IS_METH(args[ARG_unsubscribed].u_obj))) { - self->client->settings->unsubscribe_cb = (void*)unsubscribed_cb; - self->client->settings->mpy_unsubscribed_cb = args[ARG_unsubscribed].u_obj; + self->mpy_unsubscribed_cb = args[ARG_unsubscribed].u_obj; } if ((MP_OBJ_IS_FUN(args[ARG_published].u_obj)) || (MP_OBJ_IS_METH(args[ARG_published].u_obj))) { - self->client->settings->publish_cb = (void*)published_cb; - self->client->settings->mpy_published_cb = args[ARG_published].u_obj; + self->mpy_published_cb = args[ARG_published].u_obj; } - // Start the mqtt task - int res = mqtt_start(self->client); - if (res != 0) { - free(self->client->settings); - free(self->client); - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "Error starting client")); + self->base.type = &mqtt_type; + + self->client = esp_mqtt_client_init(&mqtt_cfg); + if (self->client == NULL) { + mp_raise_ValueError("Error initializing mqtt client"); } - self->base.type = &mqtt_type; + self->client->mpy_mqtt_obj = self; + //esp_mqtt_client_start(self->client); return MP_OBJ_FROM_PTR(self); } @@ -395,131 +537,257 @@ STATIC mp_obj_t mqtt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n //------------------------------------------------------------------------------------------ STATIC mp_obj_t mqtt_op_config(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_clientid, ARG_reconnect, ARG_cleansess, ARG_keepalive, ARG_qos, ARG_retain, ARG_secure, - ARG_datacb, ARG_connected, ARG_disconnected, ARG_subscribed, ARG_unsubscribed, ARG_published }; - mqtt_obj_t *self = pos_args[0]; - if (checkClient(self)) return mp_const_none; - - const mp_arg_t mqtt_config_allowed_args[] = { - { MP_QSTR_clientid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_autoreconnect, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_cleansession, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_keepalive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_qos, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_retain, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_secure, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_data_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_connected_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_disconnected_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_subscribed_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_unsubscribed_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_published_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + enum { ARG_server, ARG_user, ARG_pass, ARG_port, ARG_reconnect, ARG_clientid, ARG_cleansess, ARG_keepalive, ARG_lwt_topic, ARG_lwt_msg, + ARG_lwt_qos, ARG_lwt_retain, ARG_datacb, ARG_connected, ARG_disconnected, ARG_subscribed, ARG_unsubscribed, ARG_published }; + + const mp_arg_t mqtt_config_allowed_args[] = { + { MP_QSTR_server, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_port, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_autoreconnect, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_clientid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_cleansession, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_keepalive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_lwt_topic, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_lwt_msg, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_lwt_qos, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_lwt_retain, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_data_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_connected_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_disconnected_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_subscribed_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_unsubscribed_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_published_cb, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; - mp_arg_val_t args[MP_ARRAY_SIZE(mqtt_config_allowed_args)]; + + mqtt_obj_t *self = pos_args[0]; + if (!self->client) { + mp_raise_ValueError("Destroyed"); + } + if (self->client->state < MQTT_STATE_INIT) { + mp_raise_ValueError("Not initialized"); + } + + mp_arg_val_t args[MP_ARRAY_SIZE(mqtt_config_allowed_args)]; mp_arg_parse_all(n_args-1, pos_args+1, kw_args, MP_ARRAY_SIZE(mqtt_config_allowed_args), mqtt_config_allowed_args, args); - if (args[ARG_secure].u_int >= 0) self->client->settings->use_ssl = args[ARG_secure].u_bool; - if (MP_OBJ_IS_STR(args[ARG_clientid].u_obj)) { - snprintf(self->client->settings->client_id, CONFIG_MQTT_MAX_CLIENT_LEN, mp_obj_str_get_str(args[ARG_clientid].u_obj)); + const char *tstr = NULL; + bool has_conn_arg = false; + if ((MP_OBJ_IS_STR(args[ARG_server].u_obj)) || (args[ARG_port].u_int > 0) || (MP_OBJ_IS_STR(args[ARG_user].u_obj)) || + (MP_OBJ_IS_STR(args[ARG_pass].u_obj)) || (MP_OBJ_IS_STR(args[ARG_clientid].u_obj)) || + (args[ARG_reconnect].u_int >= 0) || (args[ARG_keepalive].u_int > 0) || (args[ARG_cleansess].u_int >= 0) || + (MP_OBJ_IS_STR(args[ARG_lwt_topic].u_obj)) ) has_conn_arg = true; + + if ((has_conn_arg) && (self->client->state < MQTT_STATE_CONNECTED)) { + // not started, we can change all parameters + // URI + if (MP_OBJ_IS_STR(args[ARG_server].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_server].u_obj); + if (strlen(tstr) >= MQTT_MAX_HOST_LEN) { + mp_raise_ValueError("URI too long"); + } + sprintf(self->client->config->uri, "%s", tstr); + } + + // Port + if (args[ARG_port].u_int > 0) self->client->config->port = args[ARG_port].u_int; + + // User name + if (MP_OBJ_IS_STR(args[ARG_user].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_user].u_obj); + if (strlen(tstr) >= MQTT_MAX_USERNAME_LEN) { + mp_raise_ValueError("User name too long"); + } + sprintf(self->client->connect_info.username, "%s", tstr); + } + // Password + if (MP_OBJ_IS_STR(args[ARG_pass].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_pass].u_obj); + if (strlen(tstr) >= MQTT_MAX_PASSWORD_LEN) { + mp_raise_ValueError("Password too long"); + } + sprintf(self->client->connect_info.password, "%s", tstr); + } + // Client ID + if (MP_OBJ_IS_STR(args[ARG_clientid].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_clientid].u_obj); + if (strlen(tstr) >= MQTT_MAX_CLIENT_LEN) { + mp_raise_ValueError("Client ID too long"); + } + sprintf(self->client->connect_info.client_id, "%s", tstr); + } + else sprintf(self->client->connect_info.client_id, "mpy_mqtt_client"); + + if (args[ARG_reconnect].u_int >= 0) self->client->config->auto_reconnect = args[ARG_reconnect].u_int ? true : false; + if (args[ARG_keepalive].u_int > 0) self->client->connect_info.keepalive = args[ARG_keepalive].u_int; + if (args[ARG_cleansess].u_int >= 0) self->client->connect_info.clean_session = args[ARG_cleansess].u_int ? true : false; + + // LWT options + if (MP_OBJ_IS_STR(args[ARG_lwt_topic].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_lwt_topic].u_obj); + if (strlen(tstr) >= MQTT_MAX_LWT_TOPIC) { + mp_raise_ValueError("LWT topic too long"); + } + sprintf(self->client->connect_info.will_topic, "%s", tstr); + if (MP_OBJ_IS_STR(args[ARG_lwt_msg].u_obj)) { + tstr = mp_obj_str_get_str(args[ARG_lwt_msg].u_obj); + if (strlen(tstr) >= MQTT_MAX_LWT_MSG) { + mp_raise_ValueError("LWT message too long"); + } + sprintf(self->client->connect_info.will_topic, "%s", tstr); + } + if (args[ARG_lwt_qos].u_int >= 0) self->client->connect_info.will_qos = args[ARG_lwt_qos].u_int; + if (args[ARG_lwt_retain].u_int >= 0) self->client->connect_info.will_retain = args[ARG_lwt_retain].u_int; + } } - if (args[ARG_reconnect].u_int >= 0) self->client->settings->auto_reconnect = args[ARG_reconnect].u_int; - if (args[ARG_keepalive].u_int >= 0) self->client->settings->keepalive = args[ARG_keepalive].u_int; - if (args[ARG_qos].u_int >= 0) self->client->settings->lwt_qos = args[ARG_qos].u_int; - if (args[ARG_retain].u_int >= 0) self->client->settings->lwt_retain = args[ARG_retain].u_int; - if (args[ARG_cleansess].u_int >= 0) self->client->settings->clean_session = args[ARG_cleansess].u_int; + // Callbacks if ((MP_OBJ_IS_FUN(args[ARG_datacb].u_obj)) || (MP_OBJ_IS_METH(args[ARG_datacb].u_obj))) { - self->client->settings->data_cb = NULL; - self->client->settings->mpy_data_cb = args[ARG_datacb].u_obj; - self->client->settings->data_cb = (void*)data_cb; + self->mpy_data_cb = NULL; + self->mpy_data_cb = args[ARG_datacb].u_obj; } + else if (args[ARG_datacb].u_obj == mp_const_false) self->mpy_data_cb = NULL; + if ((MP_OBJ_IS_FUN(args[ARG_connected].u_obj)) || (MP_OBJ_IS_METH(args[ARG_connected].u_obj))) { - self->client->settings->connected_cb = NULL; - self->client->settings->mpy_connected_cb = args[ARG_connected].u_obj; - self->client->settings->connected_cb = (void*)connected_cb; + self->mpy_connected_cb = NULL; + self->mpy_connected_cb = args[ARG_connected].u_obj; } + else if (args[ARG_connected].u_obj == mp_const_false) self->mpy_connected_cb = NULL; + if ((MP_OBJ_IS_FUN(args[ARG_disconnected].u_obj)) || (MP_OBJ_IS_METH(args[ARG_disconnected].u_obj))) { - self->client->settings->disconnected_cb = NULL; - self->client->settings->mpy_disconnected_cb = args[ARG_disconnected].u_obj; - self->client->settings->disconnected_cb = (void*)disconnected_cb; + self->mpy_disconnected_cb = NULL; + self->mpy_disconnected_cb = args[ARG_disconnected].u_obj; } + else if (args[ARG_disconnected].u_obj == mp_const_false) self->mpy_disconnected_cb = NULL; + if ((MP_OBJ_IS_FUN(args[ARG_subscribed].u_obj)) || (MP_OBJ_IS_METH(args[ARG_subscribed].u_obj))) { - self->client->settings->subscribe_cb = NULL; - self->client->settings->mpy_subscribed_cb = args[ARG_subscribed].u_obj; - self->client->settings->subscribe_cb = (void*)subscribed_cb; + self->mpy_subscribed_cb = NULL; + self->mpy_subscribed_cb = args[ARG_subscribed].u_obj; } + else if (args[ARG_subscribed].u_obj == mp_const_false) self->mpy_subscribed_cb = NULL; + if ((MP_OBJ_IS_FUN(args[ARG_unsubscribed].u_obj)) || (MP_OBJ_IS_METH(args[ARG_unsubscribed].u_obj))) { - self->client->settings->unsubscribe_cb = NULL; - self->client->settings->mpy_unsubscribed_cb = args[ARG_unsubscribed].u_obj; - self->client->settings->unsubscribe_cb = (void*)unsubscribed_cb; + self->mpy_unsubscribed_cb = NULL; + self->mpy_unsubscribed_cb = args[ARG_unsubscribed].u_obj; } + else if (args[ARG_unsubscribed].u_obj == mp_const_false) self->mpy_unsubscribed_cb = NULL; + if ((MP_OBJ_IS_FUN(args[ARG_published].u_obj)) || (MP_OBJ_IS_METH(args[ARG_published].u_obj))) { - self->client->settings->publish_cb = NULL; - self->client->settings->mpy_published_cb = args[ARG_published].u_obj; - self->client->settings->publish_cb = (void*)published_cb; + self->mpy_published_cb = NULL; + self->mpy_published_cb = args[ARG_published].u_obj; } + else if (args[ARG_published].u_obj == mp_const_false) self->mpy_published_cb = NULL; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mqtt_config_obj, 1, mqtt_op_config); -//-------------------------------------------------------------------- -STATIC mp_obj_t mqtt_op_subscribe(mp_obj_t self_in, mp_obj_t topic_in) +//----------------------------------------------------------------------- +STATIC mp_obj_t mqtt_op_subscribe(mp_uint_t n_args, const mp_obj_t *args) { - mqtt_obj_t *self = self_in; - if (checkClient(self)) return mp_const_false; + mqtt_obj_t *self = args[0]; + if (checkClient(self) != MQTT_STATE_CONNECTED) return mp_const_false; - const char *topic = mp_obj_str_get_str(topic_in); + const char *topic = mp_obj_str_get_str(args[1]); int wait = 2000; - mqtt_subscribe(self->client, topic, self->client->settings->lwt_qos); - while ((wait > 0) && (self->client->subs_flag == 0)) { + int qos = 0; + if (n_args == 3) { + qos = mp_obj_get_int(args[2]); + if ((qos < 0) || (qos > 2)) { + mp_raise_ValueError("Wrong QoS value"); + } + } + + self->subs_flag = 0; + self->client->config->user_context = (void *)topic; + + int res = esp_mqtt_client_subscribe(self->client, topic, qos); + if (res < 0) { + self->client->config->user_context = NULL; + return mp_const_false; + } + while ((wait > 0) && (self->subs_flag == 0)) { vTaskDelay(10 / portTICK_PERIOD_MS); wait -= 10; } + self->client->config->user_context = NULL; if (wait) return mp_const_true; else return mp_const_false; } -MP_DEFINE_CONST_FUN_OBJ_2(mqtt_subscribe_obj, mqtt_op_subscribe); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mqtt_subscribe_obj, 2, 3, mqtt_op_subscribe); //---------------------------------------------------------------------- STATIC mp_obj_t mqtt_op_unsubscribe(mp_obj_t self_in, mp_obj_t topic_in) { mqtt_obj_t *self = self_in; - if (checkClient(self)) return mp_const_false; + if (checkClient(self) != MQTT_STATE_CONNECTED) return mp_const_false; const char *topic = mp_obj_str_get_str(topic_in); int wait = 2000; - mqtt_unsubscribe(self->client, topic); - while ((wait > 0) && (self->client->unsubs_flag == 0)) { + self->unsubs_flag = 0; + self->client->config->user_context = (void *)topic; + + int res = esp_mqtt_client_unsubscribe(self->client, topic); + if (res < 0) { + self->client->config->user_context = NULL; + return mp_const_false; + } + while ((wait > 0) && (self->unsubs_flag == 0)) { vTaskDelay(10 / portTICK_PERIOD_MS); wait -= 10; } + self->client->config->user_context = NULL; if (wait) return mp_const_true; else return mp_const_false; } MP_DEFINE_CONST_FUN_OBJ_2(mqtt_unsubscribe_obj, mqtt_op_unsubscribe); -//----------------------------------------------------------------------------------- -STATIC mp_obj_t mqtt_op_publish(mp_obj_t self_in, mp_obj_t topic_in, mp_obj_t msg_in) +//--------------------------------------------------------------------- +STATIC mp_obj_t mqtt_op_publish(mp_uint_t n_args, const mp_obj_t *args) { - mqtt_obj_t *self = self_in; - if (checkClient(self)) return mp_const_false; + mqtt_obj_t *self = args[0]; + if (checkClient(self) != MQTT_STATE_CONNECTED) return mp_const_false; size_t len; - const char *topic = mp_obj_str_get_str(topic_in); - const char *msg = mp_obj_str_get_data(msg_in, &len); - int res = mqtt_publish(self->client, topic, msg, len, self->client->settings->lwt_qos, self->client->settings->lwt_retain); + const char *topic = mp_obj_str_get_str(args[1]); + const char *msg = mp_obj_str_get_data(args[2], &len); + + int wait = 2000; + int qos = 0; + if (n_args == 4) { + qos = mp_obj_get_int(args[3]); + if ((qos < 0) || (qos > 2)) { + mp_raise_ValueError("Wrong QoS value"); + } + } + if (qos == 0) wait = 0; + + int retain = 0; + if (n_args == 5) retain = mp_obj_is_true(args[4]); + + self->publish_flag = 0; + self->client->config->user_context = (void *)topic; + + int res = esp_mqtt_client_publish(self->client, topic, msg, len, qos, retain); + if (res < 0) { + self->client->config->user_context = NULL; + return mp_const_false; + } + while ((wait > 0) && (self->publish_flag == 0)) { + vTaskDelay(10 / portTICK_PERIOD_MS); + wait -= 10; + } + self->client->config->user_context = NULL; - if (res < 0) return mp_const_false; return mp_const_true; } -MP_DEFINE_CONST_FUN_OBJ_3(mqtt_publish_obj, mqtt_op_publish); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mqtt_publish_obj, 3, 5, mqtt_op_publish); //---------------------------------------------- STATIC mp_obj_t mqtt_op_status(mp_obj_t self_in) { mqtt_obj_t *self = self_in; - checkClient(self); char sstat[16]; mp_obj_t tuple[2]; @@ -529,12 +797,12 @@ STATIC mp_obj_t mqtt_op_status(mp_obj_t self_in) sprintf(sstat, "Destroyed"); } else { - tuple[0] = mp_obj_new_int(self->client->status); - if (self->client->status == MQTT_STATUS_CONNECTED) sprintf(sstat, "Connected"); - else if (self->client->status == MQTT_STATUS_DISCONNECTED) sprintf(sstat, "Disconnected"); - else if (self->client->status == MQTT_STATUS_STOPPING) sprintf(sstat, "Stopping"); - else if (self->client->status == MQTT_STATUS_STOPPED) sprintf(sstat, "Stopped"); - else sprintf(sstat, "Unknown"); + tuple[0] = mp_obj_new_int(self->client->state); + if (self->client->state == MQTT_STATE_CONNECTED) sprintf(sstat, "Connected"); + else if (self->client->state == MQTT_STATE_INIT) sprintf(sstat, "Initialized"); + else if (self->client->state == MQTT_STATE_WAIT_TIMEOUT) sprintf(sstat, "Wait timeout"); + else if (self->client->state == MQTT_STATE_UNKNOWN) sprintf(sstat, "Unknown"); + else sprintf(sstat, "Error"); } tuple[1] = mp_obj_new_str(sstat, strlen(sstat)); @@ -546,11 +814,13 @@ MP_DEFINE_CONST_FUN_OBJ_1(mqtt_status_obj, mqtt_op_status); STATIC mp_obj_t mqtt_op_stop(mp_obj_t self_in) { mqtt_obj_t *self = self_in; - int status = checkClient(self); - if (status < 2) { - mqtt_stop(self->client); - vTaskDelay(100 / portTICK_RATE_MS); + if ((self->client) && (self->client->state >= MQTT_STATE_INIT)) { + esp_mqtt_client_stop(self->client); + int status = 0; + while ((status < 20) && ((xEventGroupGetBits(self->client->status_bits) & 1) == 0)) { + vTaskDelay(100 / portTICK_RATE_MS); + } } return mp_const_none; } @@ -561,15 +831,12 @@ STATIC mp_obj_t mqtt_op_start(mp_obj_t self_in) { mqtt_obj_t *self = self_in; - if ((self->client) && (self->client->status == MQTT_STATUS_STOPPED) && (self->client->settings->xMqttTask == NULL)) { - int res = mqtt_start(self->client); - if (res != 0) { - free(self->client->settings); - free(self->client); + if ((self->client) && (self->client->state < MQTT_STATE_INIT)) { + int res = esp_mqtt_client_start(self->client); + if (res != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "Error starting client")); } } - return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mqtt_start_obj, mqtt_op_start); @@ -578,11 +845,32 @@ MP_DEFINE_CONST_FUN_OBJ_1(mqtt_start_obj, mqtt_op_start); STATIC mp_obj_t mqtt_op_free(mp_obj_t self_in) { mqtt_obj_t *self = self_in; - if ((self->client) && (self->client->status == MQTT_STATUS_STOPPED) && (self->client->settings->xMqttTask == NULL)) { - free(self->client->settings); - free(self->client); + + if (self->client) { + self->mpy_data_cb = NULL; + self->mpy_connected_cb = NULL; + self->mpy_disconnected_cb = NULL; + self->mpy_subscribed_cb = NULL; + self->mpy_unsubscribed_cb = NULL; + self->mpy_published_cb = NULL; + + esp_mqtt_client_destroy(self->client); self->client = NULL; - return mp_const_true; + + if (self->msgbuf) { + free(self->msgbuf); + self->msgbuf = NULL; + } + if (self->topicbuf) { + free(self->topicbuf); + self->topicbuf = NULL; + } + if (self->certbuf) { + free(self->certbuf); + self->certbuf = NULL; + } + + return mp_const_true; } return mp_const_false; } diff --git a/MicroPython_BUILD/components/micropython/esp32/modnetwork.c b/MicroPython_BUILD/components/micropython/esp32/modnetwork.c index 781f376e..30e7d6e9 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modnetwork.c +++ b/MicroPython_BUILD/components/micropython/esp32/modnetwork.c @@ -763,8 +763,13 @@ STATIC mp_obj_t esp_status(size_t n_args, const mp_obj_t *args) { wifi_sta_info_t *stations = (wifi_sta_info_t*)station_list.sta; mp_obj_t list = mp_obj_new_list(0, NULL); for (int i = 0; i < station_list.num; ++i) { - mp_obj_tuple_t *t = mp_obj_new_tuple(1, NULL); + ip4_addr_t addr; + mp_obj_tuple_t *t = mp_obj_new_tuple(2, NULL); t->items[0] = mp_obj_new_bytes(stations[i].mac, sizeof(stations[i].mac)); + if (dhcp_search_ip_on_mac(stations[i].mac , &addr)) { + t->items[1] = netutils_format_ipv4_addr((uint8_t*)&addr.addr, NETUTILS_BIG); + } + else t->items[1] = mp_const_none; mp_obj_list_append(list, t); } return list; diff --git a/MicroPython_BUILD/components/micropython/esp32/modsocket.c b/MicroPython_BUILD/components/micropython/esp32/modsocket.c index dfbd8618..76ef5b94 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modsocket.c +++ b/MicroPython_BUILD/components/micropython/esp32/modsocket.c @@ -63,11 +63,81 @@ typedef struct _socket_obj_t { uint8_t domain; uint8_t type; uint8_t proto; + bool peer_closed; unsigned int retries; + #if MICROPY_PY_USOCKET_EVENTS + mp_obj_t events_callback; + struct _socket_obj_t *events_next; + #endif } socket_obj_t; void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms); +#if MICROPY_PY_USOCKET_EVENTS +// Support for callbacks on asynchronous socket events (when socket becomes readable) + +// This divisor is used to reduce the load on the system, so it doesn't poll sockets too often +#define USOCKET_EVENTS_DIVISOR (8) + +STATIC uint8_t usocket_events_divisor; +STATIC socket_obj_t *usocket_events_head; + +void usocket_events_deinit(void) { + usocket_events_head = NULL; +} + +// Assumes the socket is not already in the linked list, and adds it +STATIC void usocket_events_add(socket_obj_t *sock) { + sock->events_next = usocket_events_head; + usocket_events_head = sock; +} + +// Assumes the socket is already in the linked list, and removes it +STATIC void usocket_events_remove(socket_obj_t *sock) { + for (socket_obj_t **s = &usocket_events_head;; s = &(*s)->events_next) { + if (*s == sock) { + *s = (*s)->events_next; + return; + } + } +} + +// Polls all registered sockets for readability and calls their callback if they are readable +void usocket_events_handler(void) { + if (usocket_events_head == NULL) { + return; + } + if (--usocket_events_divisor) { + return; + } + usocket_events_divisor = USOCKET_EVENTS_DIVISOR; + + fd_set rfds; + FD_ZERO(&rfds); + int max_fd = 0; + + for (socket_obj_t *s = usocket_events_head; s != NULL; s = s->events_next) { + FD_SET(s->fd, &rfds); + max_fd = MAX(max_fd, s->fd); + } + + // Poll the sockets + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; + int r = select(max_fd + 1, &rfds, NULL, NULL, &timeout); + if (r <= 0) { + return; + } + + // Call the callbacks + for (socket_obj_t *s = usocket_events_head; s != NULL; s = s->events_next) { + if (FD_ISSET(s->fd, &rfds)) { + mp_call_function_1_protected(s->events_callback, s); + } + } +} + +#endif // MICROPY_PY_USOCKET_EVENTS + NORETURN static void exception_from_errno(int _errno) { // Here we need to convert from lwip errno values to MicroPython's standard ones if (_errno == EINPROGRESS) { @@ -76,26 +146,9 @@ NORETURN static void exception_from_errno(int _errno) { mp_raise_OSError(_errno); } -void check_for_exceptions() { - mp_obj_t exc = MP_STATE_VM(mp_pending_exception); - if (exc != MP_OBJ_NULL) { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; - nlr_raise(exc); - } -} - -STATIC mp_obj_t socket_close(const mp_obj_t arg0) { - socket_obj_t *self = MP_OBJ_TO_PTR(arg0); - if (self->fd >= 0) { - int ret = lwip_close_r(self->fd); - if (ret != 0) { - exception_from_errno(errno); - } - self->fd = -1; - } - return mp_const_none; +static inline void check_for_exceptions(void) { + mp_handle_pending(); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); static int _socket_getaddrinfo2(const mp_obj_t host, const mp_obj_t portx, struct addrinfo **resp) { const struct addrinfo hints = { @@ -159,7 +212,7 @@ STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { struct sockaddr addr; socklen_t addr_len = sizeof(addr); - mp_hal_set_wdt_tmo(); + mp_hal_set_wdt_tmo(); int new_fd = -1; for (int i=0; i<=self->retries; i++) { MP_THREAD_GIL_EXIT(); @@ -168,7 +221,7 @@ STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { if (new_fd >= 0) break; if (errno != EAGAIN) exception_from_errno(errno); check_for_exceptions(); - mp_hal_reset_wdt(); + mp_hal_reset_wdt(); } if (new_fd < 0) mp_raise_OSError(MP_ETIMEDOUT); @@ -179,6 +232,7 @@ STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { sock->domain = self->domain; sock->type = self->type; sock->proto = self->proto; + sock->peer_closed = false; _socket_settimeout(sock, UINT64_MAX); // make the return value @@ -226,6 +280,7 @@ STATIC mp_obj_t socket_accepted(const mp_obj_t arg0) { sock->domain = self->domain; sock->type = self->type; sock->proto = self->proto; + sock->peer_closed = false; _socket_settimeout(sock, UINT64_MAX); // make the return value @@ -272,6 +327,25 @@ STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { break; } + #if MICROPY_PY_USOCKET_EVENTS + // level: SOL_SOCKET + // special "register callback" option + case 20: { + if (args[3] == mp_const_none) { + if (self->events_callback != MP_OBJ_NULL) { + usocket_events_remove(self); + self->events_callback = MP_OBJ_NULL; + } + } else { + if (self->events_callback == MP_OBJ_NULL) { + usocket_events_add(self); + } + self->events_callback = args[3]; + } + break; + } + #endif + // level: IPPROTO_IP case IP_ADD_MEMBERSHIP: { mp_buffer_info_t bufinfo; @@ -328,25 +402,59 @@ STATIC mp_obj_t socket_setblocking(const mp_obj_t arg0, const mp_obj_t arg1) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); -mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, - struct sockaddr *from, socklen_t *from_len) { +// XXX this can end up waiting a very long time if the content is dribbled in one character +// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not +// good behaviour. +STATIC mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, + struct sockaddr *from, socklen_t *from_len, int *errcode) { socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); - size_t len = mp_obj_get_int(len_in); - vstr_t vstr; - vstr_init_len(&vstr, len); + // If the peer closed the connection then the lwIP socket API will only return "0" once + // from lwip_recvfrom_r and then block on subsequent calls. To emulate POSIX behaviour, + // which continues to return "0" for each call on a closed socket, we set a flag when + // the peer closed the socket. + if (sock->peer_closed) { + return 0; + } + + mp_hal_set_wdt_tmo(); // XXX Would be nicer to use RTC to handle timeouts - mp_hal_set_wdt_tmo(); - for (int i=0; i<=sock->retries; i++) { + for (int i = 0; i <= sock->retries; ++i) { MP_THREAD_GIL_EXIT(); - int r = lwip_recvfrom_r(sock->fd, vstr.buf, len, 0, from, from_len); + int r = lwip_recvfrom_r(sock->fd, buf, size, 0, from, from_len); MP_THREAD_GIL_ENTER(); - if (r >= 0) { vstr.len = r; return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } - if (errno != EWOULDBLOCK) exception_from_errno(errno); + if (r == 0) { + sock->peer_closed = true; + } + if (r >= 0) { + return r; + } + if (errno != EWOULDBLOCK) { + *errcode = errno; + return MP_STREAM_ERROR; + } check_for_exceptions(); - mp_hal_reset_wdt(); + mp_hal_reset_wdt(); + } + + *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; + return MP_STREAM_ERROR; +} + +mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, + struct sockaddr *from, socklen_t *from_len) { + size_t len = mp_obj_get_int(len_in); + vstr_t vstr; + vstr_init_len(&vstr, len); + + int errcode; + mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); + if (ret == MP_STREAM_ERROR) { + exception_from_errno(errcode); } - mp_raise_OSError(MP_ETIMEDOUT); + + vstr.len = ret; + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { @@ -371,7 +479,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { int sentlen = 0; - mp_hal_set_wdt_tmo(); + mp_hal_set_wdt_tmo(); for (int i=0; i<=sock->retries && sentlen < datalen; i++) { MP_THREAD_GIL_EXIT(); int r = lwip_write_r(sock->fd, data+sentlen, datalen-sentlen); @@ -379,7 +487,7 @@ int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { if (r < 0 && errno != EWOULDBLOCK) exception_from_errno(errno); if (r > 0) sentlen += r; check_for_exceptions(); - mp_hal_reset_wdt(); + mp_hal_reset_wdt(); } if (sentlen == 0) mp_raise_OSError(MP_ETIMEDOUT); return sentlen; @@ -420,7 +528,7 @@ STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_ to.sin_port = lwip_htons(netutils_parse_inet_addr(addr_in, (uint8_t*)&to.sin_addr, NETUTILS_BIG)); // send the data - mp_hal_set_wdt_tmo(); + mp_hal_set_wdt_tmo(); for (int i=0; i<=self->retries; i++) { MP_THREAD_GIL_EXIT(); int ret = lwip_sendto_r(self->fd, bufinfo.buf, bufinfo.len, 0, (struct sockaddr*)&to, sizeof(to)); @@ -430,7 +538,7 @@ STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_ exception_from_errno(errno); } check_for_exceptions(); - mp_hal_reset_wdt(); + mp_hal_reset_wdt(); } mp_raise_OSError(MP_ETIMEDOUT); } @@ -449,31 +557,13 @@ STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); -// XXX this can end up waiting a very long time if the content is dribbled in one character -// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not -// good behaviour. - STATIC mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { - socket_obj_t *sock = self_in; - - // XXX Would be nicer to use RTC to handle timeouts - mp_hal_set_wdt_tmo(); - for (int i=0; i<=sock->retries; i++) { - MP_THREAD_GIL_EXIT(); - int r = lwip_recvfrom_r(sock->fd, buf, size, 0, NULL, NULL); - MP_THREAD_GIL_ENTER(); - if (r >= 0) return r; - if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } - check_for_exceptions(); - mp_hal_reset_wdt(); - } - *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; - return MP_STREAM_ERROR; + return _socket_read_data(self_in, buf, size, NULL, NULL, errcode); } STATIC mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { socket_obj_t *sock = self_in; - mp_hal_set_wdt_tmo(); + mp_hal_set_wdt_tmo(); for (int i=0; i<=sock->retries; i++) { MP_THREAD_GIL_EXIT(); int r = lwip_write_r(sock->fd, buf, size); @@ -481,7 +571,7 @@ STATIC mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_ if (r > 0) return r; if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } check_for_exceptions(); - mp_hal_reset_wdt(); + mp_hal_reset_wdt(); } *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; return MP_STREAM_ERROR; @@ -512,6 +602,12 @@ STATIC mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintpt return ret; } else if (request == MP_STREAM_CLOSE) { if (socket->fd >= 0) { + #if MICROPY_PY_USOCKET_EVENTS + if (socket->events_callback != MP_OBJ_NULL) { + usocket_events_remove(socket); + socket->events_callback = MP_OBJ_NULL; + } + #endif int ret = lwip_close_r(socket->fd); if (ret != 0) { *errcode = errno; @@ -571,6 +667,7 @@ STATIC mp_obj_t get_socket(size_t n_args, const mp_obj_t *args) { sock->domain = AF_INET; sock->type = SOCK_STREAM; sock->proto = 0; + sock->peer_closed = false; if (n_args > 0) { sock->domain = mp_obj_get_int(args[0]); if (n_args > 1) { diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py index ddeea334..132b5c16 100755 --- a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py @@ -657,6 +657,7 @@ def WriteResponsePyHTMLFile(self, filepath, headers=None) : if 'MicroWebTemplate' in globals() : with open(filepath, 'r') as file : code = file.read() + gc.collect() mWebTmpl = MicroWebTemplate(code, escapeStrFunc=MicroWebSrv.HTMLEscape, filepath=filepath) try : tmplResult = mWebTmpl.Execute() diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/upip.py b/MicroPython_BUILD/components/micropython/esp32/modules/upip.py index 7d59a90a..e9abd909 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modules/upip.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules/upip.py @@ -144,7 +144,7 @@ def url_open(url): def get_pkg_metadata(name): - f = url_open("https://pypi.python.org/pypi/%s/json" % name) + f = url_open("https://pypi.org/pypi/%s/json" % name) try: return json.load(f) finally: @@ -212,9 +212,7 @@ def install(to_install, install_path=None): deps = deps.decode("utf-8").split("\n") to_install.extend(deps) except Exception as e: - print("Error installing '{}': {}, packages may be partially installed".format( - pkg_spec, e), - file=sys.stderr) + print("Error installing '{}': {}, packages may be partially installed".format(pkg_spec, e)) def get_install_path(): global install_path diff --git a/MicroPython_BUILD/components/micropython/esp32/modules_examples/micropyGPS.py b/MicroPython_BUILD/components/micropython/esp32/modules_examples/micropyGPS.py deleted file mode 100644 index 75345c3e..00000000 --- a/MicroPython_BUILD/components/micropython/esp32/modules_examples/micropyGPS.py +++ /dev/null @@ -1,816 +0,0 @@ -""" -# MicropyGPS - a GPS NMEA sentence parser for Micropython/Python 3.X -# Copyright (c) 2017 Michael Calvin McCoy (calvin.mccoy@gmail.com) -# The MIT License (MIT) - see LICENSE file -""" - -# TODO: -# Time Since First Fix -# Distance/Time to Target -# More Helper Functions -# Dynamically limit sentences types to parse - -from math import floor, modf - -# Import pyb or time for fix time handling -try: - # Assume running on pyboard - import pyb -except ImportError: - # Otherwise default to time module for non-embedded implementations - # Note that this forces the resolution of the fix time 1 second instead - # of milliseconds as on the pyboard - import time - - -class MicropyGPS(object): - """GPS NMEA Sentence Parser. Creates object that stores all relevant GPS data and statistics. - Parses sentences one character at a time using update(). """ - - # Max Number of Characters a valid sentence can be (based on GGA sentence) - SENTENCE_LIMIT = 76 - __HEMISPHERES = ('N', 'S', 'E', 'W') - __NO_FIX = 1 - __FIX_2D = 2 - __FIX_3D = 3 - __DIRECTIONS = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', - 'WNW', 'NW', 'NNW'] - __MONTHS = ('January', 'February', 'March', 'April', 'May', - 'June', 'July', 'August', 'September', 'October', - 'November', 'December') - - def __init__(self, local_offset=0, location_formatting='ddm'): - """ - Setup GPS Object Status Flags, Internal Data Registers, etc - local_offset (int): Timzone Difference to UTC - location_formatting (str): Style For Presenting Longitude/Latitude: - Decimal Degree Minute (ddm) - 40° 26.767′ N - Degrees Minutes Seconds (dms) - 40° 26′ 46″ N - Decimal Degrees (dd) - 40.446° N - """ - - ##################### - # Object Status Flags - self.sentence_active = False - self.active_segment = 0 - self.process_crc = False - self.gps_segments = [] - self.crc_xor = 0 - self.char_count = 0 - self.fix_time = 0 - - ##################### - # Sentence Statistics - self.crc_fails = 0 - self.clean_sentences = 0 - self.parsed_sentences = 0 - - ##################### - # Logging Related - self.log_handle = None - self.log_en = False - - ##################### - # Data From Sentences - # Time - self.timestamp = (0, 0, 0) - self.date = (0, 0, 0) - self.local_offset = local_offset - - # Position/Motion - self._latitude = (0, 0.0, 'N') - self._longitude = (0, 0.0, 'W') - self.coord_format = location_formatting - self.speed = (0.0, 0.0, 0.0) - self.course = 0.0 - self.altitude = 0.0 - self.geoid_height = 0.0 - - # GPS Info - self.satellites_in_view = 0 - self.satellites_in_use = 0 - self.satellites_used = [] - self.last_sv_sentence = 0 - self.total_sv_sentences = 0 - self.satellite_data = dict() - self.hdop = 0.0 - self.pdop = 0.0 - self.vdop = 0.0 - self.valid = False - self.fix_stat = 0 - self.fix_type = 1 - - ######################################## - # Coordinates Translation Functions - ######################################## - @property - def latitude(self): - """Format Latitude Data Correctly""" - if self.coord_format == 'dd': - decimal_degrees = self._latitude[0] + (self._latitude[1] / 60) - return [decimal_degrees, self._latitude[2]] - elif self.coord_format == 'dms': - minute_parts = modf(self._latitude[1]) - seconds = round(minute_parts[0] * 60) - return [self._latitude[0], int(minute_parts[1]), seconds, self._latitude[2]] - else: - return self._latitude - - @property - def longitude(self): - """Format Longitude Data Correctly""" - if self.coord_format == 'dd': - decimal_degrees = self._longitude[0] + (self._longitude[1] / 60) - return [decimal_degrees, self._longitude[2]] - elif self.coord_format == 'dms': - minute_parts = modf(self._longitude[1]) - seconds = round(minute_parts[0] * 60) - return [self._longitude[0], int(minute_parts[1]), seconds, self._longitude[2]] - else: - return self._longitude - - ######################################## - # Logging Related Functions - ######################################## - def start_logging(self, target_file, mode="append"): - """ - Create GPS data log object - """ - # Set Write Mode Overwrite or Append - mode_code = 'w' if mode == 'new' else 'a' - - try: - self.log_handle = open(target_file, mode_code) - except AttributeError: - print("Invalid FileName") - return False - - self.log_en = True - return True - - def stop_logging(self): - """ - Closes the log file handler and disables further logging - """ - try: - self.log_handle.close() - except AttributeError: - print("Invalid Handle") - return False - - self.log_en = False - return True - - def write_log(self, log_string): - """Attempts to write the last valid NMEA sentence character to the active file handler - """ - try: - self.log_handle.write(log_string) - except TypeError: - return False - return True - - ######################################## - # Sentence Parsers - ######################################## - def gprmc(self): - """Parse Recommended Minimum Specific GPS/Transit data (RMC)Sentence. - Updates UTC timestamp, latitude, longitude, Course, Speed, Date, and fix status - """ - - # UTC Timestamp - try: - utc_string = self.gps_segments[1] - - if utc_string: # Possible timestamp found - hours = int(utc_string[0:2]) + self.local_offset - minutes = int(utc_string[2:4]) - seconds = float(utc_string[4:]) - self.timestamp = (hours, minutes, seconds) - else: # No Time stamp yet - self.timestamp = (0, 0, 0) - - except ValueError: # Bad Timestamp value present - return False - - # Date stamp - try: - date_string = self.gps_segments[9] - - # Date string printer function assumes to be year >=2000, - # date_string() must be supplied with the correct century argument to display correctly - if date_string: # Possible date stamp found - day = int(date_string[0:2]) - month = int(date_string[2:4]) - year = int(date_string[4:6]) - self.date = (day, month, year) - else: # No Date stamp yet - self.date = (0, 0, 0) - - except ValueError: # Bad Date stamp value present - return False - - # Check Receiver Data Valid Flag - if self.gps_segments[2] == 'A': # Data from Receiver is Valid/Has Fix - - # Longitude / Latitude - try: - # Latitude - l_string = self.gps_segments[3] - lat_degs = int(l_string[0:2]) - lat_mins = float(l_string[2:]) - lat_hemi = self.gps_segments[4] - - # Longitude - l_string = self.gps_segments[5] - lon_degs = int(l_string[0:3]) - lon_mins = float(l_string[3:]) - lon_hemi = self.gps_segments[6] - except ValueError: - return False - - if lat_hemi not in self.__HEMISPHERES: - return False - - if lon_hemi not in self.__HEMISPHERES: - return False - - # Speed - try: - spd_knt = float(self.gps_segments[7]) - except ValueError: - return False - - # Course - try: - course = float(self.gps_segments[8]) - except ValueError: - return False - - # TODO - Add Magnetic Variation - - # Update Object Data - self._latitude = (lat_degs, lat_mins, lat_hemi) - self._longitude = (lon_degs, lon_mins, lon_hemi) - # Include mph and hm/h - self.speed = (spd_knt, spd_knt * 1.151, spd_knt * 1.852) - self.course = course - self.valid = True - - # Update Last Fix Time - self.new_fix_time() - - else: # Clear Position Data if Sentence is 'Invalid' - self._latitude = (0, 0.0, 'N') - self._longitude = (0, 0.0, 'W') - self.speed = (0.0, 0.0, 0.0) - self.course = 0.0 - self.date = (0, 0, 0) - self.valid = False - - return True - - def gpgll(self): - """Parse Geographic Latitude and Longitude (GLL)Sentence. Updates UTC timestamp, latitude, - longitude, and fix status""" - - # UTC Timestamp - try: - utc_string = self.gps_segments[5] - - if utc_string: # Possible timestamp found - hours = int(utc_string[0:2]) + self.local_offset - minutes = int(utc_string[2:4]) - seconds = float(utc_string[4:]) - self.timestamp = (hours, minutes, seconds) - else: # No Time stamp yet - self.timestamp = (0, 0, 0) - - except ValueError: # Bad Timestamp value present - return False - - # Check Receiver Data Valid Flag - if self.gps_segments[6] == 'A': # Data from Receiver is Valid/Has Fix - - # Longitude / Latitude - try: - # Latitude - l_string = self.gps_segments[1] - lat_degs = int(l_string[0:2]) - lat_mins = float(l_string[2:]) - lat_hemi = self.gps_segments[2] - - # Longitude - l_string = self.gps_segments[3] - lon_degs = int(l_string[0:3]) - lon_mins = float(l_string[3:]) - lon_hemi = self.gps_segments[4] - except ValueError: - return False - - if lat_hemi not in self.__HEMISPHERES: - return False - - if lon_hemi not in self.__HEMISPHERES: - return False - - # Update Object Data - self._latitude = (lat_degs, lat_mins, lat_hemi) - self._longitude = (lon_degs, lon_mins, lon_hemi) - self.valid = True - - # Update Last Fix Time - self.new_fix_time() - - else: # Clear Position Data if Sentence is 'Invalid' - self._latitude = (0, 0.0, 'N') - self._longitude = (0, 0.0, 'W') - self.valid = False - - return True - - def gpvtg(self): - """Parse Track Made Good and Ground Speed (VTG) Sentence. Updates speed and course""" - try: - course = float(self.gps_segments[1]) - spd_knt = float(self.gps_segments[5]) - except ValueError: - return False - - # Include mph and km/h - self.speed = (spd_knt, spd_knt * 1.151, spd_knt * 1.852) - self.course = course - return True - - def gpgga(self): - """Parse Global Positioning System Fix Data (GGA) Sentence. Updates UTC timestamp, latitude, longitude, - fix status, satellites in use, Horizontal Dilution of Precision (HDOP), altitude, geoid height and fix status""" - - try: - # UTC Timestamp - utc_string = self.gps_segments[1] - - # Skip timestamp if receiver doesn't have on yet - if utc_string: - hours = int(utc_string[0:2]) + self.local_offset - minutes = int(utc_string[2:4]) - seconds = float(utc_string[4:]) - else: - hours = 0 - minutes = 0 - seconds = 0.0 - - # Number of Satellites in Use - satellites_in_use = int(self.gps_segments[7]) - - # Horizontal Dilution of Precision - hdop = float(self.gps_segments[8]) - - # Get Fix Status - fix_stat = int(self.gps_segments[6]) - - except ValueError: - return False - - # Process Location and Speed Data if Fix is GOOD - if fix_stat: - - # Longitude / Latitude - try: - # Latitude - l_string = self.gps_segments[2] - lat_degs = int(l_string[0:2]) - lat_mins = float(l_string[2:]) - lat_hemi = self.gps_segments[3] - - # Longitude - l_string = self.gps_segments[4] - lon_degs = int(l_string[0:3]) - lon_mins = float(l_string[3:]) - lon_hemi = self.gps_segments[5] - except ValueError: - return False - - if lat_hemi not in self.__HEMISPHERES: - return False - - if lon_hemi not in self.__HEMISPHERES: - return False - - # Altitude / Height Above Geoid - try: - altitude = float(self.gps_segments[9]) - geoid_height = float(self.gps_segments[11]) - except ValueError: - return False - - # Update Object Data - self._latitude = (lat_degs, lat_mins, lat_hemi) - self._longitude = (lon_degs, lon_mins, lon_hemi) - self.altitude = altitude - self.geoid_height = geoid_height - - # Update Object Data - self.timestamp = (hours, minutes, seconds) - self.satellites_in_use = satellites_in_use - self.hdop = hdop - self.fix_stat = fix_stat - - # If Fix is GOOD, update fix timestamp - if fix_stat: - self.new_fix_time() - - return True - - def gpgsa(self): - """Parse GNSS DOP and Active Satellites (GSA) sentence. Updates GPS fix type, list of satellites used in - fix calculation, Position Dilution of Precision (PDOP), Horizontal Dilution of Precision (HDOP), Vertical - Dilution of Precision, and fix status""" - - # Fix Type (None,2D or 3D) - try: - fix_type = int(self.gps_segments[2]) - except ValueError: - return False - - # Read All (up to 12) Available PRN Satellite Numbers - sats_used = [] - for sats in range(12): - sat_number_str = self.gps_segments[3 + sats] - if sat_number_str: - try: - sat_number = int(sat_number_str) - sats_used.append(sat_number) - except ValueError: - return False - else: - break - - # PDOP,HDOP,VDOP - try: - pdop = float(self.gps_segments[15]) - hdop = float(self.gps_segments[16]) - vdop = float(self.gps_segments[17]) - except ValueError: - return False - - # Update Object Data - self.fix_type = fix_type - - # If Fix is GOOD, update fix timestamp - if fix_type > self.__NO_FIX: - self.new_fix_time() - - self.satellites_used = sats_used - self.hdop = hdop - self.vdop = vdop - self.pdop = pdop - - return True - - def gpgsv(self): - """Parse Satellites in View (GSV) sentence. Updates number of SV Sentences,the number of the last SV sentence - parsed, and data on each satellite present in the sentence""" - try: - num_sv_sentences = int(self.gps_segments[1]) - current_sv_sentence = int(self.gps_segments[2]) - sats_in_view = int(self.gps_segments[3]) - except ValueError: - return False - - # Create a blank dict to store all the satellite data from this sentence in: - # satellite PRN is key, tuple containing telemetry is value - satellite_dict = dict() - - # Calculate Number of Satelites to pull data for and thus how many segment positions to read - if num_sv_sentences == current_sv_sentence: - sat_segment_limit = ((sats_in_view % 4) * 4) + 4 # Last sentence may have 1-4 satellites - else: - sat_segment_limit = 20 # Non-last sentences have 4 satellites and thus read up to position 20 - - # Try to recover data for up to 4 satellites in sentence - for sats in range(4, sat_segment_limit, 4): - - # If a PRN is present, grab satellite data - if self.gps_segments[sats]: - try: - sat_id = int(self.gps_segments[sats]) - except (ValueError,IndexError): - return False - - try: # elevation can be null (no value) when not tracking - elevation = int(self.gps_segments[sats+1]) - except (ValueError,IndexError): - elevation = None - - try: # azimuth can be null (no value) when not tracking - azimuth = int(self.gps_segments[sats+2]) - except (ValueError,IndexError): - azimuth = None - - try: # SNR can be null (no value) when not tracking - snr = int(self.gps_segments[sats+3]) - except (ValueError,IndexError): - snr = None - # If no PRN is found, then the sentence has no more satellites to read - else: - break - - # Add Satellite Data to Sentence Dict - satellite_dict[sat_id] = (elevation, azimuth, snr) - - # Update Object Data - self.total_sv_sentences = num_sv_sentences - self.last_sv_sentence = current_sv_sentence - self.satellites_in_view = sats_in_view - - # For a new set of sentences, we either clear out the existing sat data or - # update it as additional SV sentences are parsed - if current_sv_sentence == 1: - self.satellite_data = satellite_dict - else: - self.satellite_data.update(satellite_dict) - - return True - - ########################################## - # Data Stream Handler Functions - ########################################## - - def new_sentence(self): - """Adjust Object Flags in Preparation for a New Sentence""" - self.gps_segments = [''] - self.active_segment = 0 - self.crc_xor = 0 - self.sentence_active = True - self.process_crc = True - self.char_count = 0 - - def update(self, new_char): - """Process a new input char and updates GPS object if necessary based on special characters ('$', ',', '*') - Function builds a list of received string that are validate by CRC prior to parsing by the appropriate - sentence function. Returns sentence type on successful parse, None otherwise""" - - valid_sentence = False - - # Validate new_char is a printable char - ascii_char = ord(new_char) - - if 10 <= ascii_char <= 126: - self.char_count += 1 - - # Write Character to log file if enabled - if self.log_en: - self.write_log(new_char) - - # Check if a new string is starting ($) - if new_char == '$': - self.new_sentence() - return None - - elif self.sentence_active: - - # Check if sentence is ending (*) - if new_char == '*': - self.process_crc = False - self.active_segment += 1 - self.gps_segments.append('') - return None - - # Check if a section is ended (,), Create a new substring to feed - # characters to - elif new_char == ',': - self.active_segment += 1 - self.gps_segments.append('') - - # Store All Other printable character and check CRC when ready - else: - self.gps_segments[self.active_segment] += new_char - - # When CRC input is disabled, sentence is nearly complete - if not self.process_crc: - - if len(self.gps_segments[self.active_segment]) == 2: - try: - final_crc = int(self.gps_segments[self.active_segment], 16) - if self.crc_xor == final_crc: - valid_sentence = True - else: - self.crc_fails += 1 - except ValueError: - pass # CRC Value was deformed and could not have been correct - - # Update CRC - if self.process_crc: - self.crc_xor ^= ascii_char - - # If a Valid Sentence Was received and it's a supported sentence, then parse it!! - if valid_sentence: - self.clean_sentences += 1 # Increment clean sentences received - self.sentence_active = False # Clear Active Processing Flag - - if self.gps_segments[0] in self.supported_sentences: - - # parse the Sentence Based on the message type, return True if parse is clean - if self.supported_sentences[self.gps_segments[0]](self): - - # Let host know that the GPS object was updated by returning parsed sentence type - self.parsed_sentences += 1 - return self.gps_segments[0] - - # Check that the sentence buffer isn't filling up with Garage waiting for the sentence to complete - if self.char_count > self.SENTENCE_LIMIT: - self.sentence_active = False - - # Tell Host no new sentence was parsed - return None - - def new_fix_time(self): - """Updates a high resolution counter with current time when fix is updated. Currently only triggered from - GGA, GSA and RMC sentences""" - try: - self.fix_time = pyb.millis() - except NameError: - self.fix_time = time.time() - - ######################################### - # User Helper Functions - # These functions make working with the GPS object data easier - ######################################### - - def satellite_data_updated(self): - """ - Checks if the all the GSV sentences in a group have been read, making satellite data complete - :return: boolean - """ - if self.total_sv_sentences > 0 and self.total_sv_sentences == self.last_sv_sentence: - return True - else: - return False - - def satellites_visible(self): - """ - Returns a list of of the satellite PRNs currently visible to the receiver - :return: list - """ - return list(self.satellite_data.keys()) - - def time_since_fix(self): - """Returns number of millisecond since the last sentence with a valid fix was parsed. Returns 0 if - no fix has been found""" - - # Test if a Fix has been found - if self.fix_time == 0: - return -1 - - # Try calculating fix time assuming using millis on a pyboard; default to seconds if not - try: - current = pyb.elapsed_millis(self.fix_time) - except NameError: - current = time.time() - self.fix_time - - return current - - def compass_direction(self): - """ - Determine a cardinal or inter-cardinal direction based on current course. - :return: string - """ - # Calculate the offset for a rotated compass - if self.course >= 348.75: - offset_course = 360 - self.course - else: - offset_course = self.course + 11.25 - - # Each compass point is separated by 22.5 degrees, divide to find lookup value - dir_index = floor(offset_course / 22.5) - - final_dir = self.__DIRECTIONS[dir_index] - - return final_dir - - def latitude_string(self): - """ - Create a readable string of the current latitude data - :return: string - """ - if self.coord_format == 'dd': - formatted_latitude = self.latitude - lat_string = str(formatted_latitude[0]) + '° ' + str(self._latitude[2]) - elif self.coord_format == 'dms': - formatted_latitude = self.latitude - lat_string = str(formatted_latitude[0]) + '° ' + str(formatted_latitude[1]) + "' " + str(formatted_latitude[2]) + '" ' + str(formatted_latitude[3]) - else: - lat_string = str(self._latitude[0]) + '° ' + str(self._latitude[1]) + "' " + str(self._latitude[2]) - return lat_string - - def longitude_string(self): - """ - Create a readable string of the current longitude data - :return: string - """ - if self.coord_format == 'dd': - formatted_longitude = self.longitude - lon_string = str(formatted_longitude[0]) + '° ' + str(self._longitude[2]) - elif self.coord_format == 'dms': - formatted_longitude = self.longitude - lon_string = str(formatted_longitude[0]) + '° ' + str(formatted_longitude[1]) + "' " + str(formatted_longitude[2]) + '" ' + str(formatted_longitude[3]) - else: - lon_string = str(self._longitude[0]) + '° ' + str(self._longitude[1]) + "' " + str(self._longitude[2]) - return lon_string - - def speed_string(self, unit='kph'): - """ - Creates a readable string of the current speed data in one of three units - :param unit: string of 'kph','mph, or 'knot' - :return: - """ - if unit == 'mph': - speed_string = str(self.speed[1]) + ' mph' - - elif unit == 'knot': - if self.speed[0] == 1: - unit_str = ' knot' - else: - unit_str = ' knots' - speed_string = str(self.speed[0]) + unit_str - - else: - speed_string = str(self.speed[2]) + ' km/h' - - return speed_string - - def date_string(self, formatting='s_mdy', century='20'): - """ - Creates a readable string of the current date. - Can select between long format: Januray 1st, 2014 - or two short formats: - 11/01/2014 (MM/DD/YYYY) - 01/11/2014 (DD/MM/YYYY) - :param formatting: string 's_mdy', 's_dmy', or 'long' - :param century: int delineating the century the GPS data is from (19 for 19XX, 20 for 20XX) - :return: date_string string with long or short format date - """ - - # Long Format Januray 1st, 2014 - if formatting == 'long': - # Retrieve Month string from private set - month = self.__MONTHS[self.date[1] - 1] - - # Determine Date Suffix - if self.date[0] in (1, 21, 31): - suffix = 'st' - elif self.date[0] in (2, 22): - suffix = 'nd' - elif self.date[0] == 3: - suffix = 'rd' - else: - suffix = 'th' - - day = str(self.date[0]) + suffix # Create Day String - - year = century + str(self.date[2]) # Create Year String - - date_string = month + ' ' + day + ', ' + year # Put it all together - - else: - # Add leading zeros to day string if necessary - if self.date[0] < 10: - day = '0' + str(self.date[0]) - else: - day = str(self.date[0]) - - # Add leading zeros to month string if necessary - if self.date[1] < 10: - month = '0' + str(self.date[1]) - else: - month = str(self.date[1]) - - # Add leading zeros to year string if necessary - if self.date[2] < 10: - year = '0' + str(self.date[2]) - else: - year = str(self.date[2]) - - # Build final string based on desired formatting - if formatting == 's_dmy': - date_string = day + '/' + month + '/' + year - - else: # Default date format - date_string = month + '/' + day + '/' + year - - return date_string - - # All the currently supported NMEA sentences - supported_sentences = {'GPRMC': gprmc, 'GLRMC': gprmc, - 'GPGGA': gpgga, 'GLGGA': gpgga, - 'GPVTG': gpvtg, 'GLVTG': gpvtg, - 'GPGSA': gpgsa, 'GLGSA': gpgsa, - 'GPGSV': gpgsv, 'GLGSV': gpgsv, - 'GPGLL': gpgll, 'GLGLL': gpgll, - 'GNGGA': gpgga, 'GNRMC': gprmc, - 'GNVTG': gpvtg, - } - -if __name__ == "__main__": - pass diff --git a/MicroPython_BUILD/components/micropython/esp32/modules_examples/mqtt_example.py b/MicroPython_BUILD/components/micropython/esp32/modules_examples/mqtt_example.py index 41719ac5..0a99a93e 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modules_examples/mqtt_example.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules_examples/mqtt_example.py @@ -1,3 +1,4 @@ +import network def conncb(task): print("[{}] Connected".format(task)) @@ -14,10 +15,17 @@ def pubcb(pub): def datacb(msg): print("[{}] Data arrived from topic: {}, Message:\n".format(msg[0], msg[1]), msg[2]) -mqtt = network.mqtt("loboris", "loboris.eu", user="wifimcu", password="wifimculobo", cleansession=True, connected_cb=conncb, disconnected_cb=disconncb, subscribed_cb=subscb, published_cb=pubcb, data_cb=datacb) +mqtt = network.mqtt("loboris", "mqtt://loboris.eu", user="wifimcu", password="wifimculobo", cleansession=True, connected_cb=conncb, disconnected_cb=disconncb, subscribed_cb=subscb, published_cb=pubcb, data_cb=datacb) # secure connection requires more memory and may not work -# mqtts = network.mqtt("eclipse", "iot.eclipse.org", secure=True, cleansession=True, connected_cb=conncb, disconnected_cb=disconncb, subscribed_cb=subscb, published_cb=pubcb, data_cb=datacb) +# mqtts = network.mqtt("eclipse", "mqtts//iot.eclipse.org", cleansession=True, connected_cb=conncb, disconnected_cb=disconncb, subscribed_cb=subscb, published_cb=pubcb, data_cb=datacb) +# wsmqtt = network.mqtt("eclipse", "ws://iot.eclipse.org:80/ws", cleansession=True, data_cb=datacb) + +mqtt.start() + +#mqtt.config(lwt_topic='status', lwt_msg='Disconected') + + ''' @@ -27,5 +35,53 @@ def datacb(msg): mqtt.subscribe('test') mqtt.publish('test', 'Hi from Micropython') +mqtt.stop() + ''' +# ================== +# ThingSpeak example +# ================== + +import network + +def datacb(msg): + print("[{}] Data arrived from topic: {}, Message:\n".format(msg[0], msg[1]), msg[2]) + +thing = network.mqtt("thingspeak", "mqtt://mqtt.thingspeak.com", user="anyName", password="ThingSpeakMQTTid", cleansession=True, data_cb=datacb) +# or secure connection +#thing = network.mqtt("thingspeak", "mqtts://mqtt.thingspeak.com", user="anyName", password="ThingSpeakMQTTid", cleansession=True, data_cb=datacb) + +thingspeakChannelId = "123456" # enter Thingspeak Channel ID +thingspeakChannelWriteApiKey = "ThingspeakWriteAPIKey" # EDIT - enter Thingspeak Write API Key +thingspeakFieldNo = 1 +thingSpeakChanelFormat = "json" + +pubchan = "channels/{:s}/publish/{:s}".format(thingspeakChannelId, thingspeakChannelWriteApiKey) +pubfield = "channels/{:s}/publish/fields/field{}/{:s}".format(thingspeakChannelId, thingspeakFieldNo, thingspeakChannelWriteApiKey) +subchan = "channels/{:s}/subscribe/{:s}/{:s}".format(thingspeakChannelId, thingSpeakChanelFormat, thingspeakChannelWriteApiKey) +subfield = "channels/{:s}/subscribe/fields/field{}/{:s}".format(thingspeakChannelId, thingspeakFieldNo, thingspeakChannelWriteApiKey) + +thing.start() +tmo = 0 +while thing.status()[0] != 2: + utime.sleep_ms(100) + tmo += 1 + if tmo > 80: + print("Not connected") + break + +# subscribe to channel +thing.subscribe(subchan) + +# subscribe to field +thing.subscribe(subfield) + +# publish to channel +# Payload can include any of those fields separated b< ';': +# "field1=value;field2=value;...;field8=value;latitude=value;longitude=value;elevation=value;status=value" +thing.publish(pubchan, "field1=25.2;status=On line") + +# Publish to field +thing.publish(pubfield, "24.5") + diff --git a/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h b/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h index ac461230..f415edb5 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h +++ b/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h @@ -32,6 +32,8 @@ #include "rom/ets_sys.h" #include "sdkconfig.h" +#define MICROPY_DEBUG_PRINTERS (0) + // object representation and NLR handling #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) #define MICROPY_NLR_SETJMP (1) @@ -47,13 +49,14 @@ #define MICROPY_COMP_MODULE_CONST (1) #define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) -// optimisations +// optimizations #define MICROPY_OPT_COMPUTED_GOTO (1) #define MICROPY_OPT_MPZ_BITWISE (1) // Python internal features #define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) +//#define MICROPY_GC_CONSERVATIVE_CLEAR (0) #define MICROPY_ENABLE_FINALISER (1) #define MICROPY_STACK_CHECK (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) @@ -78,8 +81,8 @@ #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool #define MICROPY_CAN_OVERRIDE_BUILTINS (1) #define MICROPY_USE_INTERNAL_ERRNO (1) -#define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf -#define MICROPY_PY_SYS_EXC_INFO (1) +#define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf, do NOT change +//#define MICROPY_PY_SYS_EXC_INFO (0) #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_DEPTH (8) @@ -89,7 +92,11 @@ // control over Python builtins #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_STR_BYTES_CMP_WARN (1) +#ifdef CONFIG_MICROPY_USE_UNICODE #define MICROPY_PY_BUILTINS_STR_UNICODE (1) +#else +#define MICROPY_PY_BUILTINS_STR_UNICODE (0) +#endif #define MICROPY_PY_BUILTINS_STR_CENTER (1) #define MICROPY_PY_BUILTINS_STR_PARTITION (1) #define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) @@ -164,8 +171,6 @@ #define MICROPY_PY_MACHINE_SPI_MSB (0) #define MICROPY_PY_MACHINE_SPI_LSB (1) #define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hw_spi_make_new -#define MICROPY_PY_MACHINE_SPI_MIN_DELAY (0) -#define MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE (ets_get_cpu_frequency() * 1000000 / 200) // roughly #define MICROPY_PY_USSL (1) #define MICROPY_SSL_MBEDTLS (1) #define MICROPY_PY_USSL_FINALISER (1) @@ -177,14 +182,15 @@ #else #define MICROPY_PY_WEBSOCKET (0) #endif -#define MICROPY_PY_OS_DUPTERM (0) -#define MICROPY_PY_WEBREPL (0) +#define MICROPY_PY_OS_DUPTERM (0) // not supported, do NOT change +#define MICROPY_PY_WEBREPL (0) // not supported, do NOT change #ifdef CONFIG_MICROPY_PY_FRAMEBUF #define MICROPY_PY_FRAMEBUF (1) #else #define MICROPY_PY_FRAMEBUF (0) #endif +#define MICROPY_PY_USOCKET_EVENTS (MICROPY_PY_WEBREPL) /* * Defined in 'component.mk' @@ -205,8 +211,8 @@ #endif #define MICROPY_FATFS_RPATH (2) #define MICROPY_FATFS_MAX_SS (4096) -#define MICROPY_FATFS_MAX_LFN (CONFIG_FATFS_MAX_LFN) // Get from config -#define MICROPY_FATFS_LFN_CODE_PAGE (CONFIG_FATFS_CODEPAGE) // Get from config +#define MICROPY_FATFS_MAX_LFN (CONFIG_FATFS_MAX_LFN) // Get from sdkconfig +#define MICROPY_FATFS_LFN_CODE_PAGE (CONFIG_FATFS_CODEPAGE) // Get from sdkconfig #define mp_type_fileio nativefs_type_fileio #define mp_type_textio nativefs_type_textio @@ -320,16 +326,9 @@ extern const struct _mp_obj_module_t mp_module_bluetooth; #define MP_STATE_PORT MP_STATE_VM -#if CONFIG_SPIRAM_SUPPORT #define MICROPY_PORT_ROOT_POINTERS \ - const char *readline_hist[80]; \ - mp_obj_list_t mod_network_nic_list; \ + const char *readline_hist[20]; \ mp_obj_t machine_pin_irq_handler[40]; -#else -#define MICROPY_PORT_ROOT_POINTERS \ - const char *readline_hist[16]; \ - mp_obj_t machine_pin_irq_handler[40]; -#endif // type definitions for the specific machine #define BYTES_PER_WORD (4) diff --git a/MicroPython_BUILD/components/micropython/esp32/mphalport.c b/MicroPython_BUILD/components/micropython/esp32/mphalport.c index 93823bdf..54b96aea 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mphalport.c +++ b/MicroPython_BUILD/components/micropython/esp32/mphalport.c @@ -36,6 +36,7 @@ #include "freertos/task.h" #include "freertos/semphr.h" #include "rom/uart.h" +#include "driver/uart.h" #include "esp_task_wdt.h" #include "py/obj.h" @@ -52,11 +53,16 @@ #endif uint32_t mp_hal_wdg_rst_tmo = 0; -uint64_t mp_hal_ticks_base = 0; +RTC_DATA_ATTR uint64_t mp_hal_ticks_base; static bool stdin_disable = false; static char stdin_enable_pattern[16] = ""; +STATIC uint8_t stdin_ringbuf_array[CONFIG_MICROPY_RX_BUFFER_SIZE]; +ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; + +extern void mp_handle_pending(void); + //-------------------------------- void disableStdin(const char *pat) { @@ -93,9 +99,6 @@ void mp_hal_set_wdt_tmo() } -STATIC uint8_t stdin_ringbuf_array[CONFIG_MICROPY_RX_BUFFER_SIZE]; -ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; - // wait until at least one character is received or the timeout expires //--------------------------------------- int mp_hal_stdin_rx_chr(uint32_t timeout) @@ -119,13 +122,15 @@ int mp_hal_stdin_rx_chr(uint32_t timeout) c = ringbuf_get(&stdin_ringbuf); if (c < 0) { // no character in ring buffer - // wait 10 ms for character + // wait max 10 ms for character MP_THREAD_GIL_EXIT(); if ( xSemaphoreTake( uart0_semaphore, 10 / portTICK_PERIOD_MS ) == pdTRUE ) { + // received MP_THREAD_GIL_ENTER(); c = ringbuf_get(&stdin_ringbuf); } else { + // not received MP_THREAD_GIL_ENTER(); c = -1; } @@ -149,7 +154,10 @@ int mp_hal_stdin_rx_chr(uint32_t timeout) pattern[pattern_idx++] = c; pattern[pattern_idx] = '\0'; if (strstr(stdin_enable_pattern, pattern) == stdin_enable_pattern) { - if (strlen(stdin_enable_pattern) == strlen(pattern)) stdin_disable = false; + if (strlen(stdin_enable_pattern) == strlen(pattern)) { + // pattern received, enable stdin + stdin_disable = false; + } } } return -1; @@ -161,7 +169,9 @@ int mp_hal_stdin_rx_chr(uint32_t timeout) int raw = uart0_raw_input; xSemaphoreGive(uart0_mutex); if (raw == 0) { - MICROPY_EVENT_POLL_HOOK + // check pending exception + //MICROPY_EVENT_POLL_HOOK + mp_handle_pending(); } } return -1; @@ -186,26 +196,31 @@ static void telnet_stdout_tx_str(const char *str, uint32_t len) } #endif +// send newline character to printf channel //------------------------------- void mp_hal_stdout_tx_newline() { #ifdef CONFIG_MICROPY_USE_TELNET if (telnet_loggedin()) telnet_tx_strn("\r\n", 2); - else uart_tx_one_char('\n'); + else { + uart_tx_one_char('\n'); + } #else uart_tx_one_char('\n'); #endif } +// send NULL ending string to printf channel //------------------------------------------ void mp_hal_stdout_tx_str(const char *str) { + if (str == NULL) return; #ifdef CONFIG_MICROPY_USE_TELNET if (telnet_loggedin()) telnet_tx_strn(str, strlen(str)); else { - //MP_THREAD_GIL_EXIT(); + MP_THREAD_GIL_EXIT(); while (*str) { uart_tx_one_char(*str++); } - //MP_THREAD_GIL_ENTER(); + MP_THREAD_GIL_ENTER(); } #else MP_THREAD_GIL_EXIT(); @@ -216,16 +231,18 @@ void mp_hal_stdout_tx_str(const char *str) { #endif } +// send 'len' characters from string buffer to printf channel //--------------------------------------------------------- void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { + if (str == NULL) return; #ifdef CONFIG_MICROPY_USE_TELNET if (telnet_loggedin()) telnet_tx_strn(str, len); else { - //MP_THREAD_GIL_EXIT(); + MP_THREAD_GIL_EXIT(); while (len--) { uart_tx_one_char(*str++); } - //MP_THREAD_GIL_ENTER(); + MP_THREAD_GIL_ENTER(); } #else MP_THREAD_GIL_EXIT(); @@ -236,27 +253,29 @@ void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { #endif } +// Efficiently convert "\n" to "\r\n" //---------------------------------------------------------------- void mp_hal_stdout_tx_strn_cooked(const char *str, uint32_t len) { + if (str == NULL) return; #ifdef CONFIG_MICROPY_USE_TELNET if (telnet_loggedin()) telnet_stdout_tx_str(str, len); else { - //MP_THREAD_GIL_EXIT(); + MP_THREAD_GIL_EXIT(); while (len--) { if (*str == '\n') { uart_tx_one_char('\r'); } uart_tx_one_char(*str++); } - //MP_THREAD_GIL_ENTER(); + MP_THREAD_GIL_ENTER(); } #else MP_THREAD_GIL_EXIT(); while (len--) { - if (*str == '\n') { - uart_tx_one_char('\r'); - } - uart_tx_one_char(*str++); + if (*str == '\n') { + uart_tx_one_char('\r'); + } + uart_tx_one_char(*str++); } MP_THREAD_GIL_ENTER(); #endif diff --git a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c index 660a5824..2f262156 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c +++ b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c @@ -41,6 +41,7 @@ #include "py/mphal.h" #include "mpthreadport.h" #include "modnetwork.h" +#include "modmachine.h" #if defined(CONFIG_MICROPY_USE_TELNET) || defined(CONFIG_MICROPY_USE_FTPSERVER) #include "tcpip_adapter.h" @@ -90,36 +91,43 @@ typedef struct _thread_t { // the mutex controls access to the linked list STATIC mp_thread_mutex_t thread_mutex; STATIC thread_t thread_entry0; -STATIC thread_t *thread; // root pointer, handled by mp_thread_gc_others +STATIC thread_t *thread = NULL; // root pointer, handled by mp_thread_gc_others -//------------------------------- -void vPortCleanUpTCB(void *tcb) { - thread_t *prev = NULL; - mp_thread_mutex_lock(&thread_mutex, 1); - for (thread_t *th = thread; th != NULL; prev = th, th = th->next) { - // unlink the node from the list - if (th->tcb == tcb) { - if (prev != NULL) { - prev->next = th->next; - } else { - // move the start pointer - thread = th->next; - } - // explicitly release all its memory - if (th->tcb) free(th->tcb); - if (th->stack) free(th->stack); - //m_del(thread_t, th, 1); - free(th); - break; - } - } - mp_thread_mutex_unlock(&thread_mutex); +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void vPortCleanUpTCB(void *tcb) +{ + if ((MainTaskHandle) && (thread)) { + thread_t *prev = NULL; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; prev = th, th = th->next) { + // unlink the node from the list + if (th->tcb == tcb) { + if (prev != NULL) { + prev->next = th->next; + } else { + // move the start pointer + thread = th->next; + } + // explicitly release all its memory + if (th->tcb) free(th->tcb); + if (th->stack) free(th->stack); + //m_del(thread_t, th, 1); + free(th); + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); + } } // === Initialize the main MicroPython thread === -//------------------------------------------------------- -void mp_thread_preinit(void *stack, uint32_t stack_len) { +//----------------------------------------------------- +void mp_thread_preinit(void *stack, uint32_t stack_len) +{ + // Initialize threads mutex + mp_thread_mutex_init(&thread_mutex); + mp_thread_set_state(&mp_state_ctx.thread); // create first entry in linked list of all threads thread = &thread_entry0; @@ -138,11 +146,7 @@ void mp_thread_preinit(void *stack, uint32_t stack_len) { thread->type = THREAD_TYPE_MAIN; thread->next = NULL; MainTaskHandle = thread->id; -} -//------------------------- -void mp_thread_init(void) { - mp_thread_mutex_init(&thread_mutex); } //------------------------------ @@ -160,7 +164,6 @@ void mp_thread_gc_others(void) { if (!th->ready) { continue; } - //ToDo: Check if needed gc_collect_root(th->stack, th->stack_len); // probably not needed } mp_thread_mutex_unlock(&thread_mutex); @@ -229,12 +232,28 @@ TaskHandle_t mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack // ====================================================================== StaticTask_t *tcb = NULL; StackType_t *stack = NULL; + thread_t *th = NULL; + + if (mpy_use_spiram) tcb = heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + else tcb = heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + if (tcb == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread")); + } - tcb = malloc(sizeof(StaticTask_t)); - stack = malloc(*stack_size+256); + if (mpy_use_spiram) stack = heap_caps_malloc(*stack_size+256, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + else stack = heap_caps_malloc(*stack_size+256, MALLOC_CAP_DMA | MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + if (stack == NULL) { + free(tcb); + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread")); + } - //thread_t *th = m_new_obj(thread_t); - thread_t *th = (thread_t *)malloc(sizeof(thread_t)); + if (mpy_use_spiram) th = heap_caps_malloc(sizeof(thread_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + else th = heap_caps_malloc(sizeof(thread_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + if (th == NULL) { + free(stack); + free(tcb); + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread")); + } mp_thread_mutex_lock(&thread_mutex, 1); @@ -251,6 +270,8 @@ TaskHandle_t mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack #endif if (id == NULL) { mp_thread_mutex_unlock(&thread_mutex); + free(stack); + free(tcb); nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread")); } diff --git a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h index ab3348f6..c1853237 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h +++ b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h @@ -121,7 +121,6 @@ thread_msg_t thread_messages[MAX_THREAD_MESSAGES]; uint8_t main_accept_msg; void mp_thread_preinit(void *stack, uint32_t stack_len); -void mp_thread_init(void); int mp_thread_num_threads(); void mp_thread_gc_others(void); diff --git a/MicroPython_BUILD/components/micropython/esp32/mpversion.h b/MicroPython_BUILD/components/micropython/esp32/mpversion.h index 81c77efc..0795bf71 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpversion.h +++ b/MicroPython_BUILD/components/micropython/esp32/mpversion.h @@ -24,12 +24,14 @@ * THE SOFTWARE. */ -#define MICROPY_GIT_TAG "ESP32_LoBo_v3.2.12" -#define MICROPY_GIT_HASH "gbae9709a" -#define MICROPY_BUILD_DATE "2018-04-18" +#define MICROPY_GIT_TAG "ESP32_LoBo_v3.2.15" +#define MICROPY_ESPIDF_HASH "a2556229aa6f55b16b171e3325ee9ab1943e8552" +#define MICROPY_ESPIDF_VERSION "v3.1-dev-961-ga2556229" +#define MICROPY_ESPIDF_DATE "2018-05-08" +#define MICROPY_BUILD_DATE "2018-05-10" #define MICROPY_VERSION_MAJOR (3) #define MICROPY_VERSION_MINOR (2) -#define MICROPY_VERSION_MICRO (12) -#define MICROPY_VERSION_STRING "3.2.12" +#define MICROPY_VERSION_MICRO (15) +#define MICROPY_VERSION_STRING "3.2.15" #define MICROPY_CORE_VERSION "59dda71" #define MICROPY_CORE_DATE "2018-04-10" diff --git a/MicroPython_BUILD/components/micropython/esp32/uart.c b/MicroPython_BUILD/components/micropython/esp32/uart.c index 31e4060c..0c19a839 100644 --- a/MicroPython_BUILD/components/micropython/esp32/uart.c +++ b/MicroPython_BUILD/components/micropython/esp32/uart.c @@ -40,16 +40,18 @@ STATIC void uart_irq_handler(void *arg); QueueHandle_t uart0_mutex = NULL; QueueSetMemberHandle_t uart0_semaphore = NULL; int uart0_raw_input = 0; +static uart_isr_handle_t uart0_handle = NULL; //------------------ void uart_init(void) { - uart0_mutex = xSemaphoreCreateMutex(); - uart0_semaphore = xSemaphoreCreateBinary(); + if (uart0_mutex == NULL) uart0_mutex = xSemaphoreCreateMutex(); + if (uart0_semaphore == NULL) uart0_semaphore = xSemaphoreCreateBinary(); - uart_isr_handle_t handle; - uart_isr_register(UART_NUM_0, uart_irq_handler, NULL, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, &handle); - uart_enable_rx_intr(UART_NUM_0); + if (uart0_handle == NULL) { + uart_isr_register(UART_NUM_0, uart_irq_handler, NULL, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, &uart0_handle); + uart_enable_rx_intr(UART_NUM_0); + } } // all code executed in ISR must be in IRAM, and any const data must be in DRAM diff --git a/MicroPython_BUILD/components/micropython/extmod/modutimeq.c b/MicroPython_BUILD/components/micropython/extmod/modutimeq.c index d37b56b1..af51f9c9 100644 --- a/MicroPython_BUILD/components/micropython/extmod/modutimeq.c +++ b/MicroPython_BUILD/components/micropython/extmod/modutimeq.c @@ -27,15 +27,13 @@ */ #include - +#include #include "py/objlist.h" #include "py/runtime.h" #include "py/smallint.h" #if MICROPY_PY_UTIMEQ -#define MODULO MICROPY_PY_UTIME_TICKS_PERIOD - #define DEBUG 0 // the algorithm here is modelled on CPython's heapq.py @@ -56,27 +54,28 @@ typedef struct _mp_obj_utimeq_t { STATIC mp_uint_t utimeq_id; +//-------------------------------------------------- STATIC mp_obj_utimeq_t *get_heap(mp_obj_t heap_in) { return MP_OBJ_TO_PTR(heap_in); } +//---------------------------------------------------------------------- STATIC bool time_less_than(struct qentry *item, struct qentry *parent) { - mp_uint_t item_tm = item->time; - mp_uint_t parent_tm = parent->time; - mp_uint_t res = parent_tm - item_tm; + uint64_t item_tm = item->time; + uint64_t parent_tm = parent->time; + int64_t res = parent_tm - item_tm; if (res == 0) { // TODO: This actually should use the same "ring" logic // as for time, to avoid artifacts when id's overflow. - return item->id < parent->id; - } - if ((mp_int_t)res < 0) { - res += MODULO; + return (item->id < parent->id); } - return res && res < (MODULO / 2); + return (res < 0); } +//------------------------------------------------------------------------------------------------------------ STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_uint_t alloc = mp_obj_get_int(args[0]); mp_obj_utimeq_t *o = m_new_obj_var(mp_obj_utimeq_t, struct qentry, alloc); o->base.type = type; @@ -86,6 +85,7 @@ STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t return MP_OBJ_FROM_PTR(o); } +//------------------------------------------------------------------------------------ STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) { struct qentry item = heap->items[pos]; while (pos > start_pos) { @@ -102,6 +102,7 @@ STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t heap->items[pos] = item; } +//------------------------------------------------------------- STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { mp_uint_t start_pos = pos; mp_uint_t end_pos = heap->len; @@ -131,10 +132,12 @@ STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { mp_raise_msg(&mp_type_IndexError, "queue overflow"); } mp_uint_t l = heap->len; + // time argument can be float or integer + // if float, convert it to 64-bit integer uint64_t itime; if (mp_obj_is_float(args[1])) { mp_float_t time = mp_obj_float_get(args[1]); - itime = (uint64_t)time; + itime = (uint64_t)(round(time)); } else itime = mp_obj_get_int64(args[1]); @@ -160,7 +163,7 @@ STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { } struct qentry *item = &heap->items[0]; - ret->items[0] = mp_obj_new_int(item->time); + ret->items[0] = mp_obj_new_int_from_ull(item->time); ret->items[1] = item->callback; ret->items[2] = item->args; heap->len -= 1; @@ -182,11 +185,12 @@ STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) { } struct qentry *item = &heap->items[0]; - return mp_obj_new_int(item->time); + return mp_obj_new_int_from_ull(item->time); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime); #if DEBUG +//------------------------------------------------- STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) { mp_obj_utimeq_t *heap = get_heap(heap_in); for (int i = 0; i < heap->len; i++) { @@ -198,6 +202,7 @@ STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_dump_obj, mod_utimeq_dump); #endif +//------------------------------------------------------------------- STATIC mp_obj_t utimeq_unary_op(mp_unary_op_t op, mp_obj_t self_in) { mp_obj_utimeq_t *self = MP_OBJ_TO_PTR(self_in); switch (op) { @@ -207,6 +212,7 @@ STATIC mp_obj_t utimeq_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } } +//=========================================================== STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) }, { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) }, @@ -215,9 +221,9 @@ STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) }, #endif }; - STATIC MP_DEFINE_CONST_DICT(utimeq_locals_dict, utimeq_locals_dict_table); +//======================================== STATIC const mp_obj_type_t utimeq_type = { { &mp_type_type }, .name = MP_QSTR_utimeq, @@ -226,13 +232,14 @@ STATIC const mp_obj_type_t utimeq_type = { .locals_dict = (void*)&utimeq_locals_dict, }; +//================================================================= STATIC const mp_rom_map_elem_t mp_module_utimeq_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utimeq) }, { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&utimeq_type) }, }; - STATIC MP_DEFINE_CONST_DICT(mp_module_utimeq_globals, mp_module_utimeq_globals_table); +//======================================== const mp_obj_module_t mp_module_utimeq = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_utimeq_globals, diff --git a/MicroPython_BUILD/components/micropython/extmod/uos_dupterm.c b/MicroPython_BUILD/components/micropython/extmod/uos_dupterm.c deleted file mode 100644 index f77cff57..00000000 --- a/MicroPython_BUILD/components/micropython/extmod/uos_dupterm.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016 Paul Sokolovsky - * Copyright (c) 2017 Damien P. George - * - * 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 -#include "py/mpconfig.h" - -#include "py/runtime.h" -#include "py/objtuple.h" -#include "py/objarray.h" -#include "py/stream.h" -#include "lib/utils/interrupt_char.h" - -#if MICROPY_PY_OS_DUPTERM - -void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) { - mp_obj_t term = MP_STATE_VM(dupterm_objs[dupterm_idx]); - MP_STATE_VM(dupterm_objs[dupterm_idx]) = MP_OBJ_NULL; - mp_printf(&mp_plat_print, msg); - if (exc != MP_OBJ_NULL) { - mp_obj_print_exception(&mp_plat_print, exc); - } - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_stream_close(term); - nlr_pop(); - } else { - // Ignore any errors during stream closing - } -} - -int mp_uos_dupterm_rx_chr(void) { - for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { - if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { - continue; - } - - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_obj_t readinto_m[3]; - mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_readinto, readinto_m); - readinto_m[2] = MP_STATE_VM(dupterm_arr_obj); - mp_obj_t res = mp_call_method_n_kw(1, 0, readinto_m); - if (res == mp_const_none) { - nlr_pop(); - } else if (res == MP_OBJ_NEW_SMALL_INT(0)) { - nlr_pop(); - mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL); - } else { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(MP_STATE_VM(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ); - nlr_pop(); - if (*(byte*)bufinfo.buf == mp_interrupt_char) { - // Signal keyboard interrupt to be raised as soon as the VM resumes - mp_keyboard_interrupt(); - return -2; - } - return *(byte*)bufinfo.buf; - } - } else { - mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", nlr.ret_val); - } - } - - // No chars available - return -1; -} - -void mp_uos_dupterm_tx_strn(const char *str, size_t len) { - for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { - if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { - continue; - } - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_obj_t write_m[3]; - mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_write, write_m); - - mp_obj_array_t *arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj)); - void *org_items = arr->items; - arr->items = (void*)str; - arr->len = len; - write_m[2] = MP_STATE_VM(dupterm_arr_obj); - mp_call_method_n_kw(1, 0, write_m); - arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj)); - arr->items = org_items; - arr->len = 1; - nlr_pop(); - } else { - mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", nlr.ret_val); - } - } -} - -STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) { - mp_int_t idx = 0; - if (n_args == 2) { - idx = mp_obj_get_int(args[1]); - } - - if (idx < 0 || idx >= MICROPY_PY_OS_DUPTERM) { - mp_raise_ValueError("invalid dupterm index"); - } - - mp_obj_t previous_obj = MP_STATE_VM(dupterm_objs[idx]); - if (previous_obj == MP_OBJ_NULL) { - previous_obj = mp_const_none; - } - if (args[0] == mp_const_none) { - MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL; - } else { - MP_STATE_VM(dupterm_objs[idx]) = args[0]; - if (MP_STATE_VM(dupterm_arr_obj) == MP_OBJ_NULL) { - MP_STATE_VM(dupterm_arr_obj) = mp_obj_new_bytearray(1, ""); - } - } - - return previous_obj; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm); - -#endif diff --git a/MicroPython_BUILD/components/micropython/extmod/utime_mphal.c b/MicroPython_BUILD/components/micropython/extmod/utime_mphal.c index 850e6b50..d536e648 100644 --- a/MicroPython_BUILD/components/micropython/extmod/utime_mphal.c +++ b/MicroPython_BUILD/components/micropython/extmod/utime_mphal.c @@ -37,6 +37,7 @@ #include "py/runtime.h" #include "extmod/utime_mphal.h" +// Sleep for number of seconds (given as float) //------------------------------------------------------------------ STATIC mp_obj_t time_sleep(mp_uint_t n_args, const mp_obj_t *args) { #if MICROPY_PY_BUILTINS_FLOAT @@ -55,6 +56,8 @@ STATIC mp_obj_t time_sleep(mp_uint_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_utime_sleep_obj, 1, 2, time_sleep); +// Sleep for number of milliseconds (given as integer) +// If the 2nd (optional) argument is set to True, return actual number of sleep ms //--------------------------------------------------------------------- STATIC mp_obj_t time_sleep_ms(mp_uint_t n_args, const mp_obj_t *args) { mp_int_t ms = mp_obj_get_int(args[0]); @@ -66,6 +69,7 @@ STATIC mp_obj_t time_sleep_ms(mp_uint_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_utime_sleep_ms_obj, 1, 2, time_sleep_ms); +// Sleep for number of microseconds (given as integer) //------------------------------------------- STATIC mp_obj_t time_sleep_us(mp_obj_t arg) { mp_int_t us = mp_obj_get_int(arg); @@ -76,7 +80,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj, time_sleep_us); //----------------------------------- STATIC mp_obj_t time_ticks_ms(void) { - return mp_obj_new_int_from_ull(mp_hal_ticks_ms()); + return mp_obj_new_int_from_ull(mp_hal_ticks_ms()); } MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj, time_ticks_ms); @@ -103,7 +107,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj, time_ticks_diff); //---------------------------------------------------------------------- STATIC mp_obj_t time_tickscpu_diff(mp_obj_t end_in, mp_obj_t start_in) { - // we assume that the arguments come from ticks_xx so are small ints + // we assume that the arguments come from ticks_cpu so are small integers mp_uint_t start = MP_OBJ_SMALL_INT_VALUE(start_in); mp_uint_t end = MP_OBJ_SMALL_INT_VALUE(end_in); // Optimized formula avoiding if conditions. We adjust difference "forward", @@ -116,7 +120,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_tickscpu_diff_obj, time_tickscpu_diff); //-------------------------------------------------------------------- STATIC mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) { - // we assume that first argument come from ticks_xx so is 64-bit int + // both arguments can be 64-bit integers uint64_t tickin = mp_obj_get_int64(ticks_in); uint64_t delta = mp_obj_get_int64(delta_in); int64_t addtick = tickin + delta; diff --git a/MicroPython_BUILD/components/micropython/lib/libc/string0.c b/MicroPython_BUILD/components/micropython/lib/libc/string0.c deleted file mode 100644 index c2f2abd0..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libc/string0.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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 -#include - -#define likely(x) __builtin_expect((x), 1) - -void *memcpy(void *dst, const void *src, size_t n) { - if (likely(!(((uintptr_t)dst) & 3) && !(((uintptr_t)src) & 3))) { - // pointers aligned - uint32_t *d = dst; - const uint32_t *s = src; - - // copy words first - for (size_t i = (n >> 2); i; i--) { - *d++ = *s++; - } - - if (n & 2) { - // copy half-word - *(uint16_t*)d = *(const uint16_t*)s; - d = (uint32_t*)((uint16_t*)d + 1); - s = (const uint32_t*)((const uint16_t*)s + 1); - } - - if (n & 1) { - // copy byte - *((uint8_t*)d) = *((const uint8_t*)s); - } - } else { - // unaligned access, copy bytes - uint8_t *d = dst; - const uint8_t *s = src; - - for (; n; n--) { - *d++ = *s++; - } - } - - return dst; -} - -void *memmove(void *dest, const void *src, size_t n) { - if (src < dest && (uint8_t*)dest < (const uint8_t*)src + n) { - // need to copy backwards - uint8_t *d = (uint8_t*)dest + n - 1; - const uint8_t *s = (const uint8_t*)src + n - 1; - for (; n > 0; n--) { - *d-- = *s--; - } - return dest; - } else { - // can use normal memcpy - return memcpy(dest, src, n); - } -} - -void *memset(void *s, int c, size_t n) { - if (c == 0 && ((uintptr_t)s & 3) == 0) { - // aligned store of 0 - uint32_t *s32 = s; - for (size_t i = n >> 2; i > 0; i--) { - *s32++ = 0; - } - if (n & 2) { - *((uint16_t*)s32) = 0; - s32 = (uint32_t*)((uint16_t*)s32 + 1); - } - if (n & 1) { - *((uint8_t*)s32) = 0; - } - } else { - uint8_t *s2 = s; - for (; n > 0; n--) { - *s2++ = c; - } - } - return s; -} - -int memcmp(const void *s1, const void *s2, size_t n) { - const uint8_t *s1_8 = s1; - const uint8_t *s2_8 = s2; - while (n--) { - char c1 = *s1_8++; - char c2 = *s2_8++; - if (c1 < c2) return -1; - else if (c1 > c2) return 1; - } - return 0; -} - -void *memchr(const void *s, int c, size_t n) { - if (n != 0) { - const unsigned char *p = s; - - do { - if (*p++ == c) - return ((void *)(p - 1)); - } while (--n != 0); - } - return 0; -} - -size_t strlen(const char *str) { - int len = 0; - for (const char *s = str; *s; s++) { - len += 1; - } - return len; -} - -int strcmp(const char *s1, const char *s2) { - while (*s1 && *s2) { - char c1 = *s1++; // XXX UTF8 get char, next char - char c2 = *s2++; // XXX UTF8 get char, next char - if (c1 < c2) return -1; - else if (c1 > c2) return 1; - } - if (*s2) return -1; - else if (*s1) return 1; - else return 0; -} - -int strncmp(const char *s1, const char *s2, size_t n) { - while (*s1 && *s2 && n > 0) { - char c1 = *s1++; // XXX UTF8 get char, next char - char c2 = *s2++; // XXX UTF8 get char, next char - n--; - if (c1 < c2) return -1; - else if (c1 > c2) return 1; - } - if (n == 0) return 0; - else if (*s2) return -1; - else if (*s1) return 1; - else return 0; -} - -char *strcpy(char *dest, const char *src) { - char *d = dest; - while (*src) { - *d++ = *src++; - } - *d = '\0'; - return dest; -} - -// needed because gcc optimises strcpy + strcat to this -char *stpcpy(char *dest, const char *src) { - while (*src) { - *dest++ = *src++; - } - *dest = '\0'; - return dest; -} - -char *strcat(char *dest, const char *src) { - char *d = dest; - while (*d) { - d++; - } - while (*src) { - *d++ = *src++; - } - *d = '\0'; - return dest; -} - -// Public Domain implementation of strchr from: -// http://en.wikibooks.org/wiki/C_Programming/Strings#The_strchr_function -char *strchr(const char *s, int c) -{ - /* Scan s for the character. When this loop is finished, - s will either point to the end of the string or the - character we were looking for. */ - while (*s != '\0' && *s != (char)c) - s++; - return ((*s == c) ? (char *) s : 0); -} - - -// Public Domain implementation of strstr from: -// http://en.wikibooks.org/wiki/C_Programming/Strings#The_strstr_function -char *strstr(const char *haystack, const char *needle) -{ - size_t needlelen; - /* Check for the null needle case. */ - if (*needle == '\0') - return (char *) haystack; - needlelen = strlen(needle); - for (; (haystack = strchr(haystack, *needle)) != 0; haystack++) - if (strncmp(haystack, needle, needlelen) == 0) - return (char *) haystack; - return 0; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libffi/include/ffi_cfi.h b/MicroPython_BUILD/components/micropython/lib/libffi/include/ffi_cfi.h deleted file mode 100644 index 244ce572..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libffi/include/ffi_cfi.h +++ /dev/null @@ -1,55 +0,0 @@ -/* ----------------------------------------------------------------------- - ffi_cfi.h - Copyright (c) 2014 Red Hat, Inc. - - Conditionally assemble cfi directives. Only necessary for building libffi. - ----------------------------------------------------------------------- */ - -#ifndef FFI_CFI_H -#define FFI_CFI_H - -#ifdef HAVE_AS_CFI_PSEUDO_OP - -# define cfi_startproc .cfi_startproc -# define cfi_endproc .cfi_endproc -# define cfi_def_cfa(reg, off) .cfi_def_cfa reg, off -# define cfi_def_cfa_register(reg) .cfi_def_cfa_register reg -# define cfi_def_cfa_offset(off) .cfi_def_cfa_offset off -# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off -# define cfi_offset(reg, off) .cfi_offset reg, off -# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off -# define cfi_register(r1, r2) .cfi_register r1, r2 -# define cfi_return_column(reg) .cfi_return_column reg -# define cfi_restore(reg) .cfi_restore reg -# define cfi_same_value(reg) .cfi_same_value reg -# define cfi_undefined(reg) .cfi_undefined reg -# define cfi_remember_state .cfi_remember_state -# define cfi_restore_state .cfi_restore_state -# define cfi_window_save .cfi_window_save -# define cfi_personality(enc, exp) .cfi_personality enc, exp -# define cfi_lsda(enc, exp) .cfi_lsda enc, exp -# define cfi_escape(...) .cfi_escape __VA_ARGS__ - -#else - -# define cfi_startproc -# define cfi_endproc -# define cfi_def_cfa(reg, off) -# define cfi_def_cfa_register(reg) -# define cfi_def_cfa_offset(off) -# define cfi_adjust_cfa_offset(off) -# define cfi_offset(reg, off) -# define cfi_rel_offset(reg, off) -# define cfi_register(r1, r2) -# define cfi_return_column(reg) -# define cfi_restore(reg) -# define cfi_same_value(reg) -# define cfi_undefined(reg) -# define cfi_remember_state -# define cfi_restore_state -# define cfi_window_save -# define cfi_personality(enc, exp) -# define cfi_lsda(enc, exp) -# define cfi_escape(...) - -#endif /* HAVE_AS_CFI_PSEUDO_OP */ -#endif /* FFI_CFI_H */ diff --git a/MicroPython_BUILD/components/micropython/lib/libffi/include/ffi_common.h b/MicroPython_BUILD/components/micropython/lib/libffi/include/ffi_common.h deleted file mode 100644 index 37f5a9e9..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libffi/include/ffi_common.h +++ /dev/null @@ -1,132 +0,0 @@ -/* ----------------------------------------------------------------------- - ffi_common.h - Copyright (C) 2011, 2012, 2013 Anthony Green - Copyright (C) 2007 Free Software Foundation, Inc - Copyright (c) 1996 Red Hat, Inc. - - Common internal definitions and macros. Only necessary for building - libffi. - ----------------------------------------------------------------------- */ - -#ifndef FFI_COMMON_H -#define FFI_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Do not move this. Some versions of AIX are very picky about where - this is positioned. */ -#ifdef __GNUC__ -# if HAVE_ALLOCA_H -# include -# else - /* mingw64 defines this already in malloc.h. */ -# ifndef alloca -# define alloca __builtin_alloca -# endif -# endif -# define MAYBE_UNUSED __attribute__((__unused__)) -#else -# define MAYBE_UNUSED -# if HAVE_ALLOCA_H -# include -# else -# ifdef _AIX -# pragma alloca -# else -# ifndef alloca /* predefined by HP cc +Olibcalls */ -# ifdef _MSC_VER -# define alloca _alloca -# else -char *alloca (); -# endif -# endif -# endif -# endif -#endif - -/* Check for the existence of memcpy. */ -#if STDC_HEADERS -# include -#else -# ifndef HAVE_MEMCPY -# define memcpy(d, s, n) bcopy ((s), (d), (n)) -# endif -#endif - -#if defined(FFI_DEBUG) -#include -#endif - -#ifdef FFI_DEBUG -void ffi_assert(char *expr, char *file, int line); -void ffi_stop_here(void); -void ffi_type_test(ffi_type *a, char *file, int line); - -#define FFI_ASSERT(x) ((x) ? (void)0 : ffi_assert(#x, __FILE__,__LINE__)) -#define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l))) -#define FFI_ASSERT_VALID_TYPE(x) ffi_type_test (x, __FILE__, __LINE__) -#else -#define FFI_ASSERT(x) -#define FFI_ASSERT_AT(x, f, l) -#define FFI_ASSERT_VALID_TYPE(x) -#endif - -#define ALIGN(v, a) (((((size_t) (v))-1) | ((a)-1))+1) -#define ALIGN_DOWN(v, a) (((size_t) (v)) & -a) - -/* Perform machine dependent cif processing */ -ffi_status ffi_prep_cif_machdep(ffi_cif *cif); -ffi_status ffi_prep_cif_machdep_var(ffi_cif *cif, - unsigned int nfixedargs, unsigned int ntotalargs); - -/* Extended cif, used in callback from assembly routine */ -typedef struct -{ - ffi_cif *cif; - void *rvalue; - void **avalue; -} extended_cif; - -/* Terse sized type definitions. */ -#if defined(_MSC_VER) || defined(__sgi) || defined(__SUNPRO_C) -typedef unsigned char UINT8; -typedef signed char SINT8; -typedef unsigned short UINT16; -typedef signed short SINT16; -typedef unsigned int UINT32; -typedef signed int SINT32; -# ifdef _MSC_VER -typedef unsigned __int64 UINT64; -typedef signed __int64 SINT64; -# else -# include -typedef uint64_t UINT64; -typedef int64_t SINT64; -# endif -#else -typedef unsigned int UINT8 __attribute__((__mode__(__QI__))); -typedef signed int SINT8 __attribute__((__mode__(__QI__))); -typedef unsigned int UINT16 __attribute__((__mode__(__HI__))); -typedef signed int SINT16 __attribute__((__mode__(__HI__))); -typedef unsigned int UINT32 __attribute__((__mode__(__SI__))); -typedef signed int SINT32 __attribute__((__mode__(__SI__))); -typedef unsigned int UINT64 __attribute__((__mode__(__DI__))); -typedef signed int SINT64 __attribute__((__mode__(__DI__))); -#endif - -typedef float FLOAT32; - -#ifndef __GNUC__ -#define __builtin_expect(x, expected_value) (x) -#endif -#define LIKELY(x) __builtin_expect(!!(x),1) -#define UNLIKELY(x) __builtin_expect((x)!=0,0) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/ffi.c b/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/ffi.c deleted file mode 100644 index fd94dafb..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/ffi.c +++ /dev/null @@ -1,298 +0,0 @@ -/* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2013 Tensilica, Inc. - - XTENSA Foreign Function Interface - - 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 -#include - -/* - |----------------------------------------| - | | - on entry to ffi_call ----> |----------------------------------------| - | caller stack frame for registers a0-a3 | - |----------------------------------------| - | | - | additional arguments | - entry of the function ---> |----------------------------------------| - | copy of function arguments a2-a7 | - | - - - - - - - - - - - - - | - | | - - The area below the entry line becomes the new stack frame for the function. - -*/ - - -#define FFI_TYPE_STRUCT_REGS FFI_TYPE_LAST - - -extern void ffi_call_SYSV(void *rvalue, unsigned rsize, unsigned flags, - void(*fn)(void), unsigned nbytes, extended_cif*); -extern void ffi_closure_SYSV(void) FFI_HIDDEN; - -ffi_status ffi_prep_cif_machdep(ffi_cif *cif) -{ - switch(cif->rtype->type) { - case FFI_TYPE_SINT8: - case FFI_TYPE_UINT8: - case FFI_TYPE_SINT16: - case FFI_TYPE_UINT16: - cif->flags = cif->rtype->type; - break; - case FFI_TYPE_VOID: - case FFI_TYPE_FLOAT: - cif->flags = FFI_TYPE_UINT32; - break; - case FFI_TYPE_DOUBLE: - case FFI_TYPE_UINT64: - case FFI_TYPE_SINT64: - cif->flags = FFI_TYPE_UINT64; // cif->rtype->type; - break; - case FFI_TYPE_STRUCT: - cif->flags = FFI_TYPE_STRUCT; //_REGS; - /* Up to 16 bytes are returned in registers */ - if (cif->rtype->size > 4 * 4) { - /* returned structure is referenced by a register; use 8 bytes - (including 4 bytes for potential additional alignment) */ - cif->flags = FFI_TYPE_STRUCT; - cif->bytes += 8; - } - break; - - default: - cif->flags = FFI_TYPE_UINT32; - break; - } - - /* Round the stack up to a full 4 register frame, just in case - (we use this size in movsp). This way, it's also a multiple of - 8 bytes for 64-bit arguments. */ - cif->bytes = ALIGN(cif->bytes, 16); - - return FFI_OK; -} - -void ffi_prep_args(extended_cif *ecif, unsigned char* stack) -{ - unsigned int i; - unsigned long *addr; - ffi_type **ptr; - - union { - void **v; - char **c; - signed char **sc; - unsigned char **uc; - signed short **ss; - unsigned short **us; - unsigned int **i; - long long **ll; - float **f; - double **d; - } p_argv; - - /* Verify that everything is aligned up properly */ - FFI_ASSERT (((unsigned long) stack & 0x7) == 0); - - p_argv.v = ecif->avalue; - addr = (unsigned long*)stack; - - /* structures with a size greater than 16 bytes are passed in memory */ - if (ecif->cif->rtype->type == FFI_TYPE_STRUCT && ecif->cif->rtype->size > 16) - { - *addr++ = (unsigned long)ecif->rvalue; - } - - for (i = ecif->cif->nargs, ptr = ecif->cif->arg_types; - i > 0; - i--, ptr++, p_argv.v++) - { - switch ((*ptr)->type) - { - case FFI_TYPE_SINT8: - *addr++ = **p_argv.sc; - break; - case FFI_TYPE_UINT8: - *addr++ = **p_argv.uc; - break; - case FFI_TYPE_SINT16: - *addr++ = **p_argv.ss; - break; - case FFI_TYPE_UINT16: - *addr++ = **p_argv.us; - break; - case FFI_TYPE_FLOAT: - case FFI_TYPE_INT: - case FFI_TYPE_UINT32: - case FFI_TYPE_SINT32: - case FFI_TYPE_POINTER: - *addr++ = **p_argv.i; - break; - case FFI_TYPE_DOUBLE: - case FFI_TYPE_UINT64: - case FFI_TYPE_SINT64: - if (((unsigned long)addr & 4) != 0) - addr++; - *(unsigned long long*)addr = **p_argv.ll; - addr += sizeof(unsigned long long) / sizeof (addr); - break; - - case FFI_TYPE_STRUCT: - { - unsigned long offs; - unsigned long size; - - if (((unsigned long)addr & 4) != 0 && (*ptr)->alignment > 4) - addr++; - - offs = (unsigned long) addr - (unsigned long) stack; - size = (*ptr)->size; - - /* Entire structure must fit the argument registers or referenced */ - if (offs < FFI_REGISTER_NARGS * 4 - && offs + size > FFI_REGISTER_NARGS * 4) - addr = (unsigned long*) (stack + FFI_REGISTER_NARGS * 4); - - memcpy((char*) addr, *p_argv.c, size); - addr += (size + 3) / 4; - break; - } - - default: - FFI_ASSERT(0); - } - } -} - - -void ffi_call(ffi_cif* cif, void(*fn)(void), void *rvalue, void **avalue) -{ - extended_cif ecif; - unsigned long rsize = cif->rtype->size; - int flags = cif->flags; - void *alloc = NULL; - - ecif.cif = cif; - ecif.avalue = avalue; - - /* Note that for structures that are returned in registers (size <= 16 bytes) - we allocate a temporary buffer and use memcpy to copy it to the final - destination. The reason is that the target address might be misaligned or - the length not a multiple of 4 bytes. Handling all those cases would be - very complex. */ - - if (flags == FFI_TYPE_STRUCT && (rsize <= 16 || rvalue == NULL)) - { - alloc = alloca(ALIGN(rsize, 4)); - ecif.rvalue = alloc; - } - else - { - ecif.rvalue = rvalue; - } - - if (cif->abi != FFI_SYSV) - FFI_ASSERT(0); - - ffi_call_SYSV (ecif.rvalue, rsize, cif->flags, fn, cif->bytes, &ecif); - - if (alloc != NULL && rvalue != NULL) - memcpy(rvalue, alloc, rsize); -} - -extern void ffi_trampoline(); -extern void ffi_cacheflush(void* start, void* end); - -ffi_status -ffi_prep_closure_loc (ffi_closure* closure, - ffi_cif* cif, - void (*fun)(ffi_cif*, void*, void**, void*), - void *user_data, - void *codeloc) -{ - /* copye trampoline to stack and patch 'ffi_closure_SYSV' pointer */ - memcpy(closure->tramp, ffi_trampoline, FFI_TRAMPOLINE_SIZE); - *(unsigned int*)(&closure->tramp[8]) = (unsigned int)ffi_closure_SYSV; - - // Do we have this function? - // __builtin___clear_cache(closer->tramp, closer->tramp + FFI_TRAMPOLINE_SIZE) - ffi_cacheflush(closure->tramp, closure->tramp + FFI_TRAMPOLINE_SIZE); - - closure->cif = cif; - closure->fun = fun; - closure->user_data = user_data; - return FFI_OK; -} - - -long FFI_HIDDEN -ffi_closure_SYSV_inner(ffi_closure *closure, void **values, void *rvalue) -{ - ffi_cif *cif; - ffi_type **arg_types; - void **avalue; - int i, areg; - - cif = closure->cif; - if (cif->abi != FFI_SYSV) - return FFI_BAD_ABI; - - areg = 0; - - int rtype = cif->rtype->type; - if (rtype == FFI_TYPE_STRUCT && cif->rtype->size > 4 * 4) - { - rvalue = *values; - areg++; - } - - cif = closure->cif; - arg_types = cif->arg_types; - avalue = alloca(cif->nargs * sizeof(void *)); - - for (i = 0; i < cif->nargs; i++) - { - if (arg_types[i]->alignment == 8 && (areg & 1) != 0) - areg++; - - // skip the entry 16,a1 framework, add 16 bytes (4 registers) - if (areg == FFI_REGISTER_NARGS) - areg += 4; - - if (arg_types[i]->type == FFI_TYPE_STRUCT) - { - int numregs = ((arg_types[i]->size + 3) & ~3) / 4; - if (areg < FFI_REGISTER_NARGS && areg + numregs > FFI_REGISTER_NARGS) - areg = FFI_REGISTER_NARGS + 4; - } - - avalue[i] = &values[areg]; - areg += (arg_types[i]->size + 3) / 4; - } - - (closure->fun)(cif, rvalue, avalue, closure->user_data); - - return rtype; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/ffitarget.h b/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/ffitarget.h deleted file mode 100644 index 0ba728bc..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/ffitarget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2013 Tensilica, Inc. - Target configuration macros for XTENSA. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - ``Software''), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - ----------------------------------------------------------------------- */ - -#ifndef LIBFFI_TARGET_H -#define LIBFFI_TARGET_H - -#ifndef LIBFFI_H -#error "Please do not include ffitarget.h directly into your source. Use ffi.h instead." -#endif - -#ifndef LIBFFI_ASM -typedef unsigned long ffi_arg; -typedef signed long ffi_sarg; - -typedef enum ffi_abi { - FFI_FIRST_ABI = 0, - FFI_SYSV, - FFI_LAST_ABI, - FFI_DEFAULT_ABI = FFI_SYSV -} ffi_abi; -#endif - -#define FFI_REGISTER_NARGS 6 - -/* ---- Definitions for closures ----------------------------------------- */ - -#define FFI_CLOSURES 1 -#define FFI_NATIVE_RAW_API 0 -#define FFI_TRAMPOLINE_SIZE 24 - -#endif diff --git a/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/sysv.S b/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/sysv.S deleted file mode 100644 index 64e6a091..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libffi/src/xtensa/sysv.S +++ /dev/null @@ -1,253 +0,0 @@ -/* ----------------------------------------------------------------------- - sysv.S - Copyright (c) 2013 Tensilica, Inc. - - XTENSA Foreign Function Interface - - 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. - ----------------------------------------------------------------------- */ - -#define LIBFFI_ASM -#include -#include - -#define ENTRY(name) .text; .globl name; .type name,@function; .align 4; name: -#define END(name) .size name , . - name - -/* Assert that the table below is in sync with ffi.h. */ - -#if FFI_TYPE_UINT8 != 5 \ - || FFI_TYPE_SINT8 != 6 \ - || FFI_TYPE_UINT16 != 7 \ - || FFI_TYPE_SINT16 != 8 \ - || FFI_TYPE_UINT32 != 9 \ - || FFI_TYPE_SINT32 != 10 \ - || FFI_TYPE_UINT64 != 11 -#error "xtensa/sysv.S out of sync with ffi.h" -#endif - - -/* ffi_call_SYSV (rvalue, rbytes, flags, (*fnaddr)(), bytes, ecif) - void *rvalue; a2 - unsigned long rbytes; a3 - unsigned flags; a4 - void (*fnaddr)(); a5 - unsigned long bytes; a6 - extended_cif* ecif) a7 -*/ - -ENTRY(ffi_call_SYSV) - - entry a1, 32 # 32 byte frame for using call8 below - - mov a10, a7 # a10(->arg0): ecif - sub a11, a1, a6 # a11(->arg1): stack pointer - mov a7, a1 # fp - movsp a1, a11 # set new sp = old_sp - bytes - - movi a8, ffi_prep_args - callx8 a8 # ffi_prep_args(ecif, stack) - - # prepare to move stack pointer back up to 6 arguments - # note that 'bytes' is already aligned - - movi a10, 6*4 - sub a11, a6, a10 - movgez a6, a10, a11 - add a6, a1, a6 - - - # we can pass up to 6 arguments in registers - # for simplicity, just load 6 arguments - # (the stack size is at least 32 bytes, so no risk to cross boundaries) - - l32i a10, a1, 0 - l32i a11, a1, 4 - l32i a12, a1, 8 - l32i a13, a1, 12 - l32i a14, a1, 16 - l32i a15, a1, 20 - - # move stack pointer - - movsp a1, a6 - - callx8 a5 # (*fn)(args...) - - # Handle return value(s) - - beqz a2, .Lexit - - movi a5, FFI_TYPE_STRUCT - bne a4, a5, .Lstore - movi a5, 16 - blt a5, a3, .Lexit - - s32i a10, a2, 0 - blti a3, 5, .Lexit - addi a3, a3, -1 - s32i a11, a2, 4 - blti a3, 8, .Lexit - s32i a12, a2, 8 - blti a3, 12, .Lexit - s32i a13, a2, 12 - -.Lexit: retw - -.Lstore: - addi a4, a4, -FFI_TYPE_UINT8 - bgei a4, 7, .Lexit # should never happen - movi a6, store_calls - add a4, a4, a4 - addx4 a6, a4, a6 # store_table + idx * 8 - jx a6 - - .align 8 -store_calls: - # UINT8 - s8i a10, a2, 0 - retw - - # SINT8 - .align 8 - s8i a10, a2, 0 - retw - - # UINT16 - .align 8 - s16i a10, a2, 0 - retw - - # SINT16 - .align 8 - s16i a10, a2, 0 - retw - - # UINT32 - .align 8 - s32i a10, a2, 0 - retw - - # SINT32 - .align 8 - s32i a10, a2, 0 - retw - - # UINT64 - .align 8 - s32i a10, a2, 0 - s32i a11, a2, 4 - retw - -END(ffi_call_SYSV) - - -/* - * void ffi_cacheflush (unsigned long start, unsigned long end) - */ - -#define EXTRA_ARGS_SIZE 24 - -ENTRY(ffi_cacheflush) - - entry a1, 16 - -1: dhwbi a2, 0 - ihi a2, 0 - addi a2, a2, 4 - blt a2, a3, 1b - - retw - -END(ffi_cacheflush) - -/* ffi_trampoline is copied to the stack */ - -ENTRY(ffi_trampoline) - - entry a1, 16 + (FFI_REGISTER_NARGS * 4) + (4 * 4) # [ 0] - j 2f # [ 3] - .align 4 # [ 6] -1: .long 0 # [ 8] -2: l32r a15, 1b # [12] - _mov a14, a0 # [15] - callx0 a15 # [18] - # [21] -END(ffi_trampoline) - -/* - * ffi_closure() - * - * a0: closure + 21 - * a14: return address (a0) - */ - -ENTRY(ffi_closure_SYSV) - - /* intentionally omitting entry here */ - - # restore return address (a0) and move pointer to closure to a10 - addi a10, a0, -21 - mov a0, a14 - - # allow up to 4 arguments as return values - addi a11, a1, 4 * 4 - - # save up to 6 arguments to stack (allocated by entry below) - s32i a2, a11, 0 - s32i a3, a11, 4 - s32i a4, a11, 8 - s32i a5, a11, 12 - s32i a6, a11, 16 - s32i a7, a11, 20 - - movi a8, ffi_closure_SYSV_inner - mov a12, a1 - callx8 a8 # .._inner(*closure, **avalue, *rvalue) - - # load up to four return arguments - l32i a2, a1, 0 - l32i a3, a1, 4 - l32i a4, a1, 8 - l32i a5, a1, 12 - - # (sign-)extend return value - movi a11, FFI_TYPE_UINT8 - bne a10, a11, 1f - extui a2, a2, 0, 8 - retw - -1: movi a11, FFI_TYPE_SINT8 - bne a10, a11, 1f - sext a2, a2, 7 - retw - -1: movi a11, FFI_TYPE_UINT16 - bne a10, a11, 1f - extui a2, a2, 0, 16 - retw - -1: movi a11, FFI_TYPE_SINT16 - bne a10, a11, 1f - sext a2, a2, 15 - -1: retw - -END(ffi_closure_SYSV) diff --git a/MicroPython_BUILD/components/micropython/lib/libm/acoshf.c b/MicroPython_BUILD/components/micropython/lib/libm/acoshf.c deleted file mode 100644 index 8a8409f5..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/acoshf.c +++ /dev/null @@ -1,32 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// acoshf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -#include "libm.h" - -#if FLT_EVAL_METHOD==2 -#undef sqrtf -#define sqrtf sqrtl -#elif FLT_EVAL_METHOD==1 -#undef sqrtf -#define sqrtf sqrt -#endif - -/* acosh(x) = log(x + sqrt(x*x-1)) */ -float acoshf(float x) -{ - union {float f; uint32_t i;} u = {x}; - uint32_t a = u.i & 0x7fffffff; - - if (a < 0x3f800000+(1<<23)) - /* |x| < 2, invalid if x < 1 or nan */ - /* up to 2ulp error in [1,1.125] */ - return log1pf(x-1 + sqrtf((x-1)*(x-1)+2*(x-1))); - if (a < 0x3f800000+(12<<23)) - /* |x| < 0x1p12 */ - return logf(2*x - 1/(x+sqrtf(x*x-1))); - /* x >= 0x1p12 */ - return logf(x) + 0.693147180559945309417232121458176568f; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/asinfacosf.c b/MicroPython_BUILD/components/micropython/lib/libm/asinfacosf.c deleted file mode 100644 index 07ecad3f..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/asinfacosf.c +++ /dev/null @@ -1,130 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// asinf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "libm.h" - -// dpgeorge: pio2 was double in original implementation of asinf -static const float -pio2_hi = 1.5707962513e+00, /* 0x3fc90fda */ -pio2_lo = 7.5497894159e-08; /* 0x33a22168 */ - -static const float -/* coefficients for R(x^2) */ -pS0 = 1.6666586697e-01, -pS1 = -4.2743422091e-02, -pS2 = -8.6563630030e-03, -qS1 = -7.0662963390e-01; - -static float R(float z) -{ - float_t p, q; - p = z*(pS0+z*(pS1+z*pS2)); - q = 1.0f+z*qS1; - return p/q; -} - -float asinf(float x) -{ - // dpgeorge: s was double in original implementation - float s,z; - uint32_t hx,ix; - - GET_FLOAT_WORD(hx, x); - ix = hx & 0x7fffffff; - if (ix >= 0x3f800000) { /* |x| >= 1 */ - if (ix == 0x3f800000) /* |x| == 1 */ - return x*pio2_hi + 0x1p-120f; /* asin(+-1) = +-pi/2 with inexact */ - return 0/(x-x); /* asin(|x|>1) is NaN */ - } - if (ix < 0x3f000000) { /* |x| < 0.5 */ - /* if 0x1p-126 <= |x| < 0x1p-12, avoid raising underflow */ - if (ix < 0x39800000 && ix >= 0x00800000) - return x; - return x + x*R(x*x); - } - /* 1 > |x| >= 0.5 */ - z = (1 - fabsf(x))*0.5f; - s = sqrtf(z); - x = pio2_hi - (2*(s+s*R(z)) - pio2_lo); // dpgeorge: use pio2_hi and pio2_lo - if (hx >> 31) - return -x; - return x; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// acosf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -float acosf(float x) -{ - float z,w,s,c,df; - uint32_t hx,ix; - - GET_FLOAT_WORD(hx, x); - ix = hx & 0x7fffffff; - /* |x| >= 1 or nan */ - if (ix >= 0x3f800000) { - if (ix == 0x3f800000) { - if (hx >> 31) - return 2*pio2_hi + 0x1p-120f; - return 0; - } - return 0/(x-x); - } - /* |x| < 0.5 */ - if (ix < 0x3f000000) { - if (ix <= 0x32800000) /* |x| < 2**-26 */ - return pio2_hi + 0x1p-120f; - return pio2_hi - (x - (pio2_lo-x*R(x*x))); - } - /* x < -0.5 */ - if (hx >> 31) { - z = (1+x)*0.5f; - s = sqrtf(z); - w = R(z)*s-pio2_lo; - return 2*(pio2_hi - (s+w)); - } - /* x > 0.5 */ - z = (1-x)*0.5f; - s = sqrtf(z); - GET_FLOAT_WORD(hx,s); - SET_FLOAT_WORD(df,hx&0xfffff000); - c = (z-df*df)/(s+df); - w = R(z)*s+c; - return 2*(df+w); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/asinhf.c b/MicroPython_BUILD/components/micropython/lib/libm/asinhf.c deleted file mode 100644 index 4bcb3f9a..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/asinhf.c +++ /dev/null @@ -1,34 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// asinhf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -#include "libm.h" - -/* asinh(x) = sign(x)*log(|x|+sqrt(x*x+1)) ~= x - x^3/6 + o(x^5) */ -float asinhf(float x) -{ - union {float f; uint32_t i;} u = {.f = x}; - uint32_t i = u.i & 0x7fffffff; - unsigned s = u.i >> 31; - - /* |x| */ - u.i = i; - x = u.f; - - if (i >= 0x3f800000 + (12<<23)) { - /* |x| >= 0x1p12 or inf or nan */ - x = logf(x) + 0.693147180559945309417232121458176568f; - } else if (i >= 0x3f800000 + (1<<23)) { - /* |x| >= 2 */ - x = logf(2*x + 1/(sqrtf(x*x+1)+x)); - } else if (i >= 0x3f800000 - (12<<23)) { - /* |x| >= 0x1p-12, up to 1.6ulp error in [0.125,0.5] */ - x = log1pf(x + x*x/(sqrtf(x*x+1)+1)); - } else { - /* |x| < 0x1p-12, raise inexact if x!=0 */ - FORCE_EVAL(x + 0x1p120f); - } - return s ? -x : x; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/atan2f.c b/MicroPython_BUILD/components/micropython/lib/libm/atan2f.c deleted file mode 100644 index 03d000c9..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/atan2f.c +++ /dev/null @@ -1,89 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// atan2f from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "libm.h" - -static const float -pi = 3.1415927410e+00, /* 0x40490fdb */ -pi_lo = -8.7422776573e-08; /* 0xb3bbbd2e */ - -float atan2f(float y, float x) -{ - float z; - uint32_t m,ix,iy; - - if (isnan(x) || isnan(y)) - return x+y; - GET_FLOAT_WORD(ix, x); - GET_FLOAT_WORD(iy, y); - if (ix == 0x3f800000) /* x=1.0 */ - return atanf(y); - m = ((iy>>31)&1) | ((ix>>30)&2); /* 2*sign(x)+sign(y) */ - ix &= 0x7fffffff; - iy &= 0x7fffffff; - - /* when y = 0 */ - if (iy == 0) { - switch (m) { - case 0: - case 1: return y; /* atan(+-0,+anything)=+-0 */ - case 2: return pi; /* atan(+0,-anything) = pi */ - case 3: return -pi; /* atan(-0,-anything) =-pi */ - } - } - /* when x = 0 */ - if (ix == 0) - return m&1 ? -pi/2 : pi/2; - /* when x is INF */ - if (ix == 0x7f800000) { - if (iy == 0x7f800000) { - switch (m) { - case 0: return pi/4; /* atan(+INF,+INF) */ - case 1: return -pi/4; /* atan(-INF,+INF) */ - case 2: return 3*pi/4; /*atan(+INF,-INF)*/ - case 3: return -3*pi/4; /*atan(-INF,-INF)*/ - } - } else { - switch (m) { - case 0: return 0.0f; /* atan(+...,+INF) */ - case 1: return -0.0f; /* atan(-...,+INF) */ - case 2: return pi; /* atan(+...,-INF) */ - case 3: return -pi; /* atan(-...,-INF) */ - } - } - } - /* |y/x| > 0x1p26 */ - if (ix+(26<<23) < iy || iy == 0x7f800000) - return m&1 ? -pi/2 : pi/2; - - /* z = atan(|y/x|) with correct underflow */ - if ((m&2) && iy+(26<<23) < ix) /*|y/x| < 0x1p-26, x < 0 */ - z = 0.0; - else - z = atanf(fabsf(y/x)); - switch (m) { - case 0: return z; /* atan(+,+) */ - case 1: return -z; /* atan(-,+) */ - case 2: return pi - (z-pi_lo); /* atan(+,-) */ - default: /* case 3 */ - return (z-pi_lo) - pi; /* atan(-,-) */ - } -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/atanf.c b/MicroPython_BUILD/components/micropython/lib/libm/atanf.c deleted file mode 100644 index 053fc1b6..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/atanf.c +++ /dev/null @@ -1,100 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// atanf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - - -#include "libm.h" - -static const float atanhi[] = { - 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */ - 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */ - 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */ - 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */ -}; - -static const float atanlo[] = { - 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */ - 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */ - 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */ - 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */ -}; - -static const float aT[] = { - 3.3333328366e-01, - -1.9999158382e-01, - 1.4253635705e-01, - -1.0648017377e-01, - 6.1687607318e-02, -}; - -float atanf(float x) -{ - float_t w,s1,s2,z; - uint32_t ix,sign; - int id; - - GET_FLOAT_WORD(ix, x); - sign = ix>>31; - ix &= 0x7fffffff; - if (ix >= 0x4c800000) { /* if |x| >= 2**26 */ - if (isnan(x)) - return x; - z = atanhi[3] + 0x1p-120f; - return sign ? -z : z; - } - if (ix < 0x3ee00000) { /* |x| < 0.4375 */ - if (ix < 0x39800000) { /* |x| < 2**-12 */ - if (ix < 0x00800000) - /* raise underflow for subnormal x */ - FORCE_EVAL(x*x); - return x; - } - id = -1; - } else { - x = fabsf(x); - if (ix < 0x3f980000) { /* |x| < 1.1875 */ - if (ix < 0x3f300000) { /* 7/16 <= |x| < 11/16 */ - id = 0; - x = (2.0f*x - 1.0f)/(2.0f + x); - } else { /* 11/16 <= |x| < 19/16 */ - id = 1; - x = (x - 1.0f)/(x + 1.0f); - } - } else { - if (ix < 0x401c0000) { /* |x| < 2.4375 */ - id = 2; - x = (x - 1.5f)/(1.0f + 1.5f*x); - } else { /* 2.4375 <= |x| < 2**26 */ - id = 3; - x = -1.0f/x; - } - } - } - /* end of argument reduction */ - z = x*x; - w = z*z; - /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ - s1 = z*(aT[0]+w*(aT[2]+w*aT[4])); - s2 = w*(aT[1]+w*aT[3]); - if (id < 0) - return x - x*(s1+s2); - z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); - return sign ? -z : z; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/atanhf.c b/MicroPython_BUILD/components/micropython/lib/libm/atanhf.c deleted file mode 100644 index 6f95f497..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/atanhf.c +++ /dev/null @@ -1,34 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// atanhf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -#include "libm.h" - -/* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */ -float atanhf(float x) -{ - union {float f; uint32_t i;} u = {.f = x}; - unsigned s = u.i >> 31; - float_t y; - - /* |x| */ - u.i &= 0x7fffffff; - y = u.f; - - if (u.i < 0x3f800000 - (1<<23)) { - if (u.i < 0x3f800000 - (32<<23)) { - /* handle underflow */ - if (u.i < (1<<23)) - FORCE_EVAL((float)(y*y)); - } else { - /* |x| < 0.5, up to 1.7ulp error */ - y = 0.5f*log1pf(2*y + 2*y*y/(1-y)); - } - } else { - /* avoid overflow */ - y = 0.5f*log1pf(2*(y/(1-y))); - } - return s ? -y : y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/ef_rem_pio2.c b/MicroPython_BUILD/components/micropython/lib/libm/ef_rem_pio2.c deleted file mode 100644 index ca55243f..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/ef_rem_pio2.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* ef_rem_pio2.c -- float version of e_rem_pio2.c - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - * - */ - -/* __ieee754_rem_pio2f(x,y) - * - * return the remainder of x rem pi/2 in y[0]+y[1] - * use __kernel_rem_pio2f() - */ - -#include "fdlibm.h" - -/* - * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi - */ -#ifdef __STDC__ -static const __int32_t two_over_pi[] = { -#else -static __int32_t two_over_pi[] = { -#endif -0xA2, 0xF9, 0x83, 0x6E, 0x4E, 0x44, 0x15, 0x29, 0xFC, -0x27, 0x57, 0xD1, 0xF5, 0x34, 0xDD, 0xC0, 0xDB, 0x62, -0x95, 0x99, 0x3C, 0x43, 0x90, 0x41, 0xFE, 0x51, 0x63, -0xAB, 0xDE, 0xBB, 0xC5, 0x61, 0xB7, 0x24, 0x6E, 0x3A, -0x42, 0x4D, 0xD2, 0xE0, 0x06, 0x49, 0x2E, 0xEA, 0x09, -0xD1, 0x92, 0x1C, 0xFE, 0x1D, 0xEB, 0x1C, 0xB1, 0x29, -0xA7, 0x3E, 0xE8, 0x82, 0x35, 0xF5, 0x2E, 0xBB, 0x44, -0x84, 0xE9, 0x9C, 0x70, 0x26, 0xB4, 0x5F, 0x7E, 0x41, -0x39, 0x91, 0xD6, 0x39, 0x83, 0x53, 0x39, 0xF4, 0x9C, -0x84, 0x5F, 0x8B, 0xBD, 0xF9, 0x28, 0x3B, 0x1F, 0xF8, -0x97, 0xFF, 0xDE, 0x05, 0x98, 0x0F, 0xEF, 0x2F, 0x11, -0x8B, 0x5A, 0x0A, 0x6D, 0x1F, 0x6D, 0x36, 0x7E, 0xCF, -0x27, 0xCB, 0x09, 0xB7, 0x4F, 0x46, 0x3F, 0x66, 0x9E, -0x5F, 0xEA, 0x2D, 0x75, 0x27, 0xBA, 0xC7, 0xEB, 0xE5, -0xF1, 0x7B, 0x3D, 0x07, 0x39, 0xF7, 0x8A, 0x52, 0x92, -0xEA, 0x6B, 0xFB, 0x5F, 0xB1, 0x1F, 0x8D, 0x5D, 0x08, -0x56, 0x03, 0x30, 0x46, 0xFC, 0x7B, 0x6B, 0xAB, 0xF0, -0xCF, 0xBC, 0x20, 0x9A, 0xF4, 0x36, 0x1D, 0xA9, 0xE3, -0x91, 0x61, 0x5E, 0xE6, 0x1B, 0x08, 0x65, 0x99, 0x85, -0x5F, 0x14, 0xA0, 0x68, 0x40, 0x8D, 0xFF, 0xD8, 0x80, -0x4D, 0x73, 0x27, 0x31, 0x06, 0x06, 0x15, 0x56, 0xCA, -0x73, 0xA8, 0xC9, 0x60, 0xE2, 0x7B, 0xC0, 0x8C, 0x6B, -}; - -/* This array is like the one in e_rem_pio2.c, but the numbers are - single precision and the last 8 bits are forced to 0. */ -#ifdef __STDC__ -static const __int32_t npio2_hw[] = { -#else -static __int32_t npio2_hw[] = { -#endif -0x3fc90f00, 0x40490f00, 0x4096cb00, 0x40c90f00, 0x40fb5300, 0x4116cb00, -0x412fed00, 0x41490f00, 0x41623100, 0x417b5300, 0x418a3a00, 0x4196cb00, -0x41a35c00, 0x41afed00, 0x41bc7e00, 0x41c90f00, 0x41d5a000, 0x41e23100, -0x41eec200, 0x41fb5300, 0x4203f200, 0x420a3a00, 0x42108300, 0x4216cb00, -0x421d1400, 0x42235c00, 0x4229a500, 0x422fed00, 0x42363600, 0x423c7e00, -0x4242c700, 0x42490f00 -}; - -/* - * invpio2: 24 bits of 2/pi - * pio2_1: first 17 bit of pi/2 - * pio2_1t: pi/2 - pio2_1 - * pio2_2: second 17 bit of pi/2 - * pio2_2t: pi/2 - (pio2_1+pio2_2) - * pio2_3: third 17 bit of pi/2 - * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) - */ - -#ifdef __STDC__ -static const float -#else -static float -#endif -zero = 0.0000000000e+00, /* 0x00000000 */ -half = 5.0000000000e-01, /* 0x3f000000 */ -two8 = 2.5600000000e+02, /* 0x43800000 */ -invpio2 = 6.3661980629e-01, /* 0x3f22f984 */ -pio2_1 = 1.5707855225e+00, /* 0x3fc90f80 */ -pio2_1t = 1.0804334124e-05, /* 0x37354443 */ -pio2_2 = 1.0804273188e-05, /* 0x37354400 */ -pio2_2t = 6.0770999344e-11, /* 0x2e85a308 */ -pio2_3 = 6.0770943833e-11, /* 0x2e85a300 */ -pio2_3t = 6.1232342629e-17; /* 0x248d3132 */ - -#ifdef __STDC__ - __int32_t __ieee754_rem_pio2f(float x, float *y) -#else - __int32_t __ieee754_rem_pio2f(x,y) - float x,y[]; -#endif -{ - float z,w,t,r,fn; - float tx[3]; - __int32_t i,j,n,ix,hx; - int e0,nx; - - GET_FLOAT_WORD(hx,x); - ix = hx&0x7fffffff; - if(ix<=0x3f490fd8) /* |x| ~<= pi/4 , no need for reduction */ - {y[0] = x; y[1] = 0; return 0;} - if(ix<0x4016cbe4) { /* |x| < 3pi/4, special case with n=+-1 */ - if(hx>0) { - z = x - pio2_1; - if((ix&0xfffffff0)!=0x3fc90fd0) { /* 24+24 bit pi OK */ - y[0] = z - pio2_1t; - y[1] = (z-y[0])-pio2_1t; - } else { /* near pi/2, use 24+24+24 bit pi */ - z -= pio2_2; - y[0] = z - pio2_2t; - y[1] = (z-y[0])-pio2_2t; - } - return 1; - } else { /* negative x */ - z = x + pio2_1; - if((ix&0xfffffff0)!=0x3fc90fd0) { /* 24+24 bit pi OK */ - y[0] = z + pio2_1t; - y[1] = (z-y[0])+pio2_1t; - } else { /* near pi/2, use 24+24+24 bit pi */ - z += pio2_2; - y[0] = z + pio2_2t; - y[1] = (z-y[0])+pio2_2t; - } - return -1; - } - } - if(ix<=0x43490f80) { /* |x| ~<= 2^7*(pi/2), medium size */ - t = fabsf(x); - n = (__int32_t) (t*invpio2+half); - fn = (float)n; - r = t-fn*pio2_1; - w = fn*pio2_1t; /* 1st round good to 40 bit */ - if(n<32&&(ix&0xffffff00)!=npio2_hw[n-1]) { - y[0] = r-w; /* quick check no cancellation */ - } else { - __uint32_t high; - j = ix>>23; - y[0] = r-w; - GET_FLOAT_WORD(high,y[0]); - i = j-((high>>23)&0xff); - if(i>8) { /* 2nd iteration needed, good to 57 */ - t = r; - w = fn*pio2_2; - r = t-w; - w = fn*pio2_2t-((t-r)-w); - y[0] = r-w; - GET_FLOAT_WORD(high,y[0]); - i = j-((high>>23)&0xff); - if(i>25) { /* 3rd iteration need, 74 bits acc */ - t = r; /* will cover all possible cases */ - w = fn*pio2_3; - r = t-w; - w = fn*pio2_3t-((t-r)-w); - y[0] = r-w; - } - } - } - y[1] = (r-y[0])-w; - if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} - else return n; - } - /* - * all other (large) arguments - */ - if(!FLT_UWORD_IS_FINITE(ix)) { - y[0]=y[1]=x-x; return 0; - } - /* set z = scalbn(|x|,ilogb(x)-7) */ - e0 = (int)((ix>>23)-134); /* e0 = ilogb(z)-7; */ - SET_FLOAT_WORD(z, ix - ((__int32_t)e0<<23)); - for(i=0;i<2;i++) { - tx[i] = (float)((__int32_t)(z)); - z = (z-tx[i])*two8; - } - tx[2] = z; - nx = 3; - while(tx[nx-1]==zero) nx--; /* skip zero term */ - n = __kernel_rem_pio2f(tx,y,e0,nx,2,two_over_pi); - if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} - return n; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/ef_sqrt.c b/MicroPython_BUILD/components/micropython/lib/libm/ef_sqrt.c deleted file mode 100644 index 87484d0b..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/ef_sqrt.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* ef_sqrtf.c -- float version of e_sqrt.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ -static const float one = 1.0, tiny=1.0e-30; -#else -static float one = 1.0, tiny=1.0e-30; -#endif - -// sqrtf is exactly __ieee754_sqrtf when _IEEE_LIBM defined -float sqrtf(float x) -/* -#ifdef __STDC__ - float __ieee754_sqrtf(float x) -#else - float __ieee754_sqrtf(x) - float x; -#endif -*/ -{ - float z; - __uint32_t r,hx; - __int32_t ix,s,q,m,t,i; - - GET_FLOAT_WORD(ix,x); - hx = ix&0x7fffffff; - - /* take care of Inf and NaN */ - if(!FLT_UWORD_IS_FINITE(hx)) - return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf - sqrt(-inf)=sNaN */ - /* take care of zero and -ves */ - if(FLT_UWORD_IS_ZERO(hx)) return x;/* sqrt(+-0) = +-0 */ - if(ix<0) return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ - - /* normalize x */ - m = (ix>>23); - if(FLT_UWORD_IS_SUBNORMAL(hx)) { /* subnormal x */ - for(i=0;(ix&0x00800000L)==0;i++) ix<<=1; - m -= i-1; - } - m -= 127; /* unbias exponent */ - ix = (ix&0x007fffffL)|0x00800000L; - if(m&1) /* odd m, double x to make it even */ - ix += ix; - m >>= 1; /* m = [m/2] */ - - /* generate sqrt(x) bit by bit */ - ix += ix; - q = s = 0; /* q = sqrt(x) */ - r = 0x01000000L; /* r = moving bit from right to left */ - - while(r!=0) { - t = s+r; - if(t<=ix) { - s = t+r; - ix -= t; - q += r; - } - ix += ix; - r>>=1; - } - - /* use floating add to find out rounding direction */ - if(ix!=0) { - z = one-tiny; /* trigger inexact flag */ - if (z>=one) { - z = one+tiny; - if (z>one) - q += 2; - else - q += (q&1); - } - } - ix = (q>>1)+0x3f000000L; - ix += (m <<23); - SET_FLOAT_WORD(z,ix); - return z; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/erf_lgamma.c b/MicroPython_BUILD/components/micropython/lib/libm/erf_lgamma.c deleted file mode 100644 index 877816a0..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/erf_lgamma.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* erf_lgamma.c -- float version of er_lgamma.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - * - */ - -#include "fdlibm.h" - -#define __ieee754_logf logf - -#ifdef __STDC__ -static const float -#else -static float -#endif -two23= 8.3886080000e+06, /* 0x4b000000 */ -half= 5.0000000000e-01, /* 0x3f000000 */ -one = 1.0000000000e+00, /* 0x3f800000 */ -pi = 3.1415927410e+00, /* 0x40490fdb */ -a0 = 7.7215664089e-02, /* 0x3d9e233f */ -a1 = 3.2246702909e-01, /* 0x3ea51a66 */ -a2 = 6.7352302372e-02, /* 0x3d89f001 */ -a3 = 2.0580807701e-02, /* 0x3ca89915 */ -a4 = 7.3855509982e-03, /* 0x3bf2027e */ -a5 = 2.8905137442e-03, /* 0x3b3d6ec6 */ -a6 = 1.1927076848e-03, /* 0x3a9c54a1 */ -a7 = 5.1006977446e-04, /* 0x3a05b634 */ -a8 = 2.2086278477e-04, /* 0x39679767 */ -a9 = 1.0801156895e-04, /* 0x38e28445 */ -a10 = 2.5214456400e-05, /* 0x37d383a2 */ -a11 = 4.4864096708e-05, /* 0x383c2c75 */ -tc = 1.4616321325e+00, /* 0x3fbb16c3 */ -tf = -1.2148628384e-01, /* 0xbdf8cdcd */ -/* tt = -(tail of tf) */ -tt = 6.6971006518e-09, /* 0x31e61c52 */ -t0 = 4.8383611441e-01, /* 0x3ef7b95e */ -t1 = -1.4758771658e-01, /* 0xbe17213c */ -t2 = 6.4624942839e-02, /* 0x3d845a15 */ -t3 = -3.2788541168e-02, /* 0xbd064d47 */ -t4 = 1.7970675603e-02, /* 0x3c93373d */ -t5 = -1.0314224288e-02, /* 0xbc28fcfe */ -t6 = 6.1005386524e-03, /* 0x3bc7e707 */ -t7 = -3.6845202558e-03, /* 0xbb7177fe */ -t8 = 2.2596477065e-03, /* 0x3b141699 */ -t9 = -1.4034647029e-03, /* 0xbab7f476 */ -t10 = 8.8108185446e-04, /* 0x3a66f867 */ -t11 = -5.3859531181e-04, /* 0xba0d3085 */ -t12 = 3.1563205994e-04, /* 0x39a57b6b */ -t13 = -3.1275415677e-04, /* 0xb9a3f927 */ -t14 = 3.3552918467e-04, /* 0x39afe9f7 */ -u0 = -7.7215664089e-02, /* 0xbd9e233f */ -u1 = 6.3282704353e-01, /* 0x3f2200f4 */ -u2 = 1.4549225569e+00, /* 0x3fba3ae7 */ -u3 = 9.7771751881e-01, /* 0x3f7a4bb2 */ -u4 = 2.2896373272e-01, /* 0x3e6a7578 */ -u5 = 1.3381091878e-02, /* 0x3c5b3c5e */ -v1 = 2.4559779167e+00, /* 0x401d2ebe */ -v2 = 2.1284897327e+00, /* 0x4008392d */ -v3 = 7.6928514242e-01, /* 0x3f44efdf */ -v4 = 1.0422264785e-01, /* 0x3dd572af */ -v5 = 3.2170924824e-03, /* 0x3b52d5db */ -s0 = -7.7215664089e-02, /* 0xbd9e233f */ -s1 = 2.1498242021e-01, /* 0x3e5c245a */ -s2 = 3.2577878237e-01, /* 0x3ea6cc7a */ -s3 = 1.4635047317e-01, /* 0x3e15dce6 */ -s4 = 2.6642270386e-02, /* 0x3cda40e4 */ -s5 = 1.8402845599e-03, /* 0x3af135b4 */ -s6 = 3.1947532989e-05, /* 0x3805ff67 */ -r1 = 1.3920053244e+00, /* 0x3fb22d3b */ -r2 = 7.2193557024e-01, /* 0x3f38d0c5 */ -r3 = 1.7193385959e-01, /* 0x3e300f6e */ -r4 = 1.8645919859e-02, /* 0x3c98bf54 */ -r5 = 7.7794247773e-04, /* 0x3a4beed6 */ -r6 = 7.3266842264e-06, /* 0x36f5d7bd */ -w0 = 4.1893854737e-01, /* 0x3ed67f1d */ -w1 = 8.3333335817e-02, /* 0x3daaaaab */ -w2 = -2.7777778450e-03, /* 0xbb360b61 */ -w3 = 7.9365057172e-04, /* 0x3a500cfd */ -w4 = -5.9518753551e-04, /* 0xba1c065c */ -w5 = 8.3633989561e-04, /* 0x3a5b3dd2 */ -w6 = -1.6309292987e-03; /* 0xbad5c4e8 */ - -#ifdef __STDC__ -static const float zero= 0.0000000000e+00; -#else -static float zero= 0.0000000000e+00; -#endif - -#ifdef __STDC__ - static float sin_pif(float x) -#else - static float sin_pif(x) - float x; -#endif -{ - float y,z; - __int32_t n,ix; - - GET_FLOAT_WORD(ix,x); - ix &= 0x7fffffff; - - if(ix<0x3e800000) return __kernel_sinf(pi*x,zero,0); - y = -x; /* x is assume negative */ - - /* - * argument reduction, make sure inexact flag not raised if input - * is an integer - */ - z = floorf(y); - if(z!=y) { /* inexact anyway */ - y *= (float)0.5; - y = (float)2.0*(y - floorf(y)); /* y = |x| mod 2.0 */ - n = (__int32_t) (y*(float)4.0); - } else { - if(ix>=0x4b800000) { - y = zero; n = 0; /* y must be even */ - } else { - if(ix<0x4b000000) z = y+two23; /* exact */ - GET_FLOAT_WORD(n,z); - n &= 1; - y = n; - n<<= 2; - } - } - switch (n) { - case 0: y = __kernel_sinf(pi*y,zero,0); break; - case 1: - case 2: y = __kernel_cosf(pi*((float)0.5-y),zero); break; - case 3: - case 4: y = __kernel_sinf(pi*(one-y),zero,0); break; - case 5: - case 6: y = -__kernel_cosf(pi*(y-(float)1.5),zero); break; - default: y = __kernel_sinf(pi*(y-(float)2.0),zero,0); break; - } - return -y; -} - - -#ifdef __STDC__ - float __ieee754_lgammaf_r(float x, int *signgamp) -#else - float __ieee754_lgammaf_r(x,signgamp) - float x; int *signgamp; -#endif -{ - float t,y,z,nadj = 0.0,p,p1,p2,p3,q,r,w; - __int32_t i,hx,ix; - - GET_FLOAT_WORD(hx,x); - - /* purge off +-inf, NaN, +-0, and negative arguments */ - *signgamp = 1; - ix = hx&0x7fffffff; - if(ix>=0x7f800000) return x*x; - if(ix==0) return one/zero; - if(ix<0x1c800000) { /* |x|<2**-70, return -log(|x|) */ - if(hx<0) { - *signgamp = -1; - return -__ieee754_logf(-x); - } else return -__ieee754_logf(x); - } - if(hx<0) { - if(ix>=0x4b000000) /* |x|>=2**23, must be -integer */ - return one/zero; - t = sin_pif(x); - if(t==zero) return one/zero; /* -integer */ - nadj = __ieee754_logf(pi/fabsf(t*x)); - if(t=0x3f3b4a20) {y = one-x; i= 0;} - else if(ix>=0x3e6d3308) {y= x-(tc-one); i=1;} - else {y = x; i=2;} - } else { - r = zero; - if(ix>=0x3fdda618) {y=(float)2.0-x;i=0;} /* [1.7316,2] */ - else if(ix>=0x3F9da620) {y=x-tc;i=1;} /* [1.23,1.73] */ - else {y=x-one;i=2;} - } - switch(i) { - case 0: - z = y*y; - p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10)))); - p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11))))); - p = y*p1+p2; - r += (p-(float)0.5*y); break; - case 1: - z = y*y; - w = z*y; - p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */ - p2 = t1+w*(t4+w*(t7+w*(t10+w*t13))); - p3 = t2+w*(t5+w*(t8+w*(t11+w*t14))); - p = z*p1-(tt-w*(p2+y*p3)); - r += (tf + p); break; - case 2: - p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5))))); - p2 = one+y*(v1+y*(v2+y*(v3+y*(v4+y*v5)))); - r += (-(float)0.5*y + p1/p2); - } - } - else if(ix<0x41000000) { /* x < 8.0 */ - i = (__int32_t)x; - t = zero; - y = x-(float)i; - p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6)))))); - q = one+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6))))); - r = half*y+p/q; - z = one; /* lgamma(1+s) = log(s) + lgamma(s) */ - switch(i) { - case 7: z *= (y+(float)6.0); /* FALLTHRU */ - case 6: z *= (y+(float)5.0); /* FALLTHRU */ - case 5: z *= (y+(float)4.0); /* FALLTHRU */ - case 4: z *= (y+(float)3.0); /* FALLTHRU */ - case 3: z *= (y+(float)2.0); /* FALLTHRU */ - r += __ieee754_logf(z); break; - } - /* 8.0 <= x < 2**58 */ - } else if (ix < 0x5c800000) { - t = __ieee754_logf(x); - z = one/x; - y = z*z; - w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6))))); - r = (x-half)*(t-one)+w; - } else - /* 2**58 <= x <= inf */ - r = x*(__ieee754_logf(x)-one); - if(hx<0) r = nadj - r; - return r; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/fdlibm.h b/MicroPython_BUILD/components/micropython/lib/libm/fdlibm.h deleted file mode 100644 index ace3b2da..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/fdlibm.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * This file is adapted from from newlib-nano-2, the newlib/libm/common/fdlib.h, - * available from https://github.com/32bitmicro/newlib-nano-2. The main change - * is removal of anything to do with double precision. - * - * Appropriate copyright headers are reproduced below. - */ - -/* @(#)fdlibm.h 5.1 93/09/24 */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include - -/* Default to XOPEN_MODE. */ -#define _XOPEN_MODE - -/* Most routines need to check whether a float is finite, infinite, or not a - number, and many need to know whether the result of an operation will - overflow. These conditions depend on whether the largest exponent is - used for NaNs & infinities, or whether it's used for finite numbers. The - macros below wrap up that kind of information: - - FLT_UWORD_IS_FINITE(X) - True if a positive float with bitmask X is finite. - - FLT_UWORD_IS_NAN(X) - True if a positive float with bitmask X is not a number. - - FLT_UWORD_IS_INFINITE(X) - True if a positive float with bitmask X is +infinity. - - FLT_UWORD_MAX - The bitmask of FLT_MAX. - - FLT_UWORD_HALF_MAX - The bitmask of FLT_MAX/2. - - FLT_UWORD_EXP_MAX - The bitmask of the largest finite exponent (129 if the largest - exponent is used for finite numbers, 128 otherwise). - - FLT_UWORD_LOG_MAX - The bitmask of log(FLT_MAX), rounded down. This value is the largest - input that can be passed to exp() without producing overflow. - - FLT_UWORD_LOG_2MAX - The bitmask of log(2*FLT_MAX), rounded down. This value is the - largest input than can be passed to cosh() without producing - overflow. - - FLT_LARGEST_EXP - The largest biased exponent that can be used for finite numbers - (255 if the largest exponent is used for finite numbers, 254 - otherwise) */ - -#ifdef _FLT_LARGEST_EXPONENT_IS_NORMAL -#define FLT_UWORD_IS_FINITE(x) 1 -#define FLT_UWORD_IS_NAN(x) 0 -#define FLT_UWORD_IS_INFINITE(x) 0 -#define FLT_UWORD_MAX 0x7fffffff -#define FLT_UWORD_EXP_MAX 0x43010000 -#define FLT_UWORD_LOG_MAX 0x42b2d4fc -#define FLT_UWORD_LOG_2MAX 0x42b437e0 -#define HUGE ((float)0X1.FFFFFEP128) -#else -#define FLT_UWORD_IS_FINITE(x) ((x)<0x7f800000L) -#define FLT_UWORD_IS_NAN(x) ((x)>0x7f800000L) -#define FLT_UWORD_IS_INFINITE(x) ((x)==0x7f800000L) -#define FLT_UWORD_MAX 0x7f7fffffL -#define FLT_UWORD_EXP_MAX 0x43000000 -#define FLT_UWORD_LOG_MAX 0x42b17217 -#define FLT_UWORD_LOG_2MAX 0x42b2d4fc -#define HUGE ((float)3.40282346638528860e+38) -#endif -#define FLT_UWORD_HALF_MAX (FLT_UWORD_MAX-(1L<<23)) -#define FLT_LARGEST_EXP (FLT_UWORD_MAX>>23) - -/* Many routines check for zero and subnormal numbers. Such things depend - on whether the target supports denormals or not: - - FLT_UWORD_IS_ZERO(X) - True if a positive float with bitmask X is +0. Without denormals, - any float with a zero exponent is a +0 representation. With - denormals, the only +0 representation is a 0 bitmask. - - FLT_UWORD_IS_SUBNORMAL(X) - True if a non-zero positive float with bitmask X is subnormal. - (Routines should check for zeros first.) - - FLT_UWORD_MIN - The bitmask of the smallest float above +0. Call this number - REAL_FLT_MIN... - - FLT_UWORD_EXP_MIN - The bitmask of the float representation of REAL_FLT_MIN's exponent. - - FLT_UWORD_LOG_MIN - The bitmask of |log(REAL_FLT_MIN)|, rounding down. - - FLT_SMALLEST_EXP - REAL_FLT_MIN's exponent - EXP_BIAS (1 if denormals are not supported, - -22 if they are). -*/ - -#ifdef _FLT_NO_DENORMALS -#define FLT_UWORD_IS_ZERO(x) ((x)<0x00800000L) -#define FLT_UWORD_IS_SUBNORMAL(x) 0 -#define FLT_UWORD_MIN 0x00800000 -#define FLT_UWORD_EXP_MIN 0x42fc0000 -#define FLT_UWORD_LOG_MIN 0x42aeac50 -#define FLT_SMALLEST_EXP 1 -#else -#define FLT_UWORD_IS_ZERO(x) ((x)==0) -#define FLT_UWORD_IS_SUBNORMAL(x) ((x)<0x00800000L) -#define FLT_UWORD_MIN 0x00000001 -#define FLT_UWORD_EXP_MIN 0x43160000 -#define FLT_UWORD_LOG_MIN 0x42cff1b5 -#define FLT_SMALLEST_EXP -22 -#endif - -#ifdef __STDC__ -#undef __P -#define __P(p) p -#else -#define __P(p) () -#endif - -/* - * set X_TLOSS = pi*2**52, which is possibly defined in - * (one may replace the following line by "#include ") - */ - -#define X_TLOSS 1.41484755040568800000e+16 - -/* Functions that are not documented, and are not in . */ - -/* Undocumented float functions. */ -#ifdef _SCALB_INT -extern float scalbf __P((float, int)); -#else -extern float scalbf __P((float, float)); -#endif -extern float significandf __P((float)); - -/* ieee style elementary float functions */ -extern float __ieee754_sqrtf __P((float)); -extern float __ieee754_acosf __P((float)); -extern float __ieee754_acoshf __P((float)); -extern float __ieee754_logf __P((float)); -extern float __ieee754_atanhf __P((float)); -extern float __ieee754_asinf __P((float)); -extern float __ieee754_atan2f __P((float,float)); -extern float __ieee754_expf __P((float)); -extern float __ieee754_coshf __P((float)); -extern float __ieee754_fmodf __P((float,float)); -extern float __ieee754_powf __P((float,float)); -extern float __ieee754_lgammaf_r __P((float,int *)); -extern float __ieee754_gammaf_r __P((float,int *)); -extern float __ieee754_log10f __P((float)); -extern float __ieee754_sinhf __P((float)); -extern float __ieee754_hypotf __P((float,float)); -extern float __ieee754_j0f __P((float)); -extern float __ieee754_j1f __P((float)); -extern float __ieee754_y0f __P((float)); -extern float __ieee754_y1f __P((float)); -extern float __ieee754_jnf __P((int,float)); -extern float __ieee754_ynf __P((int,float)); -extern float __ieee754_remainderf __P((float,float)); -extern __int32_t __ieee754_rem_pio2f __P((float,float*)); -#ifdef _SCALB_INT -extern float __ieee754_scalbf __P((float,int)); -#else -extern float __ieee754_scalbf __P((float,float)); -#endif - -/* float versions of fdlibm kernel functions */ -extern float __kernel_sinf __P((float,float,int)); -extern float __kernel_cosf __P((float,float)); -extern float __kernel_tanf __P((float,float,int)); -extern int __kernel_rem_pio2f __P((float*,float*,int,int,int,const __int32_t*)); - -/* A union which permits us to convert between a float and a 32 bit - int. */ - -typedef union -{ - float value; - __uint32_t word; -} ieee_float_shape_type; - -/* Get a 32 bit int from a float. */ - -#define GET_FLOAT_WORD(i,d) \ -do { \ - ieee_float_shape_type gf_u; \ - gf_u.value = (d); \ - (i) = gf_u.word; \ -} while (0) - -/* Set a float from a 32 bit int. */ - -#define SET_FLOAT_WORD(d,i) \ -do { \ - ieee_float_shape_type sf_u; \ - sf_u.word = (i); \ - (d) = sf_u.value; \ -} while (0) - -/* Macros to avoid undefined behaviour that can arise if the amount - of a shift is exactly equal to the size of the shifted operand. */ - -#define SAFE_LEFT_SHIFT(op,amt) \ - (((amt) < 8 * sizeof(op)) ? ((op) << (amt)) : 0) - -#define SAFE_RIGHT_SHIFT(op,amt) \ - (((amt) < 8 * sizeof(op)) ? ((op) >> (amt)) : 0) diff --git a/MicroPython_BUILD/components/micropython/lib/libm/fmodf.c b/MicroPython_BUILD/components/micropython/lib/libm/fmodf.c deleted file mode 100644 index 69a9ad91..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/fmodf.c +++ /dev/null @@ -1,70 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// fmodf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -#include "libm.h" - -float fmodf(float x, float y) -{ - union {float f; uint32_t i;} ux = {x}, uy = {y}; - int ex = ux.i>>23 & 0xff; - int ey = uy.i>>23 & 0xff; - uint32_t sx = ux.i & 0x80000000; - uint32_t i; - uint32_t uxi = ux.i; - - if (uy.i<<1 == 0 || isnan(y) || ex == 0xff) - return (x*y)/(x*y); - if (uxi<<1 <= uy.i<<1) { - if (uxi<<1 == uy.i<<1) - return 0*x; - return x; - } - - /* normalize x and y */ - if (!ex) { - for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1); - uxi <<= -ex + 1; - } else { - uxi &= -1U >> 9; - uxi |= 1U << 23; - } - if (!ey) { - for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1); - uy.i <<= -ey + 1; - } else { - uy.i &= -1U >> 9; - uy.i |= 1U << 23; - } - - /* x mod y */ - for (; ex > ey; ex--) { - i = uxi - uy.i; - if (i >> 31 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - uxi <<= 1; - } - i = uxi - uy.i; - if (i >> 31 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - for (; uxi>>23 == 0; uxi <<= 1, ex--); - - /* scale result up */ - if (ex > 0) { - uxi -= 1U << 23; - uxi |= (uint32_t)ex << 23; - } else { - uxi >>= -ex + 1; - } - uxi |= sx; - ux.i = uxi; - return ux.f; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/kf_cos.c b/MicroPython_BUILD/components/micropython/lib/libm/kf_cos.c deleted file mode 100644 index 691f9842..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/kf_cos.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* kf_cos.c -- float version of k_cos.c - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ -static const float -#else -static float -#endif -one = 1.0000000000e+00, /* 0x3f800000 */ -C1 = 4.1666667908e-02, /* 0x3d2aaaab */ -C2 = -1.3888889225e-03, /* 0xbab60b61 */ -C3 = 2.4801587642e-05, /* 0x37d00d01 */ -C4 = -2.7557314297e-07, /* 0xb493f27c */ -C5 = 2.0875723372e-09, /* 0x310f74f6 */ -C6 = -1.1359647598e-11; /* 0xad47d74e */ - -#ifdef __STDC__ - float __kernel_cosf(float x, float y) -#else - float __kernel_cosf(x, y) - float x,y; -#endif -{ - float a,hz,z,r,qx; - __int32_t ix; - GET_FLOAT_WORD(ix,x); - ix &= 0x7fffffff; /* ix = |x|'s high word*/ - if(ix<0x32000000) { /* if x < 2**27 */ - if(((int)x)==0) return one; /* generate inexact */ - } - z = x*x; - r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*C6))))); - if(ix < 0x3e99999a) /* if |x| < 0.3 */ - return one - ((float)0.5*z - (z*r - x*y)); - else { - if(ix > 0x3f480000) { /* x > 0.78125 */ - qx = (float)0.28125; - } else { - SET_FLOAT_WORD(qx,ix-0x01000000); /* x/4 */ - } - hz = (float)0.5*z-qx; - a = one-qx; - return a - (hz - (z*r-x*y)); - } -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/kf_rem_pio2.c b/MicroPython_BUILD/components/micropython/lib/libm/kf_rem_pio2.c deleted file mode 100644 index c7e94795..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/kf_rem_pio2.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* kf_rem_pio2.c -- float version of k_rem_pio2.c - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -/* In the float version, the input parameter x contains 8 bit - integers, not 24 bit integers. 113 bit precision is not supported. */ - -#ifdef __STDC__ -static const int init_jk[] = {4,7,9}; /* initial value for jk */ -#else -static int init_jk[] = {4,7,9}; -#endif - -#ifdef __STDC__ -static const float PIo2[] = { -#else -static float PIo2[] = { -#endif - 1.5703125000e+00, /* 0x3fc90000 */ - 4.5776367188e-04, /* 0x39f00000 */ - 2.5987625122e-05, /* 0x37da0000 */ - 7.5437128544e-08, /* 0x33a20000 */ - 6.0026650317e-11, /* 0x2e840000 */ - 7.3896444519e-13, /* 0x2b500000 */ - 5.3845816694e-15, /* 0x27c20000 */ - 5.6378512969e-18, /* 0x22d00000 */ - 8.3009228831e-20, /* 0x1fc40000 */ - 3.2756352257e-22, /* 0x1bc60000 */ - 6.3331015649e-25, /* 0x17440000 */ -}; - -#ifdef __STDC__ -static const float -#else -static float -#endif -zero = 0.0, -one = 1.0, -two8 = 2.5600000000e+02, /* 0x43800000 */ -twon8 = 3.9062500000e-03; /* 0x3b800000 */ - -#ifdef __STDC__ - int __kernel_rem_pio2f(float *x, float *y, int e0, int nx, int prec, const __int32_t *ipio2) -#else - int __kernel_rem_pio2f(x,y,e0,nx,prec,ipio2) - float x[], y[]; int e0,nx,prec; __int32_t ipio2[]; -#endif -{ - __int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; - float z,fw,f[20],fq[20],q[20]; - - /* initialize jk*/ - jk = init_jk[prec]; - jp = jk; - - /* determine jx,jv,q0, note that 3>q0 */ - jx = nx-1; - jv = (e0-3)/8; if(jv<0) jv=0; - q0 = e0-8*(jv+1); - - /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ - j = jv-jx; m = jx+jk; - for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (float) ipio2[j]; - - /* compute q[0],q[1],...q[jk] */ - for (i=0;i<=jk;i++) { - for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; - q[i] = fw; - } - - jz = jk; -recompute: - /* distill q[] into iq[] reversingly */ - for(i=0,j=jz,z=q[jz];j>0;i++,j--) { - fw = (float)((__int32_t)(twon8* z)); - iq[i] = (__int32_t)(z-two8*fw); - z = q[j-1]+fw; - } - - /* compute n */ - z = scalbnf(z,(int)q0); /* actual value of z */ - z -= (float)8.0*floorf(z*(float)0.125); /* trim off integer >= 8 */ - n = (__int32_t) z; - z -= (float)n; - ih = 0; - if(q0>0) { /* need iq[jz-1] to determine n */ - i = (iq[jz-1]>>(8-q0)); n += i; - iq[jz-1] -= i<<(8-q0); - ih = iq[jz-1]>>(7-q0); - } - else if(q0==0) ih = iq[jz-1]>>8; - else if(z>=(float)0.5) ih=2; - - if(ih>0) { /* q > 0.5 */ - n += 1; carry = 0; - for(i=0;i0) { /* rare case: chance is 1 in 12 */ - switch(q0) { - case 1: - iq[jz-1] &= 0x7f; break; - case 2: - iq[jz-1] &= 0x3f; break; - } - } - if(ih==2) { - z = one - z; - if(carry!=0) z -= scalbnf(one,(int)q0); - } - } - - /* check if recomputation is needed */ - if(z==zero) { - j = 0; - for (i=jz-1;i>=jk;i--) j |= iq[i]; - if(j==0) { /* need recomputation */ - for(k=1;iq[jk-k]==0;k++); /* k = no. of terms needed */ - - for(i=jz+1;i<=jz+k;i++) { /* add q[jz+1] to q[jz+k] */ - f[jx+i] = (float) ipio2[jv+i]; - for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; - q[i] = fw; - } - jz += k; - goto recompute; - } - } - - /* chop off zero terms */ - if(z==(float)0.0) { - jz -= 1; q0 -= 8; - while(iq[jz]==0) { jz--; q0-=8;} - } else { /* break z into 8-bit if necessary */ - z = scalbnf(z,-(int)q0); - if(z>=two8) { - fw = (float)((__int32_t)(twon8*z)); - iq[jz] = (__int32_t)(z-two8*fw); - jz += 1; q0 += 8; - iq[jz] = (__int32_t) fw; - } else iq[jz] = (__int32_t) z ; - } - - /* convert integer "bit" chunk to floating-point value */ - fw = scalbnf(one,(int)q0); - for(i=jz;i>=0;i--) { - q[i] = fw*(float)iq[i]; fw*=twon8; - } - - /* compute PIo2[0,...,jp]*q[jz,...,0] */ - for(i=jz;i>=0;i--) { - for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k]; - fq[jz-i] = fw; - } - - /* compress fq[] into y[] */ - switch(prec) { - case 0: - fw = 0.0; - for (i=jz;i>=0;i--) fw += fq[i]; - y[0] = (ih==0)? fw: -fw; - break; - case 1: - case 2: - fw = 0.0; - for (i=jz;i>=0;i--) fw += fq[i]; - y[0] = (ih==0)? fw: -fw; - fw = fq[0]-fw; - for (i=1;i<=jz;i++) fw += fq[i]; - y[1] = (ih==0)? fw: -fw; - break; - case 3: /* painful */ - for (i=jz;i>0;i--) { - fw = fq[i-1]+fq[i]; - fq[i] += fq[i-1]-fw; - fq[i-1] = fw; - } - for (i=jz;i>1;i--) { - fw = fq[i-1]+fq[i]; - fq[i] += fq[i-1]-fw; - fq[i-1] = fw; - } - for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; - if(ih==0) { - y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; - } else { - y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; - } - } - return n&7; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/kf_sin.c b/MicroPython_BUILD/components/micropython/lib/libm/kf_sin.c deleted file mode 100644 index 07ea9934..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/kf_sin.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* kf_sin.c -- float version of k_sin.c - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ -static const float -#else -static float -#endif -half = 5.0000000000e-01,/* 0x3f000000 */ -S1 = -1.6666667163e-01, /* 0xbe2aaaab */ -S2 = 8.3333337680e-03, /* 0x3c088889 */ -S3 = -1.9841270114e-04, /* 0xb9500d01 */ -S4 = 2.7557314297e-06, /* 0x3638ef1b */ -S5 = -2.5050759689e-08, /* 0xb2d72f34 */ -S6 = 1.5896910177e-10; /* 0x2f2ec9d3 */ - -#ifdef __STDC__ - float __kernel_sinf(float x, float y, int iy) -#else - float __kernel_sinf(x, y, iy) - float x,y; int iy; /* iy=0 if y is zero */ -#endif -{ - float z,r,v; - __int32_t ix; - GET_FLOAT_WORD(ix,x); - ix &= 0x7fffffff; /* high word of x */ - if(ix<0x32000000) /* |x| < 2**-27 */ - {if((int)x==0) return x;} /* generate inexact */ - z = x*x; - v = z*x; - r = S2+z*(S3+z*(S4+z*(S5+z*S6))); - if(iy==0) return x+v*(S1+z*r); - else return x-((z*(half*y-v*r)-y)-v*S1); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/kf_tan.c b/MicroPython_BUILD/components/micropython/lib/libm/kf_tan.c deleted file mode 100644 index 6da9bd81..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/kf_tan.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* kf_tan.c -- float version of k_tan.c - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "libm.h" -#ifdef __STDC__ -static const float -#else -static float -#endif -one = 1.0000000000e+00, /* 0x3f800000 */ -pio4 = 7.8539812565e-01, /* 0x3f490fda */ -pio4lo= 3.7748947079e-08, /* 0x33222168 */ -T[] = { - 3.3333334327e-01, /* 0x3eaaaaab */ - 1.3333334029e-01, /* 0x3e088889 */ - 5.3968254477e-02, /* 0x3d5d0dd1 */ - 2.1869488060e-02, /* 0x3cb327a4 */ - 8.8632395491e-03, /* 0x3c11371f */ - 3.5920790397e-03, /* 0x3b6b6916 */ - 1.4562094584e-03, /* 0x3abede48 */ - 5.8804126456e-04, /* 0x3a1a26c8 */ - 2.4646313977e-04, /* 0x398137b9 */ - 7.8179444245e-05, /* 0x38a3f445 */ - 7.1407252108e-05, /* 0x3895c07a */ - -1.8558637748e-05, /* 0xb79bae5f */ - 2.5907305826e-05, /* 0x37d95384 */ -}; - -#ifdef __STDC__ - float __kernel_tanf(float x, float y, int iy) -#else - float __kernel_tanf(x, y, iy) - float x,y; int iy; -#endif -{ - float z,r,v,w,s; - __int32_t ix,hx; - GET_FLOAT_WORD(hx,x); - ix = hx&0x7fffffff; /* high word of |x| */ - if(ix<0x31800000) /* x < 2**-28 */ - {if((int)x==0) { /* generate inexact */ - if((ix|(iy+1))==0) return one/fabsf(x); - else return (iy==1)? x: -one/x; - } - } - if(ix>=0x3f2ca140) { /* |x|>=0.6744 */ - if(hx<0) {x = -x; y = -y;} - z = pio4-x; - w = pio4lo-y; - x = z+w; y = 0.0; - } - z = x*x; - w = z*z; - /* Break x^5*(T[1]+x^2*T[2]+...) into - * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + - * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) - */ - r = T[1]+w*(T[3]+w*(T[5]+w*(T[7]+w*(T[9]+w*T[11])))); - v = z*(T[2]+w*(T[4]+w*(T[6]+w*(T[8]+w*(T[10]+w*T[12]))))); - s = z*x; - r = y + z*(s*(r+v)+y); - r += T[0]*s; - w = x+r; - if(ix>=0x3f2ca140) { - v = (float)iy; - return (float)(1-((hx>>30)&2))*(v-(float)2.0*(x-(w*w/(w+v)-r))); - } - if(iy==1) return w; - else { /* if allow error up to 2 ulp, - simply return -1.0/(x+r) here */ - /* compute -1.0/(x+r) accurately */ - float a,t; - __int32_t i; - z = w; - GET_FLOAT_WORD(i,z); - SET_FLOAT_WORD(z,i&0xfffff000); - v = r-(z - x); /* z+v = r+x */ - t = a = -(float)1.0/w; /* a = -1.0/w */ - GET_FLOAT_WORD(i,t); - SET_FLOAT_WORD(t,i&0xfffff000); - s = (float)1.0+t*z; - return t+a*(s+t*v); - } -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/libm.h b/MicroPython_BUILD/components/micropython/lib/libm/libm.h deleted file mode 100644 index f782249e..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/libm.h +++ /dev/null @@ -1,54 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// portions extracted from musl-0.9.15 libm.h -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/math_private.h */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include -#include - -#define FLT_EVAL_METHOD 0 - -#define FORCE_EVAL(x) do { \ - if (sizeof(x) == sizeof(float)) { \ - volatile float __x; \ - __x = (x); \ - (void)__x; \ - } else if (sizeof(x) == sizeof(double)) { \ - volatile double __x; \ - __x = (x); \ - (void)__x; \ - } else { \ - volatile long double __x; \ - __x = (x); \ - (void)__x; \ - } \ -} while(0) - -/* Get a 32 bit int from a float. */ -#define GET_FLOAT_WORD(w,d) \ -do { \ - union {float f; uint32_t i;} __u; \ - __u.f = (d); \ - (w) = __u.i; \ -} while (0) - -/* Set a float from a 32 bit int. */ -#define SET_FLOAT_WORD(d,w) \ -do { \ - union {float f; uint32_t i;} __u; \ - __u.i = (w); \ - (d) = __u.f; \ -} while (0) diff --git a/MicroPython_BUILD/components/micropython/lib/libm/log1pf.c b/MicroPython_BUILD/components/micropython/lib/libm/log1pf.c deleted file mode 100644 index 0d32b0a2..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/log1pf.c +++ /dev/null @@ -1,83 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// log1pf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/s_log1pf.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "libm.h" - -static const float -ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ -ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ -/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ -Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */ -Lg2 = 0xccce13.0p-25, /* 0.40000972152 */ -Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */ -Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */ - -float log1pf(float x) -{ - union {float f; uint32_t i;} u = {x}; - float_t hfsq,f,c,s,z,R,w,t1,t2,dk; - uint32_t ix,iu; - int k; - - ix = u.i; - k = 1; - if (ix < 0x3ed413d0 || ix>>31) { /* 1+x < sqrt(2)+ */ - if (ix >= 0xbf800000) { /* x <= -1.0 */ - if (x == -1) - return x/0.0f; /* log1p(-1)=+inf */ - return (x-x)/0.0f; /* log1p(x<-1)=NaN */ - } - if (ix<<1 < 0x33800000<<1) { /* |x| < 2**-24 */ - /* underflow if subnormal */ - if ((ix&0x7f800000) == 0) - FORCE_EVAL(x*x); - return x; - } - if (ix <= 0xbe95f619) { /* sqrt(2)/2- <= 1+x < sqrt(2)+ */ - k = 0; - c = 0; - f = x; - } - } else if (ix >= 0x7f800000) - return x; - if (k) { - u.f = 1 + x; - iu = u.i; - iu += 0x3f800000 - 0x3f3504f3; - k = (int)(iu>>23) - 0x7f; - /* correction term ~ log(1+x)-log(u), avoid underflow in c/u */ - if (k < 25) { - c = k >= 2 ? 1-(u.f-x) : x-(u.f-1); - c /= u.f; - } else - c = 0; - /* reduce u into [sqrt(2)/2, sqrt(2)] */ - iu = (iu&0x007fffff) + 0x3f3504f3; - u.i = iu; - f = u.f - 1; - } - s = f/(2.0f + f); - z = s*s; - w = z*z; - t1= w*(Lg2+w*Lg4); - t2= z*(Lg1+w*Lg3); - R = t2 + t1; - hfsq = 0.5f*f*f; - dk = k; - return s*(hfsq+R) + (dk*ln2_lo+c) - hfsq + f + dk*ln2_hi; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/math.c b/MicroPython_BUILD/components/micropython/lib/libm/math.c deleted file mode 100644 index 6b65202c..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/math.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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 "libm.h" - -typedef float float_t; -typedef union { - float f; - struct { - uint64_t m : 23; - uint64_t e : 8; - uint64_t s : 1; - }; -} float_s_t; - -#ifndef NDEBUG -float copysignf(float x, float y) { - float_s_t fx={.f = x}; - float_s_t fy={.f = y}; - - // copy sign bit; - fx.s = fy.s; - - return fx.f; -} -#endif - -static const float _M_LN10 = 2.30258509299404; // 0x40135d8e -float log10f(float x) { return logf(x) / (float)_M_LN10; } - -float tanhf(float x) { - if (isinf(x)) { - return copysignf(1, x); - } - return sinhf(x) / coshf(x); -} - -/*****************************************************************************/ -/*****************************************************************************/ -// __fpclassifyf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -int __fpclassifyf(float x) -{ - union {float f; uint32_t i;} u = {x}; - int e = u.i>>23 & 0xff; - if (!e) return u.i<<1 ? FP_SUBNORMAL : FP_ZERO; - if (e==0xff) return u.i<<9 ? FP_NAN : FP_INFINITE; - return FP_NORMAL; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// scalbnf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -float scalbnf(float x, int n) -{ - union {float f; uint32_t i;} u; - float_t y = x; - - if (n > 127) { - y *= 0x1p127f; - n -= 127; - if (n > 127) { - y *= 0x1p127f; - n -= 127; - if (n > 127) - n = 127; - } - } else if (n < -126) { - y *= 0x1p-126f; - n += 126; - if (n < -126) { - y *= 0x1p-126f; - n += 126; - if (n < -126) - n = -126; - } - } - u.i = (uint32_t)(0x7f+n)<<23; - x = y * u.f; - return x; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// powf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/e_powf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -static const float -bp[] = {1.0, 1.5,}, -dp_h[] = { 0.0, 5.84960938e-01,}, /* 0x3f15c000 */ -dp_l[] = { 0.0, 1.56322085e-06,}, /* 0x35d1cfdc */ -two24 = 16777216.0, /* 0x4b800000 */ -huge = 1.0e30, -tiny = 1.0e-30, -/* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ -L1 = 6.0000002384e-01, /* 0x3f19999a */ -L2 = 4.2857143283e-01, /* 0x3edb6db7 */ -L3 = 3.3333334327e-01, /* 0x3eaaaaab */ -L4 = 2.7272811532e-01, /* 0x3e8ba305 */ -L5 = 2.3066075146e-01, /* 0x3e6c3255 */ -L6 = 2.0697501302e-01, /* 0x3e53f142 */ -P1 = 1.6666667163e-01, /* 0x3e2aaaab */ -P2 = -2.7777778450e-03, /* 0xbb360b61 */ -P3 = 6.6137559770e-05, /* 0x388ab355 */ -P4 = -1.6533901999e-06, /* 0xb5ddea0e */ -P5 = 4.1381369442e-08, /* 0x3331bb4c */ -lg2 = 6.9314718246e-01, /* 0x3f317218 */ -lg2_h = 6.93145752e-01, /* 0x3f317200 */ -lg2_l = 1.42860654e-06, /* 0x35bfbe8c */ -ovt = 4.2995665694e-08, /* -(128-log2(ovfl+.5ulp)) */ -cp = 9.6179670095e-01, /* 0x3f76384f =2/(3ln2) */ -cp_h = 9.6191406250e-01, /* 0x3f764000 =12b cp */ -cp_l = -1.1736857402e-04, /* 0xb8f623c6 =tail of cp_h */ -ivln2 = 1.4426950216e+00, /* 0x3fb8aa3b =1/ln2 */ -ivln2_h = 1.4426879883e+00, /* 0x3fb8aa00 =16b 1/ln2*/ -ivln2_l = 7.0526075433e-06; /* 0x36eca570 =1/ln2 tail*/ - -float powf(float x, float y) -{ - float z,ax,z_h,z_l,p_h,p_l; - float y1,t1,t2,r,s,sn,t,u,v,w; - int32_t i,j,k,yisint,n; - int32_t hx,hy,ix,iy,is; - - GET_FLOAT_WORD(hx, x); - GET_FLOAT_WORD(hy, y); - ix = hx & 0x7fffffff; - iy = hy & 0x7fffffff; - - /* x**0 = 1, even if x is NaN */ - if (iy == 0) - return 1.0f; - /* 1**y = 1, even if y is NaN */ - if (hx == 0x3f800000) - return 1.0f; - /* NaN if either arg is NaN */ - if (ix > 0x7f800000 || iy > 0x7f800000) - return x + y; - - /* determine if y is an odd int when x < 0 - * yisint = 0 ... y is not an integer - * yisint = 1 ... y is an odd int - * yisint = 2 ... y is an even int - */ - yisint = 0; - if (hx < 0) { - if (iy >= 0x4b800000) - yisint = 2; /* even integer y */ - else if (iy >= 0x3f800000) { - k = (iy>>23) - 0x7f; /* exponent */ - j = iy>>(23-k); - if ((j<<(23-k)) == iy) - yisint = 2 - (j & 1); - } - } - - /* special value of y */ - if (iy == 0x7f800000) { /* y is +-inf */ - if (ix == 0x3f800000) /* (-1)**+-inf is 1 */ - return 1.0f; - else if (ix > 0x3f800000) /* (|x|>1)**+-inf = inf,0 */ - return hy >= 0 ? y : 0.0f; - else if (ix != 0) /* (|x|<1)**+-inf = 0,inf if x!=0 */ - return hy >= 0 ? 0.0f: -y; - } - if (iy == 0x3f800000) /* y is +-1 */ - return hy >= 0 ? x : 1.0f/x; - if (hy == 0x40000000) /* y is 2 */ - return x*x; - if (hy == 0x3f000000) { /* y is 0.5 */ - if (hx >= 0) /* x >= +0 */ - return sqrtf(x); - } - - ax = fabsf(x); - /* special value of x */ - if (ix == 0x7f800000 || ix == 0 || ix == 0x3f800000) { /* x is +-0,+-inf,+-1 */ - z = ax; - if (hy < 0) /* z = (1/|x|) */ - z = 1.0f/z; - if (hx < 0) { - if (((ix-0x3f800000)|yisint) == 0) { - z = (z-z)/(z-z); /* (-1)**non-int is NaN */ - } else if (yisint == 1) - z = -z; /* (x<0)**odd = -(|x|**odd) */ - } - return z; - } - - sn = 1.0f; /* sign of result */ - if (hx < 0) { - if (yisint == 0) /* (x<0)**(non-int) is NaN */ - return (x-x)/(x-x); - if (yisint == 1) /* (x<0)**(odd int) */ - sn = -1.0f; - } - - /* |y| is huge */ - if (iy > 0x4d000000) { /* if |y| > 2**27 */ - /* over/underflow if x is not close to one */ - if (ix < 0x3f7ffff8) - return hy < 0 ? sn*huge*huge : sn*tiny*tiny; - if (ix > 0x3f800007) - return hy > 0 ? sn*huge*huge : sn*tiny*tiny; - /* now |1-x| is tiny <= 2**-20, suffice to compute - log(x) by x-x^2/2+x^3/3-x^4/4 */ - t = ax - 1; /* t has 20 trailing zeros */ - w = (t*t)*(0.5f - t*(0.333333333333f - t*0.25f)); - u = ivln2_h*t; /* ivln2_h has 16 sig. bits */ - v = t*ivln2_l - w*ivln2; - t1 = u + v; - GET_FLOAT_WORD(is, t1); - SET_FLOAT_WORD(t1, is & 0xfffff000); - t2 = v - (t1-u); - } else { - float s2,s_h,s_l,t_h,t_l; - n = 0; - /* take care subnormal number */ - if (ix < 0x00800000) { - ax *= two24; - n -= 24; - GET_FLOAT_WORD(ix, ax); - } - n += ((ix)>>23) - 0x7f; - j = ix & 0x007fffff; - /* determine interval */ - ix = j | 0x3f800000; /* normalize ix */ - if (j <= 0x1cc471) /* |x|>1) & 0xfffff000) | 0x20000000; - SET_FLOAT_WORD(t_h, is + 0x00400000 + (k<<21)); - t_l = ax - (t_h - bp[k]); - s_l = v*((u - s_h*t_h) - s_h*t_l); - /* compute log(ax) */ - s2 = s*s; - r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); - r += s_l*(s_h+s); - s2 = s_h*s_h; - t_h = 3.0f + s2 + r; - GET_FLOAT_WORD(is, t_h); - SET_FLOAT_WORD(t_h, is & 0xfffff000); - t_l = r - ((t_h - 3.0f) - s2); - /* u+v = s*(1+...) */ - u = s_h*t_h; - v = s_l*t_h + t_l*s; - /* 2/(3log2)*(s+...) */ - p_h = u + v; - GET_FLOAT_WORD(is, p_h); - SET_FLOAT_WORD(p_h, is & 0xfffff000); - p_l = v - (p_h - u); - z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ - z_l = cp_l*p_h + p_l*cp+dp_l[k]; - /* log2(ax) = (s+..)*2/(3*log2) = n + dp_h + z_h + z_l */ - t = (float)n; - t1 = (((z_h + z_l) + dp_h[k]) + t); - GET_FLOAT_WORD(is, t1); - SET_FLOAT_WORD(t1, is & 0xfffff000); - t2 = z_l - (((t1 - t) - dp_h[k]) - z_h); - } - - /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ - GET_FLOAT_WORD(is, y); - SET_FLOAT_WORD(y1, is & 0xfffff000); - p_l = (y-y1)*t1 + y*t2; - p_h = y1*t1; - z = p_l + p_h; - GET_FLOAT_WORD(j, z); - if (j > 0x43000000) /* if z > 128 */ - return sn*huge*huge; /* overflow */ - else if (j == 0x43000000) { /* if z == 128 */ - if (p_l + ovt > z - p_h) - return sn*huge*huge; /* overflow */ - } else if ((j&0x7fffffff) > 0x43160000) /* z < -150 */ // FIXME: check should be (uint32_t)j > 0xc3160000 - return sn*tiny*tiny; /* underflow */ - else if (j == 0xc3160000) { /* z == -150 */ - if (p_l <= z-p_h) - return sn*tiny*tiny; /* underflow */ - } - /* - * compute 2**(p_h+p_l) - */ - i = j & 0x7fffffff; - k = (i>>23) - 0x7f; - n = 0; - if (i > 0x3f000000) { /* if |z| > 0.5, set n = [z+0.5] */ - n = j + (0x00800000>>(k+1)); - k = ((n&0x7fffffff)>>23) - 0x7f; /* new k for n */ - SET_FLOAT_WORD(t, n & ~(0x007fffff>>k)); - n = ((n&0x007fffff)|0x00800000)>>(23-k); - if (j < 0) - n = -n; - p_h -= t; - } - t = p_l + p_h; - GET_FLOAT_WORD(is, t); - SET_FLOAT_WORD(t, is & 0xffff8000); - u = t*lg2_h; - v = (p_l-(t-p_h))*lg2 + t*lg2_l; - z = u + v; - w = v - (z - u); - t = z*z; - t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); - r = (z*t1)/(t1-2.0f) - (w+z*w); - z = 1.0f - (r - z); - GET_FLOAT_WORD(j, z); - j += n<<23; - if ((j>>23) <= 0) /* subnormal output */ - z = scalbnf(z, n); - else - SET_FLOAT_WORD(z, j); - return sn*z; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// expf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/e_expf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -static const float -half[2] = {0.5,-0.5}, -ln2hi = 6.9314575195e-1f, /* 0x3f317200 */ -ln2lo = 1.4286067653e-6f, /* 0x35bfbe8e */ -invln2 = 1.4426950216e+0f, /* 0x3fb8aa3b */ -/* - * Domain [-0.34568, 0.34568], range ~[-4.278e-9, 4.447e-9]: - * |x*(exp(x)+1)/(exp(x)-1) - p(x)| < 2**-27.74 - */ -expf_P1 = 1.6666625440e-1f, /* 0xaaaa8f.0p-26 */ -expf_P2 = -2.7667332906e-3f; /* -0xb55215.0p-32 */ - -float expf(float x) -{ - float_t hi, lo, c, xx, y; - int k, sign; - uint32_t hx; - - GET_FLOAT_WORD(hx, x); - sign = hx >> 31; /* sign bit of x */ - hx &= 0x7fffffff; /* high word of |x| */ - - /* special cases */ - if (hx >= 0x42aeac50) { /* if |x| >= -87.33655f or NaN */ - if (hx >= 0x42b17218 && !sign) { /* x >= 88.722839f */ - /* overflow */ - x *= 0x1p127f; - return x; - } - if (sign) { - /* underflow */ - FORCE_EVAL(-0x1p-149f/x); - if (hx >= 0x42cff1b5) /* x <= -103.972084f */ - return 0; - } - } - - /* argument reduction */ - if (hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */ - if (hx > 0x3f851592) /* if |x| > 1.5 ln2 */ - k = invln2*x + half[sign]; - else - k = 1 - sign - sign; - hi = x - k*ln2hi; /* k*ln2hi is exact here */ - lo = k*ln2lo; - x = hi - lo; - } else if (hx > 0x39000000) { /* |x| > 2**-14 */ - k = 0; - hi = x; - lo = 0; - } else { - /* raise inexact */ - FORCE_EVAL(0x1p127f + x); - return 1 + x; - } - - /* x is now in primary range */ - xx = x*x; - c = x - xx*(expf_P1+xx*expf_P2); - y = 1 + (x*c/(2-c) - lo + hi); - if (k == 0) - return y; - return scalbnf(y, k); -} - -/*****************************************************************************/ -/*****************************************************************************/ -// expm1f from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/s_expm1f.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -static const float -o_threshold = 8.8721679688e+01, /* 0x42b17180 */ -ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ -ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ -//invln2 = 1.4426950216e+00, /* 0x3fb8aa3b */ -/* - * Domain [-0.34568, 0.34568], range ~[-6.694e-10, 6.696e-10]: - * |6 / x * (1 + 2 * (1 / (exp(x) - 1) - 1 / x)) - q(x)| < 2**-30.04 - * Scaled coefficients: Qn_here = 2**n * Qn_for_q (see s_expm1.c): - */ -Q1 = -3.3333212137e-2, /* -0x888868.0p-28 */ -Q2 = 1.5807170421e-3; /* 0xcf3010.0p-33 */ - -float expm1f(float x) -{ - float_t y,hi,lo,c,t,e,hxs,hfx,r1,twopk; - union {float f; uint32_t i;} u = {x}; - uint32_t hx = u.i & 0x7fffffff; - int k, sign = u.i >> 31; - - /* filter out huge and non-finite argument */ - if (hx >= 0x4195b844) { /* if |x|>=27*ln2 */ - if (hx > 0x7f800000) /* NaN */ - return x; - if (sign) - return -1; - if (x > o_threshold) { - x *= 0x1p127f; - return x; - } - } - - /* argument reduction */ - if (hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */ - if (hx < 0x3F851592) { /* and |x| < 1.5 ln2 */ - if (!sign) { - hi = x - ln2_hi; - lo = ln2_lo; - k = 1; - } else { - hi = x + ln2_hi; - lo = -ln2_lo; - k = -1; - } - } else { - k = invln2*x + (sign ? -0.5f : 0.5f); - t = k; - hi = x - t*ln2_hi; /* t*ln2_hi is exact here */ - lo = t*ln2_lo; - } - x = hi-lo; - c = (hi-x)-lo; - } else if (hx < 0x33000000) { /* when |x|<2**-25, return x */ - if (hx < 0x00800000) - FORCE_EVAL(x*x); - return x; - } else - k = 0; - - /* x is now in primary range */ - hfx = 0.5f*x; - hxs = x*hfx; - r1 = 1.0f+hxs*(Q1+hxs*Q2); - t = 3.0f - r1*hfx; - e = hxs*((r1-t)/(6.0f - x*t)); - if (k == 0) /* c is 0 */ - return x - (x*e-hxs); - e = x*(e-c) - c; - e -= hxs; - /* exp(x) ~ 2^k (x_reduced - e + 1) */ - if (k == -1) - return 0.5f*(x-e) - 0.5f; - if (k == 1) { - if (x < -0.25f) - return -2.0f*(e-(x+0.5f)); - return 1.0f + 2.0f*(x-e); - } - u.i = (0x7f+k)<<23; /* 2^k */ - twopk = u.f; - if (k < 0 || k > 56) { /* suffice to return exp(x)-1 */ - y = x - e + 1.0f; - if (k == 128) - y = y*2.0f*0x1p127f; - else - y = y*twopk; - return y - 1.0f; - } - u.i = (0x7f-k)<<23; /* 2^-k */ - if (k < 23) - y = (x-e+(1-u.f))*twopk; - else - y = (x-(e+u.f)+1)*twopk; - return y; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// __expo2f from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* k is such that k*ln2 has minimal relative error and x - kln2 > log(FLT_MIN) */ -static const int k = 235; -static const float kln2 = 0x1.45c778p+7f; - -/* expf(x)/2 for x >= log(FLT_MAX), slightly better than 0.5f*expf(x/2)*expf(x/2) */ -float __expo2f(float x) -{ - float scale; - - /* note that k is odd and scale*scale overflows */ - SET_FLOAT_WORD(scale, (uint32_t)(0x7f + k/2) << 23); - /* exp(x - k ln2) * 2**(k-1) */ - return expf(x - kln2) * scale * scale; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// logf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -/* origin: FreeBSD /usr/src/lib/msun/src/e_logf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -static const float -/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ -Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */ -Lg2 = 0xccce13.0p-25, /* 0.40000972152 */ -Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */ -Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */ - -float logf(float x) -{ - union {float f; uint32_t i;} u = {x}; - float_t hfsq,f,s,z,R,w,t1,t2,dk; - uint32_t ix; - int k; - - ix = u.i; - k = 0; - if (ix < 0x00800000 || ix>>31) { /* x < 2**-126 */ - if (ix<<1 == 0) - return -1/(x*x); /* log(+-0)=-inf */ - if (ix>>31) - return (x-x)/0.0f; /* log(-#) = NaN */ - /* subnormal number, scale up x */ - k -= 25; - x *= 0x1p25f; - u.f = x; - ix = u.i; - } else if (ix >= 0x7f800000) { - return x; - } else if (ix == 0x3f800000) - return 0; - - /* reduce x into [sqrt(2)/2, sqrt(2)] */ - ix += 0x3f800000 - 0x3f3504f3; - k += (int)(ix>>23) - 0x7f; - ix = (ix&0x007fffff) + 0x3f3504f3; - u.i = ix; - x = u.f; - - f = x - 1.0f; - s = f/(2.0f + f); - z = s*s; - w = z*z; - t1= w*(Lg2+w*Lg4); - t2= z*(Lg1+w*Lg3); - R = t2 + t1; - hfsq = 0.5f*f*f; - dk = k; - return s*(hfsq+R) + dk*ln2_lo - hfsq + f + dk*ln2_hi; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// coshf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -float coshf(float x) -{ - union {float f; uint32_t i;} u = {.f = x}; - uint32_t w; - float t; - - /* |x| */ - u.i &= 0x7fffffff; - x = u.f; - w = u.i; - - /* |x| < log(2) */ - if (w < 0x3f317217) { - if (w < 0x3f800000 - (12<<23)) { - FORCE_EVAL(x + 0x1p120f); - return 1; - } - t = expm1f(x); - return 1 + t*t/(2*(1+t)); - } - - /* |x| < log(FLT_MAX) */ - if (w < 0x42b17217) { - t = expf(x); - return 0.5f*(t + 1/t); - } - - /* |x| > log(FLT_MAX) or nan */ - t = __expo2f(x); - return t; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// sinhf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -float sinhf(float x) -{ - union {float f; uint32_t i;} u = {.f = x}; - uint32_t w; - float t, h, absx; - - h = 0.5; - if (u.i >> 31) - h = -h; - /* |x| */ - u.i &= 0x7fffffff; - absx = u.f; - w = u.i; - - /* |x| < log(FLT_MAX) */ - if (w < 0x42b17217) { - t = expm1f(absx); - if (w < 0x3f800000) { - if (w < 0x3f800000 - (12<<23)) - return x; - return h*(2*t - t*t/(t+1)); - } - return h*(t + t/(t+1)); - } - - /* |x| > logf(FLT_MAX) or nan */ - t = 2*h*__expo2f(absx); - return t; -} - -/*****************************************************************************/ -/*****************************************************************************/ -// ceilf, floorf and truncf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -float ceilf(float x) -{ - union {float f; uint32_t i;} u = {x}; - int e = (int)(u.i >> 23 & 0xff) - 0x7f; - uint32_t m; - - if (e >= 23) - return x; - if (e >= 0) { - m = 0x007fffff >> e; - if ((u.i & m) == 0) - return x; - FORCE_EVAL(x + 0x1p120f); - if (u.i >> 31 == 0) - u.i += m; - u.i &= ~m; - } else { - FORCE_EVAL(x + 0x1p120f); - if (u.i >> 31) - u.f = -0.0; - else if (u.i << 1) - u.f = 1.0; - } - return u.f; -} - -float floorf(float x) -{ - union {float f; uint32_t i;} u = {x}; - int e = (int)(u.i >> 23 & 0xff) - 0x7f; - uint32_t m; - - if (e >= 23) - return x; - if (e >= 0) { - m = 0x007fffff >> e; - if ((u.i & m) == 0) - return x; - FORCE_EVAL(x + 0x1p120f); - if (u.i >> 31) - u.i += m; - u.i &= ~m; - } else { - FORCE_EVAL(x + 0x1p120f); - if (u.i >> 31 == 0) - u.i = 0; - else if (u.i << 1) - u.f = -1.0; - } - return u.f; -} - -float truncf(float x) -{ - union {float f; uint32_t i;} u = {x}; - int e = (int)(u.i >> 23 & 0xff) - 0x7f + 9; - uint32_t m; - - if (e >= 23 + 9) - return x; - if (e < 9) - e = 1; - m = -1U >> e; - if ((u.i & m) == 0) - return x; - FORCE_EVAL(x + 0x1p120f); - u.i &= ~m; - return u.f; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/nearbyintf.c b/MicroPython_BUILD/components/micropython/lib/libm/nearbyintf.c deleted file mode 100644 index 1c354594..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/nearbyintf.c +++ /dev/null @@ -1,21 +0,0 @@ -// adapted from the rintf() function from musl-1.1.16 - -#include "libm.h" - -float nearbyintf(float x) -{ - union {float f; uint32_t i;} u = {x}; - int e = u.i>>23 & 0xff; - int s = u.i>>31; - float_t y; - - if (e >= 0x7f+23) - return x; - if (s) - y = x - 0x1p23f + 0x1p23f; - else - y = x + 0x1p23f - 0x1p23f; - if (y == 0) - return s ? -0.0f : 0.0f; - return y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/roundf.c b/MicroPython_BUILD/components/micropython/lib/libm/roundf.c deleted file mode 100644 index 3da1f059..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/roundf.c +++ /dev/null @@ -1,33 +0,0 @@ -/*****************************************************************************/ -/*****************************************************************************/ -// roundf from musl-0.9.15 -/*****************************************************************************/ -/*****************************************************************************/ - -#include "libm.h" - -float roundf(float x) -{ - union {float f; uint32_t i;} u = {x}; - int e = u.i >> 23 & 0xff; - float_t y; - - if (e >= 0x7f+23) - return x; - if (u.i >> 31) - x = -x; - if (e < 0x7f-1) { - FORCE_EVAL(x + 0x1p23f); - return 0*u.f; - } - y = (float)(x + 0x1p23f) - 0x1p23f - x; - if (y > 0.5f) - y = y + x - 1; - else if (y <= -0.5f) - y = y + x + 1; - else - y = y + x; - if (u.i >> 31) - y = -y; - return y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/sf_cos.c b/MicroPython_BUILD/components/micropython/lib/libm/sf_cos.c deleted file mode 100644 index fabb129c..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/sf_cos.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* sf_cos.c -- float version of s_cos.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ - float cosf(float x) -#else - float cosf(x) - float x; -#endif -{ - float y[2],z=0.0; - __int32_t n,ix; - - GET_FLOAT_WORD(ix,x); - - /* |x| ~< pi/4 */ - ix &= 0x7fffffff; - if(ix <= 0x3f490fd8) return __kernel_cosf(x,z); - - /* cos(Inf or NaN) is NaN */ - else if (!FLT_UWORD_IS_FINITE(ix)) return x-x; - - /* argument reduction needed */ - else { - n = __ieee754_rem_pio2f(x,y); - switch(n&3) { - case 0: return __kernel_cosf(y[0],y[1]); - case 1: return -__kernel_sinf(y[0],y[1],1); - case 2: return -__kernel_cosf(y[0],y[1]); - default: - return __kernel_sinf(y[0],y[1],1); - } - } -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double cos(double x) -#else - double cos(x) - double x; -#endif -{ - return (double) cosf((float) x); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/sf_erf.c b/MicroPython_BUILD/components/micropython/lib/libm/sf_erf.c deleted file mode 100644 index 3f0172c6..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/sf_erf.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* sf_erf.c -- float version of s_erf.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#define __ieee754_expf expf - -#ifdef __v810__ -#define const -#endif - -#ifdef __STDC__ -static const float -#else -static float -#endif -tiny = 1e-30, -half= 5.0000000000e-01, /* 0x3F000000 */ -one = 1.0000000000e+00, /* 0x3F800000 */ -two = 2.0000000000e+00, /* 0x40000000 */ - /* c = (subfloat)0.84506291151 */ -erx = 8.4506291151e-01, /* 0x3f58560b */ -/* - * Coefficients for approximation to erf on [0,0.84375] - */ -efx = 1.2837916613e-01, /* 0x3e0375d4 */ -efx8= 1.0270333290e+00, /* 0x3f8375d4 */ -pp0 = 1.2837916613e-01, /* 0x3e0375d4 */ -pp1 = -3.2504209876e-01, /* 0xbea66beb */ -pp2 = -2.8481749818e-02, /* 0xbce9528f */ -pp3 = -5.7702702470e-03, /* 0xbbbd1489 */ -pp4 = -2.3763017452e-05, /* 0xb7c756b1 */ -qq1 = 3.9791721106e-01, /* 0x3ecbbbce */ -qq2 = 6.5022252500e-02, /* 0x3d852a63 */ -qq3 = 5.0813062117e-03, /* 0x3ba68116 */ -qq4 = 1.3249473704e-04, /* 0x390aee49 */ -qq5 = -3.9602282413e-06, /* 0xb684e21a */ -/* - * Coefficients for approximation to erf in [0.84375,1.25] - */ -pa0 = -2.3621185683e-03, /* 0xbb1acdc6 */ -pa1 = 4.1485610604e-01, /* 0x3ed46805 */ -pa2 = -3.7220788002e-01, /* 0xbebe9208 */ -pa3 = 3.1834661961e-01, /* 0x3ea2fe54 */ -pa4 = -1.1089469492e-01, /* 0xbde31cc2 */ -pa5 = 3.5478305072e-02, /* 0x3d1151b3 */ -pa6 = -2.1663755178e-03, /* 0xbb0df9c0 */ -qa1 = 1.0642088205e-01, /* 0x3dd9f331 */ -qa2 = 5.4039794207e-01, /* 0x3f0a5785 */ -qa3 = 7.1828655899e-02, /* 0x3d931ae7 */ -qa4 = 1.2617121637e-01, /* 0x3e013307 */ -qa5 = 1.3637083583e-02, /* 0x3c5f6e13 */ -qa6 = 1.1984500103e-02, /* 0x3c445aa3 */ -/* - * Coefficients for approximation to erfc in [1.25,1/0.35] - */ -ra0 = -9.8649440333e-03, /* 0xbc21a093 */ -ra1 = -6.9385856390e-01, /* 0xbf31a0b7 */ -ra2 = -1.0558626175e+01, /* 0xc128f022 */ -ra3 = -6.2375331879e+01, /* 0xc2798057 */ -ra4 = -1.6239666748e+02, /* 0xc322658c */ -ra5 = -1.8460508728e+02, /* 0xc3389ae7 */ -ra6 = -8.1287437439e+01, /* 0xc2a2932b */ -ra7 = -9.8143291473e+00, /* 0xc11d077e */ -sa1 = 1.9651271820e+01, /* 0x419d35ce */ -sa2 = 1.3765776062e+02, /* 0x4309a863 */ -sa3 = 4.3456588745e+02, /* 0x43d9486f */ -sa4 = 6.4538726807e+02, /* 0x442158c9 */ -sa5 = 4.2900814819e+02, /* 0x43d6810b */ -sa6 = 1.0863500214e+02, /* 0x42d9451f */ -sa7 = 6.5702495575e+00, /* 0x40d23f7c */ -sa8 = -6.0424413532e-02, /* 0xbd777f97 */ -/* - * Coefficients for approximation to erfc in [1/.35,28] - */ -rb0 = -9.8649431020e-03, /* 0xbc21a092 */ -rb1 = -7.9928326607e-01, /* 0xbf4c9dd4 */ -rb2 = -1.7757955551e+01, /* 0xc18e104b */ -rb3 = -1.6063638306e+02, /* 0xc320a2ea */ -rb4 = -6.3756646729e+02, /* 0xc41f6441 */ -rb5 = -1.0250950928e+03, /* 0xc480230b */ -rb6 = -4.8351919556e+02, /* 0xc3f1c275 */ -sb1 = 3.0338060379e+01, /* 0x41f2b459 */ -sb2 = 3.2579251099e+02, /* 0x43a2e571 */ -sb3 = 1.5367296143e+03, /* 0x44c01759 */ -sb4 = 3.1998581543e+03, /* 0x4547fdbb */ -sb5 = 2.5530502930e+03, /* 0x451f90ce */ -sb6 = 4.7452853394e+02, /* 0x43ed43a7 */ -sb7 = -2.2440952301e+01; /* 0xc1b38712 */ - -#ifdef __STDC__ - float erff(float x) -#else - float erff(x) - float x; -#endif -{ - __int32_t hx,ix,i; - float R,S,P,Q,s,y,z,r; - GET_FLOAT_WORD(hx,x); - ix = hx&0x7fffffff; - if(!FLT_UWORD_IS_FINITE(ix)) { /* erf(nan)=nan */ - i = ((__uint32_t)hx>>31)<<1; - return (float)(1-i)+one/x; /* erf(+-inf)=+-1 */ - } - - if(ix < 0x3f580000) { /* |x|<0.84375 */ - if(ix < 0x31800000) { /* |x|<2**-28 */ - if (ix < 0x04000000) - /*avoid underflow */ - return (float)0.125*((float)8.0*x+efx8*x); - return x + efx*x; - } - z = x*x; - r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); - s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); - y = r/s; - return x + x*y; - } - if(ix < 0x3fa00000) { /* 0.84375 <= |x| < 1.25 */ - s = fabsf(x)-one; - P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); - Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); - if(hx>=0) return erx + P/Q; else return -erx - P/Q; - } - if (ix >= 0x40c00000) { /* inf>|x|>=6 */ - if(hx>=0) return one-tiny; else return tiny-one; - } - x = fabsf(x); - s = one/(x*x); - if(ix< 0x4036DB6E) { /* |x| < 1/0.35 */ - R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( - ra5+s*(ra6+s*ra7)))))); - S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( - sa5+s*(sa6+s*(sa7+s*sa8))))))); - } else { /* |x| >= 1/0.35 */ - R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( - rb5+s*rb6))))); - S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( - sb5+s*(sb6+s*sb7)))))); - } - GET_FLOAT_WORD(ix,x); - SET_FLOAT_WORD(z,ix&0xfffff000); - r = __ieee754_expf(-z*z-(float)0.5625)*__ieee754_expf((z-x)*(z+x)+R/S); - if(hx>=0) return one-r/x; else return r/x-one; -} - -#ifdef __STDC__ - float erfcf(float x) -#else - float erfcf(x) - float x; -#endif -{ - __int32_t hx,ix; - float R,S,P,Q,s,y,z,r; - GET_FLOAT_WORD(hx,x); - ix = hx&0x7fffffff; - if(!FLT_UWORD_IS_FINITE(ix)) { /* erfc(nan)=nan */ - /* erfc(+-inf)=0,2 */ - return (float)(((__uint32_t)hx>>31)<<1)+one/x; - } - - if(ix < 0x3f580000) { /* |x|<0.84375 */ - if(ix < 0x23800000) /* |x|<2**-56 */ - return one-x; - z = x*x; - r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); - s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); - y = r/s; - if(hx < 0x3e800000) { /* x<1/4 */ - return one-(x+x*y); - } else { - r = x*y; - r += (x-half); - return half - r ; - } - } - if(ix < 0x3fa00000) { /* 0.84375 <= |x| < 1.25 */ - s = fabsf(x)-one; - P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); - Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); - if(hx>=0) { - z = one-erx; return z - P/Q; - } else { - z = erx+P/Q; return one+z; - } - } - if (ix < 0x41e00000) { /* |x|<28 */ - x = fabsf(x); - s = one/(x*x); - if(ix< 0x4036DB6D) { /* |x| < 1/.35 ~ 2.857143*/ - R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( - ra5+s*(ra6+s*ra7)))))); - S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( - sa5+s*(sa6+s*(sa7+s*sa8))))))); - } else { /* |x| >= 1/.35 ~ 2.857143 */ - if(hx<0&&ix>=0x40c00000) return two-tiny;/* x < -6 */ - R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( - rb5+s*rb6))))); - S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( - sb5+s*(sb6+s*sb7)))))); - } - GET_FLOAT_WORD(ix,x); - SET_FLOAT_WORD(z,ix&0xfffff000); - r = __ieee754_expf(-z*z-(float)0.5625)* - __ieee754_expf((z-x)*(z+x)+R/S); - if(hx>0) return r/x; else return two-r/x; - } else { - if(hx>0) return tiny*tiny; else return two-tiny; - } -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double erf(double x) -#else - double erf(x) - double x; -#endif -{ - return (double) erff((float) x); -} - -#ifdef __STDC__ - double erfc(double x) -#else - double erfc(x) - double x; -#endif -{ - return (double) erfcf((float) x); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/sf_frexp.c b/MicroPython_BUILD/components/micropython/lib/libm/sf_frexp.c deleted file mode 100644 index df50fb77..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/sf_frexp.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* sf_frexp.c -- float version of s_frexp.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ -static const float -#else -static float -#endif -two25 = 3.3554432000e+07; /* 0x4c000000 */ - -#ifdef __STDC__ - float frexpf(float x, int *eptr) -#else - float frexpf(x, eptr) - float x; int *eptr; -#endif -{ - __int32_t hx, ix; - GET_FLOAT_WORD(hx,x); - ix = 0x7fffffff&hx; - *eptr = 0; - if(!FLT_UWORD_IS_FINITE(ix)||FLT_UWORD_IS_ZERO(ix)) return x; /* 0,inf,nan */ - if (FLT_UWORD_IS_SUBNORMAL(ix)) { /* subnormal */ - x *= two25; - GET_FLOAT_WORD(hx,x); - ix = hx&0x7fffffff; - *eptr = -25; - } - *eptr += (ix>>23)-126; - hx = (hx&0x807fffff)|0x3f000000; - SET_FLOAT_WORD(x,hx); - return x; -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double frexp(double x, int *eptr) -#else - double frexp(x, eptr) - double x; int *eptr; -#endif -{ - return (double) frexpf((float) x, eptr); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/sf_ldexp.c b/MicroPython_BUILD/components/micropython/lib/libm/sf_ldexp.c deleted file mode 100644 index 37968d47..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/sf_ldexp.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* sf_ldexp.c -- float version of s_ldexp.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" -//#include - -#ifdef __STDC__ - float ldexpf(float value, int exp) -#else - float ldexpf(value, exp) - float value; int exp; -#endif -{ - if(!isfinite(value)||value==(float)0.0) return value; - value = scalbnf(value,exp); - //if(!finitef(value)||value==(float)0.0) errno = ERANGE; - return value; -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double ldexp(double value, int exp) -#else - double ldexp(value, exp) - double value; int exp; -#endif -{ - return (double) ldexpf((float) value, exp); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/sf_modf.c b/MicroPython_BUILD/components/micropython/lib/libm/sf_modf.c deleted file mode 100644 index 410db2a3..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/sf_modf.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/common - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* sf_modf.c -- float version of s_modf.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ -static const float one = 1.0; -#else -static float one = 1.0; -#endif - -#ifdef __STDC__ - float modff(float x, float *iptr) -#else - float modff(x, iptr) - float x,*iptr; -#endif -{ - __int32_t i0,j0; - __uint32_t i; - GET_FLOAT_WORD(i0,x); - j0 = ((i0>>23)&0xff)-0x7f; /* exponent of x */ - if(j0<23) { /* integer part in x */ - if(j0<0) { /* |x|<1 */ - SET_FLOAT_WORD(*iptr,i0&0x80000000); /* *iptr = +-0 */ - return x; - } else { - i = (0x007fffff)>>j0; - if((i0&i)==0) { /* x is integral */ - __uint32_t ix; - *iptr = x; - GET_FLOAT_WORD(ix,x); - SET_FLOAT_WORD(x,ix&0x80000000); /* return +-0 */ - return x; - } else { - SET_FLOAT_WORD(*iptr,i0&(~i)); - return x - *iptr; - } - } - } else { /* no fraction part */ - __uint32_t ix; - *iptr = x*one; - GET_FLOAT_WORD(ix,x); - SET_FLOAT_WORD(x,ix&0x80000000); /* return +-0 */ - return x; - } -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double modf(double x, double *iptr) -#else - double modf(x, iptr) - double x,*iptr; -#endif -{ - return (double) modff((float) x, (float *) iptr); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/sf_sin.c b/MicroPython_BUILD/components/micropython/lib/libm/sf_sin.c deleted file mode 100644 index d2705077..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/sf_sin.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* sf_sin.c -- float version of s_sin.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ - float sinf(float x) -#else - float sinf(x) - float x; -#endif -{ - float y[2],z=0.0; - __int32_t n,ix; - - GET_FLOAT_WORD(ix,x); - - /* |x| ~< pi/4 */ - ix &= 0x7fffffff; - if(ix <= 0x3f490fd8) return __kernel_sinf(x,z,0); - - /* sin(Inf or NaN) is NaN */ - else if (!FLT_UWORD_IS_FINITE(ix)) return x-x; - - /* argument reduction needed */ - else { - n = __ieee754_rem_pio2f(x,y); - switch(n&3) { - case 0: return __kernel_sinf(y[0],y[1],1); - case 1: return __kernel_cosf(y[0],y[1]); - case 2: return -__kernel_sinf(y[0],y[1],1); - default: - return -__kernel_cosf(y[0],y[1]); - } - } -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double sin(double x) -#else - double sin(x) - double x; -#endif -{ - return (double) sinf((float) x); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/sf_tan.c b/MicroPython_BUILD/components/micropython/lib/libm/sf_tan.c deleted file mode 100644 index 148b16d6..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/sf_tan.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* sf_tan.c -- float version of s_tan.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "fdlibm.h" - -#ifdef __STDC__ - float tanf(float x) -#else - float tanf(x) - float x; -#endif -{ - float y[2],z=0.0; - __int32_t n,ix; - - GET_FLOAT_WORD(ix,x); - - /* |x| ~< pi/4 */ - ix &= 0x7fffffff; - if(ix <= 0x3f490fda) return __kernel_tanf(x,z,1); - - /* tan(Inf or NaN) is NaN */ - else if (!FLT_UWORD_IS_FINITE(ix)) return x-x; /* NaN */ - - /* argument reduction needed */ - else { - n = __ieee754_rem_pio2f(x,y); - return __kernel_tanf(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even - -1 -- n odd */ - } -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double tan(double x) -#else - double tan(x) - double x; -#endif -{ - return (double) tanf((float) x); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/thumb_vfp_sqrtf.c b/MicroPython_BUILD/components/micropython/lib/libm/thumb_vfp_sqrtf.c deleted file mode 100644 index 12ffebf8..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/thumb_vfp_sqrtf.c +++ /dev/null @@ -1,11 +0,0 @@ -// an implementation of sqrtf for Thumb using hardware VFP instructions - -#include - -float sqrtf(float x) { - asm volatile ( - "vsqrt.f32 %[r], %[x]\n" - : [r] "=t" (x) - : [x] "t" (x)); - return x; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm/wf_lgamma.c b/MicroPython_BUILD/components/micropython/lib/libm/wf_lgamma.c deleted file mode 100644 index d86ede79..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/wf_lgamma.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* wf_lgamma.c -- float version of w_lgamma.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - * - */ - -#include "fdlibm.h" -#define _IEEE_LIBM 1 -//#include -//#include - -#ifdef __STDC__ - float lgammaf(float x) -#else - float lgammaf(x) - float x; -#endif -{ -#ifdef _IEEE_LIBM - int sign; - return __ieee754_lgammaf_r(x,&sign); -#else - float y; - struct exception exc; - y = __ieee754_lgammaf_r(x,&(_REENT_SIGNGAM(_REENT))); - if(_LIB_VERSION == _IEEE_) return y; - if(!finitef(y)&&finitef(x)) { -#ifndef HUGE_VAL -#define HUGE_VAL inf - double inf = 0.0; - - SET_HIGH_WORD(inf,0x7ff00000); /* set inf to infinite */ -#endif - exc.name = "lgammaf"; - exc.err = 0; - exc.arg1 = exc.arg2 = (double)x; - if (_LIB_VERSION == _SVID_) - exc.retval = HUGE; - else - exc.retval = HUGE_VAL; - if(floorf(x)==x&&x<=(float)0.0) { - /* lgammaf(-integer) */ - exc.type = SING; - if (_LIB_VERSION == _POSIX_) - errno = EDOM; - else if (!matherr(&exc)) { - errno = EDOM; - } - - } else { - /* lgammaf(finite) overflow */ - exc.type = OVERFLOW; - if (_LIB_VERSION == _POSIX_) - errno = ERANGE; - else if (!matherr(&exc)) { - errno = ERANGE; - } - } - if (exc.err != 0) - errno = exc.err; - return (float)exc.retval; - } else - return y; -#endif -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double lgamma(double x) -#else - double lgamma(x) - double x; -#endif -{ - return (double) lgammaf((float) x); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm/wf_tgamma.c b/MicroPython_BUILD/components/micropython/lib/libm/wf_tgamma.c deleted file mode 100644 index 64b2488d..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm/wf_tgamma.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * These math functions are taken from newlib-nano-2, the newlib/libm/math - * directory, available from https://github.com/32bitmicro/newlib-nano-2. - * - * Appropriate copyright headers are reproduced below. - */ - -/* w_gammaf.c -- float version of w_gamma.c. - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ - -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include "math.h" -#include "fdlibm.h" -#define _IEEE_LIBM 1 - -#ifdef __STDC__ - float tgammaf(float x) -#else - float tgammaf(x) - float x; -#endif -{ - float y; - int local_signgam; - y = expf(__ieee754_lgammaf_r(x,&local_signgam)); - if (local_signgam < 0) y = -y; -#ifdef _IEEE_LIBM - return y; -#else - if(_LIB_VERSION == _IEEE_) return y; - - if(!finitef(y)&&finitef(x)) { - if(floorf(x)==x&&x<=(float)0.0) - /* tgammaf pole */ - return (float)__kernel_standard((double)x,(double)x,141); - else - /* tgammaf overflow */ - return (float)__kernel_standard((double)x,(double)x,140); - } - return y; -#endif -} - -#ifdef _DOUBLE_IS_32BITS - -#ifdef __STDC__ - double tgamma(double x) -#else - double tgamma(x) - double x; -#endif -{ - return (double) tgammaf((float) x); -} - -#endif /* defined(_DOUBLE_IS_32BITS) */ diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/README b/MicroPython_BUILD/components/micropython/lib/libm_dbl/README deleted file mode 100644 index 512b3282..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/README +++ /dev/null @@ -1,32 +0,0 @@ -This directory contains source code for the standard double-precision math -functions. - -The files lgamma.c, log10.c and tanh.c are too small to have a meaningful -copyright or license. - -The rest of the files in this directory are copied from the musl library, -v1.1.16, and, unless otherwise stated in the individual file, have the -following copyright and MIT license: - ----------------------------------------------------------------------- -Copyright © 2005-2014 Rich Felker, et al. - -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. ----------------------------------------------------------------------- diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__cos.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__cos.c deleted file mode 100644 index 46cefb38..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__cos.c +++ /dev/null @@ -1,71 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* - * __cos( x, y ) - * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 - * Input x is assumed to be bounded by ~pi/4 in magnitude. - * Input y is the tail of x. - * - * Algorithm - * 1. Since cos(-x) = cos(x), we need only to consider positive x. - * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. - * 3. cos(x) is approximated by a polynomial of degree 14 on - * [0,pi/4] - * 4 14 - * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x - * where the remez error is - * - * | 2 4 6 8 10 12 14 | -58 - * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 - * | | - * - * 4 6 8 10 12 14 - * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then - * cos(x) ~ 1 - x*x/2 + r - * since cos(x+y) ~ cos(x) - sin(x)*y - * ~ cos(x) - x*y, - * a correction term is necessary in cos(x) and hence - * cos(x+y) = 1 - (x*x/2 - (r - x*y)) - * For better accuracy, rearrange to - * cos(x+y) ~ w + (tmp + (r-x*y)) - * where w = 1 - x*x/2 and tmp is a tiny correction term - * (1 - x*x/2 == w + tmp exactly in infinite precision). - * The exactness of w + tmp in infinite precision depends on w - * and tmp having the same precision as x. If they have extra - * precision due to compiler bugs, then the extra precision is - * only good provided it is retained in all terms of the final - * expression for cos(). Retention happens in all cases tested - * under FreeBSD, so don't pessimize things by forcibly clipping - * any extra precision in w. - */ - -#include "libm.h" - -static const double -C1 = 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */ -C2 = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */ -C3 = 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */ -C4 = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */ -C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ -C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ - -double __cos(double x, double y) -{ - double_t hz,z,r,w; - - z = x*x; - w = z*z; - r = z*(C1+z*(C2+z*C3)) + w*w*(C4+z*(C5+z*C6)); - hz = 0.5*z; - w = 1.0-hz; - return w + (((1.0-w)-hz) + (z*r-x*y)); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__expo2.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__expo2.c deleted file mode 100644 index 740ac680..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__expo2.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "libm.h" - -/* k is such that k*ln2 has minimal relative error and x - kln2 > log(DBL_MIN) */ -static const int k = 2043; -static const double kln2 = 0x1.62066151add8bp+10; - -/* exp(x)/2 for x >= log(DBL_MAX), slightly better than 0.5*exp(x/2)*exp(x/2) */ -double __expo2(double x) -{ - double scale; - - /* note that k is odd and scale*scale overflows */ - INSERT_WORDS(scale, (uint32_t)(0x3ff + k/2) << 20, 0); - /* exp(x - k ln2) * 2**(k-1) */ - return exp(x - kln2) * scale * scale; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__fpclassify.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__fpclassify.c deleted file mode 100644 index 5c908ba3..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__fpclassify.c +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include - -int __fpclassifyd(double x) -{ - union {double f; uint64_t i;} u = {x}; - int e = u.i>>52 & 0x7ff; - if (!e) return u.i<<1 ? FP_SUBNORMAL : FP_ZERO; - if (e==0x7ff) return u.i<<12 ? FP_NAN : FP_INFINITE; - return FP_NORMAL; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__rem_pio2.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__rem_pio2.c deleted file mode 100644 index d403f81c..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__rem_pio2.c +++ /dev/null @@ -1,177 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - * - * Optimized by Bruce D. Evans. - */ -/* __rem_pio2(x,y) - * - * return the remainder of x rem pi/2 in y[0]+y[1] - * use __rem_pio2_large() for large x - */ - -#include "libm.h" - -#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 -#define EPS DBL_EPSILON -#elif FLT_EVAL_METHOD==2 -#define EPS LDBL_EPSILON -#endif - -/* - * invpio2: 53 bits of 2/pi - * pio2_1: first 33 bit of pi/2 - * pio2_1t: pi/2 - pio2_1 - * pio2_2: second 33 bit of pi/2 - * pio2_2t: pi/2 - (pio2_1+pio2_2) - * pio2_3: third 33 bit of pi/2 - * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) - */ -static const double -toint = 1.5/EPS, -invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ -pio2_1 = 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */ -pio2_1t = 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */ -pio2_2 = 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */ -pio2_2t = 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */ -pio2_3 = 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */ -pio2_3t = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */ - -/* caller must handle the case when reduction is not needed: |x| ~<= pi/4 */ -int __rem_pio2(double x, double *y) -{ - union {double f; uint64_t i;} u = {x}; - double_t z,w,t,r,fn; - double tx[3],ty[2]; - uint32_t ix; - int sign, n, ex, ey, i; - - sign = u.i>>63; - ix = u.i>>32 & 0x7fffffff; - if (ix <= 0x400f6a7a) { /* |x| ~<= 5pi/4 */ - if ((ix & 0xfffff) == 0x921fb) /* |x| ~= pi/2 or 2pi/2 */ - goto medium; /* cancellation -- use medium case */ - if (ix <= 0x4002d97c) { /* |x| ~<= 3pi/4 */ - if (!sign) { - z = x - pio2_1; /* one round good to 85 bits */ - y[0] = z - pio2_1t; - y[1] = (z-y[0]) - pio2_1t; - return 1; - } else { - z = x + pio2_1; - y[0] = z + pio2_1t; - y[1] = (z-y[0]) + pio2_1t; - return -1; - } - } else { - if (!sign) { - z = x - 2*pio2_1; - y[0] = z - 2*pio2_1t; - y[1] = (z-y[0]) - 2*pio2_1t; - return 2; - } else { - z = x + 2*pio2_1; - y[0] = z + 2*pio2_1t; - y[1] = (z-y[0]) + 2*pio2_1t; - return -2; - } - } - } - if (ix <= 0x401c463b) { /* |x| ~<= 9pi/4 */ - if (ix <= 0x4015fdbc) { /* |x| ~<= 7pi/4 */ - if (ix == 0x4012d97c) /* |x| ~= 3pi/2 */ - goto medium; - if (!sign) { - z = x - 3*pio2_1; - y[0] = z - 3*pio2_1t; - y[1] = (z-y[0]) - 3*pio2_1t; - return 3; - } else { - z = x + 3*pio2_1; - y[0] = z + 3*pio2_1t; - y[1] = (z-y[0]) + 3*pio2_1t; - return -3; - } - } else { - if (ix == 0x401921fb) /* |x| ~= 4pi/2 */ - goto medium; - if (!sign) { - z = x - 4*pio2_1; - y[0] = z - 4*pio2_1t; - y[1] = (z-y[0]) - 4*pio2_1t; - return 4; - } else { - z = x + 4*pio2_1; - y[0] = z + 4*pio2_1t; - y[1] = (z-y[0]) + 4*pio2_1t; - return -4; - } - } - } - if (ix < 0x413921fb) { /* |x| ~< 2^20*(pi/2), medium size */ -medium: - /* rint(x/(pi/2)), Assume round-to-nearest. */ - fn = (double_t)x*invpio2 + toint - toint; - n = (int32_t)fn; - r = x - fn*pio2_1; - w = fn*pio2_1t; /* 1st round, good to 85 bits */ - y[0] = r - w; - u.f = y[0]; - ey = u.i>>52 & 0x7ff; - ex = ix>>20; - if (ex - ey > 16) { /* 2nd round, good to 118 bits */ - t = r; - w = fn*pio2_2; - r = t - w; - w = fn*pio2_2t - ((t-r)-w); - y[0] = r - w; - u.f = y[0]; - ey = u.i>>52 & 0x7ff; - if (ex - ey > 49) { /* 3rd round, good to 151 bits, covers all cases */ - t = r; - w = fn*pio2_3; - r = t - w; - w = fn*pio2_3t - ((t-r)-w); - y[0] = r - w; - } - } - y[1] = (r - y[0]) - w; - return n; - } - /* - * all other (large) arguments - */ - if (ix >= 0x7ff00000) { /* x is inf or NaN */ - y[0] = y[1] = x - x; - return 0; - } - /* set z = scalbn(|x|,-ilogb(x)+23) */ - u.f = x; - u.i &= (uint64_t)-1>>12; - u.i |= (uint64_t)(0x3ff + 23)<<52; - z = u.f; - for (i=0; i < 2; i++) { - tx[i] = (double)(int32_t)z; - z = (z-tx[i])*0x1p24; - } - tx[i] = z; - /* skip zero terms, first term is non-zero */ - while (tx[i] == 0.0) - i--; - n = __rem_pio2_large(tx,ty,(int)(ix>>20)-(0x3ff+23),i+1,1); - if (sign) { - y[0] = -ty[0]; - y[1] = -ty[1]; - return -n; - } - y[0] = ty[0]; - y[1] = ty[1]; - return n; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__rem_pio2_large.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__rem_pio2_large.c deleted file mode 100644 index 958f28c2..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__rem_pio2_large.c +++ /dev/null @@ -1,442 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/k_rem_pio2.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* - * __rem_pio2_large(x,y,e0,nx,prec) - * double x[],y[]; int e0,nx,prec; - * - * __rem_pio2_large return the last three digits of N with - * y = x - N*pi/2 - * so that |y| < pi/2. - * - * The method is to compute the integer (mod 8) and fraction parts of - * (2/pi)*x without doing the full multiplication. In general we - * skip the part of the product that are known to be a huge integer ( - * more accurately, = 0 mod 8 ). Thus the number of operations are - * independent of the exponent of the input. - * - * (2/pi) is represented by an array of 24-bit integers in ipio2[]. - * - * Input parameters: - * x[] The input value (must be positive) is broken into nx - * pieces of 24-bit integers in double precision format. - * x[i] will be the i-th 24 bit of x. The scaled exponent - * of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 - * match x's up to 24 bits. - * - * Example of breaking a double positive z into x[0]+x[1]+x[2]: - * e0 = ilogb(z)-23 - * z = scalbn(z,-e0) - * for i = 0,1,2 - * x[i] = floor(z) - * z = (z-x[i])*2**24 - * - * - * y[] ouput result in an array of double precision numbers. - * The dimension of y[] is: - * 24-bit precision 1 - * 53-bit precision 2 - * 64-bit precision 2 - * 113-bit precision 3 - * The actual value is the sum of them. Thus for 113-bit - * precison, one may have to do something like: - * - * long double t,w,r_head, r_tail; - * t = (long double)y[2] + (long double)y[1]; - * w = (long double)y[0]; - * r_head = t+w; - * r_tail = w - (r_head - t); - * - * e0 The exponent of x[0]. Must be <= 16360 or you need to - * expand the ipio2 table. - * - * nx dimension of x[] - * - * prec an integer indicating the precision: - * 0 24 bits (single) - * 1 53 bits (double) - * 2 64 bits (extended) - * 3 113 bits (quad) - * - * External function: - * double scalbn(), floor(); - * - * - * Here is the description of some local variables: - * - * jk jk+1 is the initial number of terms of ipio2[] needed - * in the computation. The minimum and recommended value - * for jk is 3,4,4,6 for single, double, extended, and quad. - * jk+1 must be 2 larger than you might expect so that our - * recomputation test works. (Up to 24 bits in the integer - * part (the 24 bits of it that we compute) and 23 bits in - * the fraction part may be lost to cancelation before we - * recompute.) - * - * jz local integer variable indicating the number of - * terms of ipio2[] used. - * - * jx nx - 1 - * - * jv index for pointing to the suitable ipio2[] for the - * computation. In general, we want - * ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8 - * is an integer. Thus - * e0-3-24*jv >= 0 or (e0-3)/24 >= jv - * Hence jv = max(0,(e0-3)/24). - * - * jp jp+1 is the number of terms in PIo2[] needed, jp = jk. - * - * q[] double array with integral value, representing the - * 24-bits chunk of the product of x and 2/pi. - * - * q0 the corresponding exponent of q[0]. Note that the - * exponent for q[i] would be q0-24*i. - * - * PIo2[] double precision array, obtained by cutting pi/2 - * into 24 bits chunks. - * - * f[] ipio2[] in floating point - * - * iq[] integer array by breaking up q[] in 24-bits chunk. - * - * fq[] final product of x*(2/pi) in fq[0],..,fq[jk] - * - * ih integer. If >0 it indicates q[] is >= 0.5, hence - * it also indicates the *sign* of the result. - * - */ -/* - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ - -#include "libm.h" - -static const int init_jk[] = {3,4,4,6}; /* initial value for jk */ - -/* - * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi - * - * integer array, contains the (24*i)-th to (24*i+23)-th - * bit of 2/pi after binary point. The corresponding - * floating value is - * - * ipio2[i] * 2^(-24(i+1)). - * - * NB: This table must have at least (e0-3)/24 + jk terms. - * For quad precision (e0 <= 16360, jk = 6), this is 686. - */ -static const int32_t ipio2[] = { -0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, -0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, -0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, -0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, -0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, -0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, -0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, -0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, -0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, -0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, -0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, - -#if LDBL_MAX_EXP > 1024 -0x47C419, 0xC367CD, 0xDCE809, 0x2A8359, 0xC4768B, 0x961CA6, -0xDDAF44, 0xD15719, 0x053EA5, 0xFF0705, 0x3F7E33, 0xE832C2, -0xDE4F98, 0x327DBB, 0xC33D26, 0xEF6B1E, 0x5EF89F, 0x3A1F35, -0xCAF27F, 0x1D87F1, 0x21907C, 0x7C246A, 0xFA6ED5, 0x772D30, -0x433B15, 0xC614B5, 0x9D19C3, 0xC2C4AD, 0x414D2C, 0x5D000C, -0x467D86, 0x2D71E3, 0x9AC69B, 0x006233, 0x7CD2B4, 0x97A7B4, -0xD55537, 0xF63ED7, 0x1810A3, 0xFC764D, 0x2A9D64, 0xABD770, -0xF87C63, 0x57B07A, 0xE71517, 0x5649C0, 0xD9D63B, 0x3884A7, -0xCB2324, 0x778AD6, 0x23545A, 0xB91F00, 0x1B0AF1, 0xDFCE19, -0xFF319F, 0x6A1E66, 0x615799, 0x47FBAC, 0xD87F7E, 0xB76522, -0x89E832, 0x60BFE6, 0xCDC4EF, 0x09366C, 0xD43F5D, 0xD7DE16, -0xDE3B58, 0x929BDE, 0x2822D2, 0xE88628, 0x4D58E2, 0x32CAC6, -0x16E308, 0xCB7DE0, 0x50C017, 0xA71DF3, 0x5BE018, 0x34132E, -0x621283, 0x014883, 0x5B8EF5, 0x7FB0AD, 0xF2E91E, 0x434A48, -0xD36710, 0xD8DDAA, 0x425FAE, 0xCE616A, 0xA4280A, 0xB499D3, -0xF2A606, 0x7F775C, 0x83C2A3, 0x883C61, 0x78738A, 0x5A8CAF, -0xBDD76F, 0x63A62D, 0xCBBFF4, 0xEF818D, 0x67C126, 0x45CA55, -0x36D9CA, 0xD2A828, 0x8D61C2, 0x77C912, 0x142604, 0x9B4612, -0xC459C4, 0x44C5C8, 0x91B24D, 0xF31700, 0xAD43D4, 0xE54929, -0x10D5FD, 0xFCBE00, 0xCC941E, 0xEECE70, 0xF53E13, 0x80F1EC, -0xC3E7B3, 0x28F8C7, 0x940593, 0x3E71C1, 0xB3092E, 0xF3450B, -0x9C1288, 0x7B20AB, 0x9FB52E, 0xC29247, 0x2F327B, 0x6D550C, -0x90A772, 0x1FE76B, 0x96CB31, 0x4A1679, 0xE27941, 0x89DFF4, -0x9794E8, 0x84E6E2, 0x973199, 0x6BED88, 0x365F5F, 0x0EFDBB, -0xB49A48, 0x6CA467, 0x427271, 0x325D8D, 0xB8159F, 0x09E5BC, -0x25318D, 0x3974F7, 0x1C0530, 0x010C0D, 0x68084B, 0x58EE2C, -0x90AA47, 0x02E774, 0x24D6BD, 0xA67DF7, 0x72486E, 0xEF169F, -0xA6948E, 0xF691B4, 0x5153D1, 0xF20ACF, 0x339820, 0x7E4BF5, -0x6863B2, 0x5F3EDD, 0x035D40, 0x7F8985, 0x295255, 0xC06437, -0x10D86D, 0x324832, 0x754C5B, 0xD4714E, 0x6E5445, 0xC1090B, -0x69F52A, 0xD56614, 0x9D0727, 0x50045D, 0xDB3BB4, 0xC576EA, -0x17F987, 0x7D6B49, 0xBA271D, 0x296996, 0xACCCC6, 0x5414AD, -0x6AE290, 0x89D988, 0x50722C, 0xBEA404, 0x940777, 0x7030F3, -0x27FC00, 0xA871EA, 0x49C266, 0x3DE064, 0x83DD97, 0x973FA3, -0xFD9443, 0x8C860D, 0xDE4131, 0x9D3992, 0x8C70DD, 0xE7B717, -0x3BDF08, 0x2B3715, 0xA0805C, 0x93805A, 0x921110, 0xD8E80F, -0xAF806C, 0x4BFFDB, 0x0F9038, 0x761859, 0x15A562, 0xBBCB61, -0xB989C7, 0xBD4010, 0x04F2D2, 0x277549, 0xF6B6EB, 0xBB22DB, -0xAA140A, 0x2F2689, 0x768364, 0x333B09, 0x1A940E, 0xAA3A51, -0xC2A31D, 0xAEEDAF, 0x12265C, 0x4DC26D, 0x9C7A2D, 0x9756C0, -0x833F03, 0xF6F009, 0x8C402B, 0x99316D, 0x07B439, 0x15200C, -0x5BC3D8, 0xC492F5, 0x4BADC6, 0xA5CA4E, 0xCD37A7, 0x36A9E6, -0x9492AB, 0x6842DD, 0xDE6319, 0xEF8C76, 0x528B68, 0x37DBFC, -0xABA1AE, 0x3115DF, 0xA1AE00, 0xDAFB0C, 0x664D64, 0xB705ED, -0x306529, 0xBF5657, 0x3AFF47, 0xB9F96A, 0xF3BE75, 0xDF9328, -0x3080AB, 0xF68C66, 0x15CB04, 0x0622FA, 0x1DE4D9, 0xA4B33D, -0x8F1B57, 0x09CD36, 0xE9424E, 0xA4BE13, 0xB52333, 0x1AAAF0, -0xA8654F, 0xA5C1D2, 0x0F3F0B, 0xCD785B, 0x76F923, 0x048B7B, -0x721789, 0x53A6C6, 0xE26E6F, 0x00EBEF, 0x584A9B, 0xB7DAC4, -0xBA66AA, 0xCFCF76, 0x1D02D1, 0x2DF1B1, 0xC1998C, 0x77ADC3, -0xDA4886, 0xA05DF7, 0xF480C6, 0x2FF0AC, 0x9AECDD, 0xBC5C3F, -0x6DDED0, 0x1FC790, 0xB6DB2A, 0x3A25A3, 0x9AAF00, 0x9353AD, -0x0457B6, 0xB42D29, 0x7E804B, 0xA707DA, 0x0EAA76, 0xA1597B, -0x2A1216, 0x2DB7DC, 0xFDE5FA, 0xFEDB89, 0xFDBE89, 0x6C76E4, -0xFCA906, 0x70803E, 0x156E85, 0xFF87FD, 0x073E28, 0x336761, -0x86182A, 0xEABD4D, 0xAFE7B3, 0x6E6D8F, 0x396795, 0x5BBF31, -0x48D784, 0x16DF30, 0x432DC7, 0x356125, 0xCE70C9, 0xB8CB30, -0xFD6CBF, 0xA200A4, 0xE46C05, 0xA0DD5A, 0x476F21, 0xD21262, -0x845CB9, 0x496170, 0xE0566B, 0x015299, 0x375550, 0xB7D51E, -0xC4F133, 0x5F6E13, 0xE4305D, 0xA92E85, 0xC3B21D, 0x3632A1, -0xA4B708, 0xD4B1EA, 0x21F716, 0xE4698F, 0x77FF27, 0x80030C, -0x2D408D, 0xA0CD4F, 0x99A520, 0xD3A2B3, 0x0A5D2F, 0x42F9B4, -0xCBDA11, 0xD0BE7D, 0xC1DB9B, 0xBD17AB, 0x81A2CA, 0x5C6A08, -0x17552E, 0x550027, 0xF0147F, 0x8607E1, 0x640B14, 0x8D4196, -0xDEBE87, 0x2AFDDA, 0xB6256B, 0x34897B, 0xFEF305, 0x9EBFB9, -0x4F6A68, 0xA82A4A, 0x5AC44F, 0xBCF82D, 0x985AD7, 0x95C7F4, -0x8D4D0D, 0xA63A20, 0x5F57A4, 0xB13F14, 0x953880, 0x0120CC, -0x86DD71, 0xB6DEC9, 0xF560BF, 0x11654D, 0x6B0701, 0xACB08C, -0xD0C0B2, 0x485551, 0x0EFB1E, 0xC37295, 0x3B06A3, 0x3540C0, -0x7BDC06, 0xCC45E0, 0xFA294E, 0xC8CAD6, 0x41F3E8, 0xDE647C, -0xD8649B, 0x31BED9, 0xC397A4, 0xD45877, 0xC5E369, 0x13DAF0, -0x3C3ABA, 0x461846, 0x5F7555, 0xF5BDD2, 0xC6926E, 0x5D2EAC, -0xED440E, 0x423E1C, 0x87C461, 0xE9FD29, 0xF3D6E7, 0xCA7C22, -0x35916F, 0xC5E008, 0x8DD7FF, 0xE26A6E, 0xC6FDB0, 0xC10893, -0x745D7C, 0xB2AD6B, 0x9D6ECD, 0x7B723E, 0x6A11C6, 0xA9CFF7, -0xDF7329, 0xBAC9B5, 0x5100B7, 0x0DB2E2, 0x24BA74, 0x607DE5, -0x8AD874, 0x2C150D, 0x0C1881, 0x94667E, 0x162901, 0x767A9F, -0xBEFDFD, 0xEF4556, 0x367ED9, 0x13D9EC, 0xB9BA8B, 0xFC97C4, -0x27A831, 0xC36EF1, 0x36C594, 0x56A8D8, 0xB5A8B4, 0x0ECCCF, -0x2D8912, 0x34576F, 0x89562C, 0xE3CE99, 0xB920D6, 0xAA5E6B, -0x9C2A3E, 0xCC5F11, 0x4A0BFD, 0xFBF4E1, 0x6D3B8E, 0x2C86E2, -0x84D4E9, 0xA9B4FC, 0xD1EEEF, 0xC9352E, 0x61392F, 0x442138, -0xC8D91B, 0x0AFC81, 0x6A4AFB, 0xD81C2F, 0x84B453, 0x8C994E, -0xCC2254, 0xDC552A, 0xD6C6C0, 0x96190B, 0xB8701A, 0x649569, -0x605A26, 0xEE523F, 0x0F117F, 0x11B5F4, 0xF5CBFC, 0x2DBC34, -0xEEBC34, 0xCC5DE8, 0x605EDD, 0x9B8E67, 0xEF3392, 0xB817C9, -0x9B5861, 0xBC57E1, 0xC68351, 0x103ED8, 0x4871DD, 0xDD1C2D, -0xA118AF, 0x462C21, 0xD7F359, 0x987AD9, 0xC0549E, 0xFA864F, -0xFC0656, 0xAE79E5, 0x362289, 0x22AD38, 0xDC9367, 0xAAE855, -0x382682, 0x9BE7CA, 0xA40D51, 0xB13399, 0x0ED7A9, 0x480569, -0xF0B265, 0xA7887F, 0x974C88, 0x36D1F9, 0xB39221, 0x4A827B, -0x21CF98, 0xDC9F40, 0x5547DC, 0x3A74E1, 0x42EB67, 0xDF9DFE, -0x5FD45E, 0xA4677B, 0x7AACBA, 0xA2F655, 0x23882B, 0x55BA41, -0x086E59, 0x862A21, 0x834739, 0xE6E389, 0xD49EE5, 0x40FB49, -0xE956FF, 0xCA0F1C, 0x8A59C5, 0x2BFA94, 0xC5C1D3, 0xCFC50F, -0xAE5ADB, 0x86C547, 0x624385, 0x3B8621, 0x94792C, 0x876110, -0x7B4C2A, 0x1A2C80, 0x12BF43, 0x902688, 0x893C78, 0xE4C4A8, -0x7BDBE5, 0xC23AC4, 0xEAF426, 0x8A67F7, 0xBF920D, 0x2BA365, -0xB1933D, 0x0B7CBD, 0xDC51A4, 0x63DD27, 0xDDE169, 0x19949A, -0x9529A8, 0x28CE68, 0xB4ED09, 0x209F44, 0xCA984E, 0x638270, -0x237C7E, 0x32B90F, 0x8EF5A7, 0xE75614, 0x08F121, 0x2A9DB5, -0x4D7E6F, 0x5119A5, 0xABF9B5, 0xD6DF82, 0x61DD96, 0x023616, -0x9F3AC4, 0xA1A283, 0x6DED72, 0x7A8D39, 0xA9B882, 0x5C326B, -0x5B2746, 0xED3400, 0x7700D2, 0x55F4FC, 0x4D5901, 0x8071E0, -#endif -}; - -static const double PIo2[] = { - 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ - 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ - 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ - 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */ - 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */ - 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */ - 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */ - 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */ -}; - -int __rem_pio2_large(double *x, double *y, int e0, int nx, int prec) -{ - int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; - double z,fw,f[20],fq[20],q[20]; - - /* initialize jk*/ - jk = init_jk[prec]; - jp = jk; - - /* determine jx,jv,q0, note that 3>q0 */ - jx = nx-1; - jv = (e0-3)/24; if(jv<0) jv=0; - q0 = e0-24*(jv+1); - - /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ - j = jv-jx; m = jx+jk; - for (i=0; i<=m; i++,j++) - f[i] = j<0 ? 0.0 : (double)ipio2[j]; - - /* compute q[0],q[1],...q[jk] */ - for (i=0; i<=jk; i++) { - for (j=0,fw=0.0; j<=jx; j++) - fw += x[j]*f[jx+i-j]; - q[i] = fw; - } - - jz = jk; -recompute: - /* distill q[] into iq[] reversingly */ - for (i=0,j=jz,z=q[jz]; j>0; i++,j--) { - fw = (double)(int32_t)(0x1p-24*z); - iq[i] = (int32_t)(z - 0x1p24*fw); - z = q[j-1]+fw; - } - - /* compute n */ - z = scalbn(z,q0); /* actual value of z */ - z -= 8.0*floor(z*0.125); /* trim off integer >= 8 */ - n = (int32_t)z; - z -= (double)n; - ih = 0; - if (q0 > 0) { /* need iq[jz-1] to determine n */ - i = iq[jz-1]>>(24-q0); n += i; - iq[jz-1] -= i<<(24-q0); - ih = iq[jz-1]>>(23-q0); - } - else if (q0 == 0) ih = iq[jz-1]>>23; - else if (z >= 0.5) ih = 2; - - if (ih > 0) { /* q > 0.5 */ - n += 1; carry = 0; - for (i=0; i 0) { /* rare case: chance is 1 in 12 */ - switch(q0) { - case 1: - iq[jz-1] &= 0x7fffff; break; - case 2: - iq[jz-1] &= 0x3fffff; break; - } - } - if (ih == 2) { - z = 1.0 - z; - if (carry != 0) - z -= scalbn(1.0,q0); - } - } - - /* check if recomputation is needed */ - if (z == 0.0) { - j = 0; - for (i=jz-1; i>=jk; i--) j |= iq[i]; - if (j == 0) { /* need recomputation */ - for (k=1; iq[jk-k]==0; k++); /* k = no. of terms needed */ - - for (i=jz+1; i<=jz+k; i++) { /* add q[jz+1] to q[jz+k] */ - f[jx+i] = (double)ipio2[jv+i]; - for (j=0,fw=0.0; j<=jx; j++) - fw += x[j]*f[jx+i-j]; - q[i] = fw; - } - jz += k; - goto recompute; - } - } - - /* chop off zero terms */ - if (z == 0.0) { - jz -= 1; - q0 -= 24; - while (iq[jz] == 0) { - jz--; - q0 -= 24; - } - } else { /* break z into 24-bit if necessary */ - z = scalbn(z,-q0); - if (z >= 0x1p24) { - fw = (double)(int32_t)(0x1p-24*z); - iq[jz] = (int32_t)(z - 0x1p24*fw); - jz += 1; - q0 += 24; - iq[jz] = (int32_t)fw; - } else - iq[jz] = (int32_t)z; - } - - /* convert integer "bit" chunk to floating-point value */ - fw = scalbn(1.0,q0); - for (i=jz; i>=0; i--) { - q[i] = fw*(double)iq[i]; - fw *= 0x1p-24; - } - - /* compute PIo2[0,...,jp]*q[jz,...,0] */ - for(i=jz; i>=0; i--) { - for (fw=0.0,k=0; k<=jp && k<=jz-i; k++) - fw += PIo2[k]*q[i+k]; - fq[jz-i] = fw; - } - - /* compress fq[] into y[] */ - switch(prec) { - case 0: - fw = 0.0; - for (i=jz; i>=0; i--) - fw += fq[i]; - y[0] = ih==0 ? fw : -fw; - break; - case 1: - case 2: - fw = 0.0; - for (i=jz; i>=0; i--) - fw += fq[i]; - // TODO: drop excess precision here once double_t is used - fw = (double)fw; - y[0] = ih==0 ? fw : -fw; - fw = fq[0]-fw; - for (i=1; i<=jz; i++) - fw += fq[i]; - y[1] = ih==0 ? fw : -fw; - break; - case 3: /* painful */ - for (i=jz; i>0; i--) { - fw = fq[i-1]+fq[i]; - fq[i] += fq[i-1]-fw; - fq[i-1] = fw; - } - for (i=jz; i>1; i--) { - fw = fq[i-1]+fq[i]; - fq[i] += fq[i-1]-fw; - fq[i-1] = fw; - } - for (fw=0.0,i=jz; i>=2; i--) - fw += fq[i]; - if (ih==0) { - y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; - } else { - y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; - } - } - return n&7; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__signbit.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__signbit.c deleted file mode 100644 index 18c6728a..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__signbit.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "libm.h" - -int __signbitd(double x) -{ - union { - double d; - uint64_t i; - } y = { x }; - return y.i>>63; -} - - diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__sin.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__sin.c deleted file mode 100644 index 40309496..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__sin.c +++ /dev/null @@ -1,64 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* __sin( x, y, iy) - * kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 - * Input x is assumed to be bounded by ~pi/4 in magnitude. - * Input y is the tail of x. - * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). - * - * Algorithm - * 1. Since sin(-x) = -sin(x), we need only to consider positive x. - * 2. Callers must return sin(-0) = -0 without calling here since our - * odd polynomial is not evaluated in a way that preserves -0. - * Callers may do the optimization sin(x) ~ x for tiny x. - * 3. sin(x) is approximated by a polynomial of degree 13 on - * [0,pi/4] - * 3 13 - * sin(x) ~ x + S1*x + ... + S6*x - * where - * - * |sin(x) 2 4 6 8 10 12 | -58 - * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 - * | x | - * - * 4. sin(x+y) = sin(x) + sin'(x')*y - * ~ sin(x) + (1-x*x/2)*y - * For better accuracy, let - * 3 2 2 2 2 - * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) - * then 3 2 - * sin(x) = x + (S1*x + (x *(r-y/2)+y)) - */ - -#include "libm.h" - -static const double -S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */ -S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */ -S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */ -S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */ -S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */ -S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */ - -double __sin(double x, double y, int iy) -{ - double_t z,r,v,w; - - z = x*x; - w = z*z; - r = S2 + z*(S3 + z*S4) + z*w*(S5 + z*S6); - v = z*x; - if (iy == 0) - return x + v*(S1 + z*r); - else - return x - ((z*(0.5*y - v*r) - y) - v*S1); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__tan.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/__tan.c deleted file mode 100644 index 8019844d..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/__tan.c +++ /dev/null @@ -1,110 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */ -/* - * ==================================================== - * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* __tan( x, y, k ) - * kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 - * Input x is assumed to be bounded by ~pi/4 in magnitude. - * Input y is the tail of x. - * Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned. - * - * Algorithm - * 1. Since tan(-x) = -tan(x), we need only to consider positive x. - * 2. Callers must return tan(-0) = -0 without calling here since our - * odd polynomial is not evaluated in a way that preserves -0. - * Callers may do the optimization tan(x) ~ x for tiny x. - * 3. tan(x) is approximated by a odd polynomial of degree 27 on - * [0,0.67434] - * 3 27 - * tan(x) ~ x + T1*x + ... + T13*x - * where - * - * |tan(x) 2 4 26 | -59.2 - * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 - * | x | - * - * Note: tan(x+y) = tan(x) + tan'(x)*y - * ~ tan(x) + (1+x*x)*y - * Therefore, for better accuracy in computing tan(x+y), let - * 3 2 2 2 2 - * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) - * then - * 3 2 - * tan(x+y) = x + (T1*x + (x *(r+y)+y)) - * - * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then - * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) - * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) - */ - -#include "libm.h" - -static const double T[] = { - 3.33333333333334091986e-01, /* 3FD55555, 55555563 */ - 1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */ - 5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */ - 2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */ - 8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */ - 3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */ - 1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */ - 5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */ - 2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */ - 7.81794442939557092300e-05, /* 3F147E88, A03792A6 */ - 7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */ - -1.85586374855275456654e-05, /* BEF375CB, DB605373 */ - 2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */ -}, -pio4 = 7.85398163397448278999e-01, /* 3FE921FB, 54442D18 */ -pio4lo = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */ - -double __tan(double x, double y, int odd) -{ - double_t z, r, v, w, s, a; - double w0, a0; - uint32_t hx; - int big, sign; - - GET_HIGH_WORD(hx,x); - big = (hx&0x7fffffff) >= 0x3FE59428; /* |x| >= 0.6744 */ - if (big) { - sign = hx>>31; - if (sign) { - x = -x; - y = -y; - } - x = (pio4 - x) + (pio4lo - y); - y = 0.0; - } - z = x * x; - w = z * z; - /* - * Break x^5*(T[1]+x^2*T[2]+...) into - * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + - * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) - */ - r = T[1] + w*(T[3] + w*(T[5] + w*(T[7] + w*(T[9] + w*T[11])))); - v = z*(T[2] + w*(T[4] + w*(T[6] + w*(T[8] + w*(T[10] + w*T[12]))))); - s = z * x; - r = y + z*(s*(r + v) + y) + s*T[0]; - w = x + r; - if (big) { - s = 1 - 2*odd; - v = s - 2.0 * (x + (r - w*w/(w + s))); - return sign ? -v : v; - } - if (!odd) - return w; - /* -1.0/(x+r) has up to 2ulp error, so compute it accurately */ - w0 = w; - SET_LOW_WORD(w0, 0); - v = r - (w0 - x); /* w0+v = r+x */ - a0 = a = -1.0 / w; - SET_LOW_WORD(a0, 0); - return a0 + a*(1.0 + a0*w0 + a0*v); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/acos.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/acos.c deleted file mode 100644 index 6104a32b..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/acos.c +++ /dev/null @@ -1,101 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* acos(x) - * Method : - * acos(x) = pi/2 - asin(x) - * acos(-x) = pi/2 + asin(x) - * For |x|<=0.5 - * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c) - * For x>0.5 - * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2))) - * = 2asin(sqrt((1-x)/2)) - * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z) - * = 2f + (2c + 2s*z*R(z)) - * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term - * for f so that f+c ~ sqrt(z). - * For x<-0.5 - * acos(x) = pi - 2asin(sqrt((1-|x|)/2)) - * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z) - * - * Special cases: - * if x is NaN, return x itself; - * if |x|>1, return NaN with invalid signal. - * - * Function needed: sqrt - */ - -#include "libm.h" - -static const double -pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ -pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ -pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ -pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ -pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ -pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ -pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ -pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ -qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ -qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ -qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ -qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ - -static double R(double z) -{ - double_t p, q; - p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); - q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4))); - return p/q; -} - -double acos(double x) -{ - double z,w,s,c,df; - uint32_t hx,ix; - - GET_HIGH_WORD(hx, x); - ix = hx & 0x7fffffff; - /* |x| >= 1 or nan */ - if (ix >= 0x3ff00000) { - uint32_t lx; - - GET_LOW_WORD(lx,x); - if (((ix-0x3ff00000) | lx) == 0) { - /* acos(1)=0, acos(-1)=pi */ - if (hx >> 31) - return 2*pio2_hi + 0x1p-120f; - return 0; - } - return 0/(x-x); - } - /* |x| < 0.5 */ - if (ix < 0x3fe00000) { - if (ix <= 0x3c600000) /* |x| < 2**-57 */ - return pio2_hi + 0x1p-120f; - return pio2_hi - (x - (pio2_lo-x*R(x*x))); - } - /* x < -0.5 */ - if (hx >> 31) { - z = (1.0+x)*0.5; - s = sqrt(z); - w = R(z)*s-pio2_lo; - return 2*(pio2_hi - (s+w)); - } - /* x > 0.5 */ - z = (1.0-x)*0.5; - s = sqrt(z); - df = s; - SET_LOW_WORD(df,0); - c = (z-df*df)/(s+df); - w = R(z)*s+c; - return 2*(df+w); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/acosh.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/acosh.c deleted file mode 100644 index badbf908..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/acosh.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "libm.h" - -#if FLT_EVAL_METHOD==2 -#undef sqrt -#define sqrt sqrtl -#endif - -/* acosh(x) = log(x + sqrt(x*x-1)) */ -double acosh(double x) -{ - union {double f; uint64_t i;} u = {.f = x}; - unsigned e = u.i >> 52 & 0x7ff; - - /* x < 1 domain error is handled in the called functions */ - - if (e < 0x3ff + 1) - /* |x| < 2, up to 2ulp error in [1,1.125] */ - return log1p(x-1 + sqrt((x-1)*(x-1)+2*(x-1))); - if (e < 0x3ff + 26) - /* |x| < 0x1p26 */ - return log(2*x - 1/(x+sqrt(x*x-1))); - /* |x| >= 0x1p26 or nan */ - return log(x) + 0.693147180559945309417232121458176568; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/asin.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/asin.c deleted file mode 100644 index 96b4cdfa..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/asin.c +++ /dev/null @@ -1,107 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* asin(x) - * Method : - * Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ... - * we approximate asin(x) on [0,0.5] by - * asin(x) = x + x*x^2*R(x^2) - * where - * R(x^2) is a rational approximation of (asin(x)-x)/x^3 - * and its remez error is bounded by - * |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75) - * - * For x in [0.5,1] - * asin(x) = pi/2-2*asin(sqrt((1-x)/2)) - * Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2; - * then for x>0.98 - * asin(x) = pi/2 - 2*(s+s*z*R(z)) - * = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) - * For x<=0.98, let pio4_hi = pio2_hi/2, then - * f = hi part of s; - * c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) - * and - * asin(x) = pi/2 - 2*(s+s*z*R(z)) - * = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) - * = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) - * - * Special cases: - * if x is NaN, return x itself; - * if |x|>1, return NaN with invalid signal. - * - */ - -#include "libm.h" - -static const double -pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ -pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ -/* coefficients for R(x^2) */ -pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ -pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ -pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ -pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ -pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ -pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ -qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ -qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ -qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ -qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ - -static double R(double z) -{ - double_t p, q; - p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); - q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4))); - return p/q; -} - -double asin(double x) -{ - double z,r,s; - uint32_t hx,ix; - - GET_HIGH_WORD(hx, x); - ix = hx & 0x7fffffff; - /* |x| >= 1 or nan */ - if (ix >= 0x3ff00000) { - uint32_t lx; - GET_LOW_WORD(lx, x); - if (((ix-0x3ff00000) | lx) == 0) - /* asin(1) = +-pi/2 with inexact */ - return x*pio2_hi + 0x1p-120f; - return 0/(x-x); - } - /* |x| < 0.5 */ - if (ix < 0x3fe00000) { - /* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */ - if (ix < 0x3e500000 && ix >= 0x00100000) - return x; - return x + x*R(x*x); - } - /* 1 > |x| >= 0.5 */ - z = (1 - fabs(x))*0.5; - s = sqrt(z); - r = R(z); - if (ix >= 0x3fef3333) { /* if |x| > 0.975 */ - x = pio2_hi-(2*(s+s*r)-pio2_lo); - } else { - double f,c; - /* f+c = sqrt(z) */ - f = s; - SET_LOW_WORD(f,0); - c = (z-f*f)/(s+f); - x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f)); - } - if (hx >> 31) - return -x; - return x; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/asinh.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/asinh.c deleted file mode 100644 index 0829f228..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/asinh.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "libm.h" - -/* asinh(x) = sign(x)*log(|x|+sqrt(x*x+1)) ~= x - x^3/6 + o(x^5) */ -double asinh(double x) -{ - union {double f; uint64_t i;} u = {.f = x}; - unsigned e = u.i >> 52 & 0x7ff; - unsigned s = u.i >> 63; - - /* |x| */ - u.i &= (uint64_t)-1/2; - x = u.f; - - if (e >= 0x3ff + 26) { - /* |x| >= 0x1p26 or inf or nan */ - x = log(x) + 0.693147180559945309417232121458176568; - } else if (e >= 0x3ff + 1) { - /* |x| >= 2 */ - x = log(2*x + 1/(sqrt(x*x+1)+x)); - } else if (e >= 0x3ff - 26) { - /* |x| >= 0x1p-26, up to 1.6ulp error in [0.125,0.5] */ - x = log1p(x + x*x/(sqrt(x*x+1)+1)); - } else { - /* |x| < 0x1p-26, raise inexact if x != 0 */ - FORCE_EVAL(x + 0x1p120f); - } - return s ? -x : x; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/atan.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/atan.c deleted file mode 100644 index 63b0ab25..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/atan.c +++ /dev/null @@ -1,116 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* atan(x) - * Method - * 1. Reduce x to positive by atan(x) = -atan(-x). - * 2. According to the integer k=4t+0.25 chopped, t=x, the argument - * is further reduced to one of the following intervals and the - * arctangent of t is evaluated by the corresponding formula: - * - * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) - * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) - * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) - * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) - * [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) - * - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ - - -#include "libm.h" - -static const double atanhi[] = { - 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */ - 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */ - 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */ - 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */ -}; - -static const double atanlo[] = { - 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */ - 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */ - 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */ - 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */ -}; - -static const double aT[] = { - 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */ - -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */ - 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */ - -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */ - 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */ - -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */ - 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */ - -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */ - 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */ - -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */ - 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */ -}; - -double atan(double x) -{ - double_t w,s1,s2,z; - uint32_t ix,sign; - int id; - - GET_HIGH_WORD(ix, x); - sign = ix >> 31; - ix &= 0x7fffffff; - if (ix >= 0x44100000) { /* if |x| >= 2^66 */ - if (isnan(x)) - return x; - z = atanhi[3] + 0x1p-120f; - return sign ? -z : z; - } - if (ix < 0x3fdc0000) { /* |x| < 0.4375 */ - if (ix < 0x3e400000) { /* |x| < 2^-27 */ - if (ix < 0x00100000) - /* raise underflow for subnormal x */ - FORCE_EVAL((float)x); - return x; - } - id = -1; - } else { - x = fabs(x); - if (ix < 0x3ff30000) { /* |x| < 1.1875 */ - if (ix < 0x3fe60000) { /* 7/16 <= |x| < 11/16 */ - id = 0; - x = (2.0*x-1.0)/(2.0+x); - } else { /* 11/16 <= |x| < 19/16 */ - id = 1; - x = (x-1.0)/(x+1.0); - } - } else { - if (ix < 0x40038000) { /* |x| < 2.4375 */ - id = 2; - x = (x-1.5)/(1.0+1.5*x); - } else { /* 2.4375 <= |x| < 2^66 */ - id = 3; - x = -1.0/x; - } - } - } - /* end of argument reduction */ - z = x*x; - w = z*z; - /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ - s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10]))))); - s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9])))); - if (id < 0) - return x - x*(s1+s2); - z = atanhi[id] - (x*(s1+s2) - atanlo[id] - x); - return sign ? -z : z; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/atan2.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/atan2.c deleted file mode 100644 index 91378b97..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/atan2.c +++ /dev/null @@ -1,107 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - * - */ -/* atan2(y,x) - * Method : - * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x). - * 2. Reduce x to positive by (if x and y are unexceptional): - * ARG (x+iy) = arctan(y/x) ... if x > 0, - * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, - * - * Special cases: - * - * ATAN2((anything), NaN ) is NaN; - * ATAN2(NAN , (anything) ) is NaN; - * ATAN2(+-0, +(anything but NaN)) is +-0 ; - * ATAN2(+-0, -(anything but NaN)) is +-pi ; - * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; - * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; - * ATAN2(+-(anything but INF and NaN), -INF) is +-pi; - * ATAN2(+-INF,+INF ) is +-pi/4 ; - * ATAN2(+-INF,-INF ) is +-3pi/4; - * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; - * - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ - -#include "libm.h" - -static const double -pi = 3.1415926535897931160E+00, /* 0x400921FB, 0x54442D18 */ -pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ - -double atan2(double y, double x) -{ - double z; - uint32_t m,lx,ly,ix,iy; - - if (isnan(x) || isnan(y)) - return x+y; - EXTRACT_WORDS(ix, lx, x); - EXTRACT_WORDS(iy, ly, y); - if (((ix-0x3ff00000) | lx) == 0) /* x = 1.0 */ - return atan(y); - m = ((iy>>31)&1) | ((ix>>30)&2); /* 2*sign(x)+sign(y) */ - ix = ix & 0x7fffffff; - iy = iy & 0x7fffffff; - - /* when y = 0 */ - if ((iy|ly) == 0) { - switch(m) { - case 0: - case 1: return y; /* atan(+-0,+anything)=+-0 */ - case 2: return pi; /* atan(+0,-anything) = pi */ - case 3: return -pi; /* atan(-0,-anything) =-pi */ - } - } - /* when x = 0 */ - if ((ix|lx) == 0) - return m&1 ? -pi/2 : pi/2; - /* when x is INF */ - if (ix == 0x7ff00000) { - if (iy == 0x7ff00000) { - switch(m) { - case 0: return pi/4; /* atan(+INF,+INF) */ - case 1: return -pi/4; /* atan(-INF,+INF) */ - case 2: return 3*pi/4; /* atan(+INF,-INF) */ - case 3: return -3*pi/4; /* atan(-INF,-INF) */ - } - } else { - switch(m) { - case 0: return 0.0; /* atan(+...,+INF) */ - case 1: return -0.0; /* atan(-...,+INF) */ - case 2: return pi; /* atan(+...,-INF) */ - case 3: return -pi; /* atan(-...,-INF) */ - } - } - } - /* |y/x| > 0x1p64 */ - if (ix+(64<<20) < iy || iy == 0x7ff00000) - return m&1 ? -pi/2 : pi/2; - - /* z = atan(|y/x|) without spurious underflow */ - if ((m&2) && iy+(64<<20) < ix) /* |y/x| < 0x1p-64, x<0 */ - z = 0; - else - z = atan(fabs(y/x)); - switch (m) { - case 0: return z; /* atan(+,+) */ - case 1: return -z; /* atan(-,+) */ - case 2: return pi - (z-pi_lo); /* atan(+,-) */ - default: /* case 3 */ - return (z-pi_lo) - pi; /* atan(-,-) */ - } -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/atanh.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/atanh.c deleted file mode 100644 index 63a035d7..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/atanh.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "libm.h" - -/* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */ -double atanh(double x) -{ - union {double f; uint64_t i;} u = {.f = x}; - unsigned e = u.i >> 52 & 0x7ff; - unsigned s = u.i >> 63; - double_t y; - - /* |x| */ - u.i &= (uint64_t)-1/2; - y = u.f; - - if (e < 0x3ff - 1) { - if (e < 0x3ff - 32) { - /* handle underflow */ - if (e == 0) - FORCE_EVAL((float)y); - } else { - /* |x| < 0.5, up to 1.7ulp error */ - y = 0.5*log1p(2*y + 2*y*y/(1-y)); - } - } else { - /* avoid overflow */ - y = 0.5*log1p(2*(y/(1-y))); - } - return s ? -y : y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/ceil.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/ceil.c deleted file mode 100644 index b13e6f2d..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/ceil.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "libm.h" - -#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 -#define EPS DBL_EPSILON -#elif FLT_EVAL_METHOD==2 -#define EPS LDBL_EPSILON -#endif -static const double_t toint = 1/EPS; - -double ceil(double x) -{ - union {double f; uint64_t i;} u = {x}; - int e = u.i >> 52 & 0x7ff; - double_t y; - - if (e >= 0x3ff+52 || x == 0) - return x; - /* y = int(x) - x, where int(x) is an integer neighbor of x */ - if (u.i >> 63) - y = x - toint + toint - x; - else - y = x + toint - toint - x; - /* special case because of non-nearest rounding modes */ - if (e <= 0x3ff-1) { - FORCE_EVAL(y); - return u.i >> 63 ? -0.0 : 1; - } - if (y < 0) - return x + y + 1; - return x + y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/cos.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/cos.c deleted file mode 100644 index ee97f68b..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/cos.c +++ /dev/null @@ -1,77 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* cos(x) - * Return cosine function of x. - * - * kernel function: - * __sin ... sine function on [-pi/4,pi/4] - * __cos ... cosine function on [-pi/4,pi/4] - * __rem_pio2 ... argument reduction routine - * - * Method. - * Let S,C and T denote the sin, cos and tan respectively on - * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 - * in [-pi/4 , +pi/4], and let n = k mod 4. - * We have - * - * n sin(x) cos(x) tan(x) - * ---------------------------------------------------------- - * 0 S C T - * 1 C -S -1/T - * 2 -S -C T - * 3 -C S -1/T - * ---------------------------------------------------------- - * - * Special cases: - * Let trig be any of sin, cos, or tan. - * trig(+-INF) is NaN, with signals; - * trig(NaN) is that NaN; - * - * Accuracy: - * TRIG(x) returns trig(x) nearly rounded - */ - -#include "libm.h" - -double cos(double x) -{ - double y[2]; - uint32_t ix; - unsigned n; - - GET_HIGH_WORD(ix, x); - ix &= 0x7fffffff; - - /* |x| ~< pi/4 */ - if (ix <= 0x3fe921fb) { - if (ix < 0x3e46a09e) { /* |x| < 2**-27 * sqrt(2) */ - /* raise inexact if x!=0 */ - FORCE_EVAL(x + 0x1p120f); - return 1.0; - } - return __cos(x, 0); - } - - /* cos(Inf or NaN) is NaN */ - if (ix >= 0x7ff00000) - return x-x; - - /* argument reduction */ - n = __rem_pio2(x, y); - switch (n&3) { - case 0: return __cos(y[0], y[1]); - case 1: return -__sin(y[0], y[1], 1); - case 2: return -__cos(y[0], y[1]); - default: - return __sin(y[0], y[1], 1); - } -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/cosh.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/cosh.c deleted file mode 100644 index 100f8231..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/cosh.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "libm.h" - -/* cosh(x) = (exp(x) + 1/exp(x))/2 - * = 1 + 0.5*(exp(x)-1)*(exp(x)-1)/exp(x) - * = 1 + x*x/2 + o(x^4) - */ -double cosh(double x) -{ - union {double f; uint64_t i;} u = {.f = x}; - uint32_t w; - double t; - - /* |x| */ - u.i &= (uint64_t)-1/2; - x = u.f; - w = u.i >> 32; - - /* |x| < log(2) */ - if (w < 0x3fe62e42) { - if (w < 0x3ff00000 - (26<<20)) { - /* raise inexact if x!=0 */ - FORCE_EVAL(x + 0x1p120f); - return 1; - } - t = expm1(x); - return 1 + t*t/(2*(1+t)); - } - - /* |x| < log(DBL_MAX) */ - if (w < 0x40862e42) { - t = exp(x); - /* note: if x>log(0x1p26) then the 1/t is not needed */ - return 0.5*(t + 1/t); - } - - /* |x| > log(DBL_MAX) or nan */ - /* note: the result is stored to handle overflow */ - t = __expo2(x); - return t; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/erf.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/erf.c deleted file mode 100644 index 2f30a298..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/erf.c +++ /dev/null @@ -1,273 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_erf.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* double erf(double x) - * double erfc(double x) - * x - * 2 |\ - * erf(x) = --------- | exp(-t*t)dt - * sqrt(pi) \| - * 0 - * - * erfc(x) = 1-erf(x) - * Note that - * erf(-x) = -erf(x) - * erfc(-x) = 2 - erfc(x) - * - * Method: - * 1. For |x| in [0, 0.84375] - * erf(x) = x + x*R(x^2) - * erfc(x) = 1 - erf(x) if x in [-.84375,0.25] - * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375] - * where R = P/Q where P is an odd poly of degree 8 and - * Q is an odd poly of degree 10. - * -57.90 - * | R - (erf(x)-x)/x | <= 2 - * - * - * Remark. The formula is derived by noting - * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....) - * and that - * 2/sqrt(pi) = 1.128379167095512573896158903121545171688 - * is close to one. The interval is chosen because the fix - * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is - * near 0.6174), and by some experiment, 0.84375 is chosen to - * guarantee the error is less than one ulp for erf. - * - * 2. For |x| in [0.84375,1.25], let s = |x| - 1, and - * c = 0.84506291151 rounded to single (24 bits) - * erf(x) = sign(x) * (c + P1(s)/Q1(s)) - * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0 - * 1+(c+P1(s)/Q1(s)) if x < 0 - * |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06 - * Remark: here we use the taylor series expansion at x=1. - * erf(1+s) = erf(1) + s*Poly(s) - * = 0.845.. + P1(s)/Q1(s) - * That is, we use rational approximation to approximate - * erf(1+s) - (c = (single)0.84506291151) - * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] - * where - * P1(s) = degree 6 poly in s - * Q1(s) = degree 6 poly in s - * - * 3. For x in [1.25,1/0.35(~2.857143)], - * erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1) - * erf(x) = 1 - erfc(x) - * where - * R1(z) = degree 7 poly in z, (z=1/x^2) - * S1(z) = degree 8 poly in z - * - * 4. For x in [1/0.35,28] - * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0 - * = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 x >= 28 - * erf(x) = sign(x) *(1 - tiny) (raise inexact) - * erfc(x) = tiny*tiny (raise underflow) if x > 0 - * = 2 - tiny if x<0 - * - * 7. Special case: - * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, - * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, - * erfc/erf(NaN) is NaN - */ - -#include "libm.h" - -static const double -erx = 8.45062911510467529297e-01, /* 0x3FEB0AC1, 0x60000000 */ -/* - * Coefficients for approximation to erf on [0,0.84375] - */ -efx8 = 1.02703333676410069053e+00, /* 0x3FF06EBA, 0x8214DB69 */ -pp0 = 1.28379167095512558561e-01, /* 0x3FC06EBA, 0x8214DB68 */ -pp1 = -3.25042107247001499370e-01, /* 0xBFD4CD7D, 0x691CB913 */ -pp2 = -2.84817495755985104766e-02, /* 0xBF9D2A51, 0xDBD7194F */ -pp3 = -5.77027029648944159157e-03, /* 0xBF77A291, 0x236668E4 */ -pp4 = -2.37630166566501626084e-05, /* 0xBEF8EAD6, 0x120016AC */ -qq1 = 3.97917223959155352819e-01, /* 0x3FD97779, 0xCDDADC09 */ -qq2 = 6.50222499887672944485e-02, /* 0x3FB0A54C, 0x5536CEBA */ -qq3 = 5.08130628187576562776e-03, /* 0x3F74D022, 0xC4D36B0F */ -qq4 = 1.32494738004321644526e-04, /* 0x3F215DC9, 0x221C1A10 */ -qq5 = -3.96022827877536812320e-06, /* 0xBED09C43, 0x42A26120 */ -/* - * Coefficients for approximation to erf in [0.84375,1.25] - */ -pa0 = -2.36211856075265944077e-03, /* 0xBF6359B8, 0xBEF77538 */ -pa1 = 4.14856118683748331666e-01, /* 0x3FDA8D00, 0xAD92B34D */ -pa2 = -3.72207876035701323847e-01, /* 0xBFD7D240, 0xFBB8C3F1 */ -pa3 = 3.18346619901161753674e-01, /* 0x3FD45FCA, 0x805120E4 */ -pa4 = -1.10894694282396677476e-01, /* 0xBFBC6398, 0x3D3E28EC */ -pa5 = 3.54783043256182359371e-02, /* 0x3FA22A36, 0x599795EB */ -pa6 = -2.16637559486879084300e-03, /* 0xBF61BF38, 0x0A96073F */ -qa1 = 1.06420880400844228286e-01, /* 0x3FBB3E66, 0x18EEE323 */ -qa2 = 5.40397917702171048937e-01, /* 0x3FE14AF0, 0x92EB6F33 */ -qa3 = 7.18286544141962662868e-02, /* 0x3FB2635C, 0xD99FE9A7 */ -qa4 = 1.26171219808761642112e-01, /* 0x3FC02660, 0xE763351F */ -qa5 = 1.36370839120290507362e-02, /* 0x3F8BEDC2, 0x6B51DD1C */ -qa6 = 1.19844998467991074170e-02, /* 0x3F888B54, 0x5735151D */ -/* - * Coefficients for approximation to erfc in [1.25,1/0.35] - */ -ra0 = -9.86494403484714822705e-03, /* 0xBF843412, 0x600D6435 */ -ra1 = -6.93858572707181764372e-01, /* 0xBFE63416, 0xE4BA7360 */ -ra2 = -1.05586262253232909814e+01, /* 0xC0251E04, 0x41B0E726 */ -ra3 = -6.23753324503260060396e+01, /* 0xC04F300A, 0xE4CBA38D */ -ra4 = -1.62396669462573470355e+02, /* 0xC0644CB1, 0x84282266 */ -ra5 = -1.84605092906711035994e+02, /* 0xC067135C, 0xEBCCABB2 */ -ra6 = -8.12874355063065934246e+01, /* 0xC0545265, 0x57E4D2F2 */ -ra7 = -9.81432934416914548592e+00, /* 0xC023A0EF, 0xC69AC25C */ -sa1 = 1.96512716674392571292e+01, /* 0x4033A6B9, 0xBD707687 */ -sa2 = 1.37657754143519042600e+02, /* 0x4061350C, 0x526AE721 */ -sa3 = 4.34565877475229228821e+02, /* 0x407B290D, 0xD58A1A71 */ -sa4 = 6.45387271733267880336e+02, /* 0x40842B19, 0x21EC2868 */ -sa5 = 4.29008140027567833386e+02, /* 0x407AD021, 0x57700314 */ -sa6 = 1.08635005541779435134e+02, /* 0x405B28A3, 0xEE48AE2C */ -sa7 = 6.57024977031928170135e+00, /* 0x401A47EF, 0x8E484A93 */ -sa8 = -6.04244152148580987438e-02, /* 0xBFAEEFF2, 0xEE749A62 */ -/* - * Coefficients for approximation to erfc in [1/.35,28] - */ -rb0 = -9.86494292470009928597e-03, /* 0xBF843412, 0x39E86F4A */ -rb1 = -7.99283237680523006574e-01, /* 0xBFE993BA, 0x70C285DE */ -rb2 = -1.77579549177547519889e+01, /* 0xC031C209, 0x555F995A */ -rb3 = -1.60636384855821916062e+02, /* 0xC064145D, 0x43C5ED98 */ -rb4 = -6.37566443368389627722e+02, /* 0xC083EC88, 0x1375F228 */ -rb5 = -1.02509513161107724954e+03, /* 0xC0900461, 0x6A2E5992 */ -rb6 = -4.83519191608651397019e+02, /* 0xC07E384E, 0x9BDC383F */ -sb1 = 3.03380607434824582924e+01, /* 0x403E568B, 0x261D5190 */ -sb2 = 3.25792512996573918826e+02, /* 0x40745CAE, 0x221B9F0A */ -sb3 = 1.53672958608443695994e+03, /* 0x409802EB, 0x189D5118 */ -sb4 = 3.19985821950859553908e+03, /* 0x40A8FFB7, 0x688C246A */ -sb5 = 2.55305040643316442583e+03, /* 0x40A3F219, 0xCEDF3BE6 */ -sb6 = 4.74528541206955367215e+02, /* 0x407DA874, 0xE79FE763 */ -sb7 = -2.24409524465858183362e+01; /* 0xC03670E2, 0x42712D62 */ - -static double erfc1(double x) -{ - double_t s,P,Q; - - s = fabs(x) - 1; - P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); - Q = 1+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); - return 1 - erx - P/Q; -} - -static double erfc2(uint32_t ix, double x) -{ - double_t s,R,S; - double z; - - if (ix < 0x3ff40000) /* |x| < 1.25 */ - return erfc1(x); - - x = fabs(x); - s = 1/(x*x); - if (ix < 0x4006db6d) { /* |x| < 1/.35 ~ 2.85714 */ - R = ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( - ra5+s*(ra6+s*ra7)))))); - S = 1.0+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( - sa5+s*(sa6+s*(sa7+s*sa8))))))); - } else { /* |x| > 1/.35 */ - R = rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( - rb5+s*rb6))))); - S = 1.0+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( - sb5+s*(sb6+s*sb7)))))); - } - z = x; - SET_LOW_WORD(z,0); - return exp(-z*z-0.5625)*exp((z-x)*(z+x)+R/S)/x; -} - -double erf(double x) -{ - double r,s,z,y; - uint32_t ix; - int sign; - - GET_HIGH_WORD(ix, x); - sign = ix>>31; - ix &= 0x7fffffff; - if (ix >= 0x7ff00000) { - /* erf(nan)=nan, erf(+-inf)=+-1 */ - return 1-2*sign + 1/x; - } - if (ix < 0x3feb0000) { /* |x| < 0.84375 */ - if (ix < 0x3e300000) { /* |x| < 2**-28 */ - /* avoid underflow */ - return 0.125*(8*x + efx8*x); - } - z = x*x; - r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); - s = 1.0+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); - y = r/s; - return x + x*y; - } - if (ix < 0x40180000) /* 0.84375 <= |x| < 6 */ - y = 1 - erfc2(ix,x); - else - y = 1 - 0x1p-1022; - return sign ? -y : y; -} - -double erfc(double x) -{ - double r,s,z,y; - uint32_t ix; - int sign; - - GET_HIGH_WORD(ix, x); - sign = ix>>31; - ix &= 0x7fffffff; - if (ix >= 0x7ff00000) { - /* erfc(nan)=nan, erfc(+-inf)=0,2 */ - return 2*sign + 1/x; - } - if (ix < 0x3feb0000) { /* |x| < 0.84375 */ - if (ix < 0x3c700000) /* |x| < 2**-56 */ - return 1.0 - x; - z = x*x; - r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); - s = 1.0+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); - y = r/s; - if (sign || ix < 0x3fd00000) { /* x < 1/4 */ - return 1.0 - (x+x*y); - } - return 0.5 - (x - 0.5 + x*y); - } - if (ix < 0x403c0000) { /* 0.84375 <= |x| < 28 */ - return sign ? 2 - erfc2(ix,x) : erfc2(ix,x); - } - return sign ? 2 - 0x1p-1022 : 0x1p-1022*0x1p-1022; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/exp.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/exp.c deleted file mode 100644 index 9ea672fa..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/exp.c +++ /dev/null @@ -1,134 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_exp.c */ -/* - * ==================================================== - * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. - * - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* exp(x) - * Returns the exponential of x. - * - * Method - * 1. Argument reduction: - * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658. - * Given x, find r and integer k such that - * - * x = k*ln2 + r, |r| <= 0.5*ln2. - * - * Here r will be represented as r = hi-lo for better - * accuracy. - * - * 2. Approximation of exp(r) by a special rational function on - * the interval [0,0.34658]: - * Write - * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ... - * We use a special Remez algorithm on [0,0.34658] to generate - * a polynomial of degree 5 to approximate R. The maximum error - * of this polynomial approximation is bounded by 2**-59. In - * other words, - * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5 - * (where z=r*r, and the values of P1 to P5 are listed below) - * and - * | 5 | -59 - * | 2.0+P1*z+...+P5*z - R(z) | <= 2 - * | | - * The computation of exp(r) thus becomes - * 2*r - * exp(r) = 1 + ---------- - * R(r) - r - * r*c(r) - * = 1 + r + ----------- (for better accuracy) - * 2 - c(r) - * where - * 2 4 10 - * c(r) = r - (P1*r + P2*r + ... + P5*r ). - * - * 3. Scale back to obtain exp(x): - * From step 1, we have - * exp(x) = 2^k * exp(r) - * - * Special cases: - * exp(INF) is INF, exp(NaN) is NaN; - * exp(-INF) is 0, and - * for finite argument, only exp(0)=1 is exact. - * - * Accuracy: - * according to an error analysis, the error is always less than - * 1 ulp (unit in the last place). - * - * Misc. info. - * For IEEE double - * if x > 709.782712893383973096 then exp(x) overflows - * if x < -745.133219101941108420 then exp(x) underflows - */ - -#include "libm.h" - -static const double -half[2] = {0.5,-0.5}, -ln2hi = 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */ -ln2lo = 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */ -invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */ -P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ -P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ -P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ -P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ -P5 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ - -double exp(double x) -{ - double_t hi, lo, c, xx, y; - int k, sign; - uint32_t hx; - - GET_HIGH_WORD(hx, x); - sign = hx>>31; - hx &= 0x7fffffff; /* high word of |x| */ - - /* special cases */ - if (hx >= 0x4086232b) { /* if |x| >= 708.39... */ - if (isnan(x)) - return x; - if (x > 709.782712893383973096) { - /* overflow if x!=inf */ - x *= 0x1p1023; - return x; - } - if (x < -708.39641853226410622) { - /* underflow if x!=-inf */ - FORCE_EVAL((float)(-0x1p-149/x)); - if (x < -745.13321910194110842) - return 0; - } - } - - /* argument reduction */ - if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ - if (hx >= 0x3ff0a2b2) /* if |x| >= 1.5 ln2 */ - k = (int)(invln2*x + half[sign]); - else - k = 1 - sign - sign; - hi = x - k*ln2hi; /* k*ln2hi is exact here */ - lo = k*ln2lo; - x = hi - lo; - } else if (hx > 0x3e300000) { /* if |x| > 2**-28 */ - k = 0; - hi = x; - lo = 0; - } else { - /* inexact if x!=0 */ - FORCE_EVAL(0x1p1023 + x); - return 1 + x; - } - - /* x is now in primary range */ - xx = x*x; - c = x - xx*(P1+xx*(P2+xx*(P3+xx*(P4+xx*P5)))); - y = 1 + (x*c/(2-c) - lo + hi); - if (k == 0) - return y; - return scalbn(y, k); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/expm1.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/expm1.c deleted file mode 100644 index ac1e61e4..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/expm1.c +++ /dev/null @@ -1,201 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_expm1.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* expm1(x) - * Returns exp(x)-1, the exponential of x minus 1. - * - * Method - * 1. Argument reduction: - * Given x, find r and integer k such that - * - * x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658 - * - * Here a correction term c will be computed to compensate - * the error in r when rounded to a floating-point number. - * - * 2. Approximating expm1(r) by a special rational function on - * the interval [0,0.34658]: - * Since - * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 - r^4/360 + ... - * we define R1(r*r) by - * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 * R1(r*r) - * That is, - * R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) - * = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) - * = 1 - r^2/60 + r^4/2520 - r^6/100800 + ... - * We use a special Remez algorithm on [0,0.347] to generate - * a polynomial of degree 5 in r*r to approximate R1. The - * maximum error of this polynomial approximation is bounded - * by 2**-61. In other words, - * R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 - * where Q1 = -1.6666666666666567384E-2, - * Q2 = 3.9682539681370365873E-4, - * Q3 = -9.9206344733435987357E-6, - * Q4 = 2.5051361420808517002E-7, - * Q5 = -6.2843505682382617102E-9; - * z = r*r, - * with error bounded by - * | 5 | -61 - * | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 - * | | - * - * expm1(r) = exp(r)-1 is then computed by the following - * specific way which minimize the accumulation rounding error: - * 2 3 - * r r [ 3 - (R1 + R1*r/2) ] - * expm1(r) = r + --- + --- * [--------------------] - * 2 2 [ 6 - r*(3 - R1*r/2) ] - * - * To compensate the error in the argument reduction, we use - * expm1(r+c) = expm1(r) + c + expm1(r)*c - * ~ expm1(r) + c + r*c - * Thus c+r*c will be added in as the correction terms for - * expm1(r+c). Now rearrange the term to avoid optimization - * screw up: - * ( 2 2 ) - * ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) - * expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) - * ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) - * ( ) - * - * = r - E - * 3. Scale back to obtain expm1(x): - * From step 1, we have - * expm1(x) = either 2^k*[expm1(r)+1] - 1 - * = or 2^k*[expm1(r) + (1-2^-k)] - * 4. Implementation notes: - * (A). To save one multiplication, we scale the coefficient Qi - * to Qi*2^i, and replace z by (x^2)/2. - * (B). To achieve maximum accuracy, we compute expm1(x) by - * (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) - * (ii) if k=0, return r-E - * (iii) if k=-1, return 0.5*(r-E)-0.5 - * (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) - * else return 1.0+2.0*(r-E); - * (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1) - * (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else - * (vii) return 2^k(1-((E+2^-k)-r)) - * - * Special cases: - * expm1(INF) is INF, expm1(NaN) is NaN; - * expm1(-INF) is -1, and - * for finite argument, only expm1(0)=0 is exact. - * - * Accuracy: - * according to an error analysis, the error is always less than - * 1 ulp (unit in the last place). - * - * Misc. info. - * For IEEE double - * if x > 7.09782712893383973096e+02 then expm1(x) overflow - * - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ - -#include "libm.h" - -static const double -o_threshold = 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */ -ln2_hi = 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */ -ln2_lo = 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */ -invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */ -/* Scaled Q's: Qn_here = 2**n * Qn_above, for R(2*z) where z = hxs = x*x/2: */ -Q1 = -3.33333333333331316428e-02, /* BFA11111 111110F4 */ -Q2 = 1.58730158725481460165e-03, /* 3F5A01A0 19FE5585 */ -Q3 = -7.93650757867487942473e-05, /* BF14CE19 9EAADBB7 */ -Q4 = 4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */ -Q5 = -2.01099218183624371326e-07; /* BE8AFDB7 6E09C32D */ - -double expm1(double x) -{ - double_t y,hi,lo,c,t,e,hxs,hfx,r1,twopk; - union {double f; uint64_t i;} u = {x}; - uint32_t hx = u.i>>32 & 0x7fffffff; - int k, sign = u.i>>63; - - /* filter out huge and non-finite argument */ - if (hx >= 0x4043687A) { /* if |x|>=56*ln2 */ - if (isnan(x)) - return x; - if (sign) - return -1; - if (x > o_threshold) { - x *= 0x1p1023; - return x; - } - } - - /* argument reduction */ - if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ - if (hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ - if (!sign) { - hi = x - ln2_hi; - lo = ln2_lo; - k = 1; - } else { - hi = x + ln2_hi; - lo = -ln2_lo; - k = -1; - } - } else { - k = invln2*x + (sign ? -0.5 : 0.5); - t = k; - hi = x - t*ln2_hi; /* t*ln2_hi is exact here */ - lo = t*ln2_lo; - } - x = hi-lo; - c = (hi-x)-lo; - } else if (hx < 0x3c900000) { /* |x| < 2**-54, return x */ - if (hx < 0x00100000) - FORCE_EVAL((float)x); - return x; - } else - k = 0; - - /* x is now in primary range */ - hfx = 0.5*x; - hxs = x*hfx; - r1 = 1.0+hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5)))); - t = 3.0-r1*hfx; - e = hxs*((r1-t)/(6.0 - x*t)); - if (k == 0) /* c is 0 */ - return x - (x*e-hxs); - e = x*(e-c) - c; - e -= hxs; - /* exp(x) ~ 2^k (x_reduced - e + 1) */ - if (k == -1) - return 0.5*(x-e) - 0.5; - if (k == 1) { - if (x < -0.25) - return -2.0*(e-(x+0.5)); - return 1.0+2.0*(x-e); - } - u.i = (uint64_t)(0x3ff + k)<<52; /* 2^k */ - twopk = u.f; - if (k < 0 || k > 56) { /* suffice to return exp(x)-1 */ - y = x - e + 1.0; - if (k == 1024) - y = y*2.0*0x1p1023; - else - y = y*twopk; - return y - 1.0; - } - u.i = (uint64_t)(0x3ff - k)<<52; /* 2^-k */ - if (k < 20) - y = (x-e+(1-u.f))*twopk; - else - y = (x-(e+u.f)+1)*twopk; - return y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/floor.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/floor.c deleted file mode 100644 index 14a31cd8..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/floor.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "libm.h" - -#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 -#define EPS DBL_EPSILON -#elif FLT_EVAL_METHOD==2 -#define EPS LDBL_EPSILON -#endif -static const double_t toint = 1/EPS; - -double floor(double x) -{ - union {double f; uint64_t i;} u = {x}; - int e = u.i >> 52 & 0x7ff; - double_t y; - - if (e >= 0x3ff+52 || x == 0) - return x; - /* y = int(x) - x, where int(x) is an integer neighbor of x */ - if (u.i >> 63) - y = x - toint + toint - x; - else - y = x + toint - toint - x; - /* special case because of non-nearest rounding modes */ - if (e <= 0x3ff-1) { - FORCE_EVAL(y); - return u.i >> 63 ? -1 : 0; - } - if (y > 0) - return x + y - 1; - return x + y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/fmod.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/fmod.c deleted file mode 100644 index 6849722b..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/fmod.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include - -double fmod(double x, double y) -{ - union {double f; uint64_t i;} ux = {x}, uy = {y}; - int ex = ux.i>>52 & 0x7ff; - int ey = uy.i>>52 & 0x7ff; - int sx = ux.i>>63; - uint64_t i; - - /* in the followings uxi should be ux.i, but then gcc wrongly adds */ - /* float load/store to inner loops ruining performance and code size */ - uint64_t uxi = ux.i; - - if (uy.i<<1 == 0 || isnan(y) || ex == 0x7ff) - return (x*y)/(x*y); - if (uxi<<1 <= uy.i<<1) { - if (uxi<<1 == uy.i<<1) - return 0*x; - return x; - } - - /* normalize x and y */ - if (!ex) { - for (i = uxi<<12; i>>63 == 0; ex--, i <<= 1); - uxi <<= -ex + 1; - } else { - uxi &= -1ULL >> 12; - uxi |= 1ULL << 52; - } - if (!ey) { - for (i = uy.i<<12; i>>63 == 0; ey--, i <<= 1); - uy.i <<= -ey + 1; - } else { - uy.i &= -1ULL >> 12; - uy.i |= 1ULL << 52; - } - - /* x mod y */ - for (; ex > ey; ex--) { - i = uxi - uy.i; - if (i >> 63 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - uxi <<= 1; - } - i = uxi - uy.i; - if (i >> 63 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - for (; uxi>>52 == 0; uxi <<= 1, ex--); - - /* scale result */ - if (ex > 0) { - uxi -= 1ULL << 52; - uxi |= (uint64_t)ex << 52; - } else { - uxi >>= -ex + 1; - } - uxi |= (uint64_t)sx << 63; - ux.i = uxi; - return ux.f; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/frexp.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/frexp.c deleted file mode 100644 index 27b6266e..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/frexp.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include - -double frexp(double x, int *e) -{ - union { double d; uint64_t i; } y = { x }; - int ee = y.i>>52 & 0x7ff; - - if (!ee) { - if (x) { - x = frexp(x*0x1p64, e); - *e -= 64; - } else *e = 0; - return x; - } else if (ee == 0x7ff) { - return x; - } - - *e = ee - 0x3fe; - y.i &= 0x800fffffffffffffull; - y.i |= 0x3fe0000000000000ull; - return y.d; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/ldexp.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/ldexp.c deleted file mode 100644 index f4d1cd6a..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/ldexp.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -double ldexp(double x, int n) -{ - return scalbn(x, n); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/lgamma.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/lgamma.c deleted file mode 100644 index ed193da1..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/lgamma.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -double __lgamma_r(double, int*); - -double lgamma(double x) { - int sign; - return __lgamma_r(x, &sign); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/libm.h b/MicroPython_BUILD/components/micropython/lib/libm_dbl/libm.h deleted file mode 100644 index dc0b431a..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/libm.h +++ /dev/null @@ -1,96 +0,0 @@ -// Portions of this file are extracted from musl-1.1.16 src/internal/libm.h - -/* origin: FreeBSD /usr/src/lib/msun/src/math_private.h */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#include -#include - -#define FLT_EVAL_METHOD 0 - -#define FORCE_EVAL(x) do { \ - if (sizeof(x) == sizeof(float)) { \ - volatile float __x; \ - __x = (x); \ - (void)__x; \ - } else if (sizeof(x) == sizeof(double)) { \ - volatile double __x; \ - __x = (x); \ - (void)__x; \ - } else { \ - volatile long double __x; \ - __x = (x); \ - (void)__x; \ - } \ -} while(0) - -/* Get two 32 bit ints from a double. */ -#define EXTRACT_WORDS(hi,lo,d) \ -do { \ - union {double f; uint64_t i;} __u; \ - __u.f = (d); \ - (hi) = __u.i >> 32; \ - (lo) = (uint32_t)__u.i; \ -} while (0) - -/* Get the more significant 32 bit int from a double. */ -#define GET_HIGH_WORD(hi,d) \ -do { \ - union {double f; uint64_t i;} __u; \ - __u.f = (d); \ - (hi) = __u.i >> 32; \ -} while (0) - -/* Get the less significant 32 bit int from a double. */ -#define GET_LOW_WORD(lo,d) \ -do { \ - union {double f; uint64_t i;} __u; \ - __u.f = (d); \ - (lo) = (uint32_t)__u.i; \ -} while (0) - -/* Set a double from two 32 bit ints. */ -#define INSERT_WORDS(d,hi,lo) \ -do { \ - union {double f; uint64_t i;} __u; \ - __u.i = ((uint64_t)(hi)<<32) | (uint32_t)(lo); \ - (d) = __u.f; \ -} while (0) - -/* Set the more significant 32 bits of a double from an int. */ -#define SET_HIGH_WORD(d,hi) \ -do { \ - union {double f; uint64_t i;} __u; \ - __u.f = (d); \ - __u.i &= 0xffffffff; \ - __u.i |= (uint64_t)(hi) << 32; \ - (d) = __u.f; \ -} while (0) - -/* Set the less significant 32 bits of a double from an int. */ -#define SET_LOW_WORD(d,lo) \ -do { \ - union {double f; uint64_t i;} __u; \ - __u.f = (d); \ - __u.i &= 0xffffffff00000000ull; \ - __u.i |= (uint32_t)(lo); \ - (d) = __u.f; \ -} while (0) - -#define DBL_EPSILON 2.22044604925031308085e-16 - -int __rem_pio2(double, double*); -int __rem_pio2_large(double*, double*, int, int, int); -double __sin(double, double, int); -double __cos(double, double); -double __tan(double, double, int); -double __expo2(double); diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/log.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/log.c deleted file mode 100644 index e61e113d..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/log.c +++ /dev/null @@ -1,118 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_log.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* log(x) - * Return the logarithm of x - * - * Method : - * 1. Argument Reduction: find k and f such that - * x = 2^k * (1+f), - * where sqrt(2)/2 < 1+f < sqrt(2) . - * - * 2. Approximation of log(1+f). - * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) - * = 2s + 2/3 s**3 + 2/5 s**5 + ....., - * = 2s + s*R - * We use a special Remez algorithm on [0,0.1716] to generate - * a polynomial of degree 14 to approximate R The maximum error - * of this polynomial approximation is bounded by 2**-58.45. In - * other words, - * 2 4 6 8 10 12 14 - * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s - * (the values of Lg1 to Lg7 are listed in the program) - * and - * | 2 14 | -58.45 - * | Lg1*s +...+Lg7*s - R(z) | <= 2 - * | | - * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. - * In order to guarantee error in log below 1ulp, we compute log - * by - * log(1+f) = f - s*(f - R) (if f is not too large) - * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) - * - * 3. Finally, log(x) = k*ln2 + log(1+f). - * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) - * Here ln2 is split into two floating point number: - * ln2_hi + ln2_lo, - * where n*ln2_hi is always exact for |n| < 2000. - * - * Special cases: - * log(x) is NaN with signal if x < 0 (including -INF) ; - * log(+INF) is +INF; log(0) is -INF with signal; - * log(NaN) is that NaN with no signal. - * - * Accuracy: - * according to an error analysis, the error is always less than - * 1 ulp (unit in the last place). - * - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ - -#include -#include - -static const double -ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ -ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ -Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ -Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ -Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ -Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ -Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ -Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ -Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ - -double log(double x) -{ - union {double f; uint64_t i;} u = {x}; - double_t hfsq,f,s,z,R,w,t1,t2,dk; - uint32_t hx; - int k; - - hx = u.i>>32; - k = 0; - if (hx < 0x00100000 || hx>>31) { - if (u.i<<1 == 0) - return -1/(x*x); /* log(+-0)=-inf */ - if (hx>>31) - return (x-x)/0.0; /* log(-#) = NaN */ - /* subnormal number, scale x up */ - k -= 54; - x *= 0x1p54; - u.f = x; - hx = u.i>>32; - } else if (hx >= 0x7ff00000) { - return x; - } else if (hx == 0x3ff00000 && u.i<<32 == 0) - return 0; - - /* reduce x into [sqrt(2)/2, sqrt(2)] */ - hx += 0x3ff00000 - 0x3fe6a09e; - k += (int)(hx>>20) - 0x3ff; - hx = (hx&0x000fffff) + 0x3fe6a09e; - u.i = (uint64_t)hx<<32 | (u.i&0xffffffff); - x = u.f; - - f = x - 1.0; - hfsq = 0.5*f*f; - s = f/(2.0+f); - z = s*s; - w = z*z; - t1 = w*(Lg2+w*(Lg4+w*Lg6)); - t2 = z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); - R = t2 + t1; - dk = k; - return s*(hfsq+R) + dk*ln2_lo - hfsq + f + dk*ln2_hi; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/log10.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/log10.c deleted file mode 100644 index bddedd68..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/log10.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -static const double _M_LN10 = 2.302585092994046; - -double log10(double x) { - return log(x) / (double)_M_LN10; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/log1p.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/log1p.c deleted file mode 100644 index 00971349..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/log1p.c +++ /dev/null @@ -1,122 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_log1p.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* double log1p(double x) - * Return the natural logarithm of 1+x. - * - * Method : - * 1. Argument Reduction: find k and f such that - * 1+x = 2^k * (1+f), - * where sqrt(2)/2 < 1+f < sqrt(2) . - * - * Note. If k=0, then f=x is exact. However, if k!=0, then f - * may not be representable exactly. In that case, a correction - * term is need. Let u=1+x rounded. Let c = (1+x)-u, then - * log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u), - * and add back the correction term c/u. - * (Note: when x > 2**53, one can simply return log(x)) - * - * 2. Approximation of log(1+f): See log.c - * - * 3. Finally, log1p(x) = k*ln2 + log(1+f) + c/u. See log.c - * - * Special cases: - * log1p(x) is NaN with signal if x < -1 (including -INF) ; - * log1p(+INF) is +INF; log1p(-1) is -INF with signal; - * log1p(NaN) is that NaN with no signal. - * - * Accuracy: - * according to an error analysis, the error is always less than - * 1 ulp (unit in the last place). - * - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - * - * Note: Assuming log() return accurate answer, the following - * algorithm can be used to compute log1p(x) to within a few ULP: - * - * u = 1+x; - * if(u==1.0) return x ; else - * return log(u)*(x/(u-1.0)); - * - * See HP-15C Advanced Functions Handbook, p.193. - */ - -#include "libm.h" - -static const double -ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ -ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ -Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ -Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ -Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ -Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ -Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ -Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ -Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ - -double log1p(double x) -{ - union {double f; uint64_t i;} u = {x}; - double_t hfsq,f,c,s,z,R,w,t1,t2,dk; - uint32_t hx,hu; - int k; - - hx = u.i>>32; - k = 1; - if (hx < 0x3fda827a || hx>>31) { /* 1+x < sqrt(2)+ */ - if (hx >= 0xbff00000) { /* x <= -1.0 */ - if (x == -1) - return x/0.0; /* log1p(-1) = -inf */ - return (x-x)/0.0; /* log1p(x<-1) = NaN */ - } - if (hx<<1 < 0x3ca00000<<1) { /* |x| < 2**-53 */ - /* underflow if subnormal */ - if ((hx&0x7ff00000) == 0) - FORCE_EVAL((float)x); - return x; - } - if (hx <= 0xbfd2bec4) { /* sqrt(2)/2- <= 1+x < sqrt(2)+ */ - k = 0; - c = 0; - f = x; - } - } else if (hx >= 0x7ff00000) - return x; - if (k) { - u.f = 1 + x; - hu = u.i>>32; - hu += 0x3ff00000 - 0x3fe6a09e; - k = (int)(hu>>20) - 0x3ff; - /* correction term ~ log(1+x)-log(u), avoid underflow in c/u */ - if (k < 54) { - c = k >= 2 ? 1-(u.f-x) : x-(u.f-1); - c /= u.f; - } else - c = 0; - /* reduce u into [sqrt(2)/2, sqrt(2)] */ - hu = (hu&0x000fffff) + 0x3fe6a09e; - u.i = (uint64_t)hu<<32 | (u.i&0xffffffff); - f = u.f - 1; - } - hfsq = 0.5*f*f; - s = f/(2.0+f); - z = s*s; - w = z*z; - t1 = w*(Lg2+w*(Lg4+w*Lg6)); - t2 = z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); - R = t2 + t1; - dk = k; - return s*(hfsq+R) + (dk*ln2_lo+c) - hfsq + f + dk*ln2_hi; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/modf.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/modf.c deleted file mode 100644 index 1c8a1db9..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/modf.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "libm.h" - -double modf(double x, double *iptr) -{ - union {double f; uint64_t i;} u = {x}; - uint64_t mask; - int e = (int)(u.i>>52 & 0x7ff) - 0x3ff; - - /* no fractional part */ - if (e >= 52) { - *iptr = x; - if (e == 0x400 && u.i<<12 != 0) /* nan */ - return x; - u.i &= 1ULL<<63; - return u.f; - } - - /* no integral part*/ - if (e < 0) { - u.i &= 1ULL<<63; - *iptr = u.f; - return x; - } - - mask = -1ULL>>12>>e; - if ((u.i & mask) == 0) { - *iptr = x; - u.i &= 1ULL<<63; - return u.f; - } - u.i &= ~mask; - *iptr = u.f; - return x - u.f; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/nearbyint.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/nearbyint.c deleted file mode 100644 index 6e9b0c1f..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/nearbyint.c +++ /dev/null @@ -1,20 +0,0 @@ -//#include -#include - -/* nearbyint is the same as rint, but it must not raise the inexact exception */ - -double nearbyint(double x) -{ -#ifdef FE_INEXACT - #pragma STDC FENV_ACCESS ON - int e; - - e = fetestexcept(FE_INEXACT); -#endif - x = rint(x); -#ifdef FE_INEXACT - if (!e) - feclearexcept(FE_INEXACT); -#endif - return x; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/pow.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/pow.c deleted file mode 100644 index 3ddc1b6f..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/pow.c +++ /dev/null @@ -1,328 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_pow.c */ -/* - * ==================================================== - * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. - * - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* pow(x,y) return x**y - * - * n - * Method: Let x = 2 * (1+f) - * 1. Compute and return log2(x) in two pieces: - * log2(x) = w1 + w2, - * where w1 has 53-24 = 29 bit trailing zeros. - * 2. Perform y*log2(x) = n+y' by simulating muti-precision - * arithmetic, where |y'|<=0.5. - * 3. Return x**y = 2**n*exp(y'*log2) - * - * Special cases: - * 1. (anything) ** 0 is 1 - * 2. 1 ** (anything) is 1 - * 3. (anything except 1) ** NAN is NAN - * 4. NAN ** (anything except 0) is NAN - * 5. +-(|x| > 1) ** +INF is +INF - * 6. +-(|x| > 1) ** -INF is +0 - * 7. +-(|x| < 1) ** +INF is +0 - * 8. +-(|x| < 1) ** -INF is +INF - * 9. -1 ** +-INF is 1 - * 10. +0 ** (+anything except 0, NAN) is +0 - * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 - * 12. +0 ** (-anything except 0, NAN) is +INF, raise divbyzero - * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF, raise divbyzero - * 14. -0 ** (+odd integer) is -0 - * 15. -0 ** (-odd integer) is -INF, raise divbyzero - * 16. +INF ** (+anything except 0,NAN) is +INF - * 17. +INF ** (-anything except 0,NAN) is +0 - * 18. -INF ** (+odd integer) is -INF - * 19. -INF ** (anything) = -0 ** (-anything), (anything except odd integer) - * 20. (anything) ** 1 is (anything) - * 21. (anything) ** -1 is 1/(anything) - * 22. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) - * 23. (-anything except 0 and inf) ** (non-integer) is NAN - * - * Accuracy: - * pow(x,y) returns x**y nearly rounded. In particular - * pow(integer,integer) - * always returns the correct integer provided it is - * representable. - * - * Constants : - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ - -#include "libm.h" - -static const double -bp[] = {1.0, 1.5,}, -dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ -dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ -two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ -huge = 1.0e300, -tiny = 1.0e-300, -/* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ -L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ -L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ -L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ -L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ -L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ -L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ -P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ -P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ -P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ -P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ -P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ -lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ -lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ -lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ -ovt = 8.0085662595372944372e-017, /* -(1024-log2(ovfl+.5ulp)) */ -cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ -cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ -cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ -ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ -ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ -ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ - -double pow(double x, double y) -{ - double z,ax,z_h,z_l,p_h,p_l; - double y1,t1,t2,r,s,t,u,v,w; - int32_t i,j,k,yisint,n; - int32_t hx,hy,ix,iy; - uint32_t lx,ly; - - EXTRACT_WORDS(hx, lx, x); - EXTRACT_WORDS(hy, ly, y); - ix = hx & 0x7fffffff; - iy = hy & 0x7fffffff; - - /* x**0 = 1, even if x is NaN */ - if ((iy|ly) == 0) - return 1.0; - /* 1**y = 1, even if y is NaN */ - if (hx == 0x3ff00000 && lx == 0) - return 1.0; - /* NaN if either arg is NaN */ - if (ix > 0x7ff00000 || (ix == 0x7ff00000 && lx != 0) || - iy > 0x7ff00000 || (iy == 0x7ff00000 && ly != 0)) - return x + y; - - /* determine if y is an odd int when x < 0 - * yisint = 0 ... y is not an integer - * yisint = 1 ... y is an odd int - * yisint = 2 ... y is an even int - */ - yisint = 0; - if (hx < 0) { - if (iy >= 0x43400000) - yisint = 2; /* even integer y */ - else if (iy >= 0x3ff00000) { - k = (iy>>20) - 0x3ff; /* exponent */ - if (k > 20) { - uint32_t j = ly>>(52-k); - if ((j<<(52-k)) == ly) - yisint = 2 - (j&1); - } else if (ly == 0) { - uint32_t j = iy>>(20-k); - if ((j<<(20-k)) == iy) - yisint = 2 - (j&1); - } - } - } - - /* special value of y */ - if (ly == 0) { - if (iy == 0x7ff00000) { /* y is +-inf */ - if (((ix-0x3ff00000)|lx) == 0) /* (-1)**+-inf is 1 */ - return 1.0; - else if (ix >= 0x3ff00000) /* (|x|>1)**+-inf = inf,0 */ - return hy >= 0 ? y : 0.0; - else /* (|x|<1)**+-inf = 0,inf */ - return hy >= 0 ? 0.0 : -y; - } - if (iy == 0x3ff00000) { /* y is +-1 */ - if (hy >= 0) - return x; - y = 1/x; -#if FLT_EVAL_METHOD!=0 - { - union {double f; uint64_t i;} u = {y}; - uint64_t i = u.i & -1ULL/2; - if (i>>52 == 0 && (i&(i-1))) - FORCE_EVAL((float)y); - } -#endif - return y; - } - if (hy == 0x40000000) /* y is 2 */ - return x*x; - if (hy == 0x3fe00000) { /* y is 0.5 */ - if (hx >= 0) /* x >= +0 */ - return sqrt(x); - } - } - - ax = fabs(x); - /* special value of x */ - if (lx == 0) { - if (ix == 0x7ff00000 || ix == 0 || ix == 0x3ff00000) { /* x is +-0,+-inf,+-1 */ - z = ax; - if (hy < 0) /* z = (1/|x|) */ - z = 1.0/z; - if (hx < 0) { - if (((ix-0x3ff00000)|yisint) == 0) { - z = (z-z)/(z-z); /* (-1)**non-int is NaN */ - } else if (yisint == 1) - z = -z; /* (x<0)**odd = -(|x|**odd) */ - } - return z; - } - } - - s = 1.0; /* sign of result */ - if (hx < 0) { - if (yisint == 0) /* (x<0)**(non-int) is NaN */ - return (x-x)/(x-x); - if (yisint == 1) /* (x<0)**(odd int) */ - s = -1.0; - } - - /* |y| is huge */ - if (iy > 0x41e00000) { /* if |y| > 2**31 */ - if (iy > 0x43f00000) { /* if |y| > 2**64, must o/uflow */ - if (ix <= 0x3fefffff) - return hy < 0 ? huge*huge : tiny*tiny; - if (ix >= 0x3ff00000) - return hy > 0 ? huge*huge : tiny*tiny; - } - /* over/underflow if x is not close to one */ - if (ix < 0x3fefffff) - return hy < 0 ? s*huge*huge : s*tiny*tiny; - if (ix > 0x3ff00000) - return hy > 0 ? s*huge*huge : s*tiny*tiny; - /* now |1-x| is tiny <= 2**-20, suffice to compute - log(x) by x-x^2/2+x^3/3-x^4/4 */ - t = ax - 1.0; /* t has 20 trailing zeros */ - w = (t*t)*(0.5 - t*(0.3333333333333333333333-t*0.25)); - u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ - v = t*ivln2_l - w*ivln2; - t1 = u + v; - SET_LOW_WORD(t1, 0); - t2 = v - (t1-u); - } else { - double ss,s2,s_h,s_l,t_h,t_l; - n = 0; - /* take care subnormal number */ - if (ix < 0x00100000) { - ax *= two53; - n -= 53; - GET_HIGH_WORD(ix,ax); - } - n += ((ix)>>20) - 0x3ff; - j = ix & 0x000fffff; - /* determine interval */ - ix = j | 0x3ff00000; /* normalize ix */ - if (j <= 0x3988E) /* |x|>1)|0x20000000) + 0x00080000 + (k<<18)); - t_l = ax - (t_h-bp[k]); - s_l = v*((u-s_h*t_h)-s_h*t_l); - /* compute log(ax) */ - s2 = ss*ss; - r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); - r += s_l*(s_h+ss); - s2 = s_h*s_h; - t_h = 3.0 + s2 + r; - SET_LOW_WORD(t_h, 0); - t_l = r - ((t_h-3.0)-s2); - /* u+v = ss*(1+...) */ - u = s_h*t_h; - v = s_l*t_h + t_l*ss; - /* 2/(3log2)*(ss+...) */ - p_h = u + v; - SET_LOW_WORD(p_h, 0); - p_l = v - (p_h-u); - z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ - z_l = cp_l*p_h+p_l*cp + dp_l[k]; - /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ - t = (double)n; - t1 = ((z_h + z_l) + dp_h[k]) + t; - SET_LOW_WORD(t1, 0); - t2 = z_l - (((t1 - t) - dp_h[k]) - z_h); - } - - /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ - y1 = y; - SET_LOW_WORD(y1, 0); - p_l = (y-y1)*t1 + y*t2; - p_h = y1*t1; - z = p_l + p_h; - EXTRACT_WORDS(j, i, z); - if (j >= 0x40900000) { /* z >= 1024 */ - if (((j-0x40900000)|i) != 0) /* if z > 1024 */ - return s*huge*huge; /* overflow */ - if (p_l + ovt > z - p_h) - return s*huge*huge; /* overflow */ - } else if ((j&0x7fffffff) >= 0x4090cc00) { /* z <= -1075 */ // FIXME: instead of abs(j) use unsigned j - if (((j-0xc090cc00)|i) != 0) /* z < -1075 */ - return s*tiny*tiny; /* underflow */ - if (p_l <= z - p_h) - return s*tiny*tiny; /* underflow */ - } - /* - * compute 2**(p_h+p_l) - */ - i = j & 0x7fffffff; - k = (i>>20) - 0x3ff; - n = 0; - if (i > 0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ - n = j + (0x00100000>>(k+1)); - k = ((n&0x7fffffff)>>20) - 0x3ff; /* new k for n */ - t = 0.0; - SET_HIGH_WORD(t, n & ~(0x000fffff>>k)); - n = ((n&0x000fffff)|0x00100000)>>(20-k); - if (j < 0) - n = -n; - p_h -= t; - } - t = p_l + p_h; - SET_LOW_WORD(t, 0); - u = t*lg2_h; - v = (p_l-(t-p_h))*lg2 + t*lg2_l; - z = u + v; - w = v - (z-u); - t = z*z; - t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); - r = (z*t1)/(t1-2.0) - (w + z*w); - z = 1.0 - (r-z); - GET_HIGH_WORD(j, z); - j += n<<20; - if ((j>>20) <= 0) /* subnormal output */ - z = scalbn(z,n); - else - SET_HIGH_WORD(z, j); - return s*z; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/rint.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/rint.c deleted file mode 100644 index fbba390e..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/rint.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include -#include - -#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 -#define EPS DBL_EPSILON -#elif FLT_EVAL_METHOD==2 -#define EPS LDBL_EPSILON -#endif -static const double_t toint = 1/EPS; - -double rint(double x) -{ - union {double f; uint64_t i;} u = {x}; - int e = u.i>>52 & 0x7ff; - int s = u.i>>63; - double_t y; - - if (e >= 0x3ff+52) - return x; - if (s) - y = x - toint + toint; - else - y = x + toint - toint; - if (y == 0) - return s ? -0.0 : 0; - return y; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/scalbn.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/scalbn.c deleted file mode 100644 index 182f5610..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/scalbn.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include - -double scalbn(double x, int n) -{ - union {double f; uint64_t i;} u; - double_t y = x; - - if (n > 1023) { - y *= 0x1p1023; - n -= 1023; - if (n > 1023) { - y *= 0x1p1023; - n -= 1023; - if (n > 1023) - n = 1023; - } - } else if (n < -1022) { - /* make sure final n < -53 to avoid double - rounding in the subnormal range */ - y *= 0x1p-1022 * 0x1p53; - n += 1022 - 53; - if (n < -1022) { - y *= 0x1p-1022 * 0x1p53; - n += 1022 - 53; - if (n < -1022) - n = -1022; - } - } - u.i = (uint64_t)(0x3ff+n)<<52; - x = y * u.f; - return x; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/sin.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/sin.c deleted file mode 100644 index 055e215b..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/sin.c +++ /dev/null @@ -1,78 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* sin(x) - * Return sine function of x. - * - * kernel function: - * __sin ... sine function on [-pi/4,pi/4] - * __cos ... cose function on [-pi/4,pi/4] - * __rem_pio2 ... argument reduction routine - * - * Method. - * Let S,C and T denote the sin, cos and tan respectively on - * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 - * in [-pi/4 , +pi/4], and let n = k mod 4. - * We have - * - * n sin(x) cos(x) tan(x) - * ---------------------------------------------------------- - * 0 S C T - * 1 C -S -1/T - * 2 -S -C T - * 3 -C S -1/T - * ---------------------------------------------------------- - * - * Special cases: - * Let trig be any of sin, cos, or tan. - * trig(+-INF) is NaN, with signals; - * trig(NaN) is that NaN; - * - * Accuracy: - * TRIG(x) returns trig(x) nearly rounded - */ - -#include "libm.h" - -double sin(double x) -{ - double y[2]; - uint32_t ix; - unsigned n; - - /* High word of x. */ - GET_HIGH_WORD(ix, x); - ix &= 0x7fffffff; - - /* |x| ~< pi/4 */ - if (ix <= 0x3fe921fb) { - if (ix < 0x3e500000) { /* |x| < 2**-26 */ - /* raise inexact if x != 0 and underflow if subnormal*/ - FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f); - return x; - } - return __sin(x, 0.0, 0); - } - - /* sin(Inf or NaN) is NaN */ - if (ix >= 0x7ff00000) - return x - x; - - /* argument reduction needed */ - n = __rem_pio2(x, y); - switch (n&3) { - case 0: return __sin(y[0], y[1], 1); - case 1: return __cos(y[0], y[1]); - case 2: return -__sin(y[0], y[1], 1); - default: - return -__cos(y[0], y[1]); - } -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/sinh.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/sinh.c deleted file mode 100644 index 00022c4e..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/sinh.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "libm.h" - -/* sinh(x) = (exp(x) - 1/exp(x))/2 - * = (exp(x)-1 + (exp(x)-1)/exp(x))/2 - * = x + x^3/6 + o(x^5) - */ -double sinh(double x) -{ - union {double f; uint64_t i;} u = {.f = x}; - uint32_t w; - double t, h, absx; - - h = 0.5; - if (u.i >> 63) - h = -h; - /* |x| */ - u.i &= (uint64_t)-1/2; - absx = u.f; - w = u.i >> 32; - - /* |x| < log(DBL_MAX) */ - if (w < 0x40862e42) { - t = expm1(absx); - if (w < 0x3ff00000) { - if (w < 0x3ff00000 - (26<<20)) - /* note: inexact and underflow are raised by expm1 */ - /* note: this branch avoids spurious underflow */ - return x; - return h*(2*t - t*t/(t+1)); - } - /* note: |x|>log(0x1p26)+eps could be just h*exp(x) */ - return h*(t + t/(t+1)); - } - - /* |x| > log(DBL_MAX) or nan */ - /* note: the result is stored to handle overflow */ - t = 2*h*__expo2(absx); - return t; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/sqrt.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/sqrt.c deleted file mode 100644 index b2775673..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/sqrt.c +++ /dev/null @@ -1,185 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/e_sqrt.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* sqrt(x) - * Return correctly rounded sqrt. - * ------------------------------------------ - * | Use the hardware sqrt if you have one | - * ------------------------------------------ - * Method: - * Bit by bit method using integer arithmetic. (Slow, but portable) - * 1. Normalization - * Scale x to y in [1,4) with even powers of 2: - * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then - * sqrt(x) = 2^k * sqrt(y) - * 2. Bit by bit computation - * Let q = sqrt(y) truncated to i bit after binary point (q = 1), - * i 0 - * i+1 2 - * s = 2*q , and y = 2 * ( y - q ). (1) - * i i i i - * - * To compute q from q , one checks whether - * i+1 i - * - * -(i+1) 2 - * (q + 2 ) <= y. (2) - * i - * -(i+1) - * If (2) is false, then q = q ; otherwise q = q + 2 . - * i+1 i i+1 i - * - * With some algebric manipulation, it is not difficult to see - * that (2) is equivalent to - * -(i+1) - * s + 2 <= y (3) - * i i - * - * The advantage of (3) is that s and y can be computed by - * i i - * the following recurrence formula: - * if (3) is false - * - * s = s , y = y ; (4) - * i+1 i i+1 i - * - * otherwise, - * -i -(i+1) - * s = s + 2 , y = y - s - 2 (5) - * i+1 i i+1 i i - * - * One may easily use induction to prove (4) and (5). - * Note. Since the left hand side of (3) contain only i+2 bits, - * it does not necessary to do a full (53-bit) comparison - * in (3). - * 3. Final rounding - * After generating the 53 bits result, we compute one more bit. - * Together with the remainder, we can decide whether the - * result is exact, bigger than 1/2ulp, or less than 1/2ulp - * (it will never equal to 1/2ulp). - * The rounding mode can be detected by checking whether - * huge + tiny is equal to huge, and whether huge - tiny is - * equal to huge for some floating point number "huge" and "tiny". - * - * Special cases: - * sqrt(+-0) = +-0 ... exact - * sqrt(inf) = inf - * sqrt(-ve) = NaN ... with invalid signal - * sqrt(NaN) = NaN ... with invalid signal for signaling NaN - */ - -#include "libm.h" - -static const double tiny = 1.0e-300; - -double sqrt(double x) -{ - double z; - int32_t sign = (int)0x80000000; - int32_t ix0,s0,q,m,t,i; - uint32_t r,t1,s1,ix1,q1; - - EXTRACT_WORDS(ix0, ix1, x); - - /* take care of Inf and NaN */ - if ((ix0&0x7ff00000) == 0x7ff00000) { - return x*x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf, sqrt(-inf)=sNaN */ - } - /* take care of zero */ - if (ix0 <= 0) { - if (((ix0&~sign)|ix1) == 0) - return x; /* sqrt(+-0) = +-0 */ - if (ix0 < 0) - return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ - } - /* normalize x */ - m = ix0>>20; - if (m == 0) { /* subnormal x */ - while (ix0 == 0) { - m -= 21; - ix0 |= (ix1>>11); - ix1 <<= 21; - } - for (i=0; (ix0&0x00100000) == 0; i++) - ix0<<=1; - m -= i - 1; - ix0 |= ix1>>(32-i); - ix1 <<= i; - } - m -= 1023; /* unbias exponent */ - ix0 = (ix0&0x000fffff)|0x00100000; - if (m & 1) { /* odd m, double x to make it even */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - } - m >>= 1; /* m = [m/2] */ - - /* generate sqrt(x) bit by bit */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ - r = 0x00200000; /* r = moving bit from right to left */ - - while (r != 0) { - t = s0 + r; - if (t <= ix0) { - s0 = t + r; - ix0 -= t; - q += r; - } - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - r >>= 1; - } - - r = sign; - while (r != 0) { - t1 = s1 + r; - t = s0; - if (t < ix0 || (t == ix0 && t1 <= ix1)) { - s1 = t1 + r; - if ((t1&sign) == sign && (s1&sign) == 0) - s0++; - ix0 -= t; - if (ix1 < t1) - ix0--; - ix1 -= t1; - q1 += r; - } - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - r >>= 1; - } - - /* use floating add to find out rounding direction */ - if ((ix0|ix1) != 0) { - z = 1.0 - tiny; /* raise inexact flag */ - if (z >= 1.0) { - z = 1.0 + tiny; - if (q1 == (uint32_t)0xffffffff) { - q1 = 0; - q++; - } else if (z > 1.0) { - if (q1 == (uint32_t)0xfffffffe) - q++; - q1 += 2; - } else - q1 += q1 & 1; - } - } - ix0 = (q>>1) + 0x3fe00000; - ix1 = q1>>1; - if (q&1) - ix1 |= sign; - ix0 += m << 20; - INSERT_WORDS(z, ix0, ix1); - return z; -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/tan.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/tan.c deleted file mode 100644 index 9c724a45..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/tan.c +++ /dev/null @@ -1,70 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* tan(x) - * Return tangent function of x. - * - * kernel function: - * __tan ... tangent function on [-pi/4,pi/4] - * __rem_pio2 ... argument reduction routine - * - * Method. - * Let S,C and T denote the sin, cos and tan respectively on - * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 - * in [-pi/4 , +pi/4], and let n = k mod 4. - * We have - * - * n sin(x) cos(x) tan(x) - * ---------------------------------------------------------- - * 0 S C T - * 1 C -S -1/T - * 2 -S -C T - * 3 -C S -1/T - * ---------------------------------------------------------- - * - * Special cases: - * Let trig be any of sin, cos, or tan. - * trig(+-INF) is NaN, with signals; - * trig(NaN) is that NaN; - * - * Accuracy: - * TRIG(x) returns trig(x) nearly rounded - */ - -#include "libm.h" - -double tan(double x) -{ - double y[2]; - uint32_t ix; - unsigned n; - - GET_HIGH_WORD(ix, x); - ix &= 0x7fffffff; - - /* |x| ~< pi/4 */ - if (ix <= 0x3fe921fb) { - if (ix < 0x3e400000) { /* |x| < 2**-27 */ - /* raise inexact if x!=0 and underflow if subnormal */ - FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f); - return x; - } - return __tan(x, 0.0, 0); - } - - /* tan(Inf or NaN) is NaN */ - if (ix >= 0x7ff00000) - return x - x; - - /* argument reduction */ - n = __rem_pio2(x, y); - return __tan(y[0], y[1], n&1); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/tanh.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/tanh.c deleted file mode 100644 index 89743ba9..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/tanh.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -double tanh(double x) { - return sinh(x) / cosh(x); -} diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/tgamma.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/tgamma.c deleted file mode 100644 index d1d0a048..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/tgamma.c +++ /dev/null @@ -1,222 +0,0 @@ -/* -"A Precision Approximation of the Gamma Function" - Cornelius Lanczos (1964) -"Lanczos Implementation of the Gamma Function" - Paul Godfrey (2001) -"An Analysis of the Lanczos Gamma Approximation" - Glendon Ralph Pugh (2004) - -approximation method: - - (x - 0.5) S(x) -Gamma(x) = (x + g - 0.5) * ---------------- - exp(x + g - 0.5) - -with - a1 a2 a3 aN -S(x) ~= [ a0 + ----- + ----- + ----- + ... + ----- ] - x + 1 x + 2 x + 3 x + N - -with a0, a1, a2, a3,.. aN constants which depend on g. - -for x < 0 the following reflection formula is used: - -Gamma(x)*Gamma(-x) = -pi/(x sin(pi x)) - -most ideas and constants are from boost and python -*/ -#include "libm.h" - -static const double pi = 3.141592653589793238462643383279502884; - -/* sin(pi x) with x > 0x1p-100, if sin(pi*x)==0 the sign is arbitrary */ -static double sinpi(double x) -{ - int n; - - /* argument reduction: x = |x| mod 2 */ - /* spurious inexact when x is odd int */ - x = x * 0.5; - x = 2 * (x - floor(x)); - - /* reduce x into [-.25,.25] */ - n = 4 * x; - n = (n+1)/2; - x -= n * 0.5; - - x *= pi; - switch (n) { - default: /* case 4 */ - case 0: - return __sin(x, 0, 0); - case 1: - return __cos(x, 0); - case 2: - return __sin(-x, 0, 0); - case 3: - return -__cos(x, 0); - } -} - -#define N 12 -//static const double g = 6.024680040776729583740234375; -static const double gmhalf = 5.524680040776729583740234375; -static const double Snum[N+1] = { - 23531376880.410759688572007674451636754734846804940, - 42919803642.649098768957899047001988850926355848959, - 35711959237.355668049440185451547166705960488635843, - 17921034426.037209699919755754458931112671403265390, - 6039542586.3520280050642916443072979210699388420708, - 1439720407.3117216736632230727949123939715485786772, - 248874557.86205415651146038641322942321632125127801, - 31426415.585400194380614231628318205362874684987640, - 2876370.6289353724412254090516208496135991145378768, - 186056.26539522349504029498971604569928220784236328, - 8071.6720023658162106380029022722506138218516325024, - 210.82427775157934587250973392071336271166969580291, - 2.5066282746310002701649081771338373386264310793408, -}; -static const double Sden[N+1] = { - 0, 39916800, 120543840, 150917976, 105258076, 45995730, 13339535, - 2637558, 357423, 32670, 1925, 66, 1, -}; -/* n! for small integer n */ -static const double fact[] = { - 1, 1, 2, 6, 24, 120, 720, 5040.0, 40320.0, 362880.0, 3628800.0, 39916800.0, - 479001600.0, 6227020800.0, 87178291200.0, 1307674368000.0, 20922789888000.0, - 355687428096000.0, 6402373705728000.0, 121645100408832000.0, - 2432902008176640000.0, 51090942171709440000.0, 1124000727777607680000.0, -}; - -/* S(x) rational function for positive x */ -static double S(double x) -{ - double_t num = 0, den = 0; - int i; - - /* to avoid overflow handle large x differently */ - if (x < 8) - for (i = N; i >= 0; i--) { - num = num * x + Snum[i]; - den = den * x + Sden[i]; - } - else - for (i = 0; i <= N; i++) { - num = num / x + Snum[i]; - den = den / x + Sden[i]; - } - return num/den; -} - -double tgamma(double x) -{ - union {double f; uint64_t i;} u = {x}; - double absx, y; - double_t dy, z, r; - uint32_t ix = u.i>>32 & 0x7fffffff; - int sign = u.i>>63; - - /* special cases */ - if (ix >= 0x7ff00000) - /* tgamma(nan)=nan, tgamma(inf)=inf, tgamma(-inf)=nan with invalid */ - return x + INFINITY; - if (ix < (0x3ff-54)<<20) - /* |x| < 2^-54: tgamma(x) ~ 1/x, +-0 raises div-by-zero */ - return 1/x; - - /* integer arguments */ - /* raise inexact when non-integer */ - if (x == floor(x)) { - if (sign) - return 0/0.0; - if (x <= sizeof fact/sizeof *fact) - return fact[(int)x - 1]; - } - - /* x >= 172: tgamma(x)=inf with overflow */ - /* x =< -184: tgamma(x)=+-0 with underflow */ - if (ix >= 0x40670000) { /* |x| >= 184 */ - if (sign) { - FORCE_EVAL((float)(0x1p-126/x)); - if (floor(x) * 0.5 == floor(x * 0.5)) - return 0; - return -0.0; - } - x *= 0x1p1023; - return x; - } - - absx = sign ? -x : x; - - /* handle the error of x + g - 0.5 */ - y = absx + gmhalf; - if (absx > gmhalf) { - dy = y - absx; - dy -= gmhalf; - } else { - dy = y - gmhalf; - dy -= absx; - } - - z = absx - 0.5; - r = S(absx) * exp(-y); - if (x < 0) { - /* reflection formula for negative x */ - /* sinpi(absx) is not 0, integers are already handled */ - r = -pi / (sinpi(absx) * absx * r); - dy = -dy; - z = -z; - } - r += dy * (gmhalf+0.5) * r / y; - z = pow(y, 0.5*z); - y = r * z * z; - return y; -} - -#if 1 -double __lgamma_r(double x, int *sign) -{ - double r, absx; - - *sign = 1; - - /* special cases */ - if (!isfinite(x)) - /* lgamma(nan)=nan, lgamma(+-inf)=inf */ - return x*x; - - /* integer arguments */ - if (x == floor(x) && x <= 2) { - /* n <= 0: lgamma(n)=inf with divbyzero */ - /* n == 1,2: lgamma(n)=0 */ - if (x <= 0) - return 1/0.0; - return 0; - } - - absx = fabs(x); - - /* lgamma(x) ~ -log(|x|) for tiny |x| */ - if (absx < 0x1p-54) { - *sign = 1 - 2*!!signbit(x); - return -log(absx); - } - - /* use tgamma for smaller |x| */ - if (absx < 128) { - x = tgamma(x); - *sign = 1 - 2*!!signbit(x); - return log(fabs(x)); - } - - /* second term (log(S)-g) could be more precise here.. */ - /* or with stirling: (|x|-0.5)*(log(|x|)-1) + poly(1/|x|) */ - r = (absx-0.5)*(log(absx+gmhalf)-1) + (log(S(absx)) - (gmhalf+0.5)); - if (x < 0) { - /* reflection formula for negative x */ - x = sinpi(absx); - *sign = 2*!!signbit(x) - 1; - r = log(pi/(fabs(x)*absx)) - r; - } - return r; -} - -//weak_alias(__lgamma_r, lgamma_r); -#endif diff --git a/MicroPython_BUILD/components/micropython/lib/libm_dbl/trunc.c b/MicroPython_BUILD/components/micropython/lib/libm_dbl/trunc.c deleted file mode 100644 index d13711b5..00000000 --- a/MicroPython_BUILD/components/micropython/lib/libm_dbl/trunc.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "libm.h" - -double trunc(double x) -{ - union {double f; uint64_t i;} u = {x}; - int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12; - uint64_t m; - - if (e >= 52 + 12) - return x; - if (e < 12) - e = 1; - m = -1ULL >> e; - if ((u.i & m) == 0) - return x; - FORCE_EVAL(x + 0x1p120f); - u.i &= ~m; - return u.f; -} diff --git a/MicroPython_BUILD/components/micropython/lib/memzip/README.md b/MicroPython_BUILD/components/micropython/lib/memzip/README.md deleted file mode 100644 index 287d0fc4..00000000 --- a/MicroPython_BUILD/components/micropython/lib/memzip/README.md +++ /dev/null @@ -1,28 +0,0 @@ -MEMZIP - a simple readonly file system - -memzip takes a zip file which is comprised of uncompressed files and -and presents it as a filesystem, allowing Python files to be imported. - -The script make-memzip.py takes a directory name and will create a zip file -containing uncompressed files found in the directory. It will then generate -a C file which contains the data from the zip file. - -A typical addition to a makefile would look like: -``` -SRC_C += \ - lib/memzip/import.c \ - lib/memzip/lexermemzip.c \ - lib/memzip/memzip.c \ - -OBJ += $(BUILD)/memzip-files.o - -MAKE_MEMZIP = ../lib/memzip/make-memzip.py - -$(BUILD)/memzip-files.o: $(BUILD)/memzip-files.c - $(call compile_c) - -$(BUILD)/memzip-files.c: $(shell find ${MEMZIP_DIR} -type f) - @$(ECHO) "Creating $@" - $(Q)$(PYTHON) $(MAKE_MEMZIP) --zip-file $(BUILD)/memzip-files.zip --c-file $@ $(MEMZIP_DIR) -``` - diff --git a/MicroPython_BUILD/components/micropython/lib/memzip/import.c b/MicroPython_BUILD/components/micropython/lib/memzip/import.c deleted file mode 100644 index 2d5225b8..00000000 --- a/MicroPython_BUILD/components/micropython/lib/memzip/import.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include "py/lexer.h" -#include "memzip.h" - -mp_import_stat_t mp_import_stat(const char *path) { - MEMZIP_FILE_INFO info; - - if (memzip_stat(path, &info) != MZ_OK) { - return MP_IMPORT_STAT_NO_EXIST; - } - - if (info.is_dir) { - return MP_IMPORT_STAT_DIR; - } - return MP_IMPORT_STAT_FILE; -} diff --git a/MicroPython_BUILD/components/micropython/lib/memzip/lexermemzip.c b/MicroPython_BUILD/components/micropython/lib/memzip/lexermemzip.c deleted file mode 100644 index 6b26961b..00000000 --- a/MicroPython_BUILD/components/micropython/lib/memzip/lexermemzip.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -#include "py/lexer.h" -#include "py/runtime.h" -#include "py/mperrno.h" -#include "memzip.h" - -mp_lexer_t *mp_lexer_new_from_file(const char *filename) -{ - void *data; - size_t len; - - if (memzip_locate(filename, &data, &len) != MZ_OK) { - mp_raise_OSError(MP_ENOENT); - } - - return mp_lexer_new_from_str_len(qstr_from_str(filename), (const char *)data, (mp_uint_t)len, 0); -} - diff --git a/MicroPython_BUILD/components/micropython/lib/memzip/make-memzip.py b/MicroPython_BUILD/components/micropython/lib/memzip/make-memzip.py deleted file mode 100755 index 9730f5e0..00000000 --- a/MicroPython_BUILD/components/micropython/lib/memzip/make-memzip.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# -# Takes a directory of files and zips them up (as uncompressed files). -# This then gets converted into a C data structure which can be read -# like a filesystem at runtime. -# -# This is somewhat like frozen modules in python, but allows arbitrary files -# to be used. - -from __future__ import print_function - -import argparse -import os -import subprocess -import sys -import types - -def create_zip(zip_filename, zip_dir): - abs_zip_filename = os.path.abspath(zip_filename) - save_cwd = os.getcwd() - os.chdir(zip_dir) - if os.path.exists(abs_zip_filename): - os.remove(abs_zip_filename) - subprocess.check_call(['zip', '-0', '-r', '-D', abs_zip_filename, '.']) - os.chdir(save_cwd) - -def create_c_from_file(c_filename, zip_filename): - with open(zip_filename, 'rb') as zip_file: - with open(c_filename, 'wb') as c_file: - print('#include ', file=c_file) - print('', file=c_file) - print('const uint8_t memzip_data[] = {', file=c_file) - while True: - buf = zip_file.read(16) - if not buf: - break - print(' ', end='', file=c_file) - for byte in buf: - if type(byte) is types.StringType: - print(' 0x{:02x},'.format(ord(byte)), end='', file=c_file) - else: - print(' 0x{:02x},'.format(byte), end='', file=c_file) - print('', file=c_file) - print('};', file=c_file) - -def main(): - parser = argparse.ArgumentParser( - prog='make-memzip.py', - usage='%(prog)s [options] [command]', - description='Generates a C source memzip file.' - ) - parser.add_argument( - '-z', '--zip-file', - dest='zip_filename', - help='Specifies the name of the created zip file.', - default='memzip_files.zip' - ) - parser.add_argument( - '-c', '--c-file', - dest='c_filename', - help='Specifies the name of the created C source file.', - default='memzip_files.c' - ) - parser.add_argument( - dest='source_dir', - default='memzip_files' - ) - args = parser.parse_args(sys.argv[1:]) - - print('args.zip_filename =', args.zip_filename) - print('args.c_filename =', args.c_filename) - print('args.source_dir =', args.source_dir) - - create_zip(args.zip_filename, args.source_dir) - create_c_from_file(args.c_filename, args.zip_filename) - -if __name__ == "__main__": - main() - diff --git a/MicroPython_BUILD/components/micropython/lib/memzip/memzip.c b/MicroPython_BUILD/components/micropython/lib/memzip/memzip.c deleted file mode 100644 index 3fbea8e1..00000000 --- a/MicroPython_BUILD/components/micropython/lib/memzip/memzip.c +++ /dev/null @@ -1,106 +0,0 @@ -#include -#include -#include -#include "py/mpconfig.h" -#include "py/misc.h" -#include "memzip.h" - -extern uint8_t memzip_data[]; - -const MEMZIP_FILE_HDR *memzip_find_file_header(const char *filename) { - - const MEMZIP_FILE_HDR *file_hdr = (const MEMZIP_FILE_HDR *)memzip_data; - uint8_t *mem_data; - - /* Zip file filenames don't have a leading /, so we strip it off */ - - if (*filename == '/') { - filename++; - } - while (file_hdr->signature == MEMZIP_FILE_HEADER_SIGNATURE) { - const char *file_hdr_filename = (const char *)&file_hdr[1]; - mem_data = (uint8_t *)file_hdr_filename; - mem_data += file_hdr->filename_len; - mem_data += file_hdr->extra_len; - if (!strncmp(file_hdr_filename, filename, file_hdr->filename_len)) { - /* We found a match */ - return file_hdr; - } - mem_data += file_hdr->uncompressed_size; - file_hdr = (const MEMZIP_FILE_HDR *)mem_data; - } - return NULL; -} - -bool memzip_is_dir(const char *filename) { - const MEMZIP_FILE_HDR *file_hdr = (const MEMZIP_FILE_HDR *)memzip_data; - uint8_t *mem_data; - - if (strcmp(filename, "/") == 0) { - // The root directory is a directory. - return true; - } - - // Zip filenames don't have a leading /, so we strip it off - if (*filename == '/') { - filename++; - } - size_t filename_len = strlen(filename); - - while (file_hdr->signature == MEMZIP_FILE_HEADER_SIGNATURE) { - const char *file_hdr_filename = (const char *)&file_hdr[1]; - if (filename_len < file_hdr->filename_len && - strncmp(file_hdr_filename, filename, filename_len) == 0 && - file_hdr_filename[filename_len] == '/') { - return true; - } - - mem_data = (uint8_t *)file_hdr_filename; - mem_data += file_hdr->filename_len; - mem_data += file_hdr->extra_len; - mem_data += file_hdr->uncompressed_size; - file_hdr = (const MEMZIP_FILE_HDR *)mem_data; - } - return NULL; - -} - -MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len) -{ - const MEMZIP_FILE_HDR *file_hdr = memzip_find_file_header(filename); - if (file_hdr == NULL) { - return MZ_NO_FILE; - } - if (file_hdr->compression_method != 0) { - return MZ_FILE_COMPRESSED; - } - - uint8_t *mem_data; - mem_data = (uint8_t *)&file_hdr[1]; - mem_data += file_hdr->filename_len; - mem_data += file_hdr->extra_len; - - *data = mem_data; - *len = file_hdr->uncompressed_size; - return MZ_OK; -} - -MEMZIP_RESULT memzip_stat(const char *path, MEMZIP_FILE_INFO *info) { - const MEMZIP_FILE_HDR *file_hdr = memzip_find_file_header(path); - if (file_hdr == NULL) { - if (memzip_is_dir(path)) { - info->file_size = 0; - info->last_mod_date = 0; - info->last_mod_time = 0; - info->is_dir = 1; - return MZ_OK; - } - return MZ_NO_FILE; - } - info->file_size = file_hdr->uncompressed_size; - info->last_mod_date = file_hdr->last_mod_date; - info->last_mod_time = file_hdr->last_mod_time; - info->is_dir = 0; - - return MZ_OK; -} diff --git a/MicroPython_BUILD/components/micropython/lib/memzip/memzip.h b/MicroPython_BUILD/components/micropython/lib/memzip/memzip.h deleted file mode 100644 index 667e2df7..00000000 --- a/MicroPython_BUILD/components/micropython/lib/memzip/memzip.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma pack(push, 1) - -#define MEMZIP_FILE_HEADER_SIGNATURE 0x04034b50 -typedef struct -{ - uint32_t signature; - uint16_t version; - uint16_t flags; - uint16_t compression_method; - uint16_t last_mod_time; - uint16_t last_mod_date; - uint32_t crc32; - uint32_t compressed_size; - uint32_t uncompressed_size; - uint16_t filename_len; - uint16_t extra_len; - - /* char filename[filename_len] */ - /* uint8_t extra[extra_len] */ - -} MEMZIP_FILE_HDR; - -#define MEMZIP_CENTRAL_DIRECTORY_SIGNATURE 0x02014b50 -typedef struct -{ - uint32_t signature; - uint16_t version_made_by; - uint16_t version_read_with; - uint16_t flags; - uint16_t compression_method; - uint16_t last_mod_time; - uint16_t last_mod_date; - uint32_t crc32; - uint32_t compressed_size; - uint32_t uncompressed_size; - uint16_t filename_len; - uint16_t extra_len; - uint16_t disk_num; - uint16_t internal_file_attributes; - uint32_t external_file_attributes; - uint32_t file_header_offset; - - /* char filename[filename_len] */ - /* uint8_t extra[extra_len] */ - -} MEMZIP_CENTRAL_DIRECTORY_HDR; - -#define MEMZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE 0x06054b50 -typedef struct -{ - uint32_t signature; - uint16_t disk_num; - uint16_t central_directory_disk; - uint16_t num_central_directories_this_disk; - uint16_t total_central_directories; - uint32_t central_directory_size; - uint32_t central_directory_offset; - uint16_t comment_len; - - /* char comment[comment_len] */ - -} MEMZIP_END_OF_CENTRAL_DIRECTORY; - -#pragma pack(pop) - -typedef enum { - MZ_OK = 0, /* (0) Succeeded */ - MZ_NO_FILE, /* (1) Could not find the file. */ - MZ_FILE_COMPRESSED, /* (2) File is compressed (expecting uncompressed) */ - -} MEMZIP_RESULT; - -typedef struct { - uint32_t file_size; - uint16_t last_mod_date; - uint16_t last_mod_time; - uint8_t is_dir; - -} MEMZIP_FILE_INFO; - -MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len); - -MEMZIP_RESULT memzip_stat(const char *path, MEMZIP_FILE_INFO *info); diff --git a/MicroPython_BUILD/components/micropython/lib/utils/printf.c b/MicroPython_BUILD/components/micropython/lib/utils/printf.c deleted file mode 100644 index 51dfa5b9..00000000 --- a/MicroPython_BUILD/components/micropython/lib/utils/printf.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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 "py/mpconfig.h" - -#if MICROPY_USE_INTERNAL_PRINTF - -#include -#include -#include - -#include "py/obj.h" -#include "py/mphal.h" - -#if MICROPY_PY_BUILTINS_FLOAT -#include "py/formatfloat.h" -#endif - -#undef putchar // Some stdlibs have a #define for putchar -int printf(const char *fmt, ...); -int vprintf(const char *fmt, va_list ap); -int putchar(int c); -int puts(const char *s); -int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); -int snprintf(char *str, size_t size, const char *fmt, ...); - -int printf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - int ret = mp_vprintf(&mp_plat_print, fmt, ap); - va_end(ap); - return ret; -} - -int vprintf(const char *fmt, va_list ap) { - return mp_vprintf(&mp_plat_print, fmt, ap); -} - -#if MICROPY_DEBUG_PRINTERS -int DEBUG_printf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - #ifndef MICROPY_DEBUG_PRINTER_DEST - #define MICROPY_DEBUG_PRINTER_DEST mp_plat_print - #endif - extern const mp_print_t MICROPY_DEBUG_PRINTER_DEST; - int ret = mp_vprintf(&MICROPY_DEBUG_PRINTER_DEST, fmt, ap); - va_end(ap); - return ret; -} -#endif - -// need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a') -int putchar(int c) { - char chr = c; - mp_hal_stdout_tx_strn_cooked(&chr, 1); - return chr; -} - -// need this because gcc optimises printf("string\n") -> puts("string") -int puts(const char *s) { - mp_hal_stdout_tx_strn_cooked(s, strlen(s)); - char chr = '\n'; - mp_hal_stdout_tx_strn_cooked(&chr, 1); - return 1; -} - -typedef struct _strn_print_env_t { - char *cur; - size_t remain; -} strn_print_env_t; - -STATIC void strn_print_strn(void *data, const char *str, size_t len) { - strn_print_env_t *strn_print_env = data; - if (len > strn_print_env->remain) { - len = strn_print_env->remain; - } - memcpy(strn_print_env->cur, str, len); - strn_print_env->cur += len; - strn_print_env->remain -= len; -} - -#if defined(__GNUC__) && !defined(__clang__) -// uClibc requires this alias to be defined, or there may be link errors -// when linkings against it statically. -int __GI_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) __attribute__((weak, alias ("vsnprintf"))); -#endif - -int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { - strn_print_env_t strn_print_env = {str, size}; - mp_print_t print = {&strn_print_env, strn_print_strn}; - int len = mp_vprintf(&print, fmt, ap); - // add terminating null byte - if (size > 0) { - if (strn_print_env.remain == 0) { - strn_print_env.cur[-1] = 0; - } else { - strn_print_env.cur[0] = 0; - } - } - return len; -} - -int snprintf(char *str, size_t size, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - int ret = vsnprintf(str, size, fmt, ap); - va_end(ap); - return ret; -} - -#endif //MICROPY_USE_INTERNAL_PRINTF diff --git a/MicroPython_BUILD/components/micropython/lib/utils/stdout_helpers.c b/MicroPython_BUILD/components/micropython/lib/utils/stdout_helpers.c deleted file mode 100644 index 3de11975..00000000 --- a/MicroPython_BUILD/components/micropython/lib/utils/stdout_helpers.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include "py/mpconfig.h" -#include "py/mphal.h" - -/* - * Extra stdout functions - * These can be either optimized for a particular port, or reference - * implementation below can be used. - */ - -// Send "cooked" string of given length, where every occurrence of -// LF character is replaced with CR LF. -void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { - while (len--) { - if (*str == '\n') { - mp_hal_stdout_tx_strn("\r", 1); - } - mp_hal_stdout_tx_strn(str++, 1); - } -} - -// Send zero-terminated string -void mp_hal_stdout_tx_str(const char *str) { - mp_hal_stdout_tx_strn(str, strlen(str)); -} diff --git a/MicroPython_BUILD/components/micropython/py/builtin.h b/MicroPython_BUILD/components/micropython/py/builtin.h index 84b99a8a..9b2584ea 100644 --- a/MicroPython_BUILD/components/micropython/py/builtin.h +++ b/MicroPython_BUILD/components/micropython/py/builtin.h @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -75,7 +76,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_sum_obj); // Defined by a port, but declared here for simplicity MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj); MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj); - +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_set_float_precision_obj); // LoBo MP_DECLARE_CONST_FUN_OBJ_2(mp_namedtuple_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_op_contains_obj); diff --git a/MicroPython_BUILD/components/micropython/py/builtinhelp.c b/MicroPython_BUILD/components/micropython/py/builtinhelp.c index 22fde16c..7106f3ce 100644 --- a/MicroPython_BUILD/components/micropython/py/builtinhelp.c +++ b/MicroPython_BUILD/components/micropython/py/builtinhelp.c @@ -4,7 +4,6 @@ * The MIT License (MIT) * * Copyright (c) 2013-2016 Damien P. George - * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,9 +33,9 @@ #if MICROPY_PY_BUILTINS_HELP const char mp_help_default_text[] = -"Welcome to LoBo MicroPython\n" +"Welcome to MicroPython!\n" "\n" -"For online documentation please visit\nhttps://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki\n" +"For online docs please visit http://docs.micropython.org/\n" "\n" "Control commands:\n" " CTRL-A -- on a blank line, enter raw REPL mode\n" diff --git a/MicroPython_BUILD/components/micropython/py/gc.c b/MicroPython_BUILD/components/micropython/py/gc.c index 84c9918f..78b4fc50 100644 --- a/MicroPython_BUILD/components/micropython/py/gc.c +++ b/MicroPython_BUILD/components/micropython/py/gc.c @@ -27,7 +27,7 @@ #include #include #include - +#include "esp_log.h" #include "py/gc.h" #include "py/runtime.h" @@ -96,7 +96,7 @@ #define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0) #endif -#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL +#if MICROPY_PY_THREAD && MICROPY_PY_THREAD_GIL #define GC_ENTER() mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1) #define GC_EXIT() mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex)) #else @@ -619,7 +619,7 @@ size_t gc_nbytes(const void *ptr) { #if 0 // old, simple realloc that didn't expand memory in place -void *gc_realloc(void *ptr, mp_uint_t n_bytes) { +void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move) { mp_uint_t n_existing = gc_nbytes(ptr); if (n_bytes <= n_existing) { return ptr; diff --git a/MicroPython_BUILD/components/micropython/py/modbuiltins.c b/MicroPython_BUILD/components/micropython/py/modbuiltins.c index 5d4ec8ff..bf84e678 100644 --- a/MicroPython_BUILD/components/micropython/py/modbuiltins.c +++ b/MicroPython_BUILD/components/micropython/py/modbuiltins.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -547,6 +548,23 @@ STATIC mp_obj_t mp_builtin_locals(void) { } MP_DEFINE_CONST_FUN_OBJ_0(mp_builtin_locals_obj, mp_builtin_locals); +// LoBo: Get or set float print precision +extern int float_precision; +//----------------------------------------------------------------------------------- +STATIC mp_obj_t mp_builtin_set_float_precision(size_t n_args, const mp_obj_t *args) { + if (n_args > 0) { + mp_int_t prec = mp_obj_get_int(args[0]); + if ((prec >= 4) && (prec <= 16)) { + float_precision = prec; + } + else { + mp_raise_ValueError("Precision must be 4 - 16"); + } + } + return mp_obj_new_int(float_precision); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_set_float_precision_obj, 0, 1, mp_builtin_set_float_precision); + // These are defined in terms of MicroPython API functions right away MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_id_obj, mp_obj_id); MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_len_obj, mp_obj_len); @@ -669,6 +687,7 @@ STATIC const mp_rom_map_elem_t mp_module_builtins_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_round), MP_ROM_PTR(&mp_builtin_round_obj) }, { MP_ROM_QSTR(MP_QSTR_sorted), MP_ROM_PTR(&mp_builtin_sorted_obj) }, { MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&mp_builtin_sum_obj) }, + { MP_ROM_QSTR(MP_QSTR_float_precision), MP_ROM_PTR(&mp_builtin_set_float_precision_obj) }, // built-in exceptions { MP_ROM_QSTR(MP_QSTR_BaseException), MP_ROM_PTR(&mp_type_BaseException) }, diff --git a/MicroPython_BUILD/components/micropython/py/modgc.c b/MicroPython_BUILD/components/micropython/py/modgc.c index 55e73def..457ae919 100644 --- a/MicroPython_BUILD/components/micropython/py/modgc.c +++ b/MicroPython_BUILD/components/micropython/py/modgc.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,6 +42,19 @@ STATIC mp_obj_t py_gc_collect(void) { } MP_DEFINE_CONST_FUN_OBJ_0(gc_collect_obj, py_gc_collect); +// collect_iflow(): run a garbage collection if more then given value is used +STATIC mp_obj_t py_gc_collect_if(mp_obj_t val_in) { + mp_int_t val = mp_obj_get_int(val_in); + gc_info_t info; + gc_info(&info); + if (info.used >= val) { + gc_collect(); + return mp_const_true; + } + else return mp_const_false; +} +MP_DEFINE_CONST_FUN_OBJ_1(gc_collect_if_obj, py_gc_collect_if); + // disable(): disable the garbage collector STATIC mp_obj_t gc_disable(void) { MP_STATE_MEM(gc_auto_collect_enabled) = 0; @@ -96,15 +110,16 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gc_threshold_obj, 0, 1, gc_threshold); #endif STATIC const mp_rom_map_elem_t mp_module_gc_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gc) }, - { MP_ROM_QSTR(MP_QSTR_collect), MP_ROM_PTR(&gc_collect_obj) }, - { MP_ROM_QSTR(MP_QSTR_disable), MP_ROM_PTR(&gc_disable_obj) }, - { MP_ROM_QSTR(MP_QSTR_enable), MP_ROM_PTR(&gc_enable_obj) }, - { MP_ROM_QSTR(MP_QSTR_isenabled), MP_ROM_PTR(&gc_isenabled_obj) }, - { MP_ROM_QSTR(MP_QSTR_mem_free), MP_ROM_PTR(&gc_mem_free_obj) }, - { MP_ROM_QSTR(MP_QSTR_mem_alloc), MP_ROM_PTR(&gc_mem_alloc_obj) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gc) }, + { MP_ROM_QSTR(MP_QSTR_collect), MP_ROM_PTR(&gc_collect_obj) }, + { MP_ROM_QSTR(MP_QSTR_collectif), MP_ROM_PTR(&gc_collect_if_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable), MP_ROM_PTR(&gc_disable_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable), MP_ROM_PTR(&gc_enable_obj) }, + { MP_ROM_QSTR(MP_QSTR_isenabled), MP_ROM_PTR(&gc_isenabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem_free), MP_ROM_PTR(&gc_mem_free_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem_alloc), MP_ROM_PTR(&gc_mem_alloc_obj) }, #if MICROPY_GC_ALLOC_THRESHOLD - { MP_ROM_QSTR(MP_QSTR_threshold), MP_ROM_PTR(&gc_threshold_obj) }, + { MP_ROM_QSTR(MP_QSTR_threshold), MP_ROM_PTR(&gc_threshold_obj) }, #endif }; diff --git a/MicroPython_BUILD/components/micropython/py/modsys.c b/MicroPython_BUILD/components/micropython/py/modsys.c index dbb61690..d8cee420 100644 --- a/MicroPython_BUILD/components/micropython/py/modsys.c +++ b/MicroPython_BUILD/components/micropython/py/modsys.c @@ -155,6 +155,7 @@ STATIC mp_obj_t mp_sys_getsizeof(mp_obj_t obj) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof); #endif +//-------------------------------- STATIC mp_obj_t mp_sys_mpycore() { mp_obj_t tuple[2]; tuple[0] = mp_obj_new_str(MICROPY_CORE_VERSION, strlen(MICROPY_CORE_VERSION)); @@ -164,6 +165,18 @@ STATIC mp_obj_t mp_sys_mpycore() { } MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_mpycore_obj, mp_sys_mpycore); +//-------------------------------------- +STATIC mp_obj_t mp_sys_espidf_info(void) +{ + mp_obj_t tuple[3]; + tuple[0] = mp_obj_new_str(MICROPY_ESPIDF_VERSION, strlen(MICROPY_ESPIDF_VERSION)); + tuple[1] = mp_obj_new_str(MICROPY_ESPIDF_HASH, strlen(MICROPY_ESPIDF_HASH)); + tuple[2] = mp_obj_new_str(MICROPY_ESPIDF_DATE, strlen(MICROPY_ESPIDF_DATE)); + + return mp_obj_new_tuple(3, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_espidf_info_obj, mp_sys_espidf_info); + #ifdef CONFIG_MICROPY_USE_THREADED_REPL //------------------------------------ STATIC mp_obj_t mp_sys_exec_repl(void) @@ -242,6 +255,7 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_argv), MP_ROM_PTR(&MP_STATE_VM(mp_sys_argv_obj)) }, { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&version_obj) }, { MP_ROM_QSTR(MP_QSTR_version_info), MP_ROM_PTR(&mp_sys_version_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_espidf_info), MP_ROM_PTR(&mp_sys_espidf_info_obj) }, { MP_ROM_QSTR(MP_QSTR_implementation), MP_ROM_PTR(&mp_sys_implementation_obj) }, { MP_ROM_QSTR(MP_QSTR_mpycore), MP_ROM_PTR(&mp_sys_mpycore_obj) }, { MP_ROM_QSTR(MP_QSTR_tz), MP_ROM_PTR(&mp_sys_timezone_obj) }, diff --git a/MicroPython_BUILD/components/micropython/py/mpprint.c b/MicroPython_BUILD/components/micropython/py/mpprint.c index c2e65301..0f28d62b 100644 --- a/MicroPython_BUILD/components/micropython/py/mpprint.c +++ b/MicroPython_BUILD/components/micropython/py/mpprint.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -51,6 +52,7 @@ STATIC void plat_print_strn(void *env, const char *str, size_t len) { const mp_print_t mp_plat_print = {NULL, plat_print_strn}; int mp_print_str(const mp_print_t *print, const char *str) { + if (str == NULL) return 0; size_t len = strlen(str); if (len) { print->print_strn(print->data, str, len); @@ -99,7 +101,7 @@ int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flag left_pad -= p; } } - if (len) { + if ((len) && (str)) { print->print_strn(print->data, str, len); total_chars_printed += len; } @@ -119,7 +121,7 @@ int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flag // 32-bits is 10 digits, add 3 for commas, 1 for sign, 1 for terminating null // We can use 16 characters for 32-bit and 32 characters for 64-bit -#define INT_BUF_SIZE (sizeof(mp_int_t) * 4) +#define INT_BUF_SIZE (sizeof(mp_int_t) * 8) // Our mp_vprintf function below does not support the '#' format modifier to // print the prefix of a non-base-10 number, so we don't need code for this. @@ -204,7 +206,7 @@ STATIC int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) { // These are the only values for "base" that are required to be supported by this // function, since Python only allows the user to format integers in these bases. - // If needed this function could be generalised to handle other values. + // If needed this function could be generalized to handle other values. assert(base == 2 || base == 8 || base == 10 || base == 16); if (!MP_OBJ_IS_INT(x)) { @@ -255,7 +257,7 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char // The size of this buffer is rather arbitrary. If it's not large // enough, a dynamic one will be allocated. - char stack_buf[sizeof(mp_int_t) * 4]; + char stack_buf[sizeof(mp_int_t) * 8]; char *buf = stack_buf; size_t buf_size = sizeof(stack_buf); size_t fmt_size = 0; @@ -498,7 +500,8 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { } #endif if (prec < 0) { - prec = strlen(str); + if (str) prec = strlen(str); + else prec = 0; } chrs += mp_print_strn(print, str, prec, flags, fill, width); break; diff --git a/MicroPython_BUILD/components/micropython/py/mpz.c b/MicroPython_BUILD/components/micropython/py/mpz.c index 24c8f27d..f85dc703 100644 --- a/MicroPython_BUILD/components/micropython/py/mpz.c +++ b/MicroPython_BUILD/components/micropython/py/mpz.c @@ -1588,8 +1588,8 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) { return true; } -bool mpz_as_int64_checked(const mpz_t *i, uint64_t *value) { - uint64_t val = 0; +bool mpz_as_int64_checked(const mpz_t *i, int64_t *value) { + int64_t val = 0; mpz_dig_t *d = i->dig + i->len; while (d-- > i->dig) { diff --git a/MicroPython_BUILD/components/micropython/py/mpz.h b/MicroPython_BUILD/components/micropython/py/mpz.h index 7e737417..bd5dee93 100644 --- a/MicroPython_BUILD/components/micropython/py/mpz.h +++ b/MicroPython_BUILD/components/micropython/py/mpz.h @@ -139,7 +139,7 @@ static inline size_t mpz_max_num_bits(const mpz_t *z) { return z->len * MPZ_DIG_ mp_int_t mpz_hash(const mpz_t *z); bool mpz_as_int_checked(const mpz_t *z, mp_int_t *value); bool mpz_as_uint_checked(const mpz_t *z, mp_uint_t *value); -bool mpz_as_int64_checked(const mpz_t *i, uint64_t *value); +bool mpz_as_int64_checked(const mpz_t *i, int64_t *value); bool mpz_as_uint64_checked(const mpz_t *i, uint64_t *value); void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf); #if MICROPY_PY_BUILTINS_FLOAT diff --git a/MicroPython_BUILD/components/micropython/py/obj.c b/MicroPython_BUILD/components/micropython/py/obj.c index 8f7ebb71..190208d1 100644 --- a/MicroPython_BUILD/components/micropython/py/obj.c +++ b/MicroPython_BUILD/components/micropython/py/obj.c @@ -67,6 +67,10 @@ void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t } #endif mp_obj_type_t *type = mp_obj_get_type(o_in); + if (type == NULL) { + mp_print_str(print, "(nil)"); + return; + } if (type->print != NULL) { type->print((mp_print_t*)print, o_in, kind); } else { @@ -243,9 +247,10 @@ mp_int_t mp_obj_get_int(mp_const_obj_t arg) { "can't convert %s to int", mp_obj_get_type_str(arg))); } } + return 0; } -uint64_t mp_obj_get_int64(mp_const_obj_t arg) { +int64_t mp_obj_get_int64(mp_const_obj_t arg) { // This function essentially performs implicit type conversion to int64 // Note that Python does NOT provide implicit type conversion from // float to int in the core expression language, try some_list[1.0]. @@ -265,6 +270,7 @@ uint64_t mp_obj_get_int64(mp_const_obj_t arg) { "can't convert %s to int", mp_obj_get_type_str(arg))); } } + return 0; } mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg) { diff --git a/MicroPython_BUILD/components/micropython/py/obj.h b/MicroPython_BUILD/components/micropython/py/obj.h index 39f9fd5e..3c38b41f 100644 --- a/MicroPython_BUILD/components/micropython/py/obj.h +++ b/MicroPython_BUILD/components/micropython/py/obj.h @@ -676,7 +676,7 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2); static inline bool mp_obj_is_integer(mp_const_obj_t o) { return MP_OBJ_IS_INT(o) || MP_OBJ_IS_TYPE(o, &mp_type_bool); } // returns true if o is bool, small int or long int mp_int_t mp_obj_get_int(mp_const_obj_t arg); -uint64_t mp_obj_get_int64(mp_const_obj_t arg); +int64_t mp_obj_get_int64(mp_const_obj_t arg); mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg); bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value); #if MICROPY_PY_BUILTINS_FLOAT @@ -703,7 +703,8 @@ void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj); mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in); // Will raise exception if value doesn't fit into mp_int_t mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in); -uint64_t mp_obj_int64_get_checked(mp_const_obj_t self_in); +int64_t mp_obj_int64_get_checked(mp_const_obj_t self_in); +uint64_t mp_obj_uint64_get_checked(mp_const_obj_t self_in); // exception #define mp_obj_is_native_exception_instance(o) (mp_obj_get_type(o)->make_new == mp_obj_exception_make_new) diff --git a/MicroPython_BUILD/components/micropython/py/objarray.c b/MicroPython_BUILD/components/micropython/py/objarray.c index a3553948..24ed251c 100644 --- a/MicroPython_BUILD/components/micropython/py/objarray.c +++ b/MicroPython_BUILD/components/micropython/py/objarray.c @@ -5,6 +5,7 @@ * * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -90,7 +91,8 @@ STATIC void array_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t #endif #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY -STATIC mp_obj_array_t *array_new(char typecode, size_t n) { +//------------------------------------------------------------------------- +STATIC mp_obj_array_t *array_new(char typecode, size_t n, uint64_t *init) { int typecode_size = mp_binary_get_size('@', typecode, NULL); mp_obj_array_t *o = m_new_obj(mp_obj_array_t); #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_PY_ARRAY @@ -104,6 +106,38 @@ STATIC mp_obj_array_t *array_new(char typecode, size_t n) { o->free = 0; o->len = n; o->items = m_new(byte, typecode_size * o->len); + + if (o->len > 0) { + memset(o->items, 0, typecode_size * o->len); + if (init) { + switch (typecode_size) { + case 2: { + uint16_t *ptr = (uint16_t *) o->items; + uint16_t *val = (uint16_t *)init; + for (int i=0; ilen; i++) { + ptr[i] = *val; + } + break; + } + case 4: { + uint32_t *ptr = (uint32_t *) o->items; + uint32_t *val = (uint32_t *)init; + for (int i=0; ilen; i++) { + ptr[i] = *val; + } + break; + } + case 8: { + uint64_t *ptr = (uint64_t *) o->items; + uint64_t *val = (uint64_t *)init; + for (int i=0; ilen; i++) { + ptr[i] = *val; + } + break; + } + } + } + } return o; } #endif @@ -113,17 +147,15 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) { // bytearrays can be raw-initialised from anything with the buffer protocol // other arrays can only be raw-initialised from bytes and bytearray objects mp_buffer_info_t bufinfo; - if (((MICROPY_PY_BUILTINS_BYTEARRAY - && typecode == BYTEARRAY_TYPECODE) - || (MICROPY_PY_ARRAY - && (MP_OBJ_IS_TYPE(initializer, &mp_type_bytes) - || (MICROPY_PY_BUILTINS_BYTEARRAY && MP_OBJ_IS_TYPE(initializer, &mp_type_bytearray))))) - && mp_get_buffer(initializer, &bufinfo, MP_BUFFER_READ)) { + if (((MICROPY_PY_BUILTINS_BYTEARRAY && typecode == BYTEARRAY_TYPECODE) || + (MICROPY_PY_ARRAY && (MP_OBJ_IS_TYPE(initializer, &mp_type_bytes) || + (MICROPY_PY_BUILTINS_BYTEARRAY && MP_OBJ_IS_TYPE(initializer, &mp_type_bytearray))))) && + mp_get_buffer(initializer, &bufinfo, MP_BUFFER_READ)) { // construct array from raw bytes // we round-down the len to make it a multiple of sz (CPython raises error) size_t sz = mp_binary_get_size('@', typecode, NULL); size_t len = bufinfo.len / sz; - mp_obj_array_t *o = array_new(typecode, len); + mp_obj_array_t *o = array_new(typecode, len, NULL); memcpy(o->items, bufinfo.buf, len * sz); return MP_OBJ_FROM_PTR(o); } @@ -137,7 +169,7 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) { len = MP_OBJ_SMALL_INT_VALUE(len_in); } - mp_obj_array_t *array = array_new(typecode, len); + mp_obj_array_t *array = array_new(typecode, len, NULL); mp_obj_t iterable = mp_getiter(initializer, NULL); mp_obj_t item; @@ -155,18 +187,30 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) { #endif #if MICROPY_PY_ARRAY +//-------------------------------------------------------------------------------------------------------------- STATIC mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 1, 2, false); - + mp_arg_check_num(n_args, n_kw, 1, 3, false); // get typecode const char *typecode = mp_obj_str_get_str(args[0]); - if (n_args == 1) { // 1 arg: make an empty array - return MP_OBJ_FROM_PTR(array_new(*typecode, 0)); + return MP_OBJ_FROM_PTR(array_new(*typecode, 0, NULL)); } else { - // 2 args: construct the array from the given object + // 2 or 3 args: + if (MP_OBJ_IS_INT(args[1])) { + // make array of specified number of items + mp_uint_t len = mp_obj_get_int(args[1]); + if (n_args == 3) { + // initialize array vith given value + uint64_t init_val = mp_obj_get_int64(args[2]); + return MP_OBJ_FROM_PTR(array_new(*typecode, len, &init_val)); + } + else { + return MP_OBJ_FROM_PTR(array_new(*typecode, len, NULL)); + } + } + //construct the array from the given object return array_construct(*typecode, args[1]); } } @@ -179,11 +223,11 @@ STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, if (n_args == 0) { // no args: construct an empty bytearray - return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0)); + return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0, NULL)); } else if (MP_OBJ_IS_INT(args[0])) { // 1 arg, an integer: construct a blank bytearray of that length mp_uint_t len = mp_obj_get_int(args[0]); - mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len); + mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len, NULL); memset(o->items, 0, len); return MP_OBJ_FROM_PTR(o); } else { @@ -254,7 +298,7 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs size_t rhs_len = rhs_bufinfo.len / sz; // note: lhs->len is element count of lhs, lhs_bufinfo.len is byte count - mp_obj_array_t *res = array_new(lhs_bufinfo.typecode, lhs->len + rhs_len); + mp_obj_array_t *res = array_new(lhs_bufinfo.typecode, lhs->len + rhs_len, NULL); mp_seq_cat((byte*)res->items, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_len * sz, byte); return MP_OBJ_FROM_PTR(res); } @@ -461,7 +505,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value res->len = slice.stop - slice.start; #endif } else { - res = array_new(o->typecode, slice.stop - slice.start); + res = array_new(o->typecode, slice.stop - slice.start, NULL); memcpy(res->items, (uint8_t*)o->items + slice.start * sz, (slice.stop - slice.start) * sz); } return MP_OBJ_FROM_PTR(res); @@ -569,7 +613,7 @@ size_t mp_obj_array_len(mp_obj_t self_in) { #if MICROPY_PY_BUILTINS_BYTEARRAY mp_obj_t mp_obj_new_bytearray(size_t n, void *items) { - mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n); + mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n, NULL); memcpy(o->items, items, n); return MP_OBJ_FROM_PTR(o); } diff --git a/MicroPython_BUILD/components/micropython/py/objfloat.c b/MicroPython_BUILD/components/micropython/py/objfloat.c index e4d5a657..aa85970c 100644 --- a/MicroPython_BUILD/components/micropython/py/objfloat.c +++ b/MicroPython_BUILD/components/micropython/py/objfloat.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -57,6 +58,17 @@ const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, M_PI}; #endif +// LoBo: Enable runtime float precision change +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + int float_precision = 6; + #else + int float_precision = 7; + #endif +#else + int float_precision = 15; +#endif + #if MICROPY_FLOAT_HIGH_QUALITY_HASH // must return actual integer value if it fits in mp_int_t mp_int_t mp_float_hash(mp_float_t src) { @@ -109,17 +121,10 @@ typedef uint32_t mp_float_uint_t; STATIC void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_float_t o_val = mp_obj_float_get(o_in); -#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - char buf[16]; - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C - const int precision = 6; - #else - const int precision = 7; - #endif -#else + // LoBo: Runtime float precision change enabled char buf[32]; - const int precision = 16; -#endif + const int precision = float_precision; + mp_format_float(o_val, buf, sizeof(buf), 'g', precision, '\0'); mp_print_str(print, buf); if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) { diff --git a/MicroPython_BUILD/components/micropython/py/objint_mpz.c b/MicroPython_BUILD/components/micropython/py/objint_mpz.c index f6339154..c6c07bc0 100644 --- a/MicroPython_BUILD/components/micropython/py/objint_mpz.c +++ b/MicroPython_BUILD/components/micropython/py/objint_mpz.c @@ -410,14 +410,15 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { mp_raise_msg(&mp_type_OverflowError, "overflow converting long int to machine word"); } } + return 0; } -uint64_t mp_obj_int64_get_checked(mp_const_obj_t self_in) { +int64_t mp_obj_int64_get_checked(mp_const_obj_t self_in) { if (MP_OBJ_IS_SMALL_INT(self_in)) { return MP_OBJ_SMALL_INT_VALUE(self_in); } else { const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); - uint64_t value; + int64_t value; if (mpz_as_int64_checked(&self->mpz, &value)) { return value; } else { @@ -425,6 +426,23 @@ uint64_t mp_obj_int64_get_checked(mp_const_obj_t self_in) { mp_raise_msg(&mp_type_OverflowError, "overflow converting int64 to machine word"); } } + return 0; +} + +uint64_t mp_obj_uint64_get_checked(mp_const_obj_t self_in) { + if (MP_OBJ_IS_SMALL_INT(self_in)) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + uint64_t value; + if (mpz_as_uint64_checked(&self->mpz, &value)) { + return value; + } else { + // overflow + mp_raise_msg(&mp_type_OverflowError, "overflow converting uint64 to machine word"); + } + } + return 0; } #if MICROPY_PY_BUILTINS_FLOAT diff --git a/MicroPython_BUILD/components/micropython/py/py.mk b/MicroPython_BUILD/components/micropython/py/py.mk index 902e2fd9..3c4baeec 100644 --- a/MicroPython_BUILD/components/micropython/py/py.mk +++ b/MicroPython_BUILD/components/micropython/py/py.mk @@ -194,9 +194,7 @@ PY_O_BASENAME = \ ../extmod/vfs.o \ ../extmod/vfs_reader.o \ ../extmod/utime_mphal.o \ - ../extmod/uos_dupterm.o \ ../lib/embed/abort_.o \ - ../lib/utils/printf.o \ ../extmod/vfs_native.o \ ../extmod/vfs_native_file.o \ ../extmod/vfs_native_misc.o diff --git a/MicroPython_BUILD/components/micropython/py/runtime.c b/MicroPython_BUILD/components/micropython/py/runtime.c index 4efb29be..d00f001a 100644 --- a/MicroPython_BUILD/components/micropython/py/runtime.c +++ b/MicroPython_BUILD/components/micropython/py/runtime.c @@ -70,7 +70,7 @@ void mp_init(void) { #endif #if MICROPY_KBD_EXCEPTION - // initialise the exception object for raising KeyboardInterrupt + // initialize the exception object for raising KeyboardInterrupt MP_STATE_VM(mp_kbd_exception).base.type = &mp_type_KeyboardInterrupt; MP_STATE_VM(mp_kbd_exception).traceback_alloc = 0; MP_STATE_VM(mp_kbd_exception).traceback_len = 0; diff --git a/MicroPython_BUILD/components/micropython/py/vm.c b/MicroPython_BUILD/components/micropython/py/vm.c index 7281e2b0..3713060b 100644 --- a/MicroPython_BUILD/components/micropython/py/vm.c +++ b/MicroPython_BUILD/components/micropython/py/vm.c @@ -37,7 +37,7 @@ #include "py/bc0.h" #include "py/bc.h" -#if 0 +#if 0 && MICROPY_DEBUG_PRINTERS #define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(ip, 1, code_state->fun_bc->const_table); #else #define TRACE(ip) @@ -118,7 +118,9 @@ // MP_VM_RETURN_NORMAL, sp valid, return value in *sp // MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp // MP_VM_RETURN_EXCEPTION, exception in fastn[0] -mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) { +//================================================================================================ +mp_vm_return_kind_t IRAM_ATTR mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) +{ mp_hal_set_wdt_tmo(); // LoBo #define SELECTIVE_EXC_IP (0) #if SELECTIVE_EXC_IP @@ -1485,4 +1487,5 @@ unwind_jump:; } } } + return MP_VM_RETURN_NORMAL; } diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip index f44820a8..9318bff6 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip index b82bb425..4ed9dbc8 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip index f142a544..c1a69e82 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip index 5df17c52..125a69dd 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip index 67caa7bb..5152d74f 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip index 98c8e092..74118af6 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip differ diff --git a/MicroPython_BUILD/firmware/README.md b/MicroPython_BUILD/firmware/README.md index e9096004..601fd58d 100644 --- a/MicroPython_BUILD/firmware/README.md +++ b/MicroPython_BUILD/firmware/README.md @@ -19,8 +19,16 @@ Available firmwares: All firmwares are configured with 1 MB SPIFFS file system.
Telnet server, FTP server, mDNS and Mqtt are enabled. -To flash, use **esptool.py**. +To flash, use **esptool.py**.
+If you don't have it installed, install it using `pip`:
+`pip install esptool` or `pip3 install esptool` -You can use the `flash.sh` script, run it in its directory. +You can use the `flash.sh` script to flash the firmware:
-**Edit the** `flash.sh` **and set the correct usb port for your board.** +Change you working directory to the selected firmware directory and run: + +``` +../flash.sh -p -b +``` + +`-p` & `-b` options are optional, default port is `/dev/ttyUSB0`, default baud rate is `460800`. diff --git a/MicroPython_BUILD/firmware/esp32/MicroPython.bin b/MicroPython_BUILD/firmware/esp32/MicroPython.bin index 6e73d815..6f4f3e76 100644 Binary files a/MicroPython_BUILD/firmware/esp32/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32/bootloader/bootloader.bin index 7e5eca95..b6dd9dab 100644 Binary files a/MicroPython_BUILD/firmware/esp32/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32/flash.sh b/MicroPython_BUILD/firmware/esp32/flash.sh deleted file mode 100755 index 39e917a4..00000000 --- a/MicroPython_BUILD/firmware/esp32/flash.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin diff --git a/MicroPython_BUILD/firmware/esp32/partitions_mpy.csv b/MicroPython_BUILD/firmware/esp32/partitions_mpy.csv index 4f13ffe6..71130655 100644 --- a/MicroPython_BUILD/firmware/esp32/partitions_mpy.csv +++ b/MicroPython_BUILD/firmware/esp32/partitions_mpy.csv @@ -1,5 +1,5 @@ # ------------------------------------------------------- -# - Partition layout generaded by BUILD.sh script - +# - Partition layout generated by BUILD.sh script - # ------------------------------------------------------- # Name, Type, SubType, Offset, Size, Flags # ------------------------------------------------------- diff --git a/MicroPython_BUILD/firmware/esp32/sdkconfig b/MicroPython_BUILD/firmware/esp32/sdkconfig index eafeaef6..1a19bc02 100644 --- a/MicroPython_BUILD/firmware/esp32/sdkconfig +++ b/MicroPython_BUILD/firmware/esp32/sdkconfig @@ -93,6 +93,7 @@ CONFIG_MICRO_PY_LOG_LEVEL2=y CONFIG_MICRO_PY_LOG_LEVEL3= CONFIG_MICRO_PY_LOG_LEVEL4= CONFIG_MICRO_PY_LOG_LEVEL5= +CONFIG_MICROPY_USE_UNICODE=y CONFIG_MICROPY_USE_THREADED_REPL= CONFIG_MICROPY_RX_BUFFER_SIZE=1080 CONFIG_MICROPY_USE_TASK_WDT=y @@ -130,6 +131,8 @@ CONFIG_MICROPY_USE_GSM= CONFIG_MICROPY_USE_ETHERNET= CONFIG_MICROPY_USE_MDNS=y CONFIG_MICROPY_USE_CURL= +CONFIG_MICROPY_USE_GPS=y +CONFIG_MICROPY_GPS_SERVICE_STACK=3072 CONFIG_MICROPY_USE_SSH= CONFIG_MICROPY_USE_MQTT=y @@ -137,9 +140,10 @@ CONFIG_MICROPY_USE_MQTT=y # MQTT Configuration # CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_PRIORITY=5 -CONFIG_MQTT_BUFFER_SIZE_BYTE=256 -CONFIG_MQTT_MAX_PAYLOAD_SIZE=2048 +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +CONFIG_MQTT_USE_CUSTOM_CONFIG= CONFIG_MQTT_LOG_LEVEL=1 CONFIG_MQTT_LOG_LEVEL0= CONFIG_MQTT_LOG_LEVEL1=y @@ -193,6 +197,7 @@ CONFIG_STACK_CHECK_NORM= CONFIG_STACK_CHECK_STRONG= CONFIG_STACK_CHECK_ALL= CONFIG_STACK_CHECK= +CONFIG_WARN_WRITE_STRINGS= # # Component config diff --git a/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin index 100358e4..04371f41 100644 Binary files a/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin index 02cc4cac..b3cceffd 100644 Binary files a/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_all/flash.sh b/MicroPython_BUILD/firmware/esp32_all/flash.sh deleted file mode 100755 index 39e917a4..00000000 --- a/MicroPython_BUILD/firmware/esp32_all/flash.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin diff --git a/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin b/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin index f37f3bfa..0bdd9d71 100644 Binary files a/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin and b/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_all/sdkconfig b/MicroPython_BUILD/firmware/esp32_all/sdkconfig index e6c32b35..bc30d299 100644 --- a/MicroPython_BUILD/firmware/esp32_all/sdkconfig +++ b/MicroPython_BUILD/firmware/esp32_all/sdkconfig @@ -93,6 +93,7 @@ CONFIG_MICRO_PY_LOG_LEVEL2=y CONFIG_MICRO_PY_LOG_LEVEL3= CONFIG_MICRO_PY_LOG_LEVEL4= CONFIG_MICRO_PY_LOG_LEVEL5= +CONFIG_MICROPY_USE_UNICODE=y CONFIG_MICROPY_USE_THREADED_REPL= CONFIG_MICROPY_RX_BUFFER_SIZE=1080 CONFIG_MICROPY_USE_TASK_WDT=y @@ -130,6 +131,8 @@ CONFIG_MICROPY_USE_GSM=y CONFIG_MICROPY_USE_ETHERNET= CONFIG_MICROPY_USE_MDNS=y CONFIG_MICROPY_USE_CURL=y +CONFIG_MICROPY_USE_GPS=y +CONFIG_MICROPY_GPS_SERVICE_STACK=3072 CONFIG_MICROPY_USE_CURL_TLS=y CONFIG_MICROPY_USE_CURLFTP=y CONFIG_MICROPY_USE_SSH=y @@ -139,9 +142,10 @@ CONFIG_MICROPY_USE_MQTT=y # MQTT Configuration # CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_PRIORITY=5 -CONFIG_MQTT_BUFFER_SIZE_BYTE=256 -CONFIG_MQTT_MAX_PAYLOAD_SIZE=2048 +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +CONFIG_MQTT_USE_CUSTOM_CONFIG= CONFIG_MQTT_LOG_LEVEL=1 CONFIG_MQTT_LOG_LEVEL0= CONFIG_MQTT_LOG_LEVEL1=y @@ -195,6 +199,7 @@ CONFIG_STACK_CHECK_NORM= CONFIG_STACK_CHECK_STRONG= CONFIG_STACK_CHECK_ALL= CONFIG_STACK_CHECK= +CONFIG_WARN_WRITE_STRINGS= # # Component config diff --git a/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin index ce0f1686..e2f2775c 100644 Binary files a/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin index 7006b2d8..e9889cdf 100644 Binary files a/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_ota/flash.sh b/MicroPython_BUILD/firmware/esp32_ota/flash.sh deleted file mode 100755 index 39e917a4..00000000 --- a/MicroPython_BUILD/firmware/esp32_ota/flash.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin diff --git a/MicroPython_BUILD/firmware/esp32_ota/partitions_mpy.csv b/MicroPython_BUILD/firmware/esp32_ota/partitions_mpy.csv index ecf24637..fe52ed1e 100644 --- a/MicroPython_BUILD/firmware/esp32_ota/partitions_mpy.csv +++ b/MicroPython_BUILD/firmware/esp32_ota/partitions_mpy.csv @@ -1,5 +1,5 @@ # ------------------------------------------------------- -# - Partition layout generaded by BUILD.sh script - +# - Partition layout generated by BUILD.sh script - # ------------------------------------------------------- # Name, Type, SubType, Offset, Size, Flags # ------------------------------------------------------- diff --git a/MicroPython_BUILD/firmware/esp32_ota/sdkconfig b/MicroPython_BUILD/firmware/esp32_ota/sdkconfig index 5ed54448..11eec78e 100644 --- a/MicroPython_BUILD/firmware/esp32_ota/sdkconfig +++ b/MicroPython_BUILD/firmware/esp32_ota/sdkconfig @@ -94,6 +94,7 @@ CONFIG_MICRO_PY_LOG_LEVEL2=y CONFIG_MICRO_PY_LOG_LEVEL3= CONFIG_MICRO_PY_LOG_LEVEL4= CONFIG_MICRO_PY_LOG_LEVEL5= +CONFIG_MICROPY_USE_UNICODE=y CONFIG_MICROPY_USE_THREADED_REPL= CONFIG_MICROPY_RX_BUFFER_SIZE=1080 CONFIG_MICROPY_USE_TASK_WDT=y @@ -131,6 +132,8 @@ CONFIG_MICROPY_USE_GSM= CONFIG_MICROPY_USE_ETHERNET= CONFIG_MICROPY_USE_MDNS=y CONFIG_MICROPY_USE_CURL= +CONFIG_MICROPY_USE_GPS=y +CONFIG_MICROPY_GPS_SERVICE_STACK=3072 CONFIG_MICROPY_USE_SSH= CONFIG_MICROPY_USE_MQTT=y @@ -138,9 +141,10 @@ CONFIG_MICROPY_USE_MQTT=y # MQTT Configuration # CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_PRIORITY=5 -CONFIG_MQTT_BUFFER_SIZE_BYTE=256 -CONFIG_MQTT_MAX_PAYLOAD_SIZE=2048 +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +CONFIG_MQTT_USE_CUSTOM_CONFIG= CONFIG_MQTT_LOG_LEVEL=1 CONFIG_MQTT_LOG_LEVEL0= CONFIG_MQTT_LOG_LEVEL1=y @@ -194,6 +198,7 @@ CONFIG_STACK_CHECK_NORM= CONFIG_STACK_CHECK_STRONG= CONFIG_STACK_CHECK_ALL= CONFIG_STACK_CHECK= +CONFIG_WARN_WRITE_STRINGS= # # Component config diff --git a/MicroPython_BUILD/firmware/esp32_psram/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_psram/MicroPython.bin index 104f9b5c..5f7daec5 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_psram/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_psram/bootloader/bootloader.bin index c8af7ef2..3820ad04 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_psram/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram/flash.sh b/MicroPython_BUILD/firmware/esp32_psram/flash.sh deleted file mode 100755 index 39e917a4..00000000 --- a/MicroPython_BUILD/firmware/esp32_psram/flash.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin diff --git a/MicroPython_BUILD/firmware/esp32_psram/partitions_mpy.csv b/MicroPython_BUILD/firmware/esp32_psram/partitions_mpy.csv index a55e8f95..adfaffd7 100644 --- a/MicroPython_BUILD/firmware/esp32_psram/partitions_mpy.csv +++ b/MicroPython_BUILD/firmware/esp32_psram/partitions_mpy.csv @@ -1,5 +1,5 @@ # ------------------------------------------------------- -# - Partition layout generaded by BUILD.sh script - +# - Partition layout generated by BUILD.sh script - # ------------------------------------------------------- # Name, Type, SubType, Offset, Size, Flags # ------------------------------------------------------- diff --git a/MicroPython_BUILD/firmware/esp32_psram/sdkconfig b/MicroPython_BUILD/firmware/esp32_psram/sdkconfig index 5975194a..10c7e0ed 100644 --- a/MicroPython_BUILD/firmware/esp32_psram/sdkconfig +++ b/MicroPython_BUILD/firmware/esp32_psram/sdkconfig @@ -93,6 +93,7 @@ CONFIG_MICRO_PY_LOG_LEVEL2=y CONFIG_MICRO_PY_LOG_LEVEL3= CONFIG_MICRO_PY_LOG_LEVEL4= CONFIG_MICRO_PY_LOG_LEVEL5= +CONFIG_MICROPY_USE_UNICODE=y CONFIG_MICROPY_USE_THREADED_REPL= CONFIG_MICROPY_RX_BUFFER_SIZE=1080 CONFIG_MICROPY_USE_TASK_WDT=y @@ -130,6 +131,8 @@ CONFIG_MICROPY_USE_GSM= CONFIG_MICROPY_USE_ETHERNET= CONFIG_MICROPY_USE_MDNS=y CONFIG_MICROPY_USE_CURL= +CONFIG_MICROPY_USE_GPS=y +CONFIG_MICROPY_GPS_SERVICE_STACK=3072 CONFIG_MICROPY_USE_SSH= CONFIG_MICROPY_USE_MQTT=y @@ -137,9 +140,10 @@ CONFIG_MICROPY_USE_MQTT=y # MQTT Configuration # CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_PRIORITY=5 -CONFIG_MQTT_BUFFER_SIZE_BYTE=256 -CONFIG_MQTT_MAX_PAYLOAD_SIZE=2048 +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +CONFIG_MQTT_USE_CUSTOM_CONFIG= CONFIG_MQTT_LOG_LEVEL=1 CONFIG_MQTT_LOG_LEVEL0= CONFIG_MQTT_LOG_LEVEL1=y @@ -193,6 +197,7 @@ CONFIG_STACK_CHECK_NORM= CONFIG_STACK_CHECK_STRONG= CONFIG_STACK_CHECK_ALL= CONFIG_STACK_CHECK= +CONFIG_WARN_WRITE_STRINGS= # # Component config @@ -237,7 +242,7 @@ CONFIG_SPIRAM_SUPPORT=y # SPI RAM config # CONFIG_SPIRAM_BOOT_INIT=y -CONFIG_SPIRAM_IGNORE_NOTFOUND= +CONFIG_SPIRAM_IGNORE_NOTFOUND=y CONFIG_SPIRAM_USE_MEMMAP= CONFIG_SPIRAM_USE_CAPS_ALLOC=y CONFIG_SPIRAM_USE_MALLOC= diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin index f5c67be5..0f6d23fa 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin index d0772cde..e2ea8bf2 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/flash.sh b/MicroPython_BUILD/firmware/esp32_psram_all/flash.sh deleted file mode 100755 index 39e917a4..00000000 --- a/MicroPython_BUILD/firmware/esp32_psram_all/flash.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.bin b/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.bin index 869be0be..1ffe5a13 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.bin and b/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.csv b/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.csv index 830f9898..8ddd2060 100644 --- a/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.csv +++ b/MicroPython_BUILD/firmware/esp32_psram_all/partitions_mpy.csv @@ -1,9 +1,9 @@ # ------------------------------------------------------- -# - Partition layout generaded by BUILD.sh script - +# - Partition layout generated by BUILD.sh script - # ------------------------------------------------------- # Name, Type, SubType, Offset, Size, Flags # ------------------------------------------------------- nvs, data, nvs, 0x9000, 24K, phy_init, data, phy, 0xf000, 4K, -MicroPython, app, factory, 0x10000, 1984K, +MicroPython, app, factory, 0x10000, 2048K, internalfs, data, spiffs, , 1024K, diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/sdkconfig b/MicroPython_BUILD/firmware/esp32_psram_all/sdkconfig index da109b9d..77ac3208 100644 --- a/MicroPython_BUILD/firmware/esp32_psram_all/sdkconfig +++ b/MicroPython_BUILD/firmware/esp32_psram_all/sdkconfig @@ -93,6 +93,7 @@ CONFIG_MICRO_PY_LOG_LEVEL2=y CONFIG_MICRO_PY_LOG_LEVEL3= CONFIG_MICRO_PY_LOG_LEVEL4= CONFIG_MICRO_PY_LOG_LEVEL5= +CONFIG_MICROPY_USE_UNICODE=y CONFIG_MICROPY_USE_THREADED_REPL= CONFIG_MICROPY_RX_BUFFER_SIZE=1080 CONFIG_MICROPY_USE_TASK_WDT=y @@ -130,6 +131,8 @@ CONFIG_MICROPY_USE_GSM=y CONFIG_MICROPY_USE_ETHERNET= CONFIG_MICROPY_USE_MDNS=y CONFIG_MICROPY_USE_CURL=y +CONFIG_MICROPY_USE_GPS=y +CONFIG_MICROPY_GPS_SERVICE_STACK=3072 CONFIG_MICROPY_USE_CURL_TLS=y CONFIG_MICROPY_USE_CURLFTP=y CONFIG_MICROPY_USE_SSH=y @@ -139,9 +142,10 @@ CONFIG_MICROPY_USE_MQTT=y # MQTT Configuration # CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_PRIORITY=5 -CONFIG_MQTT_BUFFER_SIZE_BYTE=256 -CONFIG_MQTT_MAX_PAYLOAD_SIZE=2048 +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +CONFIG_MQTT_USE_CUSTOM_CONFIG= CONFIG_MQTT_LOG_LEVEL=1 CONFIG_MQTT_LOG_LEVEL0= CONFIG_MQTT_LOG_LEVEL1=y @@ -195,6 +199,7 @@ CONFIG_STACK_CHECK_NORM= CONFIG_STACK_CHECK_STRONG= CONFIG_STACK_CHECK_ALL= CONFIG_STACK_CHECK= +CONFIG_WARN_WRITE_STRINGS= # # Component config @@ -239,7 +244,7 @@ CONFIG_SPIRAM_SUPPORT=y # SPI RAM config # CONFIG_SPIRAM_BOOT_INIT=y -CONFIG_SPIRAM_IGNORE_NOTFOUND= +CONFIG_SPIRAM_IGNORE_NOTFOUND=y CONFIG_SPIRAM_USE_MEMMAP= CONFIG_SPIRAM_USE_CAPS_ALLOC=y CONFIG_SPIRAM_USE_MALLOC= diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin index eb54087d..3f314de9 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin index 0d55aeb5..5e497f15 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/flash.sh b/MicroPython_BUILD/firmware/esp32_psram_ota/flash.sh deleted file mode 100755 index 39e917a4..00000000 --- a/MicroPython_BUILD/firmware/esp32_psram_ota/flash.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.bin b/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.bin index a953fef3..2590b822 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.bin and b/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.csv b/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.csv index 15661614..1b8a94d6 100644 --- a/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.csv +++ b/MicroPython_BUILD/firmware/esp32_psram_ota/partitions_mpy.csv @@ -1,11 +1,11 @@ # ------------------------------------------------------- -# - Partition layout generaded by BUILD.sh script - +# - Partition layout generated by BUILD.sh script - # ------------------------------------------------------- # Name, Type, SubType, Offset, Size, Flags # ------------------------------------------------------- nvs, data, nvs, 0x9000, 16K, otadata, data, ota, 0xd000, 8K, phy_init, data, phy, 0xf000, 4K, -MicroPython_1, app, ota_0, 0x10000, 1536K, -MicroPython_2, app, ota_1, , 1536K, -internalfs, data, spiffs, , 960K, +MicroPython_1, app, ota_0, 0x10000, 1600K, +MicroPython_2, app, ota_1, , 1600K, +internalfs, data, spiffs, , 832K, diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/sdkconfig b/MicroPython_BUILD/firmware/esp32_psram_ota/sdkconfig index b52f0fe5..7dfda517 100644 --- a/MicroPython_BUILD/firmware/esp32_psram_ota/sdkconfig +++ b/MicroPython_BUILD/firmware/esp32_psram_ota/sdkconfig @@ -94,6 +94,7 @@ CONFIG_MICRO_PY_LOG_LEVEL2=y CONFIG_MICRO_PY_LOG_LEVEL3= CONFIG_MICRO_PY_LOG_LEVEL4= CONFIG_MICRO_PY_LOG_LEVEL5= +CONFIG_MICROPY_USE_UNICODE=y CONFIG_MICROPY_USE_THREADED_REPL= CONFIG_MICROPY_RX_BUFFER_SIZE=1080 CONFIG_MICROPY_USE_TASK_WDT=y @@ -131,6 +132,8 @@ CONFIG_MICROPY_USE_GSM= CONFIG_MICROPY_USE_ETHERNET= CONFIG_MICROPY_USE_MDNS=y CONFIG_MICROPY_USE_CURL= +CONFIG_MICROPY_USE_GPS=y +CONFIG_MICROPY_GPS_SERVICE_STACK=3072 CONFIG_MICROPY_USE_SSH= CONFIG_MICROPY_USE_MQTT=y @@ -138,9 +141,10 @@ CONFIG_MICROPY_USE_MQTT=y # MQTT Configuration # CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_PRIORITY=5 -CONFIG_MQTT_BUFFER_SIZE_BYTE=256 -CONFIG_MQTT_MAX_PAYLOAD_SIZE=2048 +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +CONFIG_MQTT_USE_CUSTOM_CONFIG= CONFIG_MQTT_LOG_LEVEL=1 CONFIG_MQTT_LOG_LEVEL0= CONFIG_MQTT_LOG_LEVEL1=y @@ -194,6 +198,7 @@ CONFIG_STACK_CHECK_NORM= CONFIG_STACK_CHECK_STRONG= CONFIG_STACK_CHECK_ALL= CONFIG_STACK_CHECK= +CONFIG_WARN_WRITE_STRINGS= # # Component config @@ -238,7 +243,7 @@ CONFIG_SPIRAM_SUPPORT=y # SPI RAM config # CONFIG_SPIRAM_BOOT_INIT=y -CONFIG_SPIRAM_IGNORE_NOTFOUND= +CONFIG_SPIRAM_IGNORE_NOTFOUND=y CONFIG_SPIRAM_USE_MEMMAP= CONFIG_SPIRAM_USE_CAPS_ALLOC=y CONFIG_SPIRAM_USE_MALLOC= diff --git a/MicroPython_BUILD/firmware/flash.sh b/MicroPython_BUILD/firmware/flash.sh new file mode 100755 index 00000000..4411120f --- /dev/null +++ b/MicroPython_BUILD/firmware/flash.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +FLASH_COMPORT="/dev/ttyUSB0" +FLASH_BDRATE="460800" + +get_arguments() { + POSITIONAL_ARGS=() + local key="$1" + J_OPTION="" + + while [[ $# -gt 0 ]] + do + local key="$1" + case $key in + -p|--port) + FLASH_COMPORT="$2" + shift # past argument + shift # past value + ;; + -b|--bdrate) + FLASH_BDRATE="$2" + shift # past argument + shift # past value + ;; + *) # unknown option + local opt="$1" + if [ "${opt:0:2}" == "-j" ]; then + J_OPTION=${opt} + else + POSITIONAL_ARGS+=("$1") # save it in an array for later + fi + shift # past argument + ;; + esac + done + #set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters +} + +get_arguments "$@" + +esptool.py --chip esp32 --port ${FLASH_COMPORT} --baud ${FLASH_BDRATE} --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin diff --git a/MicroPython_BUILD/sdkconfig.defaults b/MicroPython_BUILD/sdkconfig.defaults index 7f91d184..8db97f27 100644 --- a/MicroPython_BUILD/sdkconfig.defaults +++ b/MicroPython_BUILD/sdkconfig.defaults @@ -39,7 +39,7 @@ CONFIG_TASK_WDT_TIMEOUT_S=15 # SPI RAM config # CONFIG_SPIRAM_BOOT_INIT=y -CONFIG_SPIRAM_IGNORE_NOTFOUND= +CONFIG_SPIRAM_IGNORE_NOTFOUND=y CONFIG_SPIRAM_USE_MEMMAP= CONFIG_SPIRAM_USE_CAPS_ALLOC=y CONFIG_SPIRAM_USE_MALLOC= diff --git a/MicroPython_BUILD/sdkconfig.lobo b/MicroPython_BUILD/sdkconfig.lobo new file mode 100644 index 00000000..7b6de3e1 --- /dev/null +++ b/MicroPython_BUILD/sdkconfig.lobo @@ -0,0 +1,676 @@ +# +# Automatically generated file; DO NOT EDIT. +# Espressif IoT Development Framework Configuration +# + +# +# SDK tool configuration +# +CONFIG_TOOLPREFIX="xtensa-esp32-elf-" +CONFIG_PYTHON="python" +CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y + +# +# Bootloader config +# +CONFIG_LOG_BOOTLOADER_LEVEL_NONE= +CONFIG_LOG_BOOTLOADER_LEVEL_ERROR= +CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y +CONFIG_LOG_BOOTLOADER_LEVEL_INFO= +CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG= +CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE= +CONFIG_LOG_BOOTLOADER_LEVEL=2 +CONFIG_BOOTLOADER_SPI_WP_PIN=7 +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y + +# +# Security features +# +CONFIG_SECURE_BOOT_ENABLED= +CONFIG_FLASH_ENCRYPTION_ENABLED= + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0" +CONFIG_ESPTOOLPY_BAUD_115200B= +CONFIG_ESPTOOLPY_BAUD_230400B= +CONFIG_ESPTOOLPY_BAUD_921600B= +CONFIG_ESPTOOLPY_BAUD_2MB= +CONFIG_ESPTOOLPY_BAUD_OTHER=y +CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=460800 +CONFIG_ESPTOOLPY_BAUD=460800 +CONFIG_ESPTOOLPY_COMPRESSED=y +CONFIG_FLASHMODE_QIO=y +CONFIG_FLASHMODE_QOUT= +CONFIG_FLASHMODE_DIO= +CONFIG_FLASHMODE_DOUT= +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_40M= +CONFIG_ESPTOOLPY_FLASHFREQ_26M= +CONFIG_ESPTOOLPY_FLASHFREQ_20M= +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_1MB= +CONFIG_ESPTOOLPY_FLASHSIZE_2MB= +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB= +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +CONFIG_ESPTOOLPY_BEFORE_NORESET= +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET= +CONFIG_ESPTOOLPY_AFTER_NORESET=y +CONFIG_ESPTOOLPY_AFTER="no_reset" +CONFIG_MONITOR_BAUD_9600B= +CONFIG_MONITOR_BAUD_57600B= +CONFIG_MONITOR_BAUD_115200B=y +CONFIG_MONITOR_BAUD_230400B= +CONFIG_MONITOR_BAUD_921600B= +CONFIG_MONITOR_BAUD_2MB= +CONFIG_MONITOR_BAUD_OTHER= +CONFIG_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_MONITOR_BAUD=115200 + +# +# MicroPython +# +CONFIG_MICROPY_HW_BOARD_NAME="ESP32_ Feather" +CONFIG_MICROPY_HW_MCU_NAME="ESP32" +CONFIG_MICROPY_TIMEZONE="GMT0BST" +CONFIG_MICROPY_USE_OTA= +CONFIG_BOOT_SET_LED=-1 + +# +# System settings +# +CONFIG_MICRO_PY_LOG_LEVEL=2 +CONFIG_MICRO_PY_LOG_LEVEL0= +CONFIG_MICRO_PY_LOG_LEVEL1= +CONFIG_MICRO_PY_LOG_LEVEL2=y +CONFIG_MICRO_PY_LOG_LEVEL3= +CONFIG_MICRO_PY_LOG_LEVEL4= +CONFIG_MICRO_PY_LOG_LEVEL5= +CONFIG_MICROPY_USE_THREADED_REPL= +CONFIG_MICROPY_RX_BUFFER_SIZE=1080 +CONFIG_MICROPY_USE_TASK_WDT=y +CONFIG_MICROPY_USE_BOTH_CORES= +CONFIG_MICROPY_TASK_PRIORITY=5 +CONFIG_MICROPY_STACK_SIZE=16 +CONFIG_MICROPY_HEAP_SIZE=3584 +CONFIG_MICROPY_THREAD_MAX_THREADS=8 +CONFIG_MICROPY_THREAD_STACK_SIZE=4 +CONFIG_MICROPY_USE_TELNET=y +CONFIG_MICROPY_USE_WEBSERVER= +CONFIG_MICROPY_USE_FTPSERVER=y + +# +# FTP Server Configuration +# +CONFIG_FTPSERVER_LOG_LEVEL=1 +CONFIG_FTPSERVER_LOG_LEVEL0= +CONFIG_FTPSERVER_LOG_LEVEL1=y +CONFIG_FTPSERVER_LOG_LEVEL2= +CONFIG_FTPSERVER_LOG_LEVEL3= +CONFIG_FTPSERVER_LOG_LEVEL4= +CONFIG_MICROPY_FTPSERVER_TIMEOUT=300 +CONFIG_MICROPY_FTPSERVER_BUFFER_SIZE=1024 + +# +# Modules +# +CONFIG_MICROPY_PY_FRAMEBUF=y +CONFIG_MICROPY_PY_USE_BTREE=y +CONFIG_MICROPY_USE_WEBSOCKETS=y +CONFIG_MICROPY_USE_DISPLAY=y +CONFIG_MICROPY_USE_EVE= +CONFIG_MICROPY_USE_GSM= +CONFIG_MICROPY_USE_ETHERNET= +CONFIG_MICROPY_USE_MDNS=y +CONFIG_MICROPY_USE_CURL=y +CONFIG_MICROPY_USE_GPS=y +CONFIG_MICROPY_GPS_SERVICE_STACK=3072 +CONFIG_MICROPY_USE_CURL_TLS=y +CONFIG_MICROPY_USE_CURLFTP=y +CONFIG_MICROPY_USE_SSH=y +CONFIG_MICROPY_USE_MQTT=y + +# +# MQTT Configuration +# +CONFIG_MQTT_PROTOCOL_311=y +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +CONFIG_MQTT_USE_CUSTOM_CONFIG= +CONFIG_MQTT_LOG_LEVEL=1 +CONFIG_MQTT_LOG_LEVEL0= +CONFIG_MQTT_LOG_LEVEL1=y +CONFIG_MQTT_LOG_LEVEL2= +CONFIG_MQTT_LOG_LEVEL3= +CONFIG_MQTT_LOG_LEVEL4= + +# +# File systems +# +CONFIG_MICROPY_FILESYSTEM_TYPE=2 +CONFIG_MICROPY_FS_TYPE0= +CONFIG_MICROPY_FS_TYPE1= +CONFIG_MICROPY_FS_TYPE2=y +CONFIG_LITTLEFLASH_USE_WEAR_LEVELING=y +CONFIG_MICROPY_FATFS_MAX_OPEN_FILES=6 +CONFIG_MICROPY_SDMMC_SHOW_INFO=y + +# +# SD Card configuration +# +CONFIG_SDCARD_MODE=2 +CONFIG_SDCARD_MODE1= +CONFIG_SDCARD_MODE2=y +CONFIG_SDCARD_MODE3= + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP= +CONFIG_PARTITION_TABLE_TWO_OTA= +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_mpy.csv" +CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 +CONFIG_PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET=0xf000 +CONFIG_PARTITION_TABLE_FILENAME="partitions_mpy.csv" +CONFIG_APP_OFFSET=0x10000 +CONFIG_PHY_DATA_OFFSET=0xf000 +CONFIG_PARTITION_TABLE_MD5=y + +# +# Compiler options +# +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_OPTIMIZATION_LEVEL_RELEASE= +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +CONFIG_OPTIMIZATION_ASSERTIONS_SILENT= +CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED= +CONFIG_CXX_EXCEPTIONS= +CONFIG_STACK_CHECK_NONE= +CONFIG_STACK_CHECK_NORM= +CONFIG_STACK_CHECK_STRONG=y +CONFIG_STACK_CHECK_ALL= +CONFIG_STACK_CHECK=y +CONFIG_WARN_WRITE_STRINGS= + +# +# Component config +# + +# +# Application Level Tracing +# +CONFIG_ESP32_APPTRACE_DEST_TRAX= +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_ENABLE= +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y + +# +# FreeRTOS SystemView Tracing +# +CONFIG_AWS_IOT_SDK= + +# +# Bluetooth +# +CONFIG_BT_ENABLED= +CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 +CONFIG_BT_RESERVE_DRAM=0 + +# +# ADC configuration +# +CONFIG_ADC_FORCE_XPD_FSM= +CONFIG_ADC2_DISABLE_DAC=y + +# +# ESP32-specific +# +CONFIG_ESP32_DEFAULT_CPU_FREQ_80= +CONFIG_ESP32_DEFAULT_CPU_FREQ_160= +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 +CONFIG_SPIRAM_SUPPORT=y + +# +# SPI RAM config +# +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_IGNORE_NOTFOUND=y +CONFIG_SPIRAM_USE_MEMMAP=y +CONFIG_SPIRAM_USE_CAPS_ALLOC= +CONFIG_SPIRAM_USE_MALLOC= +CONFIG_SPIRAM_TYPE_ESPPSRAM32=y +CONFIG_SPIRAM_SIZE=4194304 +CONFIG_SPIRAM_SPEED_40M=y +CONFIG_SPIRAM_SPEED_80M= +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_CACHE_WORKAROUND=y +CONFIG_MEMMAP_TRACEMEM= +CONFIG_MEMMAP_TRACEMEM_TWOBANKS= +CONFIG_ESP32_TRAX= +CONFIG_TRACEMEM_RESERVE_DRAM=0x0 +CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH= +CONFIG_ESP32_ENABLE_COREDUMP_TO_UART= +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +CONFIG_ESP32_ENABLE_COREDUMP= +CONFIG_TWO_UNIVERSAL_MAC_ADDRESS= +CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y +CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=64 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4096 +CONFIG_MAIN_TASK_STACK_SIZE=2048 +CONFIG_IPC_TASK_STACK_SIZE=4096 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_LF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +CONFIG_NEWLIB_NANO_FORMAT= +CONFIG_CONSOLE_UART_DEFAULT=y +CONFIG_CONSOLE_UART_CUSTOM= +CONFIG_CONSOLE_UART_NONE= +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ULP_COPROC_ENABLED= +CONFIG_ULP_COPROC_RESERVE_MEM=0 +CONFIG_ESP32_PANIC_PRINT_HALT=y +CONFIG_ESP32_PANIC_PRINT_REBOOT= +CONFIG_ESP32_PANIC_SILENT_REBOOT= +CONFIG_ESP32_PANIC_GDBSTUB= +CONFIG_ESP32_DEBUG_OCDAWARE= +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=1000 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +CONFIG_TASK_WDT_PANIC= +CONFIG_TASK_WDT_TIMEOUT_S=15 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +CONFIG_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_BROWNOUT_DET_LVL_SEL_1= +CONFIG_BROWNOUT_DET_LVL_SEL_2= +CONFIG_BROWNOUT_DET_LVL_SEL_3= +CONFIG_BROWNOUT_DET_LVL_SEL_4= +CONFIG_BROWNOUT_DET_LVL_SEL_5= +CONFIG_BROWNOUT_DET_LVL_SEL_6= +CONFIG_BROWNOUT_DET_LVL_SEL_7= +CONFIG_BROWNOUT_DET_LVL=0 +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC= +CONFIG_ESP32_TIME_SYSCALL_USE_FRC1= +CONFIG_ESP32_TIME_SYSCALL_USE_NONE= +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL= +CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 +CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES=100 +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32_XTAL_FREQ_40=y +CONFIG_ESP32_XTAL_FREQ_26= +CONFIG_ESP32_XTAL_FREQ_AUTO= +CONFIG_ESP32_XTAL_FREQ=40 +CONFIG_DISABLE_BASIC_ROM_CONSOLE= +CONFIG_NO_BLOBS= +CONFIG_ESP_TIMER_PROFILING= +CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS= +CONFIG_ESP_ERR_TO_NAME_LOOKUP= + +# +# Wi-Fi +# +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=8 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER= +CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP32_WIFI_TX_BA_WIN=6 +CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP32_WIFI_RX_BA_WIN=6 +CONFIG_ESP32_WIFI_NVS_ENABLED=y + +# +# PHY +# +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION=y +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 + +# +# Power Management +# +CONFIG_PM_ENABLE= + +# +# ADC-Calibration +# +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y + +# +# Ethernet +# +CONFIG_DMA_RX_BUF_NUM=10 +CONFIG_DMA_TX_BUF_NUM=10 +CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE= +CONFIG_EMAC_TASK_PRIORITY=20 + +# +# FAT Filesystem support +# +CONFIG_FATFS_CODEPAGE_DYNAMIC= +CONFIG_FATFS_CODEPAGE_437=y +CONFIG_FATFS_CODEPAGE_720= +CONFIG_FATFS_CODEPAGE_737= +CONFIG_FATFS_CODEPAGE_771= +CONFIG_FATFS_CODEPAGE_775= +CONFIG_FATFS_CODEPAGE_850= +CONFIG_FATFS_CODEPAGE_852= +CONFIG_FATFS_CODEPAGE_855= +CONFIG_FATFS_CODEPAGE_857= +CONFIG_FATFS_CODEPAGE_860= +CONFIG_FATFS_CODEPAGE_861= +CONFIG_FATFS_CODEPAGE_862= +CONFIG_FATFS_CODEPAGE_863= +CONFIG_FATFS_CODEPAGE_864= +CONFIG_FATFS_CODEPAGE_865= +CONFIG_FATFS_CODEPAGE_866= +CONFIG_FATFS_CODEPAGE_869= +CONFIG_FATFS_CODEPAGE_932= +CONFIG_FATFS_CODEPAGE_936= +CONFIG_FATFS_CODEPAGE_949= +CONFIG_FATFS_CODEPAGE_950= +CONFIG_FATFS_CODEPAGE=437 +CONFIG_FATFS_LFN_NONE= +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FATFS_LFN_STACK= +CONFIG_FATFS_MAX_LFN=127 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=y +CONFIG_FATFS_API_ENCODING_UTF_16= +CONFIG_FATFS_API_ENCODING_UTF_8= +CONFIG_FATFS_FS_LOCK=0 +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE= + +# +# FreeRTOS +# +CONFIG_FREERTOS_UNICORE= +CONFIG_FREERTOS_CORETIMER_0=y +CONFIG_FREERTOS_CORETIMER_1= +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION= +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE= +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL= +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK= +CONFIG_FREERTOS_INTERRUPT_BACKTRACE= +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 +CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y +CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= +CONFIG_FREERTOS_ASSERT_DISABLE= +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 +CONFIG_FREERTOS_ISR_STACKSIZE=4096 +CONFIG_FREERTOS_LEGACY_HOOKS= +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +CONFIG_SUPPORT_STATIC_ALLOCATION=y +CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK=y +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_USE_TRACE_FACILITY= +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= +CONFIG_FREERTOS_DEBUG_INTERNALS= + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED= +CONFIG_HEAP_POISONING_LIGHT= +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_HEAP_TRACING=y +CONFIG_HEAP_TRACING_STACK_DEPTH=2 +CONFIG_HEAP_TASK_TRACKING= + +# +# libsodium +# +CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y + +# +# Log output +# +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO= +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=4 +CONFIG_LOG_COLORS=y + +# +# LWIP +# +CONFIG_L2_TO_L3_COPY= +CONFIG_LWIP_IRAM_OPTIMIZATION= +CONFIG_LWIP_MAX_SOCKETS=10 +CONFIG_LWIP_SO_REUSE=y +CONFIG_LWIP_SO_REUSE_RXTOALL=y +CONFIG_LWIP_SO_RCVBUF= +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 +CONFIG_LWIP_IP_FRAG= +CONFIG_LWIP_IP_REASSEMBLY= +CONFIG_LWIP_STATS= +CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y +CONFIG_TCPIP_RECVMBOX_SIZE=32 +CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y + +# +# DHCP server +# +CONFIG_LWIP_DHCPS_LEASE_UNIT=60 +CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 +CONFIG_LWIP_AUTOIP= +CONFIG_LWIP_NETIF_LOOPBACK=y +CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# +CONFIG_LWIP_MAX_ACTIVE_TCP=16 +CONFIG_LWIP_MAX_LISTENING_TCP=16 +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=6 +CONFIG_TCP_MSS=1436 +CONFIG_TCP_MSL=60000 +CONFIG_TCP_SND_BUF_DEFAULT=5744 +CONFIG_TCP_WND_DEFAULT=5744 +CONFIG_TCP_RECVMBOX_SIZE=6 +CONFIG_TCP_QUEUE_OOSEQ=y +CONFIG_TCP_OVERSIZE_MSS=y +CONFIG_TCP_OVERSIZE_QUARTER_MSS= +CONFIG_TCP_OVERSIZE_DISABLE= + +# +# UDP +# +CONFIG_LWIP_MAX_UDP_PCBS=16 +CONFIG_UDP_RECVMBOX_SIZE=6 +CONFIG_TCPIP_TASK_STACK_SIZE=2560 +CONFIG_PPP_SUPPORT=y +CONFIG_PPP_PAP_SUPPORT=y +CONFIG_PPP_CHAP_SUPPORT= +CONFIG_PPP_MSCHAP_SUPPORT= +CONFIG_PPP_MPPE_SUPPORT= +CONFIG_PPP_DEBUG_ON= + +# +# ICMP +# +CONFIG_LWIP_MULTICAST_PING= +CONFIG_LWIP_BROADCAST_PING= + +# +# LWIP RAW API +# +CONFIG_LWIP_MAX_RAW_PCBS=16 + +# +# mbedTLS +# +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_DEBUG= +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_HARDWARE_MPI= +CONFIG_MBEDTLS_HARDWARE_SHA= +CONFIG_MBEDTLS_HAVE_TIME=y +CONFIG_MBEDTLS_HAVE_TIME_DATE= +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +CONFIG_MBEDTLS_TLS_SERVER_ONLY= +CONFIG_MBEDTLS_TLS_CLIENT_ONLY= +CONFIG_MBEDTLS_TLS_DISABLED= +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +CONFIG_MBEDTLS_PSK_MODES= +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_SSL3= +CONFIG_MBEDTLS_SSL_PROTO_TLS1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS= +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +CONFIG_MBEDTLS_CAMELLIA_C= +CONFIG_MBEDTLS_DES_C= +CONFIG_MBEDTLS_RC4_DISABLED=y +CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT= +CONFIG_MBEDTLS_RC4_ENABLED= +CONFIG_MBEDTLS_BLOWFISH_C= +CONFIG_MBEDTLS_XTEA_C= +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +CONFIG_MBEDTLS_RIPEMD160_C= + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y + +# +# OpenSSL +# +CONFIG_OPENSSL_DEBUG= +CONFIG_OPENSSL_ASSERT_DO_NOTHING=y +CONFIG_OPENSSL_ASSERT_EXIT= + +# +# PThreads +# +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 + +# +# SPI Flash driver +# +CONFIG_SPI_FLASH_VERIFY_WRITE= +CONFIG_SPI_FLASH_ENABLE_COUNTERS= +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS= +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED= + +# +# SPIFFS Configuration +# +CONFIG_SPIFFS_MAX_PARTITIONS=1 + +# +# SPIFFS Cache Configuration +# +CONFIG_SPIFFS_CACHE=y +CONFIG_SPIFFS_CACHE_WR=y +CONFIG_SPIFFS_CACHE_STATS= +CONFIG_SPIFFS_PAGE_CHECK=y +CONFIG_SPIFFS_GC_MAX_RUNS=10 +CONFIG_SPIFFS_GC_STATS= +CONFIG_SPIFFS_PAGE_SIZE=256 +CONFIG_SPIFFS_OBJ_NAME_LEN=64 +CONFIG_SPIFFS_USE_MAGIC=y +CONFIG_SPIFFS_USE_MAGIC_LENGTH=y +CONFIG_SPIFFS_META_LENGTH=5 +CONFIG_SPIFFS_USE_MTIME=y +CONFIG_SPIFFS_USE_DIR=y + +# +# Debug Configuration +# +CONFIG_SPIFFS_DBG= +CONFIG_SPIFFS_API_DBG= +CONFIG_SPIFFS_GC_DBG= +CONFIG_SPIFFS_CACHE_DBG= +CONFIG_SPIFFS_CHECK_DBG= +CONFIG_SPIFFS_TEST_VISUALISATION= + +# +# tcpip adapter +# +CONFIG_IP_LOST_TIMER_INTERVAL=120 + +# +# Wear Levelling +# +CONFIG_WL_SECTOR_SIZE_512= +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 diff --git a/Tools/.gitignore b/Tools/.gitignore index ddcbd819..156d8c76 100644 --- a/Tools/.gitignore +++ b/Tools/.gitignore @@ -3,6 +3,7 @@ esp-idf_psram/ xtensa-esp32-elf/ xtensa-esp32-elf_psram/ esp-idf_patches/ +esp_idf_patches_novi/ *.id *_new.tar.xz *_old.tar.xz diff --git a/Tools/esp-idf.tar.xz b/Tools/esp-idf.tar.xz index e11f6982..84f96aa9 100644 Binary files a/Tools/esp-idf.tar.xz and b/Tools/esp-idf.tar.xz differ diff --git a/Tools/esp_idf_patches/components/bootloader_support/src/bootloader_init.c b/Tools/esp_idf_patches/components/bootloader_support/src/bootloader_init.c new file mode 100644 index 00000000..d6029f40 --- /dev/null +++ b/Tools/esp_idf_patches/components/bootloader_support/src/bootloader_init.c @@ -0,0 +1,534 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include + +#include "esp_attr.h" +#include "esp_log.h" + +#include "rom/cache.h" +#include "rom/efuse.h" +#include "rom/ets_sys.h" +#include "rom/spi_flash.h" +#include "rom/crc.h" +#include "rom/rtc.h" +#include "rom/uart.h" +#include "rom/gpio.h" +#include "rom/secure_boot.h" + +#include "soc/soc.h" +#include "soc/cpu.h" +#include "soc/rtc.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/efuse_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" + +#include "sdkconfig.h" +#include "esp_image_format.h" +#include "esp_secure_boot.h" +#include "esp_flash_encrypt.h" +#include "esp_flash_partitions.h" +#include "bootloader_flash.h" +#include "bootloader_random.h" +#include "bootloader_config.h" +#include "bootloader_clock.h" + +#include "flash_qio_mode.h" + +extern int _bss_start; +extern int _bss_end; +extern int _data_start; +extern int _data_end; + +static const char* TAG = "boot"; + +static esp_err_t bootloader_main(); +static void print_flash_info(const esp_image_header_t* pfhdr); +static void update_flash_config(const esp_image_header_t* pfhdr); +static void vddsdio_configure(); +static void flash_gpio_configure(const esp_image_header_t* pfhdr); +static void uart_console_configure(void); +static void wdt_reset_check(void); + + +esp_err_t bootloader_init() +{ + cpu_configure_region_protection(); + + /* Sanity check that static RAM is after the stack */ +#ifndef NDEBUG + { + int *sp = get_sp(); + assert(&_bss_start <= &_bss_end); + assert(&_data_start <= &_data_end); + assert(sp < &_bss_start); + assert(sp < &_data_start); + } +#endif + + //Clear bss + memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start)); + + /* completely reset MMU for both CPUs + (in case serial bootloader was running) */ + Cache_Read_Disable(0); + Cache_Read_Disable(1); + Cache_Flush(0); + Cache_Flush(1); + mmu_init(0); + DPORT_REG_SET_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR); + mmu_init(1); + DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR); + /* (above steps probably unnecessary for most serial bootloader + usage, all that's absolutely needed is that we unmask DROM0 + cache on the following two lines - normal ROM boot exits with + DROM0 cache unmasked, but serial bootloader exits with it + masked. However can't hurt to be thorough and reset + everything.) + + The lines which manipulate DPORT_APP_CACHE_MMU_IA_CLR bit are + necessary to work around a hardware bug. + */ + DPORT_REG_CLR_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DROM0); + DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DROM0); + + if(bootloader_main() != ESP_OK){ + return ESP_FAIL; + } + return ESP_OK; +} + +static esp_err_t bootloader_main() +{ + vddsdio_configure(); + /* Read and keep flash ID, for further use. */ + g_rom_flashchip.device_id = bootloader_read_flash_id(); + esp_image_header_t fhdr; + if (bootloader_flash_read(ESP_BOOTLOADER_OFFSET, &fhdr, sizeof(esp_image_header_t), true) != ESP_OK) { + ESP_LOGE(TAG, "failed to load bootloader header!"); + return ESP_FAIL; + } + flash_gpio_configure(&fhdr); +#if (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240) + //Check if ESP32 is rated for a CPU frequency of 160MHz only + if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) && + REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) { + ESP_LOGE(TAG, "Chip CPU frequency rated for 160MHz. Modify CPU frequency in menuconfig"); + return ESP_FAIL; + } +#endif + bootloader_clock_configure(); + uart_console_configure(); + wdt_reset_check(); + + // LoBo, boot LED handling + #if CONFIG_BOOT_SET_LED >= 0 + gpio_pad_select_gpio(CONFIG_BOOT_SET_LED); + if (CONFIG_BOOT_SET_LED < 32) gpio_output_set(CONFIG_BOOT_LED_ON << CONFIG_BOOT_SET_LED, (CONFIG_BOOT_LED_ON ? 0 : 1) << CONFIG_BOOT_SET_LED, 1<spi_size) { + case ESP_IMAGE_FLASH_SIZE_1MB: + size = 1; + break; + case ESP_IMAGE_FLASH_SIZE_2MB: + size = 2; + break; + case ESP_IMAGE_FLASH_SIZE_4MB: + size = 4; + break; + case ESP_IMAGE_FLASH_SIZE_8MB: + size = 8; + break; + case ESP_IMAGE_FLASH_SIZE_16MB: + size = 16; + break; + default: + size = 2; + } + Cache_Read_Disable( 0 ); + // Set flash chip size + esp_rom_spiflash_config_param(g_rom_flashchip.device_id, size * 0x100000, 0x10000, 0x1000, 0x100, 0xffff); + // TODO: set mode + // TODO: set frequency + Cache_Flush(0); + Cache_Read_Enable( 0 ); +} + +static void print_flash_info(const esp_image_header_t* phdr) +{ +#if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) + + ESP_LOGD(TAG, "magic %02x", phdr->magic ); + ESP_LOGD(TAG, "segments %02x", phdr->segment_count ); + ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); + ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); + ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); + + const char* str; + switch ( phdr->spi_speed ) { + case ESP_IMAGE_SPI_SPEED_40M: + str = "40MHz"; + break; + case ESP_IMAGE_SPI_SPEED_26M: + str = "26.7MHz"; + break; + case ESP_IMAGE_SPI_SPEED_20M: + str = "20MHz"; + break; + case ESP_IMAGE_SPI_SPEED_80M: + str = "80MHz"; + break; + default: + str = "20MHz"; + break; + } + ESP_LOGI(TAG, "SPI Speed : %s", str ); + + /* SPI mode could have been set to QIO during boot already, + so test the SPI registers not the flash header */ + uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0)); + if (spi_ctrl & SPI_FREAD_QIO) { + str = "QIO"; + } else if (spi_ctrl & SPI_FREAD_QUAD) { + str = "QOUT"; + } else if (spi_ctrl & SPI_FREAD_DIO) { + str = "DIO"; + } else if (spi_ctrl & SPI_FREAD_DUAL) { + str = "DOUT"; + } else if (spi_ctrl & SPI_FASTRD_MODE) { + str = "FAST READ"; + } else { + str = "SLOW READ"; + } + ESP_LOGI(TAG, "SPI Mode : %s", str ); + + switch ( phdr->spi_size ) { + case ESP_IMAGE_FLASH_SIZE_1MB: + str = "1MB"; + break; + case ESP_IMAGE_FLASH_SIZE_2MB: + str = "2MB"; + break; + case ESP_IMAGE_FLASH_SIZE_4MB: + str = "4MB"; + break; + case ESP_IMAGE_FLASH_SIZE_8MB: + str = "8MB"; + break; + case ESP_IMAGE_FLASH_SIZE_16MB: + str = "16MB"; + break; + default: + str = "2MB"; + break; + } + ESP_LOGI(TAG, "SPI Flash Size : %s", str ); +#endif +} + +static void vddsdio_configure() +{ +#if CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V + rtc_vddsdio_config_t cfg = rtc_vddsdio_get_config(); + if (cfg.enable == 1 && cfg.tieh == 0) { // VDDSDIO regulator is enabled @ 1.8V + cfg.drefh = 3; + cfg.drefm = 3; + cfg.drefl = 3; + cfg.force = 1; + rtc_vddsdio_set_config(cfg); + ets_delay_us(10); // wait for regulator to become stable + } +#endif // CONFIG_BOOTLOADER_VDDSDIO_BOOST +} + +#define FLASH_CLK_IO 6 +#define FLASH_CS_IO 11 +#define FLASH_SPIQ_IO 7 +#define FLASH_SPID_IO 8 +#define FLASH_SPIWP_IO 10 +#define FLASH_SPIHD_IO 9 +#define FLASH_IO_MATRIX_DUMMY_40M 1 +#define FLASH_IO_MATRIX_DUMMY_80M 2 +#define FLASH_IO_DRIVE_GD_WITH_1V8PSRAM 3 + +/* + * Bootloader reads SPI configuration from bin header, so that + * the burning configuration can be different with compiling configuration. + */ +static void IRAM_ATTR flash_gpio_configure(const esp_image_header_t* pfhdr) +{ + int spi_cache_dummy = 0; + int drv = 2; + switch (pfhdr->spi_mode) { + case ESP_IMAGE_SPI_MODE_QIO: + spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; + break; + case ESP_IMAGE_SPI_MODE_DIO: + spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; //qio 3 + break; + case ESP_IMAGE_SPI_MODE_QOUT: + case ESP_IMAGE_SPI_MODE_DOUT: + default: + spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; + break; + } + + /* dummy_len_plus values defined in ROM for SPI flash configuration */ + extern uint8_t g_rom_spiflash_dummy_len_plus[]; + switch (pfhdr->spi_speed) { + case ESP_IMAGE_SPI_SPEED_80M: + g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_80M; + g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_80M; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_80M, + SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + drv = 3; + break; + case ESP_IMAGE_SPI_SPEED_40M: + g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_40M; + g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_40M; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_40M, + SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + break; + default: + break; + } + + uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); + uint32_t pkg_ver = chip_ver & 0x7; + + if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) { + // For ESP32D2WD the SPI pins are already configured + // flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) { + // For ESP32PICOD2 the SPI pins are already configured + // flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { + // For ESP32PICOD4 the SPI pins are already configured + // flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + } else { + const uint32_t spiconfig = ets_efuse_get_spiconfig(); + if (spiconfig == EFUSE_SPICONFIG_SPI_DEFAULTS) { + gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0); + gpio_matrix_out(FLASH_SPIQ_IO, SPIQ_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPIQ_IO, SPIQ_IN_IDX, 0); + gpio_matrix_out(FLASH_SPID_IO, SPID_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPID_IO, SPID_IN_IDX, 0); + gpio_matrix_out(FLASH_SPIWP_IO, SPIWP_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPIWP_IO, SPIWP_IN_IDX, 0); + gpio_matrix_out(FLASH_SPIHD_IO, SPIHD_OUT_IDX, 0, 0); + gpio_matrix_in(FLASH_SPIHD_IO, SPIHD_IN_IDX, 0); + //select pin function gpio + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO); + // flash clock signal should come from IO MUX. + // set drive ability for clock + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); + + #if CONFIG_SPIRAM_TYPE_ESPPSRAM32 + uint32_t flash_id = g_rom_flashchip.device_id; + if (flash_id == FLASH_ID_GD25LQ32C) { + // Set drive ability for 1.8v flash in 80Mhz. + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA0_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA1_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA2_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA3_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CMD_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S); + } + #endif + } + } +} + +static void uart_console_configure(void) +{ +#if CONFIG_CONSOLE_UART_NONE + ets_install_putc1(NULL); + ets_install_putc2(NULL); +#else // CONFIG_CONSOLE_UART_NONE + const int uart_num = CONFIG_CONSOLE_UART_NUM; + + uartAttach(); + ets_install_uart_printf(); + + // Wait for UART FIFO to be empty. + uart_tx_wait_idle(0); + +#if CONFIG_CONSOLE_UART_CUSTOM + // Some constants to make the following code less upper-case + const int uart_tx_gpio = CONFIG_CONSOLE_UART_TX_GPIO; + const int uart_rx_gpio = CONFIG_CONSOLE_UART_RX_GPIO; + // Switch to the new UART (this just changes UART number used for + // ets_printf in ROM code). + uart_tx_switch(uart_num); + // If console is attached to UART1 or if non-default pins are used, + // need to reconfigure pins using GPIO matrix + if (uart_num != 0 || uart_tx_gpio != 1 || uart_rx_gpio != 3) { + // Change pin mode for GPIO1/3 from UART to GPIO + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_GPIO3); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_GPIO1); + // Route GPIO signals to/from pins + // (arrays should be optimized away by the compiler) + const uint32_t tx_idx_list[3] = { U0TXD_OUT_IDX, U1TXD_OUT_IDX, U2TXD_OUT_IDX }; + const uint32_t rx_idx_list[3] = { U0RXD_IN_IDX, U1RXD_IN_IDX, U2RXD_IN_IDX }; + const uint32_t tx_idx = tx_idx_list[uart_num]; + const uint32_t rx_idx = rx_idx_list[uart_num]; + gpio_matrix_out(uart_tx_gpio, tx_idx, 0, 0); + gpio_matrix_in(uart_rx_gpio, rx_idx, 0); + } +#endif // CONFIG_CONSOLE_UART_CUSTOM + + // Set configured UART console baud rate + const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; + uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud); + +#endif // CONFIG_CONSOLE_UART_NONE +} + +static void wdt_reset_cpu0_info_enable(void) +{ + //We do not reset core1 info here because it didn't work before cpu1 was up. So we put it into call_start_cpu1. + DPORT_REG_SET_BIT(DPORT_PRO_CPU_RECORD_CTRL_REG, DPORT_PRO_CPU_PDEBUG_ENABLE | DPORT_PRO_CPU_RECORD_ENABLE); + DPORT_REG_CLR_BIT(DPORT_PRO_CPU_RECORD_CTRL_REG, DPORT_PRO_CPU_RECORD_ENABLE); +} + +static void wdt_reset_info_dump(int cpu) +{ + uint32_t inst = 0, pid = 0, stat = 0, data = 0, pc = 0, + lsstat = 0, lsaddr = 0, lsdata = 0, dstat = 0; + const char *cpu_name = cpu ? "APP" : "PRO"; + + if (cpu == 0) { + stat = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_STATUS_REG); + pid = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PID_REG); + inst = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGINST_REG); + dstat = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGSTATUS_REG); + data = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGDATA_REG); + pc = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGPC_REG); + lsstat = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGLS0STAT_REG); + lsaddr = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGLS0ADDR_REG); + lsdata = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGLS0DATA_REG); + + } else { + stat = DPORT_REG_READ(DPORT_APP_CPU_RECORD_STATUS_REG); + pid = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PID_REG); + inst = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGINST_REG); + dstat = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGSTATUS_REG); + data = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGDATA_REG); + pc = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGPC_REG); + lsstat = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGLS0STAT_REG); + lsaddr = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGLS0ADDR_REG); + lsdata = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGLS0DATA_REG); + } + if (DPORT_RECORD_PDEBUGINST_SZ(inst) == 0 && + DPORT_RECORD_PDEBUGSTATUS_BBCAUSE(dstat) == DPORT_RECORD_PDEBUGSTATUS_BBCAUSE_WAITI) { + ESP_LOGW(TAG, "WDT reset info: %s CPU PC=0x%x (waiti mode)", cpu_name, pc); + } else { + ESP_LOGW(TAG, "WDT reset info: %s CPU PC=0x%x", cpu_name, pc); + } + ESP_LOGD(TAG, "WDT reset info: %s CPU STATUS 0x%08x", cpu_name, stat); + ESP_LOGD(TAG, "WDT reset info: %s CPU PID 0x%08x", cpu_name, pid); + ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGINST 0x%08x", cpu_name, inst); + ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGSTATUS 0x%08x", cpu_name, dstat); + ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGDATA 0x%08x", cpu_name, data); + ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGPC 0x%08x", cpu_name, pc); + ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGLS0STAT 0x%08x", cpu_name, lsstat); + ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGLS0ADDR 0x%08x", cpu_name, lsaddr); + ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGLS0DATA 0x%08x", cpu_name, lsdata); +} + +static void wdt_reset_check(void) +{ + int wdt_rst = 0; + RESET_REASON rst_reas[2]; + + rst_reas[0] = rtc_get_reset_reason(0); + rst_reas[1] = rtc_get_reset_reason(1); + if (rst_reas[0] == RTCWDT_SYS_RESET || rst_reas[0] == TG0WDT_SYS_RESET || rst_reas[0] == TG1WDT_SYS_RESET || + rst_reas[0] == TGWDT_CPU_RESET || rst_reas[0] == RTCWDT_CPU_RESET) { + ESP_LOGW(TAG, "PRO CPU has been reset by WDT."); + wdt_rst = 1; + } + if (rst_reas[1] == RTCWDT_SYS_RESET || rst_reas[1] == TG0WDT_SYS_RESET || rst_reas[1] == TG1WDT_SYS_RESET || + rst_reas[1] == TGWDT_CPU_RESET || rst_reas[1] == RTCWDT_CPU_RESET) { + ESP_LOGW(TAG, "APP CPU has been reset by WDT."); + wdt_rst = 1; + } + if (wdt_rst) { + // if reset by WDT dump info from trace port + wdt_reset_info_dump(0); + wdt_reset_info_dump(1); + } + wdt_reset_cpu0_info_enable(); +} + +void __assert_func(const char *file, int line, const char *func, const char *expr) +{ + ESP_LOGE(TAG, "Assert failed in %s, %s:%d (%s)", func, file, line, expr); + while(1) {} +} diff --git a/Tools/esp_idf_patches/components/bootloader_support/src/bootloader_utility.c b/Tools/esp_idf_patches/components/bootloader_support/src/bootloader_utility.c new file mode 100644 index 00000000..e5abca97 --- /dev/null +++ b/Tools/esp_idf_patches/components/bootloader_support/src/bootloader_utility.c @@ -0,0 +1,508 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include + +#include "esp_attr.h" +#include "esp_log.h" + +#include "rom/cache.h" +#include "rom/efuse.h" +#include "rom/ets_sys.h" +#include "rom/spi_flash.h" +#include "rom/crc.h" +#include "rom/rtc.h" +#include "rom/uart.h" +#include "rom/gpio.h" +#include "rom/secure_boot.h" + +#include "soc/soc.h" +#include "soc/cpu.h" +#include "soc/rtc.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/efuse_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" + +#include "sdkconfig.h" +#include "esp_image_format.h" +#include "esp_secure_boot.h" +#include "esp_flash_encrypt.h" +#include "esp_flash_partitions.h" +#include "bootloader_flash.h" +#include "bootloader_random.h" +#include "bootloader_config.h" +#include "bootloader_common.h" + +static const char* TAG = "boot"; + +/* Reduce literal size for some generic string literals */ +#define MAP_ERR_MSG "Image contains multiple %s segments. Only the last one will be mapped." + +static void unpack_load_app(const esp_image_metadata_t *data); +static void set_cache_and_start_app(uint32_t drom_addr, + uint32_t drom_load_addr, + uint32_t drom_size, + uint32_t irom_addr, + uint32_t irom_load_addr, + uint32_t irom_size, + uint32_t entry_addr); + +bool bootloader_utility_load_partition_table(bootloader_state_t* bs) +{ + const esp_partition_info_t *partitions; + const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ + const char *partition_usage; + esp_err_t err; + int num_partitions; + +#ifdef CONFIG_SECURE_BOOT_ENABLED + if(esp_secure_boot_enabled()) { + ESP_LOGI(TAG, "Verifying partition table signature..."); + err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table signature."); + return false; + } + ESP_LOGD(TAG, "Partition table signature verified"); + } +#endif + + partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + if (!partitions) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + return false; + } + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); + + err = esp_partition_table_basic_verify(partitions, true, &num_partitions); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table"); + return false; + } + + ESP_LOGI(TAG, "Partition Table:"); + ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + + for(int i = 0; i < num_partitions; i++) { + const esp_partition_info_t *partition = &partitions[i]; + ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); + ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); + partition_usage = "unknown"; + + /* valid partition table */ + switch(partition->type) { + case PART_TYPE_APP: /* app partition */ + switch(partition->subtype) { + case PART_SUBTYPE_FACTORY: /* factory binary */ + bs->factory = partition->pos; + partition_usage = "factory app"; + break; + case PART_SUBTYPE_TEST: /* test binary */ + bs->test = partition->pos; + partition_usage = "test app"; + break; + default: + /* OTA binary */ + if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { + bs->ota[partition->subtype & PART_SUBTYPE_OTA_MASK] = partition->pos; + ++bs->app_count; + partition_usage = "OTA app"; + } + else { + partition_usage = "Unknown app"; + } + break; + } + break; /* PART_TYPE_APP */ + case PART_TYPE_DATA: /* data partition */ + switch(partition->subtype) { + case PART_SUBTYPE_DATA_OTA: /* ota data */ + bs->ota_info = partition->pos; + partition_usage = "OTA data"; + break; + case PART_SUBTYPE_DATA_RF: + partition_usage = "RF data"; + break; + case PART_SUBTYPE_DATA_WIFI: + partition_usage = "WiFi data"; + break; + default: + partition_usage = "Unknown data"; + break; + } + break; /* PARTITION_USAGE_DATA */ + default: /* other partition type */ + break; + } + + /* print partition type info */ + ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", i, partition->label, partition_usage, + partition->type, partition->subtype, + partition->pos.offset, partition->pos.size); + } + + bootloader_munmap(partitions); + + ESP_LOGI(TAG,"End of partition table"); + return true; +} + +/* Given a partition index, return the partition position data from the bootloader_state_t structure */ +static esp_partition_pos_t index_to_partition(const bootloader_state_t *bs, int index) +{ + if (index == FACTORY_INDEX) { + return bs->factory; + } + + if (index == TEST_APP_INDEX) { + return bs->test; + } + + if (index >= 0 && index < MAX_OTA_SLOTS && index < bs->app_count) { + return bs->ota[index]; + } + + esp_partition_pos_t invalid = { 0 }; + return invalid; +} + +static void log_invalid_app_partition(int index) +{ + const char *not_bootable = " is not bootable"; /* save a few string literal bytes */ + switch(index) { + case FACTORY_INDEX: + ESP_LOGE(TAG, "Factory app partition%s", not_bootable); + break; + case TEST_APP_INDEX: + ESP_LOGE(TAG, "Factory test app partition%s", not_bootable); + break; + default: + ESP_LOGE(TAG, "OTA app partition slot %d%s", index, not_bootable); + break; + } +} + +/** LoBo + * @function : ForceFactoryBoot + * @description: Check if boot from Factory partition is requested + * by the pin state defined in menuconfig + * + * @inputs: void + */ +//----------------------------------- +static uint8_t ForceFactoryBoot(void) +{ + #if CONFIG_GPIO_INPUT_FORCE_FACTORY + uint8_t timer = 0; + uint32_t mux_reg = GPIO_PIN_MUX_REG[CONFIG_GPIO_PIN_FORCE_FACTORY]; + if (mux_reg == 0) return 0; + + gpio_pad_select_gpio(CONFIG_GPIO_PIN_FORCE_FACTORY); + //output disable + if (CONFIG_GPIO_PIN_FORCE_FACTORY < 32) gpio_output_set(0,0,0, 1<ota_info.offset != 0 && !ForceFactoryBoot()) { // LoBo + // partition table has OTA data partition + if (bs->ota_info.size < 2 * SPI_SEC_SIZE) { + ESP_LOGE(TAG, "ota_info partition size %d is too small (minimum %d bytes)", bs->ota_info.size, sizeof(esp_ota_select_entry_t)); + return INVALID_INDEX; // can't proceed + } + + ESP_LOGD(TAG, "OTA data offset 0x%x", bs->ota_info.offset); + ota_select_map = bootloader_mmap(bs->ota_info.offset, bs->ota_info.size); + if (!ota_select_map) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs->ota_info.offset, bs->ota_info.size); + return INVALID_INDEX; // can't proceed + } + memcpy(&sa, ota_select_map, sizeof(esp_ota_select_entry_t)); + memcpy(&sb, (uint8_t *)ota_select_map + SPI_SEC_SIZE, sizeof(esp_ota_select_entry_t)); + bootloader_munmap(ota_select_map); + + ESP_LOGD(TAG, "OTA sequence values A 0x%08x B 0x%08x", sa.ota_seq, sb.ota_seq); + if(sa.ota_seq == UINT32_MAX && sb.ota_seq == UINT32_MAX) { + ESP_LOGD(TAG, "OTA sequence numbers both empty (all-0xFF)"); + if (bs->factory.offset != 0) { + ESP_LOGI(TAG, "Defaulting to factory image"); + return FACTORY_INDEX; + } else { + ESP_LOGI(TAG, "No factory image, trying OTA 0"); + return 0; + } + } else { + bool ota_valid = false; + const char *ota_msg; + int ota_seq; // Raw OTA sequence number. May be more than # of OTA slots + if(bootloader_common_ota_select_valid(&sa) && bootloader_common_ota_select_valid(&sb)) { + ota_valid = true; + ota_msg = "Both OTA values"; + ota_seq = MAX(sa.ota_seq, sb.ota_seq) - 1; + } else if(bootloader_common_ota_select_valid(&sa)) { + ota_valid = true; + ota_msg = "Only OTA sequence A is"; + ota_seq = sa.ota_seq - 1; + } else if(bootloader_common_ota_select_valid(&sb)) { + ota_valid = true; + ota_msg = "Only OTA sequence B is"; + ota_seq = sb.ota_seq - 1; + } + + if (ota_valid) { + int ota_slot = ota_seq % bs->app_count; // Actual OTA partition selection + ESP_LOGD(TAG, "%s valid. Mapping seq %d -> OTA slot %d", ota_msg, ota_seq, ota_slot); + return ota_slot; + } else if (bs->factory.offset != 0) { + ESP_LOGE(TAG, "ota data partition invalid, falling back to factory"); + return FACTORY_INDEX; + } else { + ESP_LOGE(TAG, "ota data partition invalid and no factory, will try all partitions"); + return FACTORY_INDEX; + } + } + } + + // otherwise, start from factory app partition and let the search logic + // proceed from there + return FACTORY_INDEX; +} + +/* Return true if a partition has a valid app image that was successfully loaded */ +static bool try_load_partition(const esp_partition_pos_t *partition, esp_image_metadata_t *data) +{ + if (partition->size == 0) { + ESP_LOGD(TAG, "Can't boot from zero-length partition"); + return false; + } +#ifdef BOOTLOADER_BUILD + if (esp_image_load(ESP_IMAGE_LOAD, partition, data) == ESP_OK) { + ESP_LOGI(TAG, "Loaded app from partition at offset 0x%x", + partition->offset); + return true; + } +#endif + + return false; +} + +#define TRY_LOG_FORMAT "Trying partition index %d offs 0x%x size 0x%x" + +bool bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index, esp_image_metadata_t *result) +{ + int index = start_index; + esp_partition_pos_t part; + + /* work backwards from start_index, down to the factory app */ + for(index = start_index; index >= FACTORY_INDEX; index--) { + part = index_to_partition(bs, index); + if (part.size == 0) { + continue; + } + ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size); + if (try_load_partition(&part, result)) { + return true; + } + log_invalid_app_partition(index); + } + + /* failing that work forwards from start_index, try valid OTA slots */ + for(index = start_index + 1; index < bs->app_count; index++) { + part = index_to_partition(bs, index); + if (part.size == 0) { + continue; + } + ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size); + if (try_load_partition(&part, result)) { + return true; + } + log_invalid_app_partition(index); + } + + if (try_load_partition(&bs->test, result)) { + ESP_LOGW(TAG, "Falling back to test app as only bootable partition"); + return true; + } + + ESP_LOGE(TAG, "No bootable app partitions in the partition table"); + bzero(result, sizeof(esp_image_metadata_t)); + return false; +} + +void bootloader_utility_load_image(const esp_image_metadata_t* image_data) +{ +#if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED) + esp_err_t err; +#endif +#ifdef CONFIG_SECURE_BOOT_ENABLED + /* Generate secure digest from this bootloader to protect future + modifications */ + ESP_LOGI(TAG, "Checking secure boot..."); + err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ + } +#endif + +#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED + /* encrypt flash */ + ESP_LOGI(TAG, "Checking flash encryption..."); + bool flash_encryption_enabled = esp_flash_encryption_enabled(); + err = esp_flash_encrypt_check_and_update(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Flash encryption check failed (%d).", err); + return; + } + + if (!flash_encryption_enabled && esp_flash_encryption_enabled()) { + /* Flash encryption was just enabled for the first time, + so issue a system reset to ensure flash encryption + cache resets properly */ + ESP_LOGI(TAG, "Resetting with flash encryption enabled..."); + REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); + return; + } +#endif + + ESP_LOGI(TAG, "Disabling RNG early entropy source..."); + bootloader_random_disable(); + + // copy loaded segments to RAM, set up caches for mapped segments, and start application + unpack_load_app(image_data); +} + +static void unpack_load_app(const esp_image_metadata_t* data) +{ + uint32_t drom_addr = 0; + uint32_t drom_load_addr = 0; + uint32_t drom_size = 0; + uint32_t irom_addr = 0; + uint32_t irom_load_addr = 0; + uint32_t irom_size = 0; + + // Find DROM & IROM addresses, to configure cache mappings + for (int i = 0; i < data->image.segment_count; i++) { + const esp_image_segment_header_t *header = &data->segments[i]; + if (header->load_addr >= SOC_IROM_LOW && header->load_addr < SOC_IROM_HIGH) { + if (drom_addr != 0) { + ESP_LOGE(TAG, MAP_ERR_MSG, "DROM"); + } else { + ESP_LOGD(TAG, "Mapping segment %d as %s", i, "DROM"); + } + drom_addr = data->segment_data[i]; + drom_load_addr = header->load_addr; + drom_size = header->data_len; + } + if (header->load_addr >= SOC_DROM_LOW && header->load_addr < SOC_DROM_HIGH) { + if (irom_addr != 0) { + ESP_LOGE(TAG, MAP_ERR_MSG, "IROM"); + } else { + ESP_LOGD(TAG, "Mapping segment %d as %s", i, "IROM"); + } + irom_addr = data->segment_data[i]; + irom_load_addr = header->load_addr; + irom_size = header->data_len; + } + } + + ESP_LOGD(TAG, "calling set_cache_and_start_app"); + set_cache_and_start_app(drom_addr, + drom_load_addr, + drom_size, + irom_addr, + irom_load_addr, + irom_size, + data->image.entry_addr); +} + +static void set_cache_and_start_app( + uint32_t drom_addr, + uint32_t drom_load_addr, + uint32_t drom_size, + uint32_t irom_addr, + uint32_t irom_load_addr, + uint32_t irom_size, + uint32_t entry_addr) +{ + ESP_LOGD(TAG, "configure drom and irom and start"); + Cache_Read_Disable( 0 ); + Cache_Flush( 0 ); + + /* Clear the MMU entries that are already set up, + so the new app only has the mappings it creates. + */ + for (int i = 0; i < DPORT_FLASH_MMU_TABLE_SIZE; i++) { + DPORT_PRO_FLASH_MMU_TABLE[i] = DPORT_FLASH_MMU_TABLE_INVALID_VAL; + } + + uint32_t drom_page_count = (drom_size + 64*1024 - 1) / (64*1024); // round up to 64k + ESP_LOGV(TAG, "d mmu set paddr=%08x vaddr=%08x size=%d n=%d", drom_addr & 0xffff0000, drom_load_addr & 0xffff0000, drom_size, drom_page_count ); + int rc = cache_flash_mmu_set( 0, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + rc = cache_flash_mmu_set( 1, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + uint32_t irom_page_count = (irom_size + 64*1024 - 1) / (64*1024); // round up to 64k + ESP_LOGV(TAG, "i mmu set paddr=%08x vaddr=%08x size=%d n=%d", irom_addr & 0xffff0000, irom_load_addr & 0xffff0000, irom_size, irom_page_count ); + rc = cache_flash_mmu_set( 0, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + rc = cache_flash_mmu_set( 1, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + DPORT_REG_CLR_BIT( DPORT_PRO_CACHE_CTRL1_REG, (DPORT_PRO_CACHE_MASK_IRAM0) | (DPORT_PRO_CACHE_MASK_IRAM1 & 0) | (DPORT_PRO_CACHE_MASK_IROM0 & 0) | DPORT_PRO_CACHE_MASK_DROM0 | DPORT_PRO_CACHE_MASK_DRAM1 ); + DPORT_REG_CLR_BIT( DPORT_APP_CACHE_CTRL1_REG, (DPORT_APP_CACHE_MASK_IRAM0) | (DPORT_APP_CACHE_MASK_IRAM1 & 0) | (DPORT_APP_CACHE_MASK_IROM0 & 0) | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 ); + Cache_Read_Enable( 0 ); + + // Application will need to do Cache_Flush(1) and Cache_Read_Enable(1) + + ESP_LOGD(TAG, "start: 0x%08x", entry_addr); + typedef void (*entry_t)(void); + entry_t entry = ((entry_t) entry_addr); + + // TODO: we have used quite a bit of stack at this point. + // use "movsp" instruction to reset stack back to where ROM stack starts. + (*entry)(); +} diff --git a/Tools/esp_idf_patches/components/driver/i2c.c b/Tools/esp_idf_patches/components/driver/i2c.c new file mode 100644 index 00000000..5772b6b5 --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/i2c.c @@ -0,0 +1,1338 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "malloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/dport_reg.h" +#include "soc/i2c_struct.h" +#include "soc/i2c_reg.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +static const char* I2C_TAG = "i2c"; +#define I2C_CHECK(a, str, ret) if(!(a)) { \ + ESP_LOGE(I2C_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } + +static portMUX_TYPE i2c_spinlock[I2C_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +/* DRAM_ATTR is required to avoid I2C array placed in flash, due to accessed from ISR */ +static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; + +#define I2C_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) +#define I2C_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +#define I2C_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define I2C_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +#define I2C_DRIVER_ERR_STR "i2c driver install error" +#define I2C_DRIVER_MALLOC_ERR_STR "i2c driver malloc error" +#define I2C_NUM_ERROR_STR "i2c number error" +#define I2C_TIMEING_VAL_ERR_STR "i2c timing value error" +#define I2C_ADDR_ERROR_STR "i2c null address error" +#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed" +#define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too small for slave mode" +#define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error" +#define I2C_SEM_ERR_STR "i2c semaphore error" +#define I2C_BUF_ERR_STR "i2c ringbuffer error" +#define I2C_MASTER_MODE_ERR_STR "Only allowed in master mode" +#define I2C_MODE_SLAVE_ERR_STR "Only allowed in slave mode" +#define I2C_CMD_MALLOC_ERR_STR "i2c command link malloc error" +#define I2C_TRANS_MODE_ERR_STR "i2c trans mode error" +#define I2C_MODE_ERR_STR "i2c mode error" +#define I2C_SDA_IO_ERR_STR "sda gpio number error" +#define I2C_SCL_IO_ERR_STR "scl gpio number error" +#define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error" +#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin does not support internal pull-up" +#define I2C_ACK_TYPE_ERR_STR "i2c ack type error" +#define I2C_DATA_LEN_ERR_STR "i2c data read length error" +#define I2C_FIFO_FULL_THRESH_VAL (28) +#define I2C_FIFO_EMPTY_THRESH_VAL (5) +#define I2C_IO_INIT_LEVEL (1) +#define I2C_CMD_ALIVE_INTERVAL_TICK (1000 / portTICK_PERIOD_MS) +#define I2C_CMD_EVT_ALIVE (0) +#define I2C_CMD_EVT_DONE (1) +#define I2C_EVT_QUEUE_LEN (1) +#define I2C_SLAVE_TIMEOUT_DEFAULT (32000) /* I2C slave timeout value, APB clock cycle number */ +#define I2C_SLAVE_SDA_SAMPLE_DEFAULT (10) /* I2C slave sample time after scl positive edge default value */ +#define I2C_SLAVE_SDA_HOLD_DEFAULT (10) /* I2C slave hold time after scl negative edge default value */ +#define I2C_MASTER_TOUT_CNUM_DEFAULT (8) /* I2C master timeout cycle number of I2C clock, after which the timeout interrupt will be triggered */ +#define I2C_ACKERR_CNT_MAX (10) + +typedef struct { + uint8_t byte_num; /*!< cmd byte number */ + uint8_t ack_en; /*!< ack check enable */ + uint8_t ack_exp; /*!< expected ack level to get */ + uint8_t ack_val; /*!< ack value to send */ + uint8_t* data; /*!< data address */ + uint8_t byte_cmd; /*!< to save cmd for one byte command mode */ + i2c_opmode_t op_code; /*!< haredware cmd type */ +}i2c_cmd_t; + +typedef struct i2c_cmd_link{ + i2c_cmd_t cmd; /*!< command in current cmd link */ + struct i2c_cmd_link *next; /*!< next cmd link */ +} i2c_cmd_link_t; + +typedef struct { + i2c_cmd_link_t* head; /*!< head of the command link */ + i2c_cmd_link_t* cur; /*!< last node of the command link */ + i2c_cmd_link_t* free; /*!< the first node to free of the command link */ +} i2c_cmd_desc_t; + +typedef enum { + I2C_STATUS_READ, /*!< read status for current master command */ + I2C_STATUS_WRITE, /*!< write status for current master command */ + I2C_STATUS_IDLE, /*!< idle status for current master command */ + I2C_STATUS_ACK_ERROR, /*!< ack error status for current master command */ + I2C_STATUS_DONE, /*!< I2C command done */ + I2C_STATUS_TIMEOUT, /*!< I2C bus status error, and operation timeout */ +} i2c_status_t; + +typedef struct { + int type; +} i2c_cmd_evt_t; + +typedef struct { + int i2c_num; /*!< I2C port number */ + int mode; /*!< I2C mode, master or slave */ + intr_handle_t intr_handle; /*!< I2C interrupt handle*/ + int cmd_idx; /*!< record current command index, for master mode */ + int status; /*!< record current command status, for master mode */ + int rx_cnt; /*!< record current read index, for master mode */ + uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */ + + i2c_cmd_desc_t cmd_link; /*!< I2C command link */ + QueueHandle_t cmd_evt_queue; /*!< I2C command event queue */ + xSemaphoreHandle cmd_mux; /*!< semaphore to lock command process */ + size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */ + size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */ + + xSemaphoreHandle slv_rx_mux; /*!< slave rx buffer mux */ + xSemaphoreHandle slv_tx_mux; /*!< slave tx buffer mux */ + size_t rx_buf_length; /*!< rx buffer length */ + RingbufHandle_t rx_ring_buf; /*!< rx ringbuffer handler of slave mode */ + size_t tx_buf_length; /*!< tx buffer length */ + RingbufHandle_t tx_ring_buf; /*!< tx ringbuffer handler of slave mode */ + TaskHandle_t *slave_task; /*!< slave task ID, used for notifications */ +} i2c_obj_t; + +static bool i2c_slave_trans_finished = false; +static uint32_t i2c_slave_trans_size = 0; +static i2c_obj_t *p_i2c_obj[I2C_NUM_MAX] = {0}; +static void i2c_isr_handler_default(void* arg); +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num); +static esp_err_t IRAM_ATTR i2c_hw_fsm_reset(i2c_port_t i2c_num); + +/* + For i2c master mode, we don't need to use a buffer for the data, the APIs will execute the master commands +and return after all of the commands have been sent out or when error occurs. So when we send master commands, +we should free or modify the source data only after the i2c_master_cmd_begin function returns. + For i2c slave mode, we need a data buffer to stash the sending and receiving data, because the hardware fifo +has only 32 bytes. +*/ +esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_buf_len, size_t slv_tx_buf_len, + int intr_alloc_flags) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(mode == I2C_MODE_MASTER || ( slv_rx_buf_len > 100 || slv_tx_buf_len > 100 ), I2C_SLAVE_BUFFER_LEN_ERR_STR, + ESP_ERR_INVALID_ARG); + uint32_t intr_mask = 0; + if (p_i2c_obj[i2c_num] == NULL) { + p_i2c_obj[i2c_num] = (i2c_obj_t*) calloc(1, sizeof(i2c_obj_t)); + if (p_i2c_obj[i2c_num] == NULL) { + ESP_LOGE(I2C_TAG, I2C_DRIVER_MALLOC_ERR_STR); + return ESP_FAIL; + } + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + p_i2c->i2c_num = i2c_num; + p_i2c->mode = mode; + p_i2c->slave_task = NULL; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->status = I2C_STATUS_IDLE; + + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + + if (mode == I2C_MODE_SLAVE) { + //we only use ringbuffer for slave mode. + if (slv_rx_buf_len > 0) { + p_i2c->rx_ring_buf = xRingbufferCreate(slv_rx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->rx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->rx_buf_length = slv_rx_buf_len; + } else { + p_i2c->rx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (slv_tx_buf_len > 0) { + p_i2c->tx_ring_buf = xRingbufferCreate(slv_tx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->tx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->tx_buf_length = slv_tx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + p_i2c->slv_rx_mux = xSemaphoreCreateMutex(); + p_i2c->slv_tx_mux = xSemaphoreCreateMutex(); + if (p_i2c->slv_rx_mux == NULL || p_i2c->slv_tx_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + intr_mask |= ( I2C_RXFIFO_FULL_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M); + } else { + //semaphore to sync sending process, because we only have 32 bytes for hardware fifo. + p_i2c->cmd_mux = xSemaphoreCreateMutex(); + p_i2c->cmd_evt_queue = xQueueCreate(I2C_EVT_QUEUE_LEN, sizeof(i2c_cmd_evt_t)); + if (p_i2c->cmd_mux == NULL || p_i2c->cmd_evt_queue == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + //command link + p_i2c->cmd_link.cur = NULL; + p_i2c->cmd_link.head = NULL; + p_i2c->cmd_link.free = NULL; + + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + intr_mask |= I2C_ARBITRATION_LOST_INT_ENA_M | I2C_TIME_OUT_INT_ST_M; + } + } else { + ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR); + return ESP_FAIL; + } + //hook isr handler + i2c_isr_register(i2c_num, i2c_isr_handler_default, p_i2c_obj[i2c_num], intr_alloc_flags, &p_i2c_obj[i2c_num]->intr_handle); + intr_mask |= ( I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M); + I2C[i2c_num]->int_clr.val = intr_mask; + I2C[i2c_num]->int_ena.val = intr_mask; + return ESP_OK; + + err: + //Some error has happened. Free/destroy all allocated things and return ESP_FAIL. + if (p_i2c_obj[i2c_num]) { + if (p_i2c_obj[i2c_num]->rx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->rx_ring_buf); + p_i2c_obj[i2c_num]->rx_ring_buf = NULL; + p_i2c_obj[i2c_num]->rx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->tx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->tx_ring_buf); + p_i2c_obj[i2c_num]->tx_ring_buf = NULL; + p_i2c_obj[i2c_num]->tx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->cmd_evt_queue) { + vQueueDelete(p_i2c_obj[i2c_num]->cmd_evt_queue); + p_i2c_obj[i2c_num]->cmd_evt_queue = NULL; + } + if (p_i2c_obj[i2c_num]->cmd_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux); + } + if (p_i2c_obj[i2c_num]->slv_rx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_rx_mux); + } + if (p_i2c_obj[i2c_num]->slv_tx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_tx_mux); + } + } + free(p_i2c_obj[i2c_num]); + p_i2c_obj[i2c_num] = NULL; + return ESP_FAIL; +} + +static esp_err_t i2c_hw_enable(i2c_port_t i2c_num) +{ + if (i2c_num == I2C_NUM_0) { + periph_module_enable(PERIPH_I2C0_MODULE); + } else if (i2c_num == I2C_NUM_1) { + periph_module_enable(PERIPH_I2C1_MODULE); + } + return ESP_OK; +} + +static esp_err_t i2c_hw_disable(i2c_port_t i2c_num) +{ + if (i2c_num == I2C_NUM_0) { + periph_module_disable(PERIPH_I2C0_MODULE); + } else if (i2c_num == I2C_NUM_1) { + periph_module_disable(PERIPH_I2C1_MODULE); + } + return ESP_OK; +} + +esp_err_t i2c_driver_delete(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + + I2C[i2c_num]->int_ena.val = 0; + esp_intr_free(p_i2c->intr_handle); + p_i2c->intr_handle = NULL; + p_i2c->slave_task = NULL; + + if (p_i2c->cmd_mux) { + xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY); + vSemaphoreDelete(p_i2c->cmd_mux); + } + if (p_i2c_obj[i2c_num]->cmd_evt_queue) { + vQueueDelete(p_i2c_obj[i2c_num]->cmd_evt_queue); + p_i2c_obj[i2c_num]->cmd_evt_queue = NULL; + } + if (p_i2c->slv_rx_mux) { + vSemaphoreDelete(p_i2c->slv_rx_mux); + } + if (p_i2c->slv_tx_mux) { + vSemaphoreDelete(p_i2c->slv_tx_mux); + } + + if (p_i2c->rx_ring_buf) { + vRingbufferDelete(p_i2c->rx_ring_buf); + p_i2c->rx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (p_i2c->tx_ring_buf) { + vRingbufferDelete(p_i2c->tx_ring_buf); + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + + free(p_i2c_obj[i2c_num]); + p_i2c_obj[i2c_num] = NULL; + + i2c_hw_disable(i2c_num); + return ESP_OK; +} + +esp_err_t i2c_reset_tx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_reset_rx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +static void IRAM_ATTR i2c_isr_handler_default(void* arg) +{ + i2c_obj_t* p_i2c = (i2c_obj_t*) arg; + int i2c_num = p_i2c->i2c_num; + uint32_t status = I2C[i2c_num]->int_status.val; + int idx = 0; + + portBASE_TYPE HPTaskAwoken = pdFALSE; + while (status != 0) { + status = I2C[i2c_num]->int_status.val; + if (status & I2C_TX_SEND_EMPTY_INT_ST_M) { + I2C[i2c_num]->int_clr.tx_send_empty = 1; + } else if (status & I2C_RX_REC_FULL_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_rec_full = 1; + } else if (status & I2C_ACK_ERR_INT_ST_M) { + I2C[i2c_num]->int_ena.ack_err = 0; + I2C[i2c_num]->int_clr.ack_err = 1; + if (p_i2c->mode == I2C_MODE_MASTER) { + p_i2c_obj[i2c_num]->status = I2C_STATUS_ACK_ERROR; + I2C[i2c_num]->int_clr.ack_err = 1; + //get error ack value from slave device, stop the commands + i2c_master_cmd_begin_static(i2c_num); + } + } else if (status & I2C_TRANS_START_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_start = 1; + } else if (status & I2C_TIME_OUT_INT_ST_M) { + I2C[i2c_num]->int_ena.time_out = 0; + I2C[i2c_num]->int_clr.time_out = 1; + p_i2c_obj[i2c_num]->status = I2C_STATUS_TIMEOUT; + i2c_master_cmd_begin_static(i2c_num); + } else if (status & I2C_TRANS_COMPLETE_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_complete = 1; + if (p_i2c->mode == I2C_MODE_SLAVE) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + i2c_slave_trans_size += rx_fifo_cnt; + if (i2c_slave_trans_finished) { + if ((p_i2c->slave_task) && (*p_i2c->slave_task)) { + xTaskNotifyFromISR(*p_i2c->slave_task, i2c_slave_trans_size, eSetValueWithOverwrite, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + } + i2c_slave_trans_finished = false; + i2c_slave_trans_size = 0; + } + } else { + // add check for unexcepted situations caused by noise. + if (p_i2c->status != I2C_STATUS_ACK_ERROR && p_i2c->status != I2C_STATUS_IDLE) { + i2c_master_cmd_begin_static(i2c_num); + } + } + } else if (status & I2C_MASTER_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.master_tran_comp = 1; + } else if (status & I2C_ARBITRATION_LOST_INT_ST_M) { + I2C[i2c_num]->int_clr.arbitration_lost = 1; + p_i2c_obj[i2c_num]->status = I2C_STATUS_TIMEOUT; + i2c_master_cmd_begin_static(i2c_num); + } else if (status & I2C_SLAVE_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.slave_tran_comp = 1; + i2c_slave_trans_finished = true; + } else if (status & I2C_END_DETECT_INT_ST_M) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + i2c_master_cmd_begin_static(i2c_num); + } else if (status & I2C_RXFIFO_OVF_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_fifo_ovf = 1; + } else if (status & I2C_TXFIFO_EMPTY_INT_ST_M) { + int tx_fifo_rem = I2C_FIFO_LEN - I2C[i2c_num]->status_reg.tx_fifo_cnt; + size_t size = 0; + uint8_t *data = (uint8_t*) xRingbufferReceiveUpToFromISR(p_i2c->tx_ring_buf, &size, tx_fifo_rem); + if (data) { + for (idx = 0; idx < size; idx++) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), data[idx]); + } + vRingbufferReturnItemFromISR(p_i2c->tx_ring_buf, data, &HPTaskAwoken); + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } else { + I2C[i2c_num]->int_ena.tx_fifo_empty = 0; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } + } else if (status & I2C_RXFIFO_FULL_INT_ST_M) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (p_i2c->mode == I2C_MODE_SLAVE) { + i2c_slave_trans_size += rx_fifo_cnt; + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + I2C[i2c_num]->int_clr.val = status; + } + } + if (p_i2c->mode == I2C_MODE_MASTER) { + i2c_cmd_evt_t evt; + evt.type = I2C_CMD_EVT_ALIVE; + xQueueSendFromISR(p_i2c->cmd_evt_queue, &evt, &HPTaskAwoken); + } + //We only need to check here if there is a high-priority task needs to be switched. + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(tx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(rx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = rx_trans_mode; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = tx_trans_mode; //set tx data msb first + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + if (tx_trans_mode) { + *tx_trans_mode = I2C[i2c_num]->ctr.tx_lsb_first; + } + if (rx_trans_mode) { + *rx_trans_mode = I2C[i2c_num]->ctr.rx_lsb_first; + } + return ESP_OK; +} + +/* Some slave device will die by accident and keep the SDA in low level, + * in this case, master should send several clock to make the slave release the bus. + * Slave mode of ESP32 might also get in wrong state that held the SDA low, + * in this case, master device could send a stop signal to make esp32 slave release the bus. + **/ +static esp_err_t i2c_master_clear_bus(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + int sda_in_sig = 0, scl_in_sig = 0; + if (i2c_num == I2C_NUM_0) { + sda_in_sig = I2CEXT0_SDA_IN_IDX; + scl_in_sig = I2CEXT0_SCL_IN_IDX; + } else if (i2c_num == I2C_NUM_1) { + sda_in_sig = I2CEXT1_SDA_IN_IDX; + scl_in_sig = I2CEXT1_SCL_IN_IDX; + } + int scl_io = GPIO.func_in_sel_cfg[scl_in_sig].func_sel; + int sda_io = GPIO.func_in_sel_cfg[sda_in_sig].func_sel; + I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io)), I2C_SCL_IO_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((GPIO_IS_VALID_GPIO(sda_io)), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); + // We do not check whether the SDA line is low + // because after some serious interference, the bus may keep high all the time and the i2c bus is out of service. + gpio_set_direction(scl_io, GPIO_MODE_OUTPUT_OD); + gpio_set_direction(sda_io, GPIO_MODE_OUTPUT_OD); + gpio_set_level(scl_io, 1); + gpio_set_level(sda_io, 1); + gpio_set_level(sda_io, 0); + for (int i = 0; i < 9; i++) { + gpio_set_level(scl_io, 0); + gpio_set_level(scl_io, 1); + } + gpio_set_level(sda_io, 1); + i2c_set_pin(i2c_num, sda_io, scl_io, 1, 1, I2C_MODE_MASTER); + return ESP_OK; +} + +/**if the power and SDA/SCL wires are in proper condition, everything works find with reading the slave. + * If we remove the power supply for the slave during I2C is reading, or directly connect SDA or SCL to ground, + * this would cause the I2C FSM get stuck in wrong state, all we can do is to reset the I2C hardware in this case. + **/ +static esp_err_t i2c_hw_fsm_reset(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + uint32_t ctr = I2C[i2c_num]->ctr.val; + uint32_t fifo_conf = I2C[i2c_num]->fifo_conf.val; + uint32_t scl_low_period = I2C[i2c_num]->scl_low_period.val; + uint32_t scl_high_period = I2C[i2c_num]->scl_high_period.val; + uint32_t scl_start_hold = I2C[i2c_num]->scl_start_hold.val; + uint32_t scl_rstart_setup = I2C[i2c_num]->scl_rstart_setup.val; + uint32_t scl_stop_hold = I2C[i2c_num]->scl_stop_hold.val; + uint32_t scl_stop_setup = I2C[i2c_num]->scl_stop_setup.val; + uint32_t sda_hold = I2C[i2c_num]->sda_hold.val; + uint32_t sda_sample = I2C[i2c_num]->sda_sample.val; + uint32_t timeout = I2C[i2c_num]->timeout.val; + uint32_t scl_filter_cfg = I2C[i2c_num]->scl_filter_cfg.val; + uint32_t sda_filter_cfg = I2C[i2c_num]->sda_filter_cfg.val; + uint32_t slave_addr = I2C[i2c_num]->slave_addr.val; + + //to reset the I2C hw module, we need re-enable the hw + i2c_hw_disable(i2c_num); + i2c_master_clear_bus(i2c_num); + i2c_hw_enable(i2c_num); + I2C[i2c_num]->int_ena.val = 0; + I2C[i2c_num]->ctr.val = ctr & (~I2C_TRANS_START_M); + I2C[i2c_num]->fifo_conf.val = fifo_conf; + I2C[i2c_num]->scl_low_period.val = scl_low_period; + I2C[i2c_num]->scl_high_period.val = scl_high_period; + I2C[i2c_num]->scl_start_hold.val = scl_start_hold; + I2C[i2c_num]->scl_rstart_setup.val = scl_rstart_setup; + I2C[i2c_num]->scl_stop_hold.val = scl_stop_hold; + I2C[i2c_num]->scl_stop_setup.val = scl_stop_setup; + I2C[i2c_num]->sda_hold.val = sda_hold; + I2C[i2c_num]->sda_sample.val = sda_sample; + I2C[i2c_num]->timeout.val = timeout; + I2C[i2c_num]->scl_filter_cfg.val = scl_filter_cfg; + I2C[i2c_num]->sda_filter_cfg.val = sda_filter_cfg; + uint32_t intr_mask = ( I2C_TRANS_COMPLETE_INT_ENA_M + | I2C_TRANS_START_INT_ENA_M + | I2C_ACK_ERR_INT_ENA_M + | I2C_RXFIFO_OVF_INT_ENA_M + | I2C_SLAVE_TRAN_COMP_INT_ENA_M + | I2C_TIME_OUT_INT_ENA_M); + if (I2C[i2c_num]->ctr.ms_mode == I2C_MODE_SLAVE) { + I2C[i2c_num]->slave_addr.val = slave_addr; + intr_mask |= ( I2C_RXFIFO_FULL_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M); + } else { + intr_mask |= I2C_ARBITRATION_LOST_INT_ENA_M; + } + I2C[i2c_num]->int_clr.val = intr_mask; + I2C[i2c_num]->int_ena.val = intr_mask; + return ESP_OK; +} + +esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t* i2c_conf) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf->mode < I2C_MODE_MAX, I2C_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num, + i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode); + if (ret != ESP_OK) { + return ret; + } + // Reset the I2C hardware in case there is a soft reboot. + i2c_hw_disable(i2c_num); + i2c_hw_enable(i2c_num); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set tx data msb first + I2C[i2c_num]->ctr.ms_mode = i2c_conf->mode; //mode for master or slave + I2C[i2c_num]->ctr.sda_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.scl_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.sample_scl_level = 0; //sample at high level of clock + + if (i2c_conf->mode == I2C_MODE_SLAVE) { //slave mode + I2C[i2c_num]->slave_addr.addr = i2c_conf->slave.slave_addr; + I2C[i2c_num]->slave_addr.en_10bit = i2c_conf->slave.addr_10bit_en; + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + I2C[i2c_num]->fifo_conf.fifo_addr_cfg_en = 0; + I2C[i2c_num]->fifo_conf.rx_fifo_full_thrhd = I2C_FIFO_FULL_THRESH_VAL; + I2C[i2c_num]->fifo_conf.tx_fifo_empty_thrhd = I2C_FIFO_EMPTY_THRESH_VAL; + I2C[i2c_num]->ctr.trans_start = 0; + I2C[i2c_num]->timeout.tout = I2C_SLAVE_TIMEOUT_DEFAULT; + //set timing for data + I2C[i2c_num]->sda_hold.time = I2C_SLAVE_SDA_HOLD_DEFAULT; + I2C[i2c_num]->sda_sample.time = I2C_SLAVE_SDA_SAMPLE_DEFAULT; + } else { + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + int cycle = (I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed); + int half_cycle = cycle / 2; + I2C[i2c_num]->timeout.tout = cycle * I2C_MASTER_TOUT_CNUM_DEFAULT; + //set timing for data + I2C[i2c_num]->sda_hold.time = half_cycle / 2; + I2C[i2c_num]->sda_sample.time = half_cycle / 2; + + I2C[i2c_num]->scl_low_period.period = half_cycle; + I2C[i2c_num]->scl_high_period.period = half_cycle; + //set timing for start signal + I2C[i2c_num]->scl_start_hold.time = half_cycle; + I2C[i2c_num]->scl_rstart_setup.time = half_cycle; + //set timing for stop signal + I2C[i2c_num]->scl_stop_hold.time = half_cycle; + I2C[i2c_num]->scl_stop_setup.time = half_cycle; + } + + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((high_period <= I2C_SCL_HIGH_PERIOD_V) && (high_period > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((low_period <= I2C_SCL_LOW_PERIOD_V) && (low_period > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->scl_high_period.period = high_period; + I2C[i2c_num]->scl_low_period.period = low_period; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_period(i2c_port_t i2c_num, int* high_period, int* low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (high_period) { + *high_period = I2C[i2c_num]->scl_high_period.period; + } + if (low_period) { + *low_period = I2C[i2c_num]->scl_low_period.period; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((hold_time <= I2C_SCL_START_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((setup_time <= I2C_SCL_RSTART_SETUP_TIME_V) && (setup_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->scl_start_hold.time = hold_time; + I2C[i2c_num]->scl_rstart_setup.time = setup_time; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_start_hold.time; + } + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_rstart_setup.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((setup_time <= I2C_SCL_STOP_SETUP_TIME_V) && (setup_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((hold_time <= I2C_SCL_STOP_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->scl_stop_hold.time = hold_time; + I2C[i2c_num]->scl_stop_setup.time = setup_time; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_stop_setup.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_stop_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((sample_time <= I2C_SDA_SAMPLE_TIME_V) && (sample_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((hold_time <= I2C_SDA_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->sda_hold.time = hold_time; + I2C[i2c_num]->sda_sample.time = sample_time; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (sample_time) { + *sample_time = I2C[i2c_num]->sda_sample.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->sda_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_timeout(i2c_port_t i2c_num, int timeout) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((timeout <= I2C_TIME_OUT_REG_V) && (timeout > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->timeout.tout = timeout; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_timeout(i2c_port_t i2c_num, int* timeout) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + if (timeout) { + *timeout = I2C[i2c_num]->timeout.tout; + } + return ESP_OK; +} + +esp_err_t i2c_isr_register(i2c_port_t i2c_num, void (*fn)(void*), void * arg, int intr_alloc_flags, intr_handle_t *handle) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(fn != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + esp_err_t ret; + switch (i2c_num) { + case I2C_NUM_1: + ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + case I2C_NUM_0: + default: + ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + } + return ret; +} + +esp_err_t i2c_isr_free(intr_handle_t handle) +{ + return esp_intr_free(handle); +} + +esp_err_t i2c_set_pin(i2c_port_t i2c_num, int sda_io_num, int scl_io_num, gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(((sda_io_num < 0) || ((GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num)))), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(scl_io_num < 0 || + (GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + (GPIO_IS_VALID_GPIO(scl_io_num) && mode == I2C_MODE_SLAVE), + I2C_SCL_IO_ERR_STR, + ESP_ERR_INVALID_ARG); + I2C_CHECK(sda_io_num < 0 || + (sda_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num)) || + sda_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(scl_io_num < 0 || + (scl_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + scl_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + + int sda_in_sig, sda_out_sig, scl_in_sig, scl_out_sig; + switch (i2c_num) { + case I2C_NUM_1: + sda_out_sig = I2CEXT1_SDA_OUT_IDX; + sda_in_sig = I2CEXT1_SDA_IN_IDX; + scl_out_sig = I2CEXT1_SCL_OUT_IDX; + scl_in_sig = I2CEXT1_SCL_IN_IDX; + break; + case I2C_NUM_0: + default: + sda_out_sig = I2CEXT0_SDA_OUT_IDX; + sda_in_sig = I2CEXT0_SDA_IN_IDX; + scl_out_sig = I2CEXT0_SCL_OUT_IDX; + scl_in_sig = I2CEXT0_SCL_IN_IDX; + break; + } + if (sda_io_num >= 0) { + gpio_set_level(sda_io_num, I2C_IO_INIT_LEVEL); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[sda_io_num], PIN_FUNC_GPIO); + gpio_set_direction(sda_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + + if (sda_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(sda_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(sda_io_num, GPIO_FLOATING); + } + gpio_matrix_out(sda_io_num, sda_out_sig, 0, 0); + gpio_matrix_in(sda_io_num, sda_in_sig, 0); + } + + if (scl_io_num >= 0) { + gpio_set_level(scl_io_num, I2C_IO_INIT_LEVEL); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[scl_io_num], PIN_FUNC_GPIO); + if (mode == I2C_MODE_MASTER) { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_matrix_out(scl_io_num, scl_out_sig, 0, 0); + } else { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT); + } + if (scl_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(scl_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(scl_io_num, GPIO_FLOATING); + } + gpio_matrix_in(scl_io_num, scl_in_sig, 0); + } + return ESP_OK; +} + +i2c_cmd_handle_t i2c_cmd_link_create() +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) calloc(1, sizeof(i2c_cmd_desc_t)); + return (i2c_cmd_handle_t) cmd_desc; +} + +void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle) +{ + if (cmd_handle == NULL) { + return; + } + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + while (cmd->free) { + i2c_cmd_link_t* ptmp = cmd->free; + cmd->free = cmd->free->next; + free(ptmp); + } + cmd->cur = NULL; + cmd->free = NULL; + cmd->head = NULL; + free(cmd_handle); + return; +} + +static esp_err_t i2c_cmd_link_append(i2c_cmd_handle_t cmd_handle, i2c_cmd_t* cmd) +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) cmd_handle; + if (cmd_desc->head == NULL) { + cmd_desc->head = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->head == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->head; + cmd_desc->free = cmd_desc->head; + } else { + cmd_desc->cur->next = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->cur->next == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->cur->next; + } + memcpy((uint8_t*) &cmd_desc->cur->cmd, (uint8_t*) cmd, sizeof(i2c_cmd_t)); + cmd_desc->cur->next = NULL; + return ESP_OK; + + err: + return ESP_FAIL; +} + +esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_RESTART; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_STOP; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, bool ack_en) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + uint8_t len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = NULL; + cmd.byte_cmd = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +static esp_err_t i2c_master_read_static(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, i2c_ack_type_t ack) +{ + int len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_READ; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, i2c_ack_type_t ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(ack < I2C_MASTER_ACK_MAX, I2C_ACK_TYPE_ERR_STR, ESP_ERR_INVALID_ARG); + + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ((ack == I2C_MASTER_LAST_NACK) ? I2C_MASTER_NACK : (ack & 0x1)); + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_READ; + cmd.data = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, i2c_ack_type_t ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(ack < I2C_MASTER_ACK_MAX, I2C_ACK_TYPE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(data_len > 0, I2C_DATA_LEN_ERR_STR, ESP_ERR_INVALID_ARG); + + if(ack != I2C_MASTER_LAST_NACK) { + return i2c_master_read_static(cmd_handle, data, data_len, ack); + } else { + if(data_len == 1) { + return i2c_master_read_byte(cmd_handle, data, I2C_MASTER_NACK); + } else { + esp_err_t ret; + if((ret = i2c_master_read_static(cmd_handle, data, data_len - 1, I2C_MASTER_ACK)) != ESP_OK) { + return ret; + } + return i2c_master_read_byte(cmd_handle, data + data_len - 1, I2C_MASTER_NACK); + } + } +} + +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE HPTaskAwoken = pdFALSE; + i2c_cmd_evt_t evt; + //This should never happen + if (p_i2c->mode == I2C_MODE_SLAVE) { + return; + } + if (p_i2c->status == I2C_STATUS_DONE) { + return; + } else if ((p_i2c->status == I2C_STATUS_ACK_ERROR) + || (p_i2c->status == I2C_STATUS_TIMEOUT)) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + if(p_i2c->status == I2C_STATUS_TIMEOUT) { + I2C[i2c_num]->int_clr.time_out = 1; + I2C[i2c_num]->int_ena.val = 0; + } + evt.type = I2C_CMD_EVT_DONE; + xQueueOverwriteFromISR(p_i2c->cmd_evt_queue, &evt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } else if (p_i2c->cmd_link.head != NULL && p_i2c->status == I2C_STATUS_READ) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + while (p_i2c->rx_cnt-- > 0) { + *cmd->data++ = READ_PERI_REG(I2C_DATA_APB_REG(i2c_num)); + } + if (cmd->byte_num > 0) { + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + } + if (p_i2c->cmd_link.head == NULL) { + p_i2c->cmd_link.cur = NULL; + evt.type = I2C_CMD_EVT_DONE; + xQueueOverwriteFromISR(p_i2c->cmd_evt_queue, &evt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + // Return to the IDLE status after cmd_eve_done signal were send out. + p_i2c->status = I2C_STATUS_IDLE; + return; + } + while (p_i2c->cmd_link.head) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + I2C[i2c_num]->command[p_i2c->cmd_idx].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_en = cmd->ack_en; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_exp = cmd->ack_exp; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx].op_code = cmd->op_code; + if (cmd->op_code == I2C_CMD_WRITE) { + uint32_t wr_filled = 0; + //TODO: to reduce interrupt number + if (cmd->data) { + while (p_i2c->tx_fifo_remain > 0 && cmd->byte_num > 0) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), *cmd->data++); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + wr_filled++; + } + } else { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), cmd->byte_cmd); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + wr_filled ++; + } + //Workaround for register field operation. + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = wr_filled; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + if (cmd->byte_num > 0) { + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + p_i2c->status = I2C_STATUS_WRITE; + break; + } else if(cmd->op_code == I2C_CMD_READ) { + //TODO: to reduce interrupt number + p_i2c->rx_cnt = cmd->byte_num > p_i2c->rx_fifo_remain ? p_i2c->rx_fifo_remain : cmd->byte_num; + cmd->byte_num -= p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->status = I2C_STATUS_READ; + break; + } else { + } + p_i2c->cmd_idx++; + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + if (p_i2c->cmd_link.head == NULL || p_i2c->cmd_idx >= 15) { + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + break; + } + } + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_ena.end_detect = 1; + I2C[i2c_num]->ctr.trans_start = 0; + I2C[i2c_num]->ctr.trans_start = 1; + return; +} + +esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_NOT_INSTALL_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_MASTER, I2C_MASTER_MODE_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + // Sometimes when the FSM get stuck, the ACK_ERR interrupt will occur endlessly until we reset the FSM and clear bus. + static uint8_t clear_bus_cnt = 0; + esp_err_t ret = ESP_FAIL; + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portTickType ticks_start = xTaskGetTickCount(); + portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait); + if (res == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + xQueueReset(p_i2c->cmd_evt_queue); + if (p_i2c->status == I2C_STATUS_TIMEOUT + || I2C[i2c_num]->status_reg.bus_busy == 1) { + i2c_hw_fsm_reset(i2c_num); + clear_bus_cnt = 0; + } + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + p_i2c->cmd_link.free = cmd->free; + p_i2c->cmd_link.cur = cmd->cur; + p_i2c->cmd_link.head = cmd->head; + p_i2c->status = I2C_STATUS_IDLE; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + // These two interrupts some times can not be cleared when the FSM gets stuck. + // so we disable them when these two interrupt occurs and re-enable them here. + I2C[i2c_num]->int_ena.ack_err = 1; + I2C[i2c_num]->int_ena.time_out = 1; + //start send commands, at most 32 bytes one time, isr handler will process the remaining commands. + i2c_master_cmd_begin_static(i2c_num); + + // Wait event bits + i2c_cmd_evt_t evt; + while (1) { + TickType_t wait_time = xTaskGetTickCount(); + if (wait_time - ticks_start > ticks_to_wait) { // out of time + wait_time = I2C_CMD_ALIVE_INTERVAL_TICK; + } else { + wait_time = ticks_to_wait - (wait_time - ticks_start); + if (wait_time < I2C_CMD_ALIVE_INTERVAL_TICK) { + wait_time = I2C_CMD_ALIVE_INTERVAL_TICK; + } + } + // In master mode, since we don't have an interrupt to detective bus error or FSM state, what we do here is to make + // sure the interrupt mechanism for master mode is still working. + // If the command sending is not finished and there is no interrupt any more, the bus is probably dead caused by external noise. + portBASE_TYPE evt_res = xQueueReceive(p_i2c->cmd_evt_queue, &evt, wait_time); + if (evt_res == pdTRUE) { + if (evt.type == I2C_CMD_EVT_DONE) { + if (p_i2c->status == I2C_STATUS_TIMEOUT) { + // If the I2C slave are powered off or the SDA/SCL are connected to ground, for example, + // I2C hw FSM would get stuck in wrong state, we have to reset the I2C module in this case. + i2c_hw_fsm_reset(i2c_num); + clear_bus_cnt = 0; + ret = ESP_ERR_TIMEOUT; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + clear_bus_cnt++; + if(clear_bus_cnt >= I2C_ACKERR_CNT_MAX) { + i2c_master_clear_bus(i2c_num); + clear_bus_cnt = 0; + } + ret = ESP_FAIL; + } else { + ret = ESP_OK; + } + break; + } + if (evt.type == I2C_CMD_EVT_ALIVE) { + } + } else { + ret = ESP_ERR_TIMEOUT; + // If the I2C slave are powered off or the SDA/SCL are connected to ground, for example, + // I2C hw FSM would get stuck in wrong state, we have to reset the I2C module in this case. + i2c_hw_fsm_reset(i2c_num); + clear_bus_cnt = 0; + break; + } + } + p_i2c->status = I2C_STATUS_DONE; + xSemaphoreGive(p_i2c->cmd_mux); + return ret; +} + +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, TickType_t ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + + portBASE_TYPE res; + int cnt = 0; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + + res = xSemaphoreTake(p_i2c->slv_tx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xRingbufferSend(p_i2c->tx_ring_buf, data, size, ticks_to_wait); + if (res == pdFALSE) { + cnt = 0; + } else { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + cnt = size; + } + xSemaphoreGive(p_i2c->slv_tx_mux); + return cnt; +} + +static int i2c_slave_read(i2c_port_t i2c_num, uint8_t* data, size_t max_size, TickType_t ticks_to_wait) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + size_t size = 0; + uint8_t* pdata = (uint8_t*) xRingbufferReceiveUpTo(p_i2c->rx_ring_buf, &size, ticks_to_wait, max_size); + if (pdata && size > 0) { + memcpy(data, pdata, size); + vRingbufferReturnItem(p_i2c->rx_ring_buf, pdata); + } + return size; +} + +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, TickType_t ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE res; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + res = xSemaphoreTake(p_i2c->slv_rx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + int cnt = i2c_slave_read(i2c_num, data, max_size, ticks_to_wait); + if (cnt > 0) { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + if (cnt < max_size && ticks_to_wait > 0) { + cnt += i2c_slave_read(i2c_num, data + cnt, max_size - cnt, ticks_to_wait); + } + } else { + cnt = 0; + } + xSemaphoreGive(p_i2c->slv_rx_mux); + return cnt; +} + +esp_err_t i2c_slave_add_task(i2c_port_t i2c_num, TaskHandle_t *slv_task) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + p_i2c->slave_task = slv_task; + + return ESP_OK; +} + +esp_err_t i2c_slave_remove_task(i2c_port_t i2c_num) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + p_i2c->slave_task = NULL; + + return ESP_OK; +} + diff --git a/Tools/esp_idf_patches/components/driver/include/driver/i2c.h b/Tools/esp_idf_patches/components/driver/include/driver/i2c.h new file mode 100644 index 00000000..acf4f47a --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/include/driver/i2c.h @@ -0,0 +1,564 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright (c) 2017-2018 LoBo (https://loboris@github.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_I2C_H_ +#define _DRIVER_I2C_H_ + + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" +#include "driver/gpio.h" + +#define I2C_APB_CLK_FREQ APB_CLK_FREQ /*!< I2C source clock is APB clock, 80MHz */ +#define I2C_FIFO_LEN (32) /*!< I2C hardware fifo length */ +typedef enum{ + I2C_MODE_SLAVE = 0, /*!< I2C slave mode */ + I2C_MODE_MASTER, /*!< I2C master mode */ + I2C_MODE_MAX, +}i2c_mode_t; + +typedef enum { + I2C_MASTER_WRITE = 0, /*!< I2C write data */ + I2C_MASTER_READ, /*!< I2C read data */ +} i2c_rw_t; + +typedef enum { + I2C_DATA_MODE_MSB_FIRST = 0, /*!< I2C data msb first */ + I2C_DATA_MODE_LSB_FIRST = 1, /*!< I2C data lsb first */ + I2C_DATA_MODE_MAX +} i2c_trans_mode_t; + +typedef enum{ + I2C_CMD_RESTART = 0, /*!=0) The number of data bytes that pushed to the I2C slave buffer. + */ +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, TickType_t ticks_to_wait); + +/** + * @brief I2C slave read data from internal buffer. When I2C slave receive data, isr will copy received data + * from hardware rx fifo to internal ringbuffer. Then users can read from internal ringbuffer. + * @note + * Only call this function in I2C slave mode + * + * @param i2c_num I2C port number + * @param data data pointer to write into internal buffer + * @param max_size Maximum data size to read + * @param ticks_to_wait Maximum waiting ticks + * + * @return + * - ESP_FAIL(-1) Parameter error + * - Others(>=0) The number of data bytes that read from I2C slave buffer. + */ +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, TickType_t ticks_to_wait); + +/** + * @brief set I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period clock cycle number during SCL is high level, high_period is a 14 bit value + * @param low_period clock cycle number during SCL is low level, low_period is a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period); + +/** + * @brief get I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period pointer to get clock cycle number during SCL is high level, will get a 14 bit value + * @param low_period pointer to get clock cycle number during SCL is low level, will get a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_period(i2c_port_t i2c_num, int* high_period, int* low_period); + +/** + * @brief set I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock number between the falling-edge of SDA and rising-edge of SCL for start mark, it's a 10-bit value. + * @param hold_time clock num between the falling-edge of SDA and falling-edge of SCL for start mark, it's a 10-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock num between the rising-edge of SCL and the rising-edge of SDA, it's a 10-bit value. + * @param hold_time clock number after the STOP bit's rising-edge, it's a 14-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time. + * @param hold_time pointer to get hold time. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time clock number I2C used to sample data on SDA after the rising-edge of SCL, it's a 10-bit value + * @param hold_time clock number I2C used to hold the data after the falling-edge of SCL, it's a 10-bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time); + +/** + * @brief get I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time pointer to get sample time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time); + +/** + * @brief set I2C timeout value + * @param i2c_num I2C port number + * @param timeout timeout value for I2C bus (unit: APB 80Mhz clock cycle) + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_timeout(i2c_port_t i2c_num, int timeout); + +/** + * @brief get I2C timeout value + * @param i2c_num I2C port number + * @param timeout pointer to get timeout value + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_timeout(i2c_port_t i2c_num, int* timeout); +/** + * @brief set I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode I2C sending data mode + * @param rx_trans_mode I2C receving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode); + +/** + * @brief get I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode pointer to get I2C sending data mode + * @param rx_trans_mode pointer to get I2C receiving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode); + +/** + * @brief Add the pointer to the FreeRTOS task handling i2c slave + * + * @param i2c_num I2C port number + * @param slv_task pointer to the i2c slave task + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error +*/ +esp_err_t i2c_slave_add_task(i2c_port_t i2c_num, TaskHandle_t *slv_task); + +/** + * @brief Remove the pointer to the FreeRTOS task handling i2c slave + * + * @param i2c_num I2C port number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error +*/ +esp_err_t i2c_slave_remove_task(i2c_port_t i2c_num); + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_I2C_H_*/ diff --git a/Tools/esp_idf_patches/components/driver/include/driver/sdspi_host.h b/Tools/esp_idf_patches/components/driver/include/driver/sdspi_host.h new file mode 100644 index 00000000..c1ce34fc --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/include/driver/sdspi_host.h @@ -0,0 +1,164 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// Copyright 2018 LoBo (https://github.com/loboris) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include "esp_err.h" +#include "sdmmc_types.h" +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "driver/spi_master_utils.h" +#include "driver/sdmmc_host.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default sdmmc_host_t structure initializer for SD over SPI driver + * + * Uses SPI mode and max frequency set to 20MHz + * + * 'slot' can be set to one of HSPI_HOST, VSPI_HOST. + */ +#define SDSPI_HOST_DEFAULT() {\ + .flags = SDMMC_HOST_FLAG_SPI, \ + .slot = HSPI_HOST, \ + .max_freq_khz = SDMMC_FREQ_DEFAULT, \ + .io_voltage = 3.3f, \ + .init = &sdspi_host_init, \ + .set_bus_width = NULL, \ + .get_bus_width = NULL, \ + .set_card_clk = &sdspi_host_set_card_clk, \ + .do_transaction = &sdspi_host_do_transaction, \ + .deinit = &sdspi_host_deinit, \ + .io_int_enable = NULL, \ + .io_int_wait = NULL, \ + .command_timeout_ms = 0, \ +} + +/** + * Extra configuration for SPI host + */ +typedef struct { + gpio_num_t gpio_miso; ///< GPIO number of MISO signal + gpio_num_t gpio_mosi; ///< GPIO number of MOSI signal + gpio_num_t gpio_sck; ///< GPIO number of SCK signal + gpio_num_t gpio_cs; ///< GPIO number of CS signal + gpio_num_t gpio_cd; ///< GPIO number of card detect signal + gpio_num_t gpio_wp; ///< GPIO number of write protect signal + int dma_channel; ///< DMA channel to be used by SPI driver (1 or 2) +} sdspi_slot_config_t; + +#define SDSPI_SLOT_NO_CD ((gpio_num_t) -1) ///< indicates that card detect line is not used +#define SDSPI_SLOT_NO_WP ((gpio_num_t) -1) ///< indicates that write protect line is not used + +/** + * Macro defining default configuration of SPI host + */ +#define SDSPI_SLOT_CONFIG_DEFAULT() {\ + .gpio_miso = GPIO_NUM_2, \ + .gpio_mosi = GPIO_NUM_15, \ + .gpio_sck = GPIO_NUM_14, \ + .gpio_cs = GPIO_NUM_13, \ + .gpio_cd = SDSPI_SLOT_NO_CD, \ + .gpio_wp = SDSPI_SLOT_NO_WP, \ + .dma_channel = 1 \ +} + +esp_err_t reinit_sdspi_dev(int slot); + +/** + * @brief Initialize SD SPI driver + * + * @note This function is not thread safe + * + * @return + * - ESP_OK on success + * - other error codes may be returned in future versions + */ +esp_err_t sdspi_host_init(); + +/** +* @brief Initialize SD SPI driver for the specific SPI controller +* +* @note This function is not thread safe +* +* @param slot SPI controller to use (HSPI_HOST or VSPI_HOST) +* @param slot_config pointer to slot configuration structure +* +* @return +* - ESP_OK on success +* - ESP_ERR_INVALID_ARG if sdspi_init_slot has invalid arguments +* - ESP_ERR_NO_MEM if memory can not be allocated +* - other errors from the underlying spi_master and gpio drivers +*/ +esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config); + +/** + * @brief Send command to the card and get response + * + * This function returns when command is sent and response is received, + * or data is transferred, or timeout occurs. + * + * @note This function is not thread safe w.r.t. init/deinit functions, + * and bus width/clock speed configuration functions. Multiple tasks + * can call sdspi_host_do_transaction as long as other sdspi_host_* + * functions are not called. + * + * @param slot SPI controller (HSPI_HOST or VSPI_HOST) + * @param cmdinfo pointer to structure describing command and data to transfer + * @return + * - ESP_OK on success + * - ESP_ERR_TIMEOUT if response or data transfer has timed out + * - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed + * - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response + */ +esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo); + +/** + * @brief Set card clock frequency + * + * Currently only integer fractions of 40MHz clock can be used. + * For High Speed cards, 40MHz can be used. + * For Default Speed cards, 20MHz can be used. + * + * @note This function is not thread safe + * + * @param slot SPI controller (HSPI_HOST or VSPI_HOST) + * @param freq_khz card clock frequency, in kHz + * @return + * - ESP_OK on success + * - other error codes may be returned in the future + */ +esp_err_t sdspi_host_set_card_clk(int slot, uint32_t freq_khz); + + +/** + * @brief Release resources allocated using sdspi_host_init + * + * @note This function is not thread safe + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if sdspi_host_init function has not been called + */ +esp_err_t sdspi_host_deinit(); + +#ifdef __cplusplus +} +#endif diff --git a/Tools/esp_idf_patches/components/driver/include/driver/spi_master_internal.h b/Tools/esp_idf_patches/components/driver/include/driver/spi_master_internal.h new file mode 100644 index 00000000..a1b1162a --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/include/driver/spi_master_internal.h @@ -0,0 +1,77 @@ +// Copyright 2010-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#ifndef _DRIVER_SPI_MASTER_INTERNAL_H_ +#define _DRIVER_SPI_MASTER_INTERNAL_H_ + +#include "driver/spi_master.h" +#include "esp_pm.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct spi_device_t spi_device_t; +typedef typeof(SPI1.clock) spi_clock_reg_t; + +#define NO_CS 6 //Number of CS pins per SPI host including software handled +#define NO_HWCS 3 //Number of hardware handled CS pins per SPI host + + +/// struct to hold private transaction data (like tx and rx buffer for DMA). +typedef struct { + spi_transaction_t *trans; + uint32_t *buffer_to_send; //equals to tx_data, if SPI_TRANS_USE_RXDATA is applied; otherwise if original buffer wasn't in DMA-capable memory, this gets the address of a temporary buffer that is; + //otherwise sets to the original buffer or NULL if no buffer is assigned. + uint32_t *buffer_to_rcv; // similar to buffer_to_send +} spi_trans_priv; + +typedef struct { + spi_device_t *device[NO_CS]; + intr_handle_t intr; + spi_dev_t *hw; + spi_trans_priv cur_trans_buf; + int cur_cs; + int prev_cs; + lldesc_t *dmadesc_tx; + lldesc_t *dmadesc_rx; + uint32_t flags; + int dma_chan; + int max_transfer_sz; +#ifdef CONFIG_PM_ENABLE + esp_pm_lock_handle_t pm_lock; +#endif +} spi_host_t; + +typedef struct { + spi_clock_reg_t reg; + int eff_clk; + int dummy_num; +} clock_config_t; + +struct spi_device_t { + QueueHandle_t trans_queue; + QueueHandle_t ret_queue; + spi_device_interface_config_t cfg; + clock_config_t clk_cfg; + spi_host_t *host; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Tools/esp_idf_patches/components/driver/include/driver/spi_master_utils.h b/Tools/esp_idf_patches/components/driver/include/driver/spi_master_utils.h new file mode 100644 index 00000000..c26da53d --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/include/driver/spi_master_utils.h @@ -0,0 +1,111 @@ +// Copyright 2010-2017 Espressif Systems (Shanghai) PTE LTD +// Copyright 2017 LoBo (https://github.com/loboris) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_SPI_MASTER_UTILS_H_ +#define _DRIVER_SPI_MASTER_UTILS_H_ + +#include "driver/spi_master.h" +#include "driver/spi_master_internal.h" + +typedef struct { + spi_device_handle_t handle; + int8_t cs; + int8_t dc; + uint8_t selected; + uint8_t spihost; + uint8_t dma_channel; + uint32_t curr_clock; + spi_bus_config_t *buscfg; + spi_device_interface_config_t devcfg; +} exspi_device_handle_t; + + +#define SPI_SEMAPHORE_WAIT 2000 // Time in ms to wait for SPI mutex +#define SDSPI_HOST_ID -2 // sdspi ID +#define TOTAL_CS (NO_CS*2) + +extern spi_bus_config_t *SPIbus_configs[3]; +extern QueueHandle_t spi_utils_mutex; + +/** + * @brief Check if spi slot with the same/different configuration is used + * + * @param spidev ext spidev structure to check + * + * @return + * - ESP_OK if other device uses the same spi host with the same configuration + * - ESP_FAIL if other device uses the same spi host with different configuration + * - 1 if the spi sost is not used + */ +esp_err_t check_spi_host(exspi_device_handle_t *spidev); + +/** + * @brief Check if some spi bus is used by sdspi driver + * + * @return + * - spi host slot used by sdspi driver or 0 if sdspi driver not used + */ +int spi_host_used_by_sdspi(); + +/** + * @brief Check for spi bus slots used by other than sdspi drivers + * + * @return + * - HSPI_HOST | VSPI_HOST combined integer + */ +int spi_host_not_used_by_sdspi(); + +/** + * @brief Add ext spi device to the list of used ext spi devices + * + * Checks for conflicts with other devices + * Initializes the spi bud if needed + * Add the device to the spi buscfg + * + * @return + * - ESP_OK on success + * - ESP_FAIL on error + */ +esp_err_t add_extspi_device(exspi_device_handle_t *spidev); + +/** + * @brief Remove ext spi device from the list of used ext spi devices + * + * @return + * - ESP_OK on success + * - ESP_FAIL on error + */ +esp_err_t remove_extspi_device(exspi_device_handle_t *spidev); + +esp_err_t spi_device_select(exspi_device_handle_t *spidev, int force); + +esp_err_t spi_device_deselect(exspi_device_handle_t *spidev); + +esp_err_t spi_transfer_data_nodma(exspi_device_handle_t *spidev, spi_transaction_t *trans); + +uint32_t spi_get_speed(exspi_device_handle_t *spidev); + +uint32_t spi_set_speed(exspi_device_handle_t *spidev, uint32_t speed); + +bool spi_uses_native_pins(spi_device_handle_t handle); + +void _spi_transfer_start(exspi_device_handle_t *spi_dev, int wrbits, int rdbits); + +void _dma_send(exspi_device_handle_t *spi_dev, uint8_t *data, uint32_t size); + +esp_err_t _wait_trans_finish(exspi_device_handle_t *spi_dev); + + +#endif diff --git a/Tools/esp_idf_patches/components/driver/sdspi_host.c b/Tools/esp_idf_patches/components/driver/sdspi_host.c new file mode 100644 index 00000000..7d2d8cb9 --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/sdspi_host.c @@ -0,0 +1,962 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// Copyright 2018 LoBo (https://github.com/loboris) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include "esp_log.h" +#include "esp_heap_caps.h" +#include "driver/gpio.h" +#include "driver/sdmmc_defs.h" +#include "driver/sdspi_host.h" +#include "sdspi_private.h" +#include "sdspi_crc.h" +#include "esp_timer.h" + + +/// Max number of transactions in flight (used in start_command_write_blocks) +#define SDSPI_TRANSACTION_COUNT 4 +#define SDSPI_MOSI_IDLE_VAL 0xff //!< Data value which causes MOSI to stay high +#define GPIO_UNUSED 0xff //!< Flag indicating that CD/WP is unused +/// Size of the buffer returned by get_block_buf +#define SDSPI_BLOCK_BUF_SIZE (SDSPI_MAX_DATA_LEN + 4) +/// Maximum number of dummy bytes between the request and response (minimum is 1) +#define SDSPI_RESPONSE_MAX_DELAY 8 + + +/// Structure containing run time configuration for a single SD slot +typedef struct { + exspi_device_handle_t spidev; //!< ext SPI device handle, used for transactions + uint8_t gpio_cd; //!< Card detect GPIO, or GPIO_UNUSED + uint8_t gpio_wp; //!< Write protect GPIO, or GPIO_UNUSED + /// Set to 1 if the higher layer has asked the card to enable CRC checks + uint8_t data_crc_enabled : 1; + /// Number of transactions in 'transactions' array which are in use + uint8_t used_transaction_count: 3; + /// Intermediate buffer used when application buffer is not in DMA memory; + /// allocated on demand, SDSPI_BLOCK_BUF_SIZE bytes long. May be zero. + uint8_t* block_buf; + /// array with SDSPI_TRANSACTION_COUNT transaction structures + spi_transaction_t* transactions; +} slot_info_t; + +static slot_info_t s_slots[3]; +static const char *TAG = "sdspi_host"; + +/// Functions to send out different kinds of commands +static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd, + uint8_t *data, uint32_t rx_length); + +static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd, + const uint8_t *data, uint32_t tx_length); + +static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd); + +/// A few helper functions + +/// Set CS high for given slot +//--------------------------- +static void cs_high(int slot) +{ + spi_device_deselect(&s_slots[slot].spidev); +} + +/// Set CS low for given slot +//-------------------------- +static void cs_low(int slot) +{ + spi_device_select(&s_slots[slot].spidev, 0); +} + +/// Return true if WP pin is configured and is low +//---------------------------------------- +static bool card_write_protected(int slot) +{ + if (s_slots[slot].gpio_wp == GPIO_UNUSED) { + return false; + } + return gpio_get_level(s_slots[slot].gpio_wp) == 0; +} + +/// Return true if CD pin is configured and is high +//-------------------------------- +static bool card_missing(int slot) +{ + if (s_slots[slot].gpio_cd == GPIO_UNUSED) { + return false; + } + return gpio_get_level(s_slots[slot].gpio_cd) == 1; +} + +/// Check if slot number is within bounds +//--------------------------------- +static bool is_valid_slot(int slot) +{ + return slot == VSPI_HOST || slot == HSPI_HOST; +} + +//--------------------------------------------- +static spi_device_handle_t spi_handle(int slot) +{ + return s_slots[slot].spidev.handle; +} + +//--------------------------------------- +static bool is_slot_initialized(int slot) +{ + return spi_handle(slot) != NULL; +} + +//------------------------------------ +static bool data_crc_enabled(int slot) +{ + return s_slots[slot].data_crc_enabled; +} + +/// Get pointer to a block of DMA memory, allocate if necessary. +/// This is used if the application provided buffer is not in DMA capable memory. +//--------------------------------------------------------- +static esp_err_t get_block_buf(int slot, uint8_t** out_buf) +{ + if (s_slots[slot].block_buf == NULL) { + s_slots[slot].block_buf = heap_caps_malloc(SDSPI_BLOCK_BUF_SIZE, MALLOC_CAP_DMA); + if (s_slots[slot].block_buf == NULL) { + return ESP_ERR_NO_MEM; + } + } + *out_buf = s_slots[slot].block_buf; + return ESP_OK; +} + +//------------------------------------------------- +static spi_transaction_t* get_transaction(int slot) +{ + size_t used_transaction_count = s_slots[slot].used_transaction_count; + assert(used_transaction_count < SDSPI_TRANSACTION_COUNT); + spi_transaction_t* ret = &s_slots[slot].transactions[used_transaction_count]; + ++s_slots[slot].used_transaction_count; + return ret; +} + +//--------------------------------------- +static void release_transaction(int slot) +{ + --s_slots[slot].used_transaction_count; +} + +//----------------------------------------- +static void wait_for_transactions(int slot) +{ + size_t used_transaction_count = s_slots[slot].used_transaction_count; + for (size_t i = 0; i < used_transaction_count; ++i) { + spi_transaction_t* t_out; + spi_device_get_trans_result(spi_handle(slot), &t_out, portMAX_DELAY); + release_transaction(slot); + } +} + +/// Clock out one byte (CS has to be high) to make the card release MISO +/// (clocking one bit would work as well, but that triggers a bug in SPI DMA) +//------------------------------- +static void release_bus(int slot) +{ + ESP_LOGV(TAG, "RELEASE_BUS"); + + cs_low(slot); + cs_high(slot); + spi_transaction_t t = { + .flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA, + .length = 8, + .tx_data = {0xff} + }; + spi_device_transmit(spi_handle(slot), &t); + // don't care if this failed +} + +/// Clock out 80 cycles (10 bytes) before GO_IDLE command +//------------------------------------ +static void go_idle_clockout(int slot) +{ + ESP_LOGV(TAG, "GO_IDLE_CLOCK"); + + cs_low(slot); + cs_high(slot); + //actually we need 10, declare 12 to meet requirement of RXDMA + uint8_t data[12]; + memset(data, 0xff, sizeof(data)); + spi_transaction_t t = { + .length = 10*8, + .tx_buffer = data, + .rx_buffer = data, + }; + spi_device_transmit(spi_handle(slot), &t); + // don't care if this failed +} + + +/// Return true if the pointer can be used for DMA +//--------------------------------------------- +static bool ptr_dma_compatible(const void* ptr) +{ + return (uintptr_t) ptr >= 0x3FFAE000 && + (uintptr_t) ptr < 0x40000000; +} + +/* + * Set the spi clock according to pre-calculated register value. + */ +//-------------------------------------------------------------------- +static inline void spi_set_clock(spi_dev_t *hw, spi_clock_reg_t reg) { + hw->clock.val = reg.val; +} + +/** + * Initialize SPI device or change clock speed. + * @param slot SPI host number + * @param clock_speed_hz clock speed, Hz + * @return ESP_OK on success + */ +//--------------------------------------------------------- +static esp_err_t init_spi_dev(int slot, int clock_speed_hz) +{ + if (s_slots[slot].spidev.curr_clock != clock_speed_hz) { + ESP_LOGD(TAG, "Change clock %d -> %d", s_slots[slot].spidev.curr_clock, clock_speed_hz); + spi_device_handle_t handle = s_slots[slot].spidev.handle; + spi_host_t *host=(spi_host_t*)handle->host; + + int apbclk=APB_CLK_FREQ; + handle->clk_cfg.eff_clk = spi_cal_clock(apbclk, clock_speed_hz, handle->cfg.duty_cycle_pos, (uint32_t*)&handle->clk_cfg.reg); + spi_set_clock(host->hw, handle->clk_cfg.reg); + s_slots[slot].spidev.curr_clock = clock_speed_hz; + } + return ESP_OK; +} + +//------------------------- +esp_err_t sdspi_host_init() +{ + return ESP_OK; +} + +//--------------------------- +esp_err_t sdspi_host_deinit() +{ + for (size_t i = 0; i < sizeof(s_slots)/sizeof(s_slots[0]); ++i) { + if (s_slots[i].spidev.handle) { + remove_extspi_device(&s_slots[i].spidev); + free(s_slots[i].block_buf); + s_slots[i].block_buf = NULL; + free(s_slots[i].transactions); + s_slots[i].transactions = NULL; + } + } + return ESP_OK; +} + +//---------------------------------- +esp_err_t reinit_sdspi_dev(int slot) +{ + if (s_slots[slot].spidev.handle) { + // DeInit SPI bus + esp_err_t ret = spi_bus_remove_device(s_slots[slot].spidev.handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Select: Error Removing sdspi device from bus %d", s_slots[slot].spidev.spihost); + return ret; + } + s_slots[slot].spidev.handle = NULL; + + // Free the spi bus + spi_bus_free((spi_host_device_t) s_slots[slot].spidev.spihost); + + // ReInitialize SPI bus + ret = spi_bus_initialize(s_slots[slot].spidev.spihost, s_slots[slot].spidev.buscfg, s_slots[slot].spidev.dma_channel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Select: Error ReInitializing spi bus %d", s_slots[slot].spidev.spihost); + return ret; + } + // Add device + ret = spi_bus_add_device(s_slots[slot].spidev.spihost, &s_slots[slot].spidev.devcfg, &s_slots[slot].spidev.handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Select: Error ReAdding sdspi device to bus %d", s_slots[slot].spidev.spihost); + s_slots[slot].spidev.handle = NULL; + return ret; + } + init_spi_dev(slot, s_slots[slot].spidev.curr_clock); + } + return ESP_OK; +} + +//------------------------------------------------------------ +esp_err_t sdspi_host_set_card_clk(int slot, uint32_t freq_khz) +{ + if (!is_valid_slot(slot)) { + return ESP_ERR_INVALID_ARG; + } + if (!is_slot_initialized(slot)) { + return ESP_ERR_INVALID_STATE; + } + ESP_LOGD(TAG, "Setting card clock to %d kHz", freq_khz); + return init_spi_dev(slot, freq_khz * 1000); +} + +//============================================================================== +esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config) +{ + ESP_LOGD(TAG, "%s: SPI%d miso=%d mosi=%d sck=%d cs=%d cd=%d wp=%d, dma_ch=%d", + __func__, slot + 1, + slot_config->gpio_miso, slot_config->gpio_mosi, + slot_config->gpio_sck, slot_config->gpio_cs, + slot_config->gpio_cd, slot_config->gpio_wp, + slot_config->dma_channel); + + if (!is_valid_slot(slot)) { + return ESP_ERR_INVALID_ARG; + } + + // Check if requested slot is uded by another device + int used_spi = spi_host_not_used_by_sdspi(); + if (used_spi != 0) { + if (used_spi == (HSPI_HOST | VSPI_HOST)) { + // all slots used + ESP_LOGE(TAG, "Error configuring spi bus, no free slots"); + return ESP_ERR_INVALID_ARG; + } + if (used_spi == HSPI_HOST) slot = VSPI_HOST; + else if (used_spi == VSPI_HOST) slot = HSPI_HOST; + else return ESP_ERR_INVALID_ARG; + ESP_LOGW(TAG, "spi bus changed (%d -> %d)", used_spi, slot); + } + + // Configure the spi bus + s_slots[slot].spidev.buscfg = SPIbus_configs[slot]; + if (s_slots[slot].spidev.buscfg == NULL) { + ESP_LOGE(TAG, "spi bus %d not available ", slot); + return ESP_ERR_INVALID_ARG; + } + + s_slots[slot].spidev.buscfg->miso_io_num = slot_config->gpio_miso; + s_slots[slot].spidev.buscfg->mosi_io_num = slot_config->gpio_mosi; + s_slots[slot].spidev.buscfg->sclk_io_num = slot_config->gpio_sck; + s_slots[slot].spidev.buscfg->quadwp_io_num = -1; + s_slots[slot].spidev.buscfg->quadhd_io_num = -1; + + // Configure the spi device + s_slots[slot].spidev.devcfg.clock_speed_hz = SDMMC_FREQ_PROBING * 1000; + s_slots[slot].spidev.devcfg.mode = 0; + // For SD cards, CS must stay low during the whole read/write operation, + // rather than a single SPI transaction, so we use external CS. + s_slots[slot].spidev.devcfg.spics_io_num = -1, + s_slots[slot].spidev.devcfg.queue_size = SDSPI_TRANSACTION_COUNT, + + s_slots[slot].spidev.dma_channel = slot_config->dma_channel; + s_slots[slot].spidev.curr_clock = SDMMC_FREQ_PROBING * 1000; + s_slots[slot].spidev.spihost = slot; + s_slots[slot].spidev.handle = NULL; + s_slots[slot].spidev.dc = SDSPI_HOST_ID; + s_slots[slot].spidev.selected = 0; + + esp_err_t ret = check_spi_host(&s_slots[slot].spidev); + if (ret != 1) { + // Other spi host uses different pins + ESP_LOGE(TAG, "spi bus already used with different configuration (%d)", slot); + return ESP_ERR_INVALID_ARG; + } + // Initialize the spi bus and add the device + ret = add_extspi_device(&s_slots[slot].spidev); + if (ret != ESP_OK) return ret; + + // Configure CS pin + s_slots[slot].spidev.cs = (uint8_t) slot_config->gpio_cs; + gpio_config_t io_conf = { + .intr_type = GPIO_PIN_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1LL << slot_config->gpio_cs, + }; + ret = gpio_config(&io_conf); + if (ret != ESP_OK) { + ESP_LOGD(TAG, "gpio_config (CS) failed with rc=0x%x", ret); + remove_extspi_device(&s_slots[slot].spidev); + return ret; + } + // set the CS high; + gpio_set_level(s_slots[slot].spidev.cs, 1); + + // Configure CD and WP pins + io_conf = (gpio_config_t) { + .intr_type = GPIO_PIN_INTR_DISABLE, + .mode = GPIO_MODE_INPUT, + .pin_bit_mask = 0, + .pull_up_en = true + }; + if (slot_config->gpio_cd != SDSPI_SLOT_NO_CD) { + io_conf.pin_bit_mask |= (1 << slot_config->gpio_cd); + s_slots[slot].gpio_cd = slot_config->gpio_cd; + } else { + s_slots[slot].gpio_cd = GPIO_UNUSED; + } + + if (slot_config->gpio_wp != SDSPI_SLOT_NO_WP) { + io_conf.pin_bit_mask |= (1 << slot_config->gpio_wp); + s_slots[slot].gpio_wp = slot_config->gpio_wp; + } else { + s_slots[slot].gpio_wp = GPIO_UNUSED; + } + + if (io_conf.pin_bit_mask != 0) { + ret = gpio_config(&io_conf); + if (ret != ESP_OK) { + ESP_LOGD(TAG, "gpio_config (CD/WP) failed with rc=0x%x", ret); + remove_extspi_device(&s_slots[slot].spidev); + return ret; + } + } + + s_slots[slot].transactions = calloc(SDSPI_TRANSACTION_COUNT, sizeof(spi_transaction_t)); + if (s_slots[slot].transactions == NULL) { + remove_extspi_device(&s_slots[slot].spidev); + return ESP_ERR_NO_MEM; + } + + return ESP_OK; +} + +//--------------------------------------------------------------------------- +esp_err_t sdspi_host_start_command(int slot, sdspi_hw_cmd_t *cmd, void *data, + uint32_t data_size, int flags) +{ + if (!is_valid_slot(slot)) { + return ESP_ERR_INVALID_ARG; + } + if (!is_slot_initialized(slot)) { + return ESP_ERR_INVALID_STATE; + } + if (card_missing(slot)) { + return ESP_ERR_NOT_FOUND; + } + // save some parts of cmd, as its contents will be overwritten + int cmd_index = cmd->cmd_index; + uint32_t cmd_arg; + memcpy(&cmd_arg, cmd->arguments, sizeof(cmd_arg)); + cmd_arg = __builtin_bswap32(cmd_arg); + ESP_LOGV(TAG, "%s: slot=%i, CMD%d, arg=0x%08x flags=0x%x, data=%p, data_size=%i crc=0x%02x", + __func__, slot, cmd_index, cmd_arg, flags, data, data_size, cmd->crc7); + + + // For CMD0, clock out 80 cycles to help the card enter idle state, + // *before* CS is asserted. + release_bus(slot); + if (cmd_index == MMC_GO_IDLE_STATE) { + go_idle_clockout(slot); + } + // actual transaction + esp_err_t ret = ESP_OK; + cs_low(slot); + if (flags & SDSPI_CMD_FLAG_DATA) { + if (flags & SDSPI_CMD_FLAG_WRITE) { + ret = start_command_write_blocks(slot, cmd, data, data_size); + } else { + ret = start_command_read_blocks(slot, cmd, data, data_size); + } + } else { + ret = start_command_default(slot, flags, cmd); + } + cs_high(slot); + + release_bus(slot); + + if (ret != ESP_OK) { + ESP_LOGD(TAG, "%s: cmd=%d error=0x%x", __func__, cmd_index, ret); + } else { + // Update internal state when some commands are sent successfully + if (cmd_index == SD_CRC_ON_OFF) { + s_slots[slot].data_crc_enabled = (uint8_t) cmd_arg; + ESP_LOGD(TAG, "data CRC set=%d", s_slots[slot].data_crc_enabled); + } + } + return ret; +} + +//------------------------------------------------------------------------------ +static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd) +{ + size_t cmd_size = SDSPI_CMD_R1_SIZE; + if ((flags & SDSPI_CMD_FLAG_RSP_R1) || + (flags & SDSPI_CMD_FLAG_NORSP)) { + cmd_size = SDSPI_CMD_R1_SIZE; + } else if (flags & SDSPI_CMD_FLAG_RSP_R2) { + cmd_size = SDSPI_CMD_R2_SIZE; + } else if (flags & SDSPI_CMD_FLAG_RSP_R3) { + cmd_size = SDSPI_CMD_R3_SIZE; + } else if (flags & SDSPI_CMD_FLAG_RSP_R7) { + cmd_size = SDSPI_CMD_R7_SIZE; + } + spi_transaction_t t = { + .flags = 0, + .length = cmd_size * 8, + .tx_buffer = cmd, + .rx_buffer = cmd + }; + esp_err_t ret = spi_device_transmit(spi_handle(slot), &t); + if (cmd->cmd_index == MMC_STOP_TRANSMISSION) { + /* response is a stuff byte from previous transfer, ignore it */ + cmd->r1 = 0xff; + } + if (flags & SDSPI_CMD_FLAG_NORSP) { + /* no (correct) response expected from the card, so skip polling loop */ + ESP_LOGV(TAG, "%s: ignoring response byte", __func__); + cmd->r1 = 0x00; + } + int response_delay_bytes = SDSPI_RESPONSE_MAX_DELAY; + while ((cmd->r1 & SD_SPI_R1_NO_RESPONSE) != 0 && response_delay_bytes-- > 0) { + spi_transaction_t* t = get_transaction(slot); + *t = (spi_transaction_t) { + .flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA, + .length = 8, + }; + t->tx_data[0] = 0xff; + ret = spi_device_transmit(spi_handle(slot), t); + uint8_t r1 = t->rx_data[0]; + release_transaction(slot); + if (ret != ESP_OK) { + return ret; + } + cmd->r1 = r1; + } + if (cmd->r1 & SD_SPI_R1_NO_RESPONSE) { + ESP_LOGD(TAG, "%s: no response token found", __func__); + return ESP_ERR_TIMEOUT; + } + return ret; +} + +// Wait until MISO goes high +//------------------------------------------------------------------------ +static esp_err_t poll_busy(int slot, spi_transaction_t* t, int timeout_ms) +{ + uint8_t t_rx; + *t = (spi_transaction_t) { + .tx_buffer = &t_rx, + .flags = SPI_TRANS_USE_RXDATA, //data stored in rx_data + .length = 8, + }; + esp_err_t ret; + + uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000; + int nonzero_count = 0; + do { + t_rx = SDSPI_MOSI_IDLE_VAL; + t->rx_data[0] = 0; + ret = spi_device_transmit(spi_handle(slot), t); + if (ret != ESP_OK) { + return ret; + } + if (t->rx_data[0] != 0) { + if (++nonzero_count == 2) { + return ESP_OK; + } + } + } while(esp_timer_get_time() < t_end); + ESP_LOGD(TAG, "%s: timeout", __func__); + return ESP_ERR_TIMEOUT; +} + +// Wait for response token +//---------------------------------------------------------------------------------- +static esp_err_t poll_response_token(int slot, spi_transaction_t* t, int timeout_ms) +{ + uint8_t t_rx; + *t = (spi_transaction_t) { + .tx_buffer = &t_rx, + .flags = SPI_TRANS_USE_RXDATA, + .length = 8, + }; + esp_err_t ret; + uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000; + do { + t_rx = SDSPI_MOSI_IDLE_VAL; + t->rx_data[0] = 0; + ret = spi_device_transmit(spi_handle(slot), t); + if (ret != ESP_OK) { + return ret; + } + if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_OK) { + return ESP_OK; + } + if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_CRC_ERR) { + return ESP_ERR_INVALID_CRC; + } + if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_WRITE_ERR) { + return ESP_ERR_INVALID_RESPONSE; + } + } while (esp_timer_get_time() < t_end); + + ESP_LOGD(TAG, "%s: timeout", __func__); + return ESP_ERR_TIMEOUT; +} + +// Wait for data token, reading 8 bytes at a time. +// If the token is found, write all subsequent bytes to extra_ptr, +// and store the number of bytes written to extra_size. +//-------------------------------------------------------------- +static esp_err_t poll_data_token(int slot, spi_transaction_t* t, + uint8_t* extra_ptr, size_t* extra_size, int timeout_ms) +{ + uint8_t t_rx[8]; + *t = (spi_transaction_t) { + .tx_buffer = &t_rx, + .rx_buffer = &t_rx, + .length = sizeof(t_rx) * 8, + }; + esp_err_t ret; + uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000; + do { + memset(t_rx, SDSPI_MOSI_IDLE_VAL, sizeof(t_rx)); + ret = spi_device_transmit(spi_handle(slot), t); + if (ret != ESP_OK) { + return ret; + } + bool found = false; + for (int byte_idx = 0; byte_idx < sizeof(t_rx); byte_idx++) { + uint8_t rd_data = t_rx[byte_idx]; + if (rd_data == TOKEN_BLOCK_START) { + found = true; + memcpy(extra_ptr, t_rx + byte_idx + 1, sizeof(t_rx) - byte_idx - 1); + *extra_size = sizeof(t_rx) - byte_idx - 1; + break; + } + if (rd_data != 0xff && rd_data != 0) { + ESP_LOGD(TAG, "%s: received 0x%02x while waiting for data", + __func__, rd_data); + return ESP_ERR_INVALID_RESPONSE; + } + } + if (found) { + return ESP_OK; + } + } while (esp_timer_get_time() < t_end); + ESP_LOGD(TAG, "%s: timeout (%d)", __func__, timeout_ms); + return ESP_ERR_TIMEOUT; +} + + +/** + * Receiving one or more blocks of data happens as follows: + * 1. send command + receive r1 response (SDSPI_CMD_R1_SIZE bytes total) + * 2. keep receiving bytes until TOKEN_BLOCK_START is encountered (this may + * take a while, depending on card's read speed) + * 3. receive up to SDSPI_MAX_DATA_LEN = 512 bytes of actual data + * 4. receive 2 bytes of CRC + * 5. for multi block transfers, go to step 2 + * + * These steps can be done separately, but that leads to a less than optimal + * performance on large transfers because of delays between each step. + * For example, if steps 3 and 4 are separate SPI transactions queued one after + * another, there will be ~16 microseconds of dead time between end of step 3 + * and the beginning of step 4. A delay between two blocking SPI transactions + * in step 2 is even higher (~60 microseconds). + * + * To improve read performance the following sequence is adopted: + * 1. Do the first transfer: command + r1 response + 8 extra bytes. + * Set pre_scan_data_ptr to point to the 8 extra bytes, and set + * pre_scan_data_size to 8. + * 2. Search pre_scan_data_size bytes for TOKEN_BLOCK_START. + * If found, the rest of the bytes contain part of the actual data. + * Store pointer to and size of that extra data as extra_data_{ptr,size}. + * If not found, fall back to polling for TOKEN_BLOCK_START, 8 bytes at a + * time (in poll_data_token function). Deal with extra data in the same way, + * by setting extra_data_{ptr,size}. + * 3. Receive the remaining 512 - extra_data_size bytes, plus 4 extra bytes + * (i.e. 516 - extra_data_size). Of the 4 extra bytes, first two will capture + * the CRC value, and the other two will capture 0xff 0xfe sequence + * indicating the start of the next block. Actual scanning is done by + * setting pre_scan_data_ptr to point to these last 2 bytes, and setting + * pre_scan_data_size = 2, then going to step 2 to receive the next block. + * When the final block is being received, the number of extra bytes is 2 + * (only for CRC), because we don't need to wait for start token of the + * next block, and some cards are getting confused by these two extra bytes. + * + * With this approach the delay between blocks of a multi-block transfer is + * ~95 microseconds, out of which 35 microseconds are spend doing the CRC check. + * Further speedup is possible by pipelining transfers and CRC checks, at an + * expense of one extra temporary buffer. + */ +//----------------------------------------------------------------------- +static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd, + uint8_t *data, uint32_t rx_length) +{ + bool need_stop_command = rx_length > SDSPI_MAX_DATA_LEN; + spi_transaction_t* t_command = get_transaction(slot); + *t_command = (spi_transaction_t) { + .length = (SDSPI_CMD_R1_SIZE + SDSPI_RESPONSE_MAX_DELAY) * 8, + .tx_buffer = cmd, + .rx_buffer = cmd, + }; + esp_err_t ret = spi_device_transmit(spi_handle(slot), t_command); + if (ret != ESP_OK) { + return ret; + } + release_transaction(slot); + + uint8_t* cmd_u8 = (uint8_t*) cmd; + size_t pre_scan_data_size = SDSPI_RESPONSE_MAX_DELAY; + uint8_t* pre_scan_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE; + + /* R1 response is delayed by 1-8 bytes from the request. + * This loop searches for the response and writes it to cmd->r1. + */ + while ((cmd->r1 & SD_SPI_R1_NO_RESPONSE) != 0 && pre_scan_data_size > 0) { + cmd->r1 = *pre_scan_data_ptr; + ++pre_scan_data_ptr; + --pre_scan_data_size; + } + if (cmd->r1 & SD_SPI_R1_NO_RESPONSE) { + ESP_LOGD(TAG, "no response token found"); + return ESP_ERR_TIMEOUT; + } + + while (rx_length > 0) { + size_t extra_data_size = 0; + const uint8_t* extra_data_ptr = NULL; + bool need_poll = true; + + for (int i = 0; i < pre_scan_data_size; ++i) { + if (pre_scan_data_ptr[i] == TOKEN_BLOCK_START) { + extra_data_size = pre_scan_data_size - i - 1; + extra_data_ptr = pre_scan_data_ptr + i + 1; + need_poll = false; + break; + } + } + + if (need_poll) { + // Wait for data to be ready + spi_transaction_t* t_poll = get_transaction(slot); + ret = poll_data_token(slot, t_poll, cmd_u8 + SDSPI_CMD_R1_SIZE, &extra_data_size, cmd->timeout_ms); + release_transaction(slot); + if (ret != ESP_OK) { + return ret; + } + if (extra_data_size) { + extra_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE; + } + } + + // Arrange RX buffer + size_t will_receive = MIN(rx_length, SDSPI_MAX_DATA_LEN) - extra_data_size; + uint8_t* rx_data; + ret = get_block_buf(slot, &rx_data); + if (ret != ESP_OK) { + return ret; + } + + // receive actual data + const size_t receive_extra_bytes = (rx_length > SDSPI_MAX_DATA_LEN) ? 4 : 2; + memset(rx_data, 0xff, will_receive + receive_extra_bytes); + spi_transaction_t* t_data = get_transaction(slot); + *t_data = (spi_transaction_t) { + .length = (will_receive + receive_extra_bytes) * 8, + .rx_buffer = rx_data, + .tx_buffer = rx_data + }; + + ret = spi_device_transmit(spi_handle(slot), t_data); + if (ret != ESP_OK) { + return ret; + } + release_transaction(slot); + + // CRC bytes need to be received even if CRC is not enabled + uint16_t crc = UINT16_MAX; + memcpy(&crc, rx_data + will_receive, sizeof(crc)); + + // Bytes to scan for the start token + pre_scan_data_size = receive_extra_bytes - sizeof(crc); + pre_scan_data_ptr = rx_data + will_receive + sizeof(crc); + + // Copy data to the destination buffer + memcpy(data + extra_data_size, rx_data, will_receive); + if (extra_data_size) { + memcpy(data, extra_data_ptr, extra_data_size); + } + + // compute CRC of the received data + uint16_t crc_of_data = 0; + if (data_crc_enabled(slot)) { + crc_of_data = sdspi_crc16(data, will_receive + extra_data_size); + if (crc_of_data != crc) { + ESP_LOGE(TAG, "data CRC failed, got=0x%04x expected=0x%04x", crc_of_data, crc); + esp_log_buffer_hex(TAG, data, 16); + return ESP_ERR_INVALID_CRC; + } + } + + data += will_receive + extra_data_size; + rx_length -= will_receive + extra_data_size; + extra_data_size = 0; + extra_data_ptr = NULL; + } + + if (need_stop_command) { + // To end multi block transfer, send stop command and wait for the + // card to process it + sdspi_hw_cmd_t stop_cmd; + make_hw_cmd(MMC_STOP_TRANSMISSION, 0, cmd->timeout_ms, &stop_cmd); + ret = start_command_default(slot, SDSPI_CMD_FLAG_RSP_R1, &stop_cmd); + if (ret != ESP_OK) { + return ret; + } + if (stop_cmd.r1 != 0) { + ESP_LOGD(TAG, "%s: STOP_TRANSMISSION response 0x%02x", __func__, stop_cmd.r1); + } + spi_transaction_t* t_poll = get_transaction(slot); + ret = poll_busy(slot, t_poll, cmd->timeout_ms); + release_transaction(slot); + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +//------------------------------------------------------------------------ +static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd, + const uint8_t *data, uint32_t tx_length) +{ + if (card_write_protected(slot)) { + ESP_LOGW(TAG, "%s: card write protected", __func__); + return ESP_ERR_INVALID_STATE; + } + spi_transaction_t* t_command = get_transaction(slot); + *t_command = (spi_transaction_t) { + .length = SDSPI_CMD_R1_SIZE * 8, + .tx_buffer = cmd, + .rx_buffer = cmd, + }; + esp_err_t ret = spi_device_queue_trans(spi_handle(slot), t_command, 0); + if (ret != ESP_OK) { + return ret; + } + uint8_t start_token = tx_length <= SDSPI_MAX_DATA_LEN ? + TOKEN_BLOCK_START : TOKEN_BLOCK_START_WRITE_MULTI; + wait_for_transactions(slot); + + while (tx_length > 0) { + + // Write block start token + spi_transaction_t* t_start_token = get_transaction(slot); + *t_start_token = (spi_transaction_t) { + .length = sizeof(start_token) * 8, + .tx_buffer = &start_token + }; + esp_err_t ret = spi_device_queue_trans(spi_handle(slot), t_start_token, 0); + if (ret != ESP_OK) { + return ret; + } + + // Prepare data to be sent + size_t will_send = MIN(tx_length, SDSPI_MAX_DATA_LEN); + const uint8_t* tx_data = data; + if (!ptr_dma_compatible(tx_data)) { + // If the pointer can't be used with DMA, copy data into a new buffer + uint8_t* tmp; + ret = get_block_buf(slot, &tmp); + if (ret != ESP_OK) { + return ret; + } + memcpy(tmp, tx_data, will_send); + tx_data = tmp; + } + + // Write data + spi_transaction_t* t_data = get_transaction(slot); + *t_data = (spi_transaction_t) { + .length = will_send * 8, + .tx_buffer = tx_data, + }; + ret = spi_device_queue_trans(spi_handle(slot), t_data, 0); + if (ret != ESP_OK) { + return ret; + } + + // Write CRC + uint16_t crc = sdspi_crc16(data, will_send); + spi_transaction_t* t_crc = get_transaction(slot); + *t_crc = (spi_transaction_t) { + .length = sizeof(crc) * 8, + .tx_buffer = (uint8_t*) &crc, + }; + ret = spi_device_queue_trans(spi_handle(slot), t_crc, 0); + if (ret != ESP_OK) { + return ret; + } + + // Wait for data to be sent + wait_for_transactions(slot); + + // Check if R1 response for the command was correct + if (cmd->r1 != 0) { + ESP_LOGD(TAG, "%s: invalid R1 response: 0x%02x", __func__, cmd->r1); + return ESP_ERR_INVALID_RESPONSE; + } + + // Poll for response + spi_transaction_t* t_poll = get_transaction(slot); + ret = poll_response_token(slot, t_poll, cmd->timeout_ms); + release_transaction(slot); + if (ret != ESP_OK) { + return ret; + } + + // Wait for the card to finish writing data + t_poll = get_transaction(slot); + ret = poll_busy(slot, t_poll, cmd->timeout_ms); + release_transaction(slot); + if (ret != ESP_OK) { + return ret; + } + + tx_length -= will_send; + data += will_send; + } + + if (start_token == TOKEN_BLOCK_START_WRITE_MULTI) { + uint8_t stop_token[2] = { + TOKEN_BLOCK_STOP_WRITE_MULTI, + SDSPI_MOSI_IDLE_VAL + }; + spi_transaction_t* t_stop_token = get_transaction(slot); + *t_stop_token = (spi_transaction_t) { + .length = sizeof(stop_token) * 8, + .tx_buffer = &stop_token, + }; + ret = spi_device_queue_trans(spi_handle(slot), t_stop_token, 0); + if (ret != ESP_OK) { + return ret; + } + wait_for_transactions(slot); + + spi_transaction_t* t_poll = get_transaction(slot); + ret = poll_busy(slot, t_poll, cmd->timeout_ms); + release_transaction(slot); + if (ret != ESP_OK) { + return ret; + } + } + + return ESP_OK; +} diff --git a/Tools/esp_idf_patches/components/driver/spi_master.c b/Tools/esp_idf_patches/components/driver/spi_master.c new file mode 100644 index 00000000..e5a58d57 --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/spi_master.c @@ -0,0 +1,798 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* +Architecture: + +We can initialize a SPI driver, but we don't talk to the SPI driver itself, we address a device. A device essentially +is a combination of SPI port and CS pin, plus some information about the specifics of communication to the device +(timing, command/address length etc) + +The essence of the interface to a device is a set of queues; one per device. The idea is that to send something to a SPI +device, you allocate a transaction descriptor. It contains some information about the transfer like the length, address, +command etc, plus pointers to transmit and receive buffer. The address of this block gets pushed into the transmit queue. +The SPI driver does its magic, and sends and retrieves the data eventually. The data gets written to the receive buffers, +if needed the transaction descriptor is modified to indicate returned parameters and the entire thing goes into the return +queue, where whatever software initiated the transaction can retrieve it. + +The entire thing is run from the SPI interrupt handler. If SPI is done transmitting/receiving but nothing is in the queue, +it will not clear the SPI interrupt but just disable it. This way, when a new thing is sent, pushing the packet into the send +queue and re-enabling the interrupt will trigger the interrupt again, which can then take care of the sending. +*/ + + + +#include +#include "driver/spi_common.h" +#include "driver/spi_master.h" +#include "driver/spi_master_internal.h" +#include "soc/gpio_sig_map.h" +#include "soc/spi_reg.h" +#include "soc/dport_reg.h" +#include "soc/spi_struct.h" +#include "rom/ets_sys.h" +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_intr_alloc.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_pm.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/soc.h" +#include "soc/soc_memory_layout.h" +#include "soc/dport_reg.h" +#include "rom/lldesc.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" +#include "esp_heap_caps.h" + +static spi_host_t *spihost[3]; + + +static const char *SPI_TAG = "spi_master"; +#define SPI_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } + + +static void spi_intr(void *arg); + +esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus_config, int dma_chan) +{ + bool spi_chan_claimed, dma_chan_claimed; + esp_err_t ret = ESP_OK; + esp_err_t err; + /* ToDo: remove this when we have flash operations cooperating with this */ + SPI_CHECK(host!=SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED); + + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK( dma_chan >= 0 && dma_chan <= 2, "invalid dma channel", ESP_ERR_INVALID_ARG ); + + spi_chan_claimed=spicommon_periph_claim(host); + SPI_CHECK(spi_chan_claimed, "host already in use", ESP_ERR_INVALID_STATE); + + if ( dma_chan != 0 ) { + dma_chan_claimed=spicommon_dma_chan_claim(dma_chan); + if ( !dma_chan_claimed ) { + spicommon_periph_free( host ); + SPI_CHECK(dma_chan_claimed, "dma channel already in use", ESP_ERR_INVALID_STATE); + } + } + + spihost[host]=malloc(sizeof(spi_host_t)); + if (spihost[host]==NULL) { + ret = ESP_ERR_NO_MEM; + goto cleanup; + } + memset(spihost[host], 0, sizeof(spi_host_t)); +#ifdef CONFIG_PM_ENABLE + err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "spi_master", + &spihost[host]->pm_lock); + if (err != ESP_OK) { + ret = err; + goto cleanup; + } +#endif //CONFIG_PM_ENABLE + + err = spicommon_bus_initialize_io(host, bus_config, dma_chan, SPICOMMON_BUSFLAG_MASTER|bus_config->flags, &spihost[host]->flags); + if (err != ESP_OK) { + ret = err; + goto cleanup; + } + + spihost[host]->dma_chan=dma_chan; + if (dma_chan == 0) { + spihost[host]->max_transfer_sz = 32; + } else { + //See how many dma descriptors we need and allocate them + int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN; + if (dma_desc_ct==0) dma_desc_ct=1; //default to 4k when max is not given + spihost[host]->max_transfer_sz = dma_desc_ct*SPI_MAX_DMA_LEN; + spihost[host]->dmadesc_tx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA); + spihost[host]->dmadesc_rx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA); + if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) { + ret = ESP_ERR_NO_MEM; + goto cleanup; + } + } + err = esp_intr_alloc(spicommon_irqsource_for_host(host), ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void*)spihost[host], &spihost[host]->intr); + if (err != ESP_OK) { + ret = err; + goto cleanup; + } + spihost[host]->hw=spicommon_hw_for_host(host); + + spihost[host]->cur_cs = NO_CS; + spihost[host]->prev_cs = NO_CS; + + //Reset DMA + spihost[host]->hw->dma_conf.val|=SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + spihost[host]->hw->dma_out_link.start=0; + spihost[host]->hw->dma_in_link.start=0; + spihost[host]->hw->dma_conf.val&=~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + //Reset timing + spihost[host]->hw->ctrl2.val=0; + + //Disable unneeded ints + spihost[host]->hw->slave.rd_buf_done=0; + spihost[host]->hw->slave.wr_buf_done=0; + spihost[host]->hw->slave.rd_sta_done=0; + spihost[host]->hw->slave.wr_sta_done=0; + spihost[host]->hw->slave.rd_buf_inten=0; + spihost[host]->hw->slave.wr_buf_inten=0; + spihost[host]->hw->slave.rd_sta_inten=0; + spihost[host]->hw->slave.wr_sta_inten=0; + + //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as + //disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling + //any transactions that are queued. + spihost[host]->hw->slave.trans_inten=1; + spihost[host]->hw->slave.trans_done=1; + + return ESP_OK; + +cleanup: + if (spihost[host]) { + free(spihost[host]->dmadesc_tx); + free(spihost[host]->dmadesc_rx); +#ifdef CONFIG_PM_ENABLE + if (spihost[host]->pm_lock) { + esp_pm_lock_delete(spihost[host]->pm_lock); + } +#endif + } + free(spihost[host]); + spicommon_periph_free(host); + spicommon_dma_chan_free(dma_chan); + return ret; +} + +esp_err_t spi_bus_free(spi_host_device_t host) +{ + int x; + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE); + for (x=0; xdevice[x]==NULL, "not all CSses freed", ESP_ERR_INVALID_STATE); + } + + if ( spihost[host]->dma_chan > 0 ) { + spicommon_dma_chan_free ( spihost[host]->dma_chan ); + } +#ifdef CONFIG_PM_ENABLE + esp_pm_lock_delete(spihost[host]->pm_lock); +#endif + spihost[host]->hw->slave.trans_inten=0; + spihost[host]->hw->slave.trans_done=0; + esp_intr_free(spihost[host]->intr); + spicommon_periph_free(host); + free(spihost[host]->dmadesc_tx); + free(spihost[host]->dmadesc_rx); + free(spihost[host]); + spihost[host]=NULL; + return ESP_OK; +} + +static inline uint32_t spi_dummy_limit(bool gpio_is_used) +{ + const int apbclk=APB_CLK_FREQ; + if (!gpio_is_used) { + return apbclk; //dummy bit workaround is not used when native pins are used + } else { + return apbclk/2; //the dummy bit workaround is used when freq is 40MHz and GPIO matrix is used. + } +} + +/* + Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks + up the CS pin to whatever is specified. +*/ +esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_config_t *dev_config, spi_device_handle_t *handle) +{ + int freecs, freehwcs=NO_HWCS; + int apbclk=APB_CLK_FREQ; + int eff_clk; + int duty_cycle; + spi_clock_reg_t clk_reg; + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE); + SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(dev_config->clock_speed_hz > 0, "invalid sclk speed", ESP_ERR_INVALID_ARG); + if (dev_config->spics_io_num >= 0) { + // 3 hardware handled CS are available per host, check if there is a free one + for (freecs=0; freecsdevice[freecs]) && (spihost[host]->device[freecs]->cfg.spics_io_num >=0)) freehwcs--; + SPI_CHECK(freehwcs>0, "no free hw cs pins for host", ESP_ERR_NOT_FOUND); + } + } + for (freecs=0; freecsdevice[freecs], NULL, (spi_device_t *)1)) break; + } + SPI_CHECK(freecs!=NO_CS, "no free cs pins for host", ESP_ERR_NOT_FOUND); + //The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full + //duplex mode does absolutely nothing on the ESP32. + SPI_CHECK(dev_config->cs_ena_pretrans==0 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay incompatible with full-duplex", ESP_ERR_INVALID_ARG); + //Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections. + duty_cycle = (dev_config->duty_cycle_pos==0? 128: dev_config->duty_cycle_pos); + eff_clk = spi_cal_clock(apbclk, dev_config->clock_speed_hz, duty_cycle, (uint32_t*)&clk_reg); + uint32_t dummy_limit = spi_dummy_limit(!(spihost[host]->flags & SPICOMMON_BUSFLAG_NATIVE_PINS)); + SPI_CHECK( dev_config->flags & SPI_DEVICE_HALFDUPLEX || (eff_clk/1000/1000) < (dummy_limit/1000/1000) || + dev_config->flags & SPI_DEVICE_NO_DUMMY, +"When GPIO matrix is used in full-duplex mode at frequency > 26MHz, device cannot read correct data.\n\ +Please note the SPI can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\ +Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.", + ESP_ERR_INVALID_ARG ); + + //Allocate memory for device + spi_device_t *dev=malloc(sizeof(spi_device_t)); + if (dev==NULL) goto nomem; + memset(dev, 0, sizeof(spi_device_t)); + spihost[host]->device[freecs]=dev; + + //Allocate queues, set defaults + dev->trans_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv)); + dev->ret_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv)); + if (!dev->trans_queue || !dev->ret_queue) goto nomem; + dev->host=spihost[host]; + + //We want to save a copy of the dev config in the dev struct. + memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t)); + dev->cfg.duty_cycle_pos = duty_cycle; + // TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is acquired. + dev->clk_cfg= (clock_config_t) { + .eff_clk = eff_clk, + .dummy_num = (dev->clk_cfg.eff_clk >= dummy_limit? 1: 0), + .reg = clk_reg, + }; + + //Set CS pin, CS options + if (dev_config->spics_io_num >= 0) { + gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT); + spicommon_cs_initialize(host, dev_config->spics_io_num, freecs, !(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS)); + if (dev_config->flags&SPI_DEVICE_CLK_AS_CS) { + spihost[host]->hw->pin.master_ck_sel |= (1<hw->pin.master_ck_sel &= (1<flags&SPI_DEVICE_POSITIVE_CS) { + spihost[host]->hw->pin.master_cs_pol |= (1<hw->pin.master_cs_pol &= (1<clk_cfg.eff_clk/1000); + } + else { + spihost[host]->hw->pin.master_ck_sel &= (1<<(freecs&3)); + spihost[host]->hw->pin.master_cs_pol &= (1<<(freecs&3)); + ESP_LOGD(SPI_TAG, "SPI%d: New device added, external CS%d, effective clock: %dkHz", host, freecs, dev->clk_cfg.eff_clk/1000); + } + *handle=dev; + return ESP_OK; + +nomem: + if (dev) { + if (dev->trans_queue) vQueueDelete(dev->trans_queue); + if (dev->ret_queue) vQueueDelete(dev->ret_queue); + } + free(dev); + return ESP_ERR_NO_MEM; +} + +esp_err_t spi_bus_remove_device(spi_device_handle_t handle) +{ + int x; + SPI_CHECK(handle!=NULL, "invalid handle", ESP_ERR_INVALID_ARG); + //These checks aren't exhaustive; another thread could sneak in a transaction inbetween. These are only here to + //catch design errors and aren't meant to be triggered during normal operation. + SPI_CHECK(uxQueueMessagesWaiting(handle->trans_queue)==0, "Have unfinished transactions (trans)", ESP_ERR_INVALID_STATE); + SPI_CHECK(handle->host->cur_cs == NO_CS || handle->host->device[handle->host->cur_cs]!=handle, "Have unfinished transactions (cs)", ESP_ERR_INVALID_STATE); + SPI_CHECK(uxQueueMessagesWaiting(handle->ret_queue)==0, "Have unfinished transactions (ret)", ESP_ERR_INVALID_STATE); + + //Kill queues + vQueueDelete(handle->trans_queue); + vQueueDelete(handle->ret_queue); + //Remove device from list of csses and free memory + for (x=0; xhost->device[x] == handle){ + handle->host->device[x]=NULL; + if ( x == handle->host->prev_cs ) handle->host->prev_cs = NO_CS; + } + } + free(handle); + return ESP_OK; +} + +static int spi_freq_for_pre_n(int fapb, int pre, int n) { + return (fapb / (pre * n)); +} + +int spi_cal_clock(int fapb, int hz, int duty_cycle, uint32_t *reg_o) +{ + spi_clock_reg_t reg; + int eff_clk; + + //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value. + if (hz>((fapb/4)*3)) { + //Using Fapb directly will give us the best result here. + reg.clkcnt_l=0; + reg.clkcnt_h=0; + reg.clkcnt_n=0; + reg.clkdiv_pre=0; + reg.clk_equ_sysclk=1; + eff_clk=fapb; + } else { + //For best duty cycle resolution, we want n to be as close to 32 as possible, but + //we also need a pre/n combo that gets us as close as possible to the intended freq. + //To do this, we bruteforce n and calculate the best pre to go along with that. + //If there's a choice between pre/n combos that give the same result, use the one + //with the higher n. + int pre, n, h, l; + int bestn=-1; + int bestpre=-1; + int besterr=0; + int errval; + for (n=2; n<=64; n++) { //Start at 2: we need to be able to set h/l so we have at least one high and one low pulse. + //Effectively, this does pre=round((fapb/n)/hz). + pre=((fapb/n)+(hz/2))/hz; + if (pre<=0) pre=1; + if (pre>8192) pre=8192; + errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz); + if (bestn==-1 || errval<=besterr) { + besterr=errval; + bestn=n; + bestpre=pre; + } + } + + n=bestn; + pre=bestpre; + l=n; + //This effectively does round((duty_cycle*n)/256) + h=(duty_cycle*n+127)/256; + if (h<=0) h=1; + + reg.clk_equ_sysclk=0; + reg.clkcnt_n=n-1; + reg.clkdiv_pre=pre-1; + reg.clkcnt_h=h-1; + reg.clkcnt_l=l-1; + eff_clk=spi_freq_for_pre_n(fapb, pre, n); + } + if ( reg_o != NULL ) *reg_o = reg.val; + return eff_clk; +} + +/* + * Set the spi clock according to pre-calculated register value. + */ +static inline void spi_set_clock(spi_dev_t *hw, spi_clock_reg_t reg) { + hw->clock.val = reg.val; +} + +//This is run in interrupt context and apart from initialization and destruction, this is the only code +//touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are +//no muxes in this code. +static void IRAM_ATTR spi_intr(void *arg) +{ + int i; + BaseType_t r; + BaseType_t do_yield=pdFALSE; + spi_trans_priv *trans_buf=NULL; + spi_transaction_t *trans=NULL; + spi_host_t *host=(spi_host_t*)arg; + + //Ignore all but the trans_done int. + if (!host->hw->slave.trans_done) return; + + /*------------ deal with the in-flight transaction -----------------*/ + if (host->cur_cs != NO_CS) { + spi_transaction_t *cur_trans = host->cur_trans_buf.trans; + //Okay, transaction is done. + if (host->cur_trans_buf.buffer_to_rcv && host->dma_chan == 0 ) { + //Need to copy from SPI regs to result buffer. + for (int x=0; x < cur_trans->rxlength; x+=32) { + //Do a memcpy to get around possible alignment issues in rx_buffer + uint32_t word=host->hw->data_buf[x/32]; + int len=cur_trans->rxlength-x; + if (len>32) len=32; + memcpy(&host->cur_trans_buf.buffer_to_rcv[x/32], &word, (len+7)/8); + } + } + //Call post-transaction callback, if any + if (host->device[host->cur_cs]->cfg.post_cb) host->device[host->cur_cs]->cfg.post_cb(cur_trans); + //Return transaction descriptor. + xQueueSendFromISR(host->device[host->cur_cs]->ret_queue, &host->cur_trans_buf, &do_yield); + host->cur_cs = NO_CS; + } + //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. + if (host->dma_chan) spicommon_dmaworkaround_idle(host->dma_chan); + + /*------------ new transaction starts here ------------------*/ + //ToDo: This is a stupidly simple low-cs-first priority scheme. Make this configurable somehow. - JD + for (i=0; idevice[i]) { + r=xQueueReceiveFromISR(host->device[i]->trans_queue, &host->cur_trans_buf, &do_yield); + trans_buf = &host->cur_trans_buf; + //Stop looking if we have a transaction to send. + if (r) break; + } + } + if (i==NO_CS) { + //No packet waiting. Disable interrupt. + esp_intr_disable(host->intr); +#ifdef CONFIG_PM_ENABLE + //Release APB frequency lock + esp_pm_lock_release(host->pm_lock); +#endif + } else { + host->hw->slave.trans_done=0; //clear int bit + //We have a transaction. Send it. + spi_device_t *dev=host->device[i]; + trans = trans_buf->trans; + host->cur_cs=i; + //We should be done with the transmission. + assert(host->hw->cmd.usr == 0); + + //Reconfigure according to device settings, but only if we change CSses. + if (i!=host->prev_cs) { + const int apbclk=APB_CLK_FREQ; + int effclk=dev->clk_cfg.eff_clk; + spi_set_clock(host->hw, dev->clk_cfg.reg); + //Configure bit order + host->hw->ctrl.rd_bit_order=(dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0; + host->hw->ctrl.wr_bit_order=(dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0; + + //Configure polarity + //SPI interface needs to be configured for a delay in some cases. + int nodelay=0; + if ((host->flags&SPICOMMON_BUSFLAG_NATIVE_PINS)!=0) { + if (effclk >= apbclk/2) { + nodelay=1; + } + } else { + uint32_t delay_limit = apbclk/4; + if (effclk >= delay_limit) { + nodelay=1; + } + } + + if (dev->cfg.mode==0) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } else if (dev->cfg.mode==1) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (dev->cfg.mode==2) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (dev->cfg.mode==3) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } + + //Configure misc stuff + host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1; + host->hw->user.sio=(dev->cfg.flags & SPI_DEVICE_3WIRE)?1:0; + + host->hw->ctrl2.setup_time=dev->cfg.cs_ena_pretrans-1; + host->hw->user.cs_setup=dev->cfg.cs_ena_pretrans?1:0; + host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans-1; + host->hw->user.cs_hold=(dev->cfg.cs_ena_posttrans)?1:0; + + //Configure CS pin, if external cs is used, disable it + if (dev->cfg.spics_io_num < 0) i= -1; + host->hw->pin.cs0_dis=(i==0)?0:1; + host->hw->pin.cs1_dis=(i==1)?0:1; + host->hw->pin.cs2_dis=(i==2)?0:1; + } + host->prev_cs = i; + //Reset SPI peripheral + host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + host->hw->dma_out_link.start=0; + host->hw->dma_in_link.start=0; + host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + host->hw->dma_conf.out_data_burst_en=1; + //Set up QIO/DIO if needed + host->hw->ctrl.val &= ~(SPI_FREAD_DUAL|SPI_FREAD_QUAD|SPI_FREAD_DIO|SPI_FREAD_QIO); + host->hw->user.val &= ~(SPI_FWRITE_DUAL|SPI_FWRITE_QUAD|SPI_FWRITE_DIO|SPI_FWRITE_QIO); + if (trans->flags & SPI_TRANS_MODE_DIO) { + if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { + host->hw->ctrl.fread_dio=1; + host->hw->user.fwrite_dio=1; + } else { + host->hw->ctrl.fread_dual=1; + host->hw->user.fwrite_dual=1; + } + host->hw->ctrl.fastrd_mode=1; + } else if (trans->flags & SPI_TRANS_MODE_QIO) { + if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { + host->hw->ctrl.fread_qio=1; + host->hw->user.fwrite_qio=1; + } else { + host->hw->ctrl.fread_quad=1; + host->hw->user.fwrite_quad=1; + } + host->hw->ctrl.fastrd_mode=1; + } + + + //Fill DMA descriptors + int extra_dummy=0; + if (trans_buf->buffer_to_rcv) { + host->hw->user.usr_miso_highpart=0; + if (host->dma_chan == 0) { + //No need to setup anything; we'll copy the result out of the work registers directly later. + } else { + spicommon_dmaworkaround_transfer_active(host->dma_chan); //mark channel as active + spicommon_setup_dma_desc_links(host->dmadesc_rx, ((trans->rxlength+7)/8), (uint8_t*)trans_buf->buffer_to_rcv, true); + host->hw->dma_in_link.addr=(int)(&host->dmadesc_rx[0]) & 0xFFFFF; + host->hw->dma_in_link.start=1; + } + //when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist + if (((dev->cfg.flags&SPI_DEVICE_NO_DUMMY)==0) && (dev->cfg.flags&SPI_DEVICE_HALFDUPLEX)) { + extra_dummy=dev->clk_cfg.dummy_num; + } + } else { + //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon + if (host->dma_chan != 0 ) { + host->hw->dma_in_link.addr=0; + host->hw->dma_in_link.start=1; + } + } + + if (trans_buf->buffer_to_send) { + if (host->dma_chan == 0) { + //Need to copy data to registers manually + for (int x=0; x < trans->length; x+=32) { + //Use memcpy to get around alignment issues for txdata + uint32_t word; + memcpy(&word, &trans_buf->buffer_to_send[x/32], 4); + host->hw->data_buf[(x/32)+8]=word; + } + host->hw->user.usr_mosi_highpart=1; + } else { + spicommon_dmaworkaround_transfer_active(host->dma_chan); //mark channel as active + spicommon_setup_dma_desc_links(host->dmadesc_tx, (trans->length+7)/8, (uint8_t*)trans_buf->buffer_to_send, false); + host->hw->user.usr_mosi_highpart=0; + host->hw->dma_out_link.addr=(int)(&host->dmadesc_tx[0]) & 0xFFFFF; + host->hw->dma_out_link.start=1; + host->hw->user.usr_mosi_highpart=0; + } + } + + //configure dummy bits + host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0; + host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1; + host->hw->mosi_dlen.usr_mosi_dbitlen=trans->length-1; + if ( dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ) { + host->hw->miso_dlen.usr_miso_dbitlen=trans->rxlength-1; + } else { + //rxlength is not used in full-duplex mode + host->hw->miso_dlen.usr_miso_dbitlen=trans->length-1; + } + + //Configure bit sizes, load addr and command + int cmdlen; + if ( trans->flags & SPI_TRANS_VARIABLE_CMD ) { + cmdlen = ((spi_transaction_ext_t*)trans)->command_bits; + } else { + cmdlen = dev->cfg.command_bits; + } + int addrlen; + if ( trans->flags & SPI_TRANS_VARIABLE_ADDR ) { + addrlen = ((spi_transaction_ext_t*)trans)->address_bits; + } else { + addrlen = dev->cfg.address_bits; + } + host->hw->user1.usr_addr_bitlen=addrlen-1; + host->hw->user2.usr_command_bitlen=cmdlen-1; + host->hw->user.usr_addr=addrlen?1:0; + host->hw->user.usr_command=cmdlen?1:0; + + // output command will be sent from bit 7 to 0 of command_value, and then bit 15 to 8 of the same register field. + uint16_t command = trans->cmd << (16-cmdlen); //shift to MSB + host->hw->user2.usr_command_value = (command>>8)|(command<<8); //swap the first and second byte + // shift the address to MSB of addr (and maybe slv_wr_status) register. + // output address will be sent from MSB to LSB of addr register, then comes the MSB to LSB of slv_wr_status register. + if (addrlen>32) { + host->hw->addr = trans->addr >> (addrlen- 32); + host->hw->slv_wr_status = trans->addr << (64 - addrlen); + } else { + host->hw->addr = trans->addr << (32 - addrlen); + } + + host->hw->user.usr_mosi=( (!(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX) && trans_buf->buffer_to_rcv) || trans_buf->buffer_to_send)?1:0; + host->hw->user.usr_miso=(trans_buf->buffer_to_rcv)?1:0; + + //Call pre-transmission callback, if any + if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans); + //Kick off transfer + host->hw->cmd.usr=1; + } + if (do_yield) portYIELD_FROM_ISR(); +} + + +esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait) +{ + esp_err_t ret = ESP_OK; + BaseType_t r; + SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); + //check transmission length + SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 ||trans_desc->rxlength <= 32, "rxdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG); + SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG); + SPI_CHECK(trans_desc->length <= handle->host->max_transfer_sz*8, "txdata transfer > host maximum", ESP_ERR_INVALID_ARG); + SPI_CHECK(trans_desc->rxlength <= handle->host->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG); + SPI_CHECK((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG); + //check working mode + SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG); + SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG); + SPI_CHECK( !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || handle->host->dma_chan == 0 || !(trans_desc->flags & SPI_TRANS_USE_RXDATA || trans_desc->rx_buffer != NULL) + || !(trans_desc->flags & SPI_TRANS_USE_TXDATA || trans_desc->tx_buffer!=NULL), "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG ); + + //In Full duplex mode, default rxlength to be the same as length, if not filled in. + // set rxlength to length is ok, even when rx buffer=NULL + if (trans_desc->rxlength==0 && !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX)) { + trans_desc->rxlength=trans_desc->length; + } + + spi_trans_priv trans_buf; + memset( &trans_buf, 0, sizeof(spi_trans_priv) ); + trans_buf.trans = trans_desc; + + // rx memory assign + if ( trans_desc->flags & SPI_TRANS_USE_RXDATA ) { + trans_buf.buffer_to_rcv = (uint32_t*)&trans_desc->rx_data[0]; + } else { + //if not use RXDATA neither rx_buffer, buffer_to_rcv assigned to NULL + trans_buf.buffer_to_rcv = trans_desc->rx_buffer; + } + if ( trans_buf.buffer_to_rcv && handle->host->dma_chan && (!esp_ptr_dma_capable( trans_buf.buffer_to_rcv ) || ((int)trans_buf.buffer_to_rcv%4!=0)) ) { + //if rxbuf in the desc not DMA-capable, malloc a new one. The rx buffer need to be length of multiples of 32 bits to avoid heap corruption. + ESP_LOGV( SPI_TAG, "Allocate RX buffer for DMA" ); + trans_buf.buffer_to_rcv = heap_caps_malloc((trans_desc->rxlength+31)/8, MALLOC_CAP_DMA); + if ( trans_buf.buffer_to_rcv==NULL ) { + ret = ESP_ERR_NO_MEM; + goto clean_up; + } + } + + const uint32_t *txdata; + // tx memory assign + if ( trans_desc->flags & SPI_TRANS_USE_TXDATA ) { + txdata = (uint32_t*)&trans_desc->tx_data[0]; + } else { + //if not use TXDATA neither tx_buffer, tx data assigned to NULL + txdata = trans_desc->tx_buffer ; + } + if ( txdata && handle->host->dma_chan && !esp_ptr_dma_capable( txdata )) { + //if txbuf in the desc not DMA-capable, malloc a new one + ESP_LOGV( SPI_TAG, "Allocate TX buffer for DMA" ); + trans_buf.buffer_to_send = heap_caps_malloc((trans_desc->length+7)/8, MALLOC_CAP_DMA); + if ( trans_buf.buffer_to_send==NULL ) { + ret = ESP_ERR_NO_MEM; + goto clean_up; + } + memcpy( trans_buf.buffer_to_send, txdata, (trans_desc->length+7)/8 ); + } else { + // else use the original buffer (forced-conversion) or assign to NULL + trans_buf.buffer_to_send = (uint32_t*)txdata; + } + +#ifdef CONFIG_PM_ENABLE + esp_pm_lock_acquire(handle->host->pm_lock); +#endif + r=xQueueSend(handle->trans_queue, (void*)&trans_buf, ticks_to_wait); + if (!r) { + ret = ESP_ERR_TIMEOUT; +#ifdef CONFIG_PM_ENABLE + //Release APB frequency lock + esp_pm_lock_release(handle->host->pm_lock); +#endif + goto clean_up; + } + esp_intr_enable(handle->host->intr); + return ESP_OK; + +clean_up: + // free malloc-ed buffer (if needed) before return. + if ( (void*)trans_buf.buffer_to_rcv != trans_desc->rx_buffer && (void*)trans_buf.buffer_to_rcv != &trans_desc->rx_data[0] ) { + free( trans_buf.buffer_to_rcv ); + } + if ( (void*)trans_buf.buffer_to_send!= trans_desc->tx_buffer && (void*)trans_buf.buffer_to_send != &trans_desc->tx_data[0] ) { + free( trans_buf.buffer_to_send ); + } + assert( ret != ESP_OK ); + return ret; +} + +esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transaction_t **trans_desc, TickType_t ticks_to_wait) +{ + BaseType_t r; + spi_trans_priv trans_buf; + + SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); + r=xQueueReceive(handle->ret_queue, (void*)&trans_buf, ticks_to_wait); + if (!r) { + // The memory occupied by rx and tx DMA buffer destroyed only when receiving from the queue (transaction finished). + // If timeout, wait and retry. + // Every on-flight transaction request occupies internal memory as DMA buffer if needed. + return ESP_ERR_TIMEOUT; + } + + (*trans_desc) = trans_buf.trans; + + if ( (void*)trans_buf.buffer_to_send != &(*trans_desc)->tx_data[0] && trans_buf.buffer_to_send != (*trans_desc)->tx_buffer ) { + free( trans_buf.buffer_to_send ); + } + + //copy data from temporary DMA-capable buffer back to IRAM buffer and free the temporary one. + if ( (void*)trans_buf.buffer_to_rcv != &(*trans_desc)->rx_data[0] && trans_buf.buffer_to_rcv != (*trans_desc)->rx_buffer ) { + if ( (*trans_desc)->flags & SPI_TRANS_USE_RXDATA ) { + memcpy( (uint8_t*)&(*trans_desc)->rx_data[0], trans_buf.buffer_to_rcv, ((*trans_desc)->rxlength+7)/8 ); + } else { + memcpy( (*trans_desc)->rx_buffer, trans_buf.buffer_to_rcv, ((*trans_desc)->rxlength+7)/8 ); + } + free( trans_buf.buffer_to_rcv ); + } + + return ESP_OK; +} + +//Porcelain to do one blocking transmission. +esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc) +{ + esp_err_t ret; + spi_transaction_t *ret_trans; + //ToDo: check if any spi transfers in flight + ret=spi_device_queue_trans(handle, trans_desc, portMAX_DELAY); + if (ret!=ESP_OK) return ret; + ret=spi_device_get_trans_result(handle, &ret_trans, portMAX_DELAY); + if (ret!=ESP_OK) return ret; + assert(ret_trans==trans_desc); + return ESP_OK; +} + diff --git a/Tools/esp_idf_patches/components/driver/spi_master_utils.c b/Tools/esp_idf_patches/components/driver/spi_master_utils.c new file mode 100644 index 00000000..a1f1648a --- /dev/null +++ b/Tools/esp_idf_patches/components/driver/spi_master_utils.c @@ -0,0 +1,818 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2017-2018 LoBo (https://github.com/loboris) +// +// 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. + +/* +Additional spi_master functions and utilities + +*/ + +#include "driver/spi_master_utils.h" +#include "soc/spi_reg.h" +#include "esp_log.h" +#include "sdspi_host.h" +#include "sdkconfig.h" + +/* + * There are two SPI hosts on ESP32 available to the user, HSPI_HOST & VSPI_HOST + * If psRAM is used and is configured to run at 80 MHz, only HSPI_HOST is available ! + * Each SPI host is configured with miso, mosi & sck pins on which it operates + * Up to 6 spi devices can be attached to each SPI host, all must use different CS pin + * CS is handled by the driver using spi_device_select() / spi_device_deselect() functions + */ + +// SPI bus configuration is used by generic SPI, Display and SDCard SPI +#if CONFIG_SPIRAM_SPEED_80M +static spi_bus_config_t HSPI_buscfg = {-1, -1, -1, -1, -1, 0, 0}; +spi_bus_config_t *SPIbus_configs[3] = {NULL, &HSPI_buscfg, NULL}; +#else +static spi_bus_config_t HSPI_buscfg = {-1, -1, -1, -1, -1, 0, 0}; +static spi_bus_config_t VSPI_buscfg = {-1, -1, -1, -1, -1, 0, 0}; +spi_bus_config_t *SPIbus_configs[3] = {NULL, &HSPI_buscfg, &VSPI_buscfg}; +#endif + + +QueueHandle_t spi_utils_mutex = NULL; + +static exspi_device_handle_t *extspi_devices[TOTAL_CS] = {NULL}; +static uint8_t _dma_sending = 0; +static uint8_t prev_bus = 0; + +static const char TAG[] = "[SPI_UTILS]"; + + +/* + * returns ESP_OK if the bus is initialized by other device using the same bus configuration + * returns ESP_FAIL if the bus is initialized by other device using different bus configuration + * returns 1 if bus is not initialized (not used by any device) +*/ +//----------------------------------------------------- +esp_err_t check_spi_host(exspi_device_handle_t *spidev) +{ + exspi_device_handle_t *extspidev; + esp_err_t res = ESP_OK; + int extdev = TOTAL_CS; + + for (extdev=0; extdevspihost == spidev->spihost) { + // same spi bus host, check pins + if (extspidev->buscfg->miso_io_num != spidev->buscfg->miso_io_num) res = ESP_FAIL; + if (extspidev->buscfg->mosi_io_num != spidev->buscfg->mosi_io_num) res = ESP_FAIL; + if (extspidev->buscfg->sclk_io_num != spidev->buscfg->sclk_io_num) res = ESP_FAIL; + if (extspidev->buscfg->quadwp_io_num != spidev->buscfg->quadwp_io_num) res = ESP_FAIL; + if (extspidev->buscfg->quadhd_io_num != spidev->buscfg->quadhd_io_num) res = ESP_FAIL; + break; + } + } + } + if (extdev == TOTAL_CS) res = 1; // no device uses this bus, bus not initialized + return res; +} + +// Returns spi host used by sdcard driver, 0 if sdcard driver not used +//-------------------------- +int spi_host_used_by_sdspi() +{ + int res = 0; + + for (int extdev=0; extdevdc == SDSPI_HOST_ID) { + res = extspi_devices[extdev]->spihost; + break; + } + } + } + return res; +} + +// Returns spi buses used by other than sdcard driver devices +//------------------------------ +int spi_host_not_used_by_sdspi() +{ + int res = 0; + + for (int extdev=0; extdevdc != SDSPI_HOST_ID) { + if (extspi_devices[extdev]->spihost == HSPI_HOST) res |= 1; + if (extspi_devices[extdev]->spihost == VSPI_HOST) res |= 2; + } + } + } + return res; +} + +//-------------------------------------------------------- +esp_err_t add_extspi_device(exspi_device_handle_t *spidev) +{ + int extdev; + esp_err_t ret; + + // Find free ext spi device possition + for (extdev=0; extdevspihost); + return ret; + } + + if (ret == 1) { + // SPI host bus not yet initialized + ret=spi_bus_initialize(spidev->spihost, spidev->buscfg, spidev->dma_channel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "spi bus initialization failed with rc=0x%x", ret); + return ret; + } + prev_bus = spidev->spihost; + ESP_LOGD(TAG, "spi bus initialized (%d)", spidev->spihost); + } + else if (ret == ESP_OK) { + ESP_LOGD(TAG, "spi bus already initialized (%d)", spidev->spihost); + } + + // add the spi device + ret=spi_bus_add_device(spidev->spihost, &spidev->devcfg, &spidev->handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Adding spi device failed with rc=0x%x", ret); + spi_bus_free(spidev->spihost); + spidev->handle = NULL; + return ret; + } + + // Add the ext spi device to the list + extspi_devices[extdev] = spidev; + + ESP_LOGV(TAG, "== Added ext spi device (%d)==", extdev); + + return ESP_OK; +} + +//----------------------------------------------------------- +esp_err_t remove_extspi_device(exspi_device_handle_t *spidev) +{ + int extdev; + esp_err_t ret; + + // Find this ext spi device possition in list + for (extdev=0; extdevhandle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Removing spi device failed with rc=0x%x", ret); + spidev->handle = NULL; + return ret; + } + + spidev->handle = NULL; + extspi_devices[extdev] = NULL; // remove from list + + // Check for other devices using the same spi bus + for (extdev=0; extdevspihost == spidev->spihost)) break; + } + if (extdev == TOTAL_CS) { + // No devices on this spi bus, free it + spi_bus_free(spidev->spihost); + } + + return ESP_OK; +} + +/* + * Set the spi clock according to pre-calculated register value. + */ +//-------------------------------------------------------------------- +static inline void spi_set_clock(spi_dev_t *hw, spi_clock_reg_t reg) { + hw->clock.val = reg.val; +} + +//----------------------------------------------------------- +static esp_err_t reinit_spidev(exspi_device_handle_t *spidev) +{ + int extdev; + esp_err_t ret; + + // Remove **all** devices on this bus + for (extdev=0; extdevspihost == spidev->spihost) { + if (extspi_devices[extdev]->handle) { + ret = spi_bus_remove_device(extspi_devices[extdev]->handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Select: Error Removing device %d from bus %d", extdev, spidev->spihost); + return ret; + } + extspi_devices[extdev]->handle = NULL; + } + } + } + } + + // Free the spi bus + spi_bus_free((spi_host_device_t) spidev->spihost); + + // ReInitialize the spi bus + ret = spi_bus_initialize(spidev->spihost, spidev->buscfg, spidev->dma_channel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Select: Error ReInitializing spi bus %d", spidev->spihost); + return ret; + } + // add **all** devices + for (extdev=0; extdevspihost == spidev->spihost) { + ret = spi_bus_add_device(extspi_devices[extdev]->spihost, &extspi_devices[extdev]->devcfg, &extspi_devices[extdev]->handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Select: Error ReAdding device %d to bus %d", extdev, extspi_devices[extdev]->spihost); + extspi_devices[extdev]->handle = NULL; + return ret; + } + } + } + } + return ESP_OK; +} + +//------------------------------------------------------------------- +esp_err_t spi_device_select(exspi_device_handle_t *spidev, int force) +{ + if (spidev->handle == NULL) return ESP_ERR_INVALID_ARG; + if ((!force) && (spidev->selected)) return ESP_OK; // already selected + + // Get the spi device handle pointer + spi_device_handle_t handle = spidev->handle; + // Get the spi bus host pointer + spi_host_t *host=(spi_host_t*)handle->host; + + int curr_dev; + + // find spi device's slot (based on CS) on spi bus + for (curr_dev=0; curr_devdevice[curr_dev] == handle) break; + } + if (curr_dev == NO_CS) { + ESP_LOGE(TAG, "Select: Error, device not found"); + return ESP_ERR_INVALID_ARG; + } + + if (spi_utils_mutex == NULL) { + spi_utils_mutex = xSemaphoreCreateMutex(); + if (spi_utils_mutex == NULL) { + ESP_LOGE(TAG, "Select: Error creating spi mutex"); + return ESP_ERR_NO_MEM; + } + ESP_LOGV(TAG, "Select: SPI mutex created"); + } + if (spi_utils_mutex) { + if (!(xSemaphoreTake(spi_utils_mutex, SPI_SEMAPHORE_WAIT))) { + ESP_LOGE(TAG, "Select: Timeout waiting for mutex"); + return ESP_ERR_INVALID_STATE; + } + } + + if (prev_bus != spidev->spihost) { + // ** SPI BUS was changed ** + ESP_LOGD(TAG, "Select: REINITIALIZE SPI BUS (%d)", spidev->spihost); + if (spidev->dc == SDSPI_HOST_ID) reinit_sdspi_dev(spidev->spihost); // sdspi host + else { + // ** Other hosts, ReInitialize SPI bus + esp_err_t ret = reinit_spidev(spidev); + if (ret != ESP_OK) { + if (spi_utils_mutex) xSemaphoreGive(spi_utils_mutex); + return ret; + } + + // Restore this device handle & host + if (spidev->handle == NULL) { + if (spi_utils_mutex) xSemaphoreGive(spi_utils_mutex); + return ESP_ERR_INVALID_ARG; + } + handle = spidev->handle; + host=(spi_host_t*)handle->host; + // find device's host slot + for (curr_dev=0; curr_devdevice[curr_dev] == handle) break; + } + if (curr_dev == NO_CS) { + ESP_LOGE(TAG, "Select: Error, device not found"); + if (spi_utils_mutex) xSemaphoreGive(spi_utils_mutex); + return ESP_ERR_INVALID_ARG; + } + } + } + + if (spidev->dc != SDSPI_HOST_ID) { + // === Reconfigure according to device settings, but only if the device was changed or reconfiguration forced. === + if ((force) || (curr_dev != host->prev_cs)) { + const int apbclk=APB_CLK_FREQ; + handle->clk_cfg.eff_clk = spi_cal_clock(apbclk, spidev->devcfg.clock_speed_hz, handle->cfg.duty_cycle_pos, (uint32_t*)&handle->clk_cfg.reg); + int effclk=handle->clk_cfg.eff_clk; + spi_set_clock(host->hw, handle->clk_cfg.reg); + + //Configure bit order + host->hw->ctrl.rd_bit_order=(handle->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0; + host->hw->ctrl.wr_bit_order=(handle->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0; + + //Configure polarity + //SPI interface needs to be configured for a delay in some cases. + int nodelay=0; + if ((host->flags&SPICOMMON_BUSFLAG_NATIVE_PINS)!=0) { + if (effclk >= apbclk/2) { + nodelay=1; + } + } else { + uint32_t delay_limit = apbclk/4; + if (effclk >= delay_limit) { + nodelay=1; + } + } + + if (handle->cfg.mode==0) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } else if (handle->cfg.mode==1) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (handle->cfg.mode==2) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (handle->cfg.mode==3) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } + + //Configure misc stuff + host->hw->user.doutdin=(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1; + host->hw->user.sio=(handle->cfg.flags & SPI_DEVICE_3WIRE)?1:0; + + host->hw->ctrl2.setup_time=handle->cfg.cs_ena_pretrans-1; + host->hw->user.cs_setup=handle->cfg.cs_ena_pretrans?1:0; + host->hw->ctrl2.hold_time=handle->cfg.cs_ena_posttrans-1; + host->hw->user.cs_hold=(handle->cfg.cs_ena_posttrans)?1:0; + + host->cur_cs = curr_dev; + + //Configure CS pin + if (handle->cfg.spics_io_num >= 0) curr_dev = -1; + host->hw->pin.cs0_dis = (curr_dev==0) ? 0:1; + host->hw->pin.cs1_dis = (curr_dev==1) ? 0:1; + host->hw->pin.cs2_dis = (curr_dev==2) ? 0:1; + } + host->prev_cs = curr_dev; + } + + // If the device uses external CS, activate it + if ((handle->cfg.spics_io_num < 0) && (spidev->cs >= 0)) { + gpio_set_level(spidev->cs, 0); + ESP_LOGV(TAG, "Select: extCS:%d", spidev->cs); + } + + spidev->selected = 1; + + return ESP_OK; +} + +//---------------------------------------------------------- +esp_err_t spi_device_deselect(exspi_device_handle_t *spidev) +{ + if (spidev->handle == NULL) return ESP_ERR_INVALID_ARG; + if (spidev->selected == 0) return ESP_OK; // already deselected + + _wait_trans_finish(spidev); + int currDev; + spi_host_t *host=(spi_host_t*)spidev->handle->host; + + // find device's host slot + for (currDev=0; currDevdevice[currDev] == spidev->handle) break; + } + if (currDev == NO_CS) { + if (spi_utils_mutex) xSemaphoreGive(spi_utils_mutex); + ESP_LOGE(TAG, "DeSelect: Error, device not found"); + return ESP_ERR_INVALID_ARG; + } + + // If the device uses external CS, deactivate it + if ((spidev->handle->cfg.spics_io_num < 0) && (spidev->cs >= 0)) { + gpio_set_level(spidev->cs, 1); + ESP_LOGV(TAG, "DeSelect: extCS=%d ==", spidev->cs); + } + + spidev->selected = 0; + prev_bus = spidev->spihost; + host->cur_cs = NO_CS; + + if (spi_utils_mutex) xSemaphoreGive(spi_utils_mutex); + + return ESP_OK; +} + +// ----------------------------- +// Direct (no DMA) data transfer +//---------------------------------------------------------------------------------------- +esp_err_t spi_transfer_data_nodma(exspi_device_handle_t *spidev, spi_transaction_t *trans) +{ + if (!spidev->handle) return ESP_ERR_INVALID_ARG; + + // *** For now we can only handle 8-bit bytes transmission + if (((trans->length % 8) != 0) || ((trans->rxlength % 8) != 0)) return ESP_ERR_INVALID_ARG; + + spi_device_handle_t handle = spidev->handle; + spi_host_t *host=(spi_host_t*)spidev->handle->host; + esp_err_t ret; + uint8_t do_deselect = 0; + const uint8_t *txbuffer = NULL; + uint8_t *rxbuffer = NULL; + + // find device's host slot + int dev_num; + //spi_device_t *dev=NULL; + for (dev_num=0; dev_numdevice[dev_num] == handle) { + //dev=host->device[dev_num]; + break; + } + } + if (dev_num==NO_CS) return ESP_ERR_INVALID_ARG; + + if (trans->flags & SPI_TRANS_USE_TXDATA) { + // Send data from 'trans->tx_data' + txbuffer=(uint8_t*)&trans->tx_data[0]; + } else { + // Send data from 'trans->tx_buffer' + txbuffer=(uint8_t*)trans->tx_buffer; + } + if (trans->flags & SPI_TRANS_USE_RXDATA) { + // Receive data to 'trans->rx_data' + rxbuffer=(uint8_t*)&trans->rx_data[0]; + } else { + // Receive data to 'trans->rx_buffer' + rxbuffer=(uint8_t*)trans->rx_buffer; + } + + // ** Set transmit & receive length in bytes + uint32_t txlen = trans->length / 8; + uint32_t rxlen = trans->rxlength / 8; + + if (txbuffer == NULL) txlen = 0; + if (rxbuffer == NULL) rxlen = 0; + if ((rxlen == 0) && (txlen == 0)) { + // ** NOTHING TO SEND or RECEIVE, return + return ESP_ERR_INVALID_ARG; + } + + // If using 'trans->tx_data' and/or 'trans->rx_data', maximum 4 bytes can be sent/received + if ((txbuffer == &trans->tx_data[0]) && (txlen > 4)) return ESP_ERR_INVALID_ARG; + if ((rxbuffer == &trans->rx_data[0]) && (rxlen > 4)) return ESP_ERR_INVALID_ARG; + + // --- Wait for SPI bus ready --- + while (host->hw->cmd.usr); + + // --- If the device was not selected, select it --- + if (spidev->selected == 0) { + ret = spi_device_select(spidev, 0); + if (ret) return ret; + do_deselect = 1; // We will deselect the device after the operation ! + } + + // --- Call pre-transmission callback, if any --- + if (handle->cfg.pre_cb) handle->cfg.pre_cb(trans); + + // Test if operating in full duplex mode + uint8_t duplex = 1; + if (handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) duplex = 0; // Half duplex mode ! + + uint32_t bits, rdbits; + uint32_t wd; + uint8_t bc, rdidx; + uint32_t rdcount = rxlen; // Total number of bytes to read + uint32_t count = 0; // number of bytes transmitted + uint32_t rd_read = 0; // Number of bytes read so far + + host->hw->user.usr_mosi_highpart = 0; // use the whole spi buffer + + // --- Check if address and/or command phase will be used --- + int cmdlen; + if ( trans->flags & SPI_TRANS_VARIABLE_CMD ) { + cmdlen = ((spi_transaction_ext_t*)trans)->command_bits; + } else { + cmdlen = handle->cfg.command_bits; + } + int addrlen; + if ( trans->flags & SPI_TRANS_VARIABLE_ADDR ) { + addrlen = ((spi_transaction_ext_t*)trans)->address_bits; + } else { + addrlen = handle->cfg.address_bits; + } + host->hw->user1.usr_addr_bitlen=addrlen-1; + host->hw->user2.usr_command_bitlen=cmdlen-1; + host->hw->user.usr_addr=addrlen?1:0; + host->hw->user.usr_command=cmdlen?1:0; + + ESP_LOGV(TAG, "Starting no-dma transaction on %d", dev_num); + // Check if we have to transmit some data + if (txlen > 0) { + host->hw->user.usr_mosi = 1; + uint8_t idx; + bits = 0; // remaining bits to send + idx = 0; // index to spi hw data_buf (16 32-bit words, 64 bytes, 512 bits) + + // ** Transmit 'txlen' bytes + while (count < txlen) { + wd = 0; + for (bc=0;bc<32;bc+=8) { + wd |= (uint32_t)txbuffer[count] << bc; + count++; // Increment sent data count + bits += 8; // Increment bits count + if (count == txlen) break; // If all transmit data pushed to hw spi buffer break from the loop + } + host->hw->data_buf[idx] = wd; + idx++; + if (idx == 16) { + // hw SPI buffer full (all 64 bytes filled, START THE TRANSSACTION + host->hw->mosi_dlen.usr_mosi_dbitlen=bits-1; // Set mosi dbitlen + + if ((duplex) && (rdcount > 0)) { + // In full duplex mode we are receiving while sending ! + host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen + host->hw->user.usr_miso = 1; + } + else { + host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received + host->hw->user.usr_miso = 0; + } + + // ** Start the transaction *** + host->hw->cmd.usr=1; + // Wait the transaction to finish + while (host->hw->cmd.usr); + + if ((duplex) && (rdcount > 0)) { + // *** in full duplex mode transfer received data to input buffer *** + rdidx = 0; + while (bits > 0) { + wd = host->hw->data_buf[rdidx]; + rdidx++; + for (bc=0;bc<32;bc+=8) { // get max 4 bytes + rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF); + rdcount--; + bits -= 8; + if (rdcount == 0) { + bits = 0; + break; // Finished reading data + } + } + } + } + bits = 0; // nothing in hw spi buffer yet + idx = 0; // start from the beginning of the hw spi buffer + } + } + // *** All transmit data are sent or pushed to hw spi buffer + // bits > 0 IF THERE ARE SOME DATA STILL WAITING IN THE HW SPI TRANSMIT BUFFER + if (bits > 0) { + // ** WE HAVE SOME DATA IN THE HW SPI TRANSMIT BUFFER + host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // Set mosi dbitlen + + if ((duplex) && (rdcount > 0)) { + // In full duplex mode we are receiving while sending ! + host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen + host->hw->user.usr_miso = 1; + } + else { + host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received + host->hw->user.usr_miso = 0; + } + + // ** Start the transaction *** + host->hw->cmd.usr=1; + // Wait the transaction to finish + while (host->hw->cmd.usr); + + if ((duplex) && (rdcount > 0)) { + // *** in full duplex mode transfer received data to input buffer *** + rdidx = 0; + while (bits > 0) { + wd = host->hw->data_buf[rdidx]; + rdidx++; + for (bc=0;bc<32;bc+=8) { // get max 4 bytes + rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF); + rdcount--; + bits -= 8; + if (bits == 0) break; + if (rdcount == 0) { + bits = 0; + break; // Finished reading data + } + } + } + } + } + //if (duplex) rdcount = 0; // In duplex mode receive only as many bytes as was transmitted + } + + // ------------------------------------------------------------------------ + // *** If rdcount = 0 we have nothing to receive and we exit the function + // This is true if no data receive was requested, + // or all the data was received in Full duplex mode during the transmission + // ------------------------------------------------------------------------ + if (rdcount > 0) { + // ---------------------------------------------------------------------------------------------------------------- + // *** rdcount > 0, we have to receive some data + // This is true if we operate in Half duplex mode when receiving after transmission is done, + // or not all data was received in Full duplex mode during the transmission (trans->rxlength > trans->txlength) + // ---------------------------------------------------------------------------------------------------------------- + host->hw->user.usr_mosi = 0; // do not send + host->hw->user.usr_miso = 1; // do receive + while (rdcount > 0) { + if (rdcount <= 64) rdbits = rdcount * 8; + else rdbits = 64 * 8; + + // Load receive buffer + host->hw->mosi_dlen.usr_mosi_dbitlen=0; + host->hw->miso_dlen.usr_miso_dbitlen=rdbits-1; + + // ** Start the transaction *** + host->hw->cmd.usr=1; + // Wait the transaction to finish + while (host->hw->cmd.usr); + + // *** transfer received data to input buffer *** + rdidx = 0; + while (rdbits > 0) { + wd = host->hw->data_buf[rdidx]; + rdidx++; + for (bc=0;bc<32;bc+=8) { + rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF); + rdcount--; + rdbits -= 8; + if (rdcount == 0) { + rdbits = 0; + break; + } + } + } + } + } + + // --- Call post-transmission callback, if any --- + if (handle->cfg.post_cb) handle->cfg.post_cb(trans); + + if (do_deselect) { + // Spi device was selected in this function, deselect it now + ret = spi_device_deselect(spidev); + if (ret) return ret; + } + + return ESP_OK; +} + +//--------------------------------------------------- +uint32_t spi_get_speed(exspi_device_handle_t *spidev) +{ + if (spidev->handle == NULL) return 0; + + spi_device_handle_t handle = spidev->handle; + return handle->clk_cfg.eff_clk; +} + +//------------------------------------------------------------------- +uint32_t spi_set_speed(exspi_device_handle_t *spidev, uint32_t speed) +{ + if (spidev->handle == NULL) return 0; + + spi_device_handle_t handle = spidev->handle; + if (spidev->curr_clock != speed) { + spi_device_deselect(spidev); + + spi_device_handle_t handle = spidev->handle; + spi_host_t *host=(spi_host_t*)handle->host; + + int apbclk=APB_CLK_FREQ; + handle->clk_cfg.eff_clk = spi_cal_clock(apbclk, speed, handle->cfg.duty_cycle_pos, (uint32_t*)&handle->clk_cfg.reg); + spi_set_clock(host->hw, handle->clk_cfg.reg); + spidev->devcfg.clock_speed_hz = handle->clk_cfg.eff_clk; + spidev->curr_clock = handle->clk_cfg.eff_clk; + + // select the device, force clock reconfigure + spi_device_select(spidev, 1); + spi_device_deselect(spidev); + } + + return handle->clk_cfg.eff_clk; +} + +//--------------------------------------------------- +bool spi_uses_native_pins(spi_device_handle_t handle) +{ + return ((handle->host->flags & SPICOMMON_BUSFLAG_NATIVE_PINS) != 0); +} + +// Start hardware SPI data transfer, spi device must be selected +// No address or command phase is executed +//---------------------------------------------------------------------------------------- +void IRAM_ATTR _spi_transfer_start(exspi_device_handle_t *spi_dev, int wrbits, int rdbits) +{ + while (spi_dev->handle->host->hw->cmd.usr); // Wait for SPI bus ready + + spi_dev->handle->host->hw->user1.usr_addr_bitlen = 0; + spi_dev->handle->host->hw->user2.usr_command_bitlen = 0; + spi_dev->handle->host->hw->user.usr_addr = 0; + spi_dev->handle->host->hw->user.usr_command = 0; + spi_dev->handle->host->hw->user.usr_mosi_highpart = 0; + + if (wrbits) { + spi_dev->handle->host->hw->mosi_dlen.usr_mosi_dbitlen = wrbits-1; + spi_dev->handle->host->hw->user.usr_mosi = 1; + } + else { + spi_dev->handle->host->hw->mosi_dlen.usr_mosi_dbitlen = 0; + spi_dev->handle->host->hw->user.usr_mosi = 0; + } + if (rdbits) { + spi_dev->handle->host->hw->miso_dlen.usr_miso_dbitlen = rdbits-1; + spi_dev->handle->host->hw->user.usr_miso = 1; + } + else { + spi_dev->handle->host->hw->miso_dlen.usr_miso_dbitlen = 0; + spi_dev->handle->host->hw->user.usr_miso = 0; + } + // Start transfer + spi_dev->handle->host->hw->cmd.usr = 1; +} + +//----------------------------------------------------------------------------------- +void IRAM_ATTR _dma_send(exspi_device_handle_t *spi_dev, uint8_t *data, uint32_t size) +{ + //Fill DMA descriptors + + spicommon_dmaworkaround_transfer_active(spi_dev->handle->host->dma_chan); //mark channel as active + spicommon_setup_dma_desc_links(spi_dev->handle->host->dmadesc_tx, size, data, false); + spi_dev->handle->host->hw->user.usr_addr = 0; + spi_dev->handle->host->hw->user.usr_command = 0; + spi_dev->handle->host->hw->user.usr_mosi_highpart=0; + spi_dev->handle->host->hw->dma_out_link.addr=(int)(&spi_dev->handle->host->dmadesc_tx[0]) & 0xFFFFF; + spi_dev->handle->host->hw->dma_out_link.start=1; + spi_dev->handle->host->hw->user.usr_mosi_highpart=0; + + spi_dev->handle->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1; + + _dma_sending = 1; + // Start transfer + spi_dev->handle->host->hw->cmd.usr = 1; +} + +//-------------------------------------------------------------------- +esp_err_t IRAM_ATTR _wait_trans_finish(exspi_device_handle_t *spi_dev) +{ + while (spi_dev->handle->host->hw->cmd.usr); // Wait for SPI bus ready + if (_dma_sending) { + //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. + if (spi_dev->handle->host->dma_chan) spicommon_dmaworkaround_idle(spi_dev->handle->host->dma_chan); + + // Reset DMA + spi_dev->handle->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + spi_dev->handle->host->hw->dma_out_link.start=0; + spi_dev->handle->host->hw->dma_in_link.start=0; + spi_dev->handle->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + spi_dev->handle->host->hw->dma_conf.out_data_burst_en=1; + _dma_sending = 0; + } + return ESP_OK; +} + + diff --git a/Tools/esp_idf_patches/components/esp32/cpu_start.c b/Tools/esp_idf_patches/components/esp32/cpu_start.c new file mode 100644 index 00000000..b288aeb6 --- /dev/null +++ b/Tools/esp_idf_patches/components/esp32/cpu_start.c @@ -0,0 +1,469 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "esp_attr.h" +#include "esp_err.h" + +#include "rom/ets_sys.h" +#include "rom/uart.h" +#include "rom/rtc.h" +#include "rom/cache.h" + +#include "soc/cpu.h" +#include "soc/rtc.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" + +#include "driver/rtc_io.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" + +#include "esp_heap_caps_init.h" +#include "sdkconfig.h" +#include "esp_system.h" +#include "esp_spi_flash.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_spi_flash.h" +#include "esp_ipc.h" +#include "esp_crosscore_int.h" +#include "esp_dport_access.h" +#include "esp_log.h" +#include "esp_vfs_dev.h" +#include "esp_newlib.h" +#include "esp_brownout.h" +#include "esp_int_wdt.h" +#include "esp_task.h" +#include "esp_task_wdt.h" +#include "esp_phy_init.h" +#include "esp_cache_err_int.h" +#include "esp_coexist.h" +#include "esp_panic.h" +#include "esp_core_dump.h" +#include "esp_app_trace.h" +#include "esp_efuse.h" +#include "esp_spiram.h" +#include "esp_clk_internal.h" +#include "esp_timer.h" +#include "esp_pm.h" +#include "pm_impl.h" +#include "trax.h" + +#define STRINGIFY(s) STRINGIFY2(s) +#define STRINGIFY2(s) #s + +void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn)); +void start_cpu0_default(void) IRAM_ATTR __attribute__((noreturn)); +#if !CONFIG_FREERTOS_UNICORE +static void IRAM_ATTR call_start_cpu1() __attribute__((noreturn)); +void start_cpu1(void) __attribute__((weak, alias("start_cpu1_default"))) __attribute__((noreturn)); +void start_cpu1_default(void) IRAM_ATTR __attribute__((noreturn)); +static bool app_cpu_started = false; +#endif //!CONFIG_FREERTOS_UNICORE + +static void do_global_ctors(void); +static void main_task(void* args); +extern void app_main(void); +extern esp_err_t esp_pthread_init(void); + +extern int _bss_start; +extern int _bss_end; +extern int _rtc_bss_start; +extern int _rtc_bss_end; +extern int _init_start; +extern void (*__init_array_start)(void); +extern void (*__init_array_end)(void); +extern volatile int port_xSchedulerRunning[2]; + +static const char* TAG = "cpu_start"; + +struct object { long placeholder[ 10 ]; }; +void __register_frame_info (const void *begin, struct object *ob); +extern char __eh_frame[]; + +//If CONFIG_SPIRAM_IGNORE_NOTFOUND is set and external RAM is not found or errors out on testing, this is set to false. +bool s_spiram_okay=true; + +/* + * We arrive here after the bootloader finished loading the program from flash. The hardware is mostly uninitialized, + * and the app CPU is in reset. We do have a stack, so we can do the initialization in C. + */ + +void IRAM_ATTR call_start_cpu0() +{ +#if CONFIG_FREERTOS_UNICORE + RESET_REASON rst_reas[1]; +#else + RESET_REASON rst_reas[2]; +#endif + cpu_configure_region_protection(); + + //Move exception vectors to IRAM + asm volatile (\ + "wsr %0, vecbase\n" \ + ::"r"(&_init_start)); + + rst_reas[0] = rtc_get_reset_reason(0); + +#if !CONFIG_FREERTOS_UNICORE + rst_reas[1] = rtc_get_reset_reason(1); +#endif + + // from panic handler we can be reset by RWDT or TG0WDT + if (rst_reas[0] == RTCWDT_SYS_RESET || rst_reas[0] == TG0WDT_SYS_RESET +#if !CONFIG_FREERTOS_UNICORE + || rst_reas[1] == RTCWDT_SYS_RESET || rst_reas[1] == TG0WDT_SYS_RESET +#endif + ) { + esp_panic_wdt_stop(); + } + + //Clear BSS. Please do not attempt to do any complex stuff (like early logging) before this. + memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start)); + + /* Unless waking from deep sleep (implying RTC memory is intact), clear RTC bss */ + if (rst_reas[0] != DEEPSLEEP_RESET) { + memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start)); + } + +#if CONFIG_SPIRAM_BOOT_INIT + esp_spiram_init_cache(); + if (esp_spiram_init() != ESP_OK) { +#if CONFIG_SPIRAM_IGNORE_NOTFOUND + ESP_EARLY_LOGI(TAG, "Failed to init external RAM; continuing without it."); + s_spiram_okay = false; +#else + ESP_EARLY_LOGE(TAG, "Failed to init external RAM!"); + abort(); +#endif + } +#endif + + ESP_EARLY_LOGI(TAG, "Pro cpu up."); + +#if !CONFIG_FREERTOS_UNICORE + ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1); + //Flush and enable icache for APP CPU + Cache_Flush(1); + Cache_Read_Enable(1); + esp_cpu_unstall(1); + // Enable clock and reset APP CPU. Note that OpenOCD may have already + // enabled clock and taken APP CPU out of reset. In this case don't reset + // APP CPU again, as that will clear the breakpoints which may have already + // been set. + if (!DPORT_GET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN)) { + DPORT_SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_C_REG, DPORT_APPCPU_RUNSTALL); + DPORT_SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); + } + ets_set_appcpu_boot_addr((uint32_t)call_start_cpu1); + + while (!app_cpu_started) { + ets_delay_us(100); + } +#else + ESP_EARLY_LOGI(TAG, "Single core mode"); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); +#endif + + +#if CONFIG_SPIRAM_MEMTEST + if (s_spiram_okay) { + bool ext_ram_ok=esp_spiram_test(); + if (!ext_ram_ok) { + ESP_EARLY_LOGE(TAG, "External RAM failed memory test!"); + abort(); + } + } +#endif + + /* Initialize heap allocator. WARNING: This *needs* to happen *after* the app cpu has booted. + If the heap allocator is initialized first, it will put free memory linked list items into + memory also used by the ROM. Starting the app cpu will let its ROM initialize that memory, + corrupting those linked lists. Initializing the allocator *after* the app cpu has booted + works around this problem. + With SPI RAM enabled, there's a second reason: half of the SPI RAM will be managed by the + app CPU, and when that is not up yet, the memory will be inaccessible and heap_caps_init may + fail initializing it properly. */ + heap_caps_init(); + + ESP_EARLY_LOGI(TAG, "Pro cpu start user code"); + start_cpu0(); +} + +#if !CONFIG_FREERTOS_UNICORE + +static void wdt_reset_cpu1_info_enable(void) +{ + DPORT_REG_SET_BIT(DPORT_APP_CPU_RECORD_CTRL_REG, DPORT_APP_CPU_PDEBUG_ENABLE | DPORT_APP_CPU_RECORD_ENABLE); + DPORT_REG_CLR_BIT(DPORT_APP_CPU_RECORD_CTRL_REG, DPORT_APP_CPU_RECORD_ENABLE); +} + +void IRAM_ATTR call_start_cpu1() +{ + asm volatile (\ + "wsr %0, vecbase\n" \ + ::"r"(&_init_start)); + + ets_set_appcpu_boot_addr(0); + cpu_configure_region_protection(); + +#if CONFIG_CONSOLE_UART_NONE + ets_install_putc1(NULL); + ets_install_putc2(NULL); +#else // CONFIG_CONSOLE_UART_NONE + uartAttach(); + ets_install_uart_printf(); + uart_tx_switch(CONFIG_CONSOLE_UART_NUM); +#endif + + wdt_reset_cpu1_info_enable(); + ESP_EARLY_LOGI(TAG, "App cpu up."); + app_cpu_started = 1; + start_cpu1(); +} +#endif //!CONFIG_FREERTOS_UNICORE + +static void intr_matrix_clear(void) +{ + //Clear all the interrupt matrix register + for (int i = ETS_WIFI_MAC_INTR_SOURCE; i <= ETS_CACHE_IA_INTR_SOURCE; i++) { + intr_matrix_set(0, i, ETS_INVALID_INUM); +#if !CONFIG_FREERTOS_UNICORE + intr_matrix_set(1, i, ETS_INVALID_INUM); +#endif + } +} + +void start_cpu0_default(void) +{ + esp_err_t err; + esp_setup_syscall_table(); + + if (s_spiram_okay) { +#if CONFIG_SPIRAM_BOOT_INIT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC) + esp_err_t r=esp_spiram_add_to_heapalloc(); + if (r != ESP_OK) { + ESP_EARLY_LOGE(TAG, "External RAM could not be added to heap!"); + abort(); + } +#if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL + r=esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL); + if (r != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool!"); + abort(); + } +#endif +#if CONFIG_SPIRAM_USE_MALLOC + heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL); +#endif +#endif + } + +//Enable trace memory and immediately start trace. +#if CONFIG_ESP32_TRAX +#if CONFIG_ESP32_TRAX_TWOBANKS + trax_enable(TRAX_ENA_PRO_APP); +#else + trax_enable(TRAX_ENA_PRO); +#endif + trax_start_trace(TRAX_DOWNCOUNT_WORDS); +#endif + esp_clk_init(); + esp_perip_clk_init(); + intr_matrix_clear(); + +#ifndef CONFIG_CONSOLE_UART_NONE +#ifdef CONFIG_PM_ENABLE + const int uart_clk_freq = REF_CLK_FREQ; + /* When DFS is enabled, use REFTICK as UART clock source */ + CLEAR_PERI_REG_MASK(UART_CONF0_REG(CONFIG_CONSOLE_UART_NUM), UART_TICK_REF_ALWAYS_ON); +#else + const int uart_clk_freq = APB_CLK_FREQ; +#endif // CONFIG_PM_DFS_ENABLE + uart_div_modify(CONFIG_CONSOLE_UART_NUM, (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE); +#endif // CONFIG_CONSOLE_UART_NONE + +#if CONFIG_BROWNOUT_DET + esp_brownout_init(); +#endif +#if CONFIG_DISABLE_BASIC_ROM_CONSOLE + esp_efuse_disable_basic_rom_console(); +#endif + rtc_gpio_force_hold_dis_all(); + esp_vfs_dev_uart_register(); + esp_reent_init(_GLOBAL_REENT); +#ifndef CONFIG_CONSOLE_UART_NONE + const char* default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_CONSOLE_UART_NUM); + _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); + _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); + _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); +#else + _GLOBAL_REENT->_stdin = (FILE*) &__sf_fake_stdin; + _GLOBAL_REENT->_stdout = (FILE*) &__sf_fake_stdout; + _GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr; +#endif + esp_timer_init(); + esp_set_time_from_rtc(); +#if CONFIG_ESP32_APPTRACE_ENABLE + err = esp_apptrace_init(); + assert(err == ESP_OK && "Failed to init apptrace module on PRO CPU!"); +#endif +#if CONFIG_SYSVIEW_ENABLE + SEGGER_SYSVIEW_Conf(); +#endif + err = esp_pthread_init(); + assert(err == ESP_OK && "Failed to init pthread module!"); + + do_global_ctors(); +#if CONFIG_INT_WDT + esp_int_wdt_init(); + //Initialize the interrupt watch dog for CPU0. + esp_int_wdt_cpu_init(); +#endif + esp_cache_err_int_init(); + esp_crosscore_int_init(); + esp_ipc_init(); +#ifndef CONFIG_FREERTOS_UNICORE + esp_dport_access_int_init(); +#endif + spi_flash_init(); + /* init default OS-aware flash access critical section */ + spi_flash_guard_set(&g_flash_guard_default_ops); +#ifdef CONFIG_PM_ENABLE + esp_pm_impl_init(); +#ifdef CONFIG_PM_DFS_INIT_AUTO + rtc_cpu_freq_t max_freq; + rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &max_freq); + esp_pm_config_esp32_t cfg = { + .max_cpu_freq = max_freq, + .min_cpu_freq = RTC_CPU_FREQ_XTAL + }; + esp_pm_configure(&cfg); +#endif //CONFIG_PM_DFS_INIT_AUTO +#endif //CONFIG_PM_ENABLE + +#if CONFIG_ESP32_ENABLE_COREDUMP + esp_core_dump_init(); +#endif + + portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main", + ESP_TASK_MAIN_STACK, NULL, + ESP_TASK_MAIN_PRIO, NULL, 0); + assert(res == pdTRUE); + ESP_LOGI(TAG, "Starting scheduler on PRO CPU."); + vTaskStartScheduler(); + abort(); /* Only get to here if not enough free heap to start scheduler */ +} + +#if !CONFIG_FREERTOS_UNICORE +void start_cpu1_default(void) +{ + // Wait for FreeRTOS initialization to finish on PRO CPU + while (port_xSchedulerRunning[0] == 0) { + ; + } +#if CONFIG_ESP32_TRAX_TWOBANKS + trax_start_trace(TRAX_DOWNCOUNT_WORDS); +#endif +#if CONFIG_ESP32_APPTRACE_ENABLE + esp_err_t err = esp_apptrace_init(); + assert(err == ESP_OK && "Failed to init apptrace module on APP CPU!"); +#endif +#if CONFIG_INT_WDT + //Initialize the interrupt watch dog for CPU1. + esp_int_wdt_cpu_init(); +#endif + //Take care putting stuff here: if asked, FreeRTOS will happily tell you the scheduler + //has started, but it isn't active *on this CPU* yet. + esp_cache_err_int_init(); + esp_crosscore_int_init(); + esp_dport_access_int_init(); + + ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU."); + xPortStartScheduler(); + abort(); /* Only get to here if FreeRTOS somehow very broken */ +} +#endif //!CONFIG_FREERTOS_UNICORE + +#ifdef CONFIG_CXX_EXCEPTIONS +size_t __cxx_eh_arena_size_get() +{ + return CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE; +} +#endif + +static void do_global_ctors(void) +{ +#ifdef CONFIG_CXX_EXCEPTIONS + static struct object ob; + __register_frame_info( __eh_frame, &ob ); +#endif + + void (**p)(void); + for (p = &__init_array_end - 1; p >= &__init_array_start; --p) { + (*p)(); + } +} + +static void main_task(void* args) +{ + // Now that the application is about to start, disable boot watchdogs + REG_CLR_BIT(TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN_S); + REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN); +#if !CONFIG_FREERTOS_UNICORE + // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack + while (port_xSchedulerRunning[1] == 0) { + ; + } +#endif + //Enable allocation in region where the startup stacks were located. + heap_caps_enable_nonos_stack_heaps(); + + //Initialize task wdt if configured to do so +#ifdef CONFIG_TASK_WDT_PANIC + ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, true)) +#elif CONFIG_TASK_WDT + ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, false)) +#endif + + //Add IDLE 0 to task wdt +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 + TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); + if(idle_0 != NULL){ + ESP_ERROR_CHECK(esp_task_wdt_add(idle_0)) + } +#endif + //Add IDLE 1 to task wdt +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 + TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); + if(idle_1 != NULL){ + ESP_ERROR_CHECK(esp_task_wdt_add(idle_1)) + } +#endif + + app_main(); + vTaskDelete(NULL); +} + diff --git a/Tools/esp_idf_patches/components/esp32/intr_alloc.c b/Tools/esp_idf_patches/components/esp32/intr_alloc.c new file mode 100644 index 00000000..9a1a6a3d --- /dev/null +++ b/Tools/esp_idf_patches/components/esp32/intr_alloc.c @@ -0,0 +1,900 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +#include "sdkconfig.h" +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#include "esp_log.h" +#include "esp_intr.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include +#include + +static const char* TAG = "intr_alloc"; + +#define ETS_INTERNAL_TIMER0_INTR_NO 6 +#define ETS_INTERNAL_TIMER1_INTR_NO 15 +#define ETS_INTERNAL_TIMER2_INTR_NO 16 +#define ETS_INTERNAL_SW0_INTR_NO 7 +#define ETS_INTERNAL_SW1_INTR_NO 29 +#define ETS_INTERNAL_PROFILING_INTR_NO 11 + + +/* +Define this to debug the choices made when allocating the interrupt. This leads to much debugging +output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog +being triggered, that is why it is separate from the normal LOG* scheme. +*/ +//define DEBUG_INT_ALLOC_DECISIONS +#ifdef DEBUG_INT_ALLOC_DECISIONS +# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__) +#else +# define ALCHLOG(...) do {} while (0) +#endif + + +typedef enum { + INTDESC_NORMAL=0, + INTDESC_RESVD, + INTDESC_SPECIAL //for xtensa timers / software ints +} int_desc_flag_t; + +typedef enum { + INTTP_LEVEL=0, + INTTP_EDGE, + INTTP_NA +} int_type_t; + +typedef struct { + int level; + int_type_t type; + int_desc_flag_t cpuflags[2]; +} int_desc_t; + + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h +const static int_desc_t int_desc[32]={ + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5 + { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 + { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 + { 1, INTTP_EDGE , {INTDESC_NORMAL, INTDESC_NORMAL} }, //10 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 + { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI + { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 + { 5, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL} }, //16 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 + { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 + { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //26 + { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 + { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 + { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 +}; + +typedef struct shared_vector_desc_t shared_vector_desc_t; +typedef struct vector_desc_t vector_desc_t; + +struct shared_vector_desc_t { + int disabled: 1; + int source: 8; + volatile uint32_t *statusreg; + uint32_t statusmask; + intr_handler_t isr; + void *arg; + shared_vector_desc_t *next; +}; + + +#define VECDESC_FL_RESERVED (1<<0) +#define VECDESC_FL_INIRAM (1<<1) +#define VECDESC_FL_SHARED (1<<2) +#define VECDESC_FL_NONSHARED (1<<3) + +//Pack using bitfields for better memory use +struct vector_desc_t { + int flags: 16; //OR of VECDESC_FLAG_* defines + unsigned int cpu: 1; + unsigned int intno: 5; + int source: 8; //Interrupt mux flags, used when not shared + shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED + vector_desc_t *next; +}; + +struct intr_handle_data_t { + vector_desc_t *vector_desc; + shared_vector_desc_t *shared_vector_desc; +}; + +typedef struct non_shared_isr_arg_t non_shared_isr_arg_t; + +struct non_shared_isr_arg_t { + intr_handler_t isr; + void *isr_arg; + int source; +}; + +//Linked list of vector descriptions, sorted by cpu.intno value +static vector_desc_t *vector_desc_head = NULL; + +//This bitmask has an 1 if the int should be disabled when the flash is disabled. +static uint32_t non_iram_int_mask[portNUM_PROCESSORS]; +//This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. +static uint32_t non_iram_int_disabled[portNUM_PROCESSORS]; +static bool non_iram_int_disabled_flag[portNUM_PROCESSORS]; + +#if CONFIG_SYSVIEW_ENABLE +extern uint32_t port_switch_flag[]; +#endif + +static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; + +//Inserts an item into vector_desc list so that the list is sorted +//with an incrementing cpu.intno value. +static void insert_vector_desc(vector_desc_t *to_insert) +{ + vector_desc_t *vd=vector_desc_head; + vector_desc_t *prev=NULL; + while(vd!=NULL) { + if (vd->cpu > to_insert->cpu) break; + if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) break; + prev=vd; + vd=vd->next; + } + if (vd==NULL && prev==NULL) { + //First item + vector_desc_head=to_insert; + vector_desc_head->next=NULL; + } else { + prev->next=to_insert; + to_insert->next=vd; + } +} + +//Returns a vector_desc entry for an intno/cpu, or NULL if none exists. +static vector_desc_t *find_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=vector_desc_head; + while(vd!=NULL) { + if (vd->cpu==cpu && vd->intno==intno) break; + vd=vd->next; + } + return vd; +} + +//Returns a vector_desc entry for an intno/cpu. +//Either returns a preexisting one or allocates a new one and inserts +//it into the list. Returns NULL on malloc fail. +static vector_desc_t *get_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=find_desc_for_int(intno, cpu); + if (vd==NULL) { + vector_desc_t *newvd=heap_caps_malloc(sizeof(vector_desc_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); + if (newvd==NULL) return NULL; + memset(newvd, 0, sizeof(vector_desc_t)); + newvd->intno=intno; + newvd->cpu=cpu; + insert_vector_desc(newvd); + return newvd; + } else { + return vd; + } +} + +//Returns a vector_desc entry for an source, the cpu parameter is used to tell GPIO_INT and GPIO_NMI from different CPUs +static vector_desc_t * find_desc_for_source(int source, int cpu) +{ + vector_desc_t *vd=vector_desc_head; + while(vd!=NULL) { + if ( !(vd->flags & VECDESC_FL_SHARED) ) { + if ( vd->source == source && cpu == vd->cpu ) break; + } else if ( vd->cpu == cpu ) { + // check only shared vds for the correct cpu, otherwise skip + bool found = false; + shared_vector_desc_t *svd = vd->shared_vec_info; + assert(svd != NULL ); + while(svd) { + if ( svd->source == source ) { + found = true; + break; + } + svd = svd->next; + } + if ( found ) break; + } + vd=vd->next; + } + return vd; +} + +esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_SHARED; + if (is_int_ram) vd->flags|=VECDESC_FL_INIRAM; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +esp_err_t esp_intr_reserve(int intno, int cpu) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_RESERVED; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +//Interrupt handler table and unhandled uinterrupt routine. Duplicated +//from xtensa_intr.c... it's supposed to be private, but we need to look +//into it in order to see if someone allocated an int using +//xt_set_interrupt_handler. +typedef struct xt_handler_table_entry { + void * handler; + void * arg; +} xt_handler_table_entry; +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; +extern void xt_unhandled_interrupt(void * arg); + +//Returns true if handler for interrupt is not the default unhandled interrupt handler +static bool int_has_handler(int intr, int cpu) +{ + return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); +} + +static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force) +{ + //Check if interrupt is not reserved by design + int x = vd->intno; + if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { + ALCHLOG("....Unusable: reserved"); + return false; + } + if (int_desc[x].cpuflags[cpu]==INTDESC_SPECIAL && force==-1) { + ALCHLOG("....Unusable: special-purpose int"); + return false; + } + //Check if the interrupt level is acceptable + if (!(flags&(1<flags&VECDESC_FL_RESERVED) { + ALCHLOG("....Unusable: reserved at runtime."); + return false; + } + + //Ints can't be both shared and non-shared. + assert(!((vd->flags&VECDESC_FL_SHARED)&&(vd->flags&VECDESC_FL_NONSHARED))); + //check if interrupt already is in use by a non-shared interrupt + if (vd->flags&VECDESC_FL_NONSHARED) { + ALCHLOG("....Unusable: already in (non-shared) use."); + return false; + } + // check shared interrupt flags + if (vd->flags&VECDESC_FL_SHARED ) { + if (flags&ESP_INTR_FLAG_SHARED) { + bool in_iram_flag=((flags&ESP_INTR_FLAG_IRAM)!=0); + bool desc_in_iram_flag=((vd->flags&VECDESC_FL_INIRAM)!=0); + //Bail out if int is shared, but iram property doesn't match what we want. + if ((vd->flags&VECDESC_FL_SHARED) && (desc_in_iram_flag!=in_iram_flag)) { + ALCHLOG("....Unusable: shared but iram prop doesn't match"); + return false; + } + } else { + //We need an unshared IRQ; can't use shared ones; bail out if this is shared. + ALCHLOG("...Unusable: int is shared, we need non-shared."); + return false; + } + } else if (int_has_handler(x, cpu)) { + //Check if interrupt already is allocated by xt_set_interrupt_handler + ALCHLOG("....Unusable: already allocated"); + return false; + } + + return true; +} + +//Locate a free interrupt compatible with the flags given. +//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. +//When a CPU is forced, the INTDESC_SPECIAL marked interrupts are also accepted. +static int get_available_int(int flags, int cpu, int force, int source) +{ + int x; + int best=-1; + int bestLevel=9; + int bestSharedCt=INT_MAX; + //Default vector desc, for vectors not in the linked list + vector_desc_t empty_vect_desc; + memset(&empty_vect_desc, 0, sizeof(vector_desc_t)); + + + //Level defaults to any low/med interrupt + if (!(flags&ESP_INTR_FLAG_LEVELMASK)) flags|=ESP_INTR_FLAG_LOWMED; + + ALCHLOG("get_available_int: try to find existing. Cpu: %d, Source: %d", cpu, source); + vector_desc_t *vd = find_desc_for_source(source, cpu); + if ( vd ) { + // if existing vd found, don't need to search any more. + ALCHLOG("get_avalible_int: existing vd found. intno: %d", vd->intno); + if ( force != -1 && force != vd->intno ) { + ALCHLOG("get_avalible_int: intr forced but not matach existing. existing intno: %d, force: %d", vd->intno, force); + } else if ( !is_vect_desc_usable(vd, flags, cpu, force) ) { + ALCHLOG("get_avalible_int: existing vd invalid."); + } else { + best = vd->intno; + } + return best; + } + if (force!=-1) { + ALCHLOG("get_available_int: try to find force. Cpu: %d, Source: %d, Force: %d", cpu, source, force); + //if force assigned, don't need to search any more. + vd = find_desc_for_int(force, cpu); + if (vd == NULL ) { + //if existing vd not found, just check the default state for the intr. + empty_vect_desc.intno = force; + vd = &empty_vect_desc; + } + if ( is_vect_desc_usable(vd, flags, cpu, force) ) { + best = vd->intno; + } else { + ALCHLOG("get_avalible_int: forced vd invalid."); + } + return best; + } + + ALCHLOG("get_free_int: start looking. Current cpu: %d", cpu); + //No allocated handlers as well as forced intr, iterate over the 32 possible interrupts + for (x=0; x<32; x++) { + //Grab the vector_desc for this vector. + vd=find_desc_for_int(x, cpu); + if (vd==NULL) { + empty_vect_desc.intno = x; + vd=&empty_vect_desc; + } + + ALCHLOG("Int %d reserved %d level %d %s hasIsr %d", + x, int_desc[x].cpuflags[cpu]==INTDESC_RESVD, int_desc[x].level, + int_desc[x].type==INTTP_LEVEL?"LEVEL":"EDGE", int_has_handler(x, cpu)); + + if ( !is_vect_desc_usable(vd, flags, cpu, force) ) continue; + + if (flags&ESP_INTR_FLAG_SHARED) { + //We're allocating a shared int. + + //See if int already is used as a shared interrupt. + if (vd->flags&VECDESC_FL_SHARED) { + //We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see + //how useful it is. + int no=0; + shared_vector_desc_t *svdesc=vd->shared_vec_info; + while (svdesc!=NULL) { + no++; + svdesc=svdesc->next; + } + if (noint_desc[x].level) { + //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. + best=x; + bestSharedCt=no; + bestLevel=int_desc[x].level; + ALCHLOG("...int %d more usable as a shared int: has %d existing vectors", x, no); + } else { + ALCHLOG("...worse than int %d", best); + } + } else { + if (best==-1) { + //We haven't found a feasible shared interrupt yet. This one is still free and usable, even if + //not marked as shared. + //Remember it in case we don't find any other shared interrupt that qualifies. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + ALCHLOG("...int %d usable as a new shared int", x); + } + } else { + ALCHLOG("...already have a shared int"); + } + } + } else { + //Seems this interrupt is feasible. Select it and break out of the loop; no need to search further. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + } else { + ALCHLOG("...worse than int %d", best); + } + } + } + ALCHLOG("get_available_int: using int %d", best); + + //Okay, by now we have looked at all potential interrupts and hopefully have selected the best one in best. + return best; +} + +//Common shared isr handler. Chain-call all ISRs. +static void IRAM_ATTR shared_intr_isr(void *arg) +{ + vector_desc_t *vd=(vector_desc_t*)arg; + shared_vector_desc_t *sh_vec=vd->shared_vec_info; + portENTER_CRITICAL(&spinlock); + while(sh_vec) { + if (!sh_vec->disabled) { + if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { +#if CONFIG_SYSVIEW_ENABLE + traceISR_ENTER(sh_vec->source+ETS_INTERNAL_INTR_SOURCE_OFF); +#endif + sh_vec->isr(sh_vec->arg); +#if CONFIG_SYSVIEW_ENABLE + // check if we will return to scheduler or to interrupted task after ISR + if (!port_switch_flag[xPortGetCoreID()]) { + traceISR_EXIT(); + } +#endif + } + } + sh_vec=sh_vec->next; + } + portEXIT_CRITICAL(&spinlock); +} + +#if CONFIG_SYSVIEW_ENABLE +//Common non-shared isr handler wrapper. +static void IRAM_ATTR non_shared_intr_isr(void *arg) +{ + non_shared_isr_arg_t *ns_isr_arg=(non_shared_isr_arg_t*)arg; + portENTER_CRITICAL(&spinlock); + traceISR_ENTER(ns_isr_arg->source+ETS_INTERNAL_INTR_SOURCE_OFF); + // FIXME: can we call ISR and check port_switch_flag after releasing spinlock? + // when CONFIG_SYSVIEW_ENABLE = 0 ISRs for non-shared IRQs are called without spinlock + ns_isr_arg->isr(ns_isr_arg->isr_arg); + // check if we will return to scheduler or to interrupted task after ISR + if (!port_switch_flag[xPortGetCoreID()]) { + traceISR_EXIT(); + } + portEXIT_CRITICAL(&spinlock); +} +#endif + +//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running. +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, + void *arg, intr_handle_t *ret_handle) +{ + intr_handle_data_t *ret=NULL; + int force=-1; + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); + //Shared interrupts should be level-triggered. + if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG; + //You can't set an handler / arg for a non-C-callable interrupt. + if ((flags&ESP_INTR_FLAG_HIGH) && (handler)) return ESP_ERR_INVALID_ARG; + //Shared ints should have handler and non-processor-local source + if ((flags&ESP_INTR_FLAG_SHARED) && (!handler || source<0)) return ESP_ERR_INVALID_ARG; + //Statusreg should have a mask + if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; + //If the ISR is marked to be IRAM-resident, the handler must not be in the cached region + if ((flags&ESP_INTR_FLAG_IRAM) && + (ptrdiff_t) handler >= 0x400C0000 && + (ptrdiff_t) handler < 0x50000000 ) { + return ESP_ERR_INVALID_ARG; + } + + //Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts. + if ((flags&ESP_INTR_FLAG_LEVELMASK)==0) { + if (flags&ESP_INTR_FLAG_SHARED) { + flags|=ESP_INTR_FLAG_LEVEL1; + } else { + flags|=ESP_INTR_FLAG_LOWMED; + } + } + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); + + //Check 'special' interrupt sources. These are tied to one specific interrupt, so we + //have to force get_free_int to only look at that. + if (source==ETS_INTERNAL_TIMER0_INTR_SOURCE) force=ETS_INTERNAL_TIMER0_INTR_NO; + if (source==ETS_INTERNAL_TIMER1_INTR_SOURCE) force=ETS_INTERNAL_TIMER1_INTR_NO; + if (source==ETS_INTERNAL_TIMER2_INTR_SOURCE) force=ETS_INTERNAL_TIMER2_INTR_NO; + if (source==ETS_INTERNAL_SW0_INTR_SOURCE) force=ETS_INTERNAL_SW0_INTR_NO; + if (source==ETS_INTERNAL_SW1_INTR_SOURCE) force=ETS_INTERNAL_SW1_INTR_NO; + if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO; + + //Allocate a return handle. If we end up not needing it, we'll free it later on. + ret=heap_caps_malloc(sizeof(intr_handle_data_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); + if (ret==NULL) return ESP_ERR_NO_MEM; + + portENTER_CRITICAL(&spinlock); + int cpu=xPortGetCoreID(); + //See if we can find an interrupt that matches the flags. + int intr=get_available_int(flags, cpu, force, source); + if (intr==-1) { + //None found. Bail out. + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NOT_FOUND; + } + //Get an int vector desc for int. + vector_desc_t *vd=get_desc_for_int(intr, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + + //Allocate that int! + if (flags&ESP_INTR_FLAG_SHARED) { + //Populate vector entry and add to linked list. + shared_vector_desc_t *sh_vec=malloc(sizeof(shared_vector_desc_t)); + if (sh_vec==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + memset(sh_vec, 0, sizeof(shared_vector_desc_t)); + sh_vec->statusreg=(uint32_t*)intrstatusreg; + sh_vec->statusmask=intrstatusmask; + sh_vec->isr=handler; + sh_vec->arg=arg; + sh_vec->next=vd->shared_vec_info; + sh_vec->source=source; + sh_vec->disabled=0; + vd->shared_vec_info=sh_vec; + vd->flags|=VECDESC_FL_SHARED; + //(Re-)set shared isr handler to new value. + xt_set_interrupt_handler(intr, shared_intr_isr, vd); + } else { + //Mark as unusable for other interrupt sources. This is ours now! + vd->flags=VECDESC_FL_NONSHARED; + if (handler) { +#if CONFIG_SYSVIEW_ENABLE + non_shared_isr_arg_t *ns_isr_arg=malloc(sizeof(non_shared_isr_arg_t)); + if (!ns_isr_arg) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + ns_isr_arg->isr=handler; + ns_isr_arg->isr_arg=arg; + ns_isr_arg->source=source; + xt_set_interrupt_handler(intr, non_shared_intr_isr, ns_isr_arg); +#else + xt_set_interrupt_handler(intr, handler, arg); +#endif + } + if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr); + vd->source=source; + } + if (flags&ESP_INTR_FLAG_IRAM) { + vd->flags|=VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]&=~(1<flags&=~VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]|=(1<=0) { + intr_matrix_set(cpu, source, intr); + } + + //Fill return handle data. + ret->vector_desc=vd; + ret->shared_vector_desc=vd->shared_vec_info; + + //Enable int at CPU-level; + ESP_INTR_ENABLE(intr); + + //If interrupt has to be started disabled, do that now; ints won't be enabled for real until the end + //of the critical section. + if (flags&ESP_INTR_FLAG_INTRDISABLED) { + esp_intr_disable(ret); + } + + portEXIT_CRITICAL(&spinlock); + + //Fill return handle if needed, otherwise free handle. + if (ret_handle!=NULL) { + *ret_handle=ret; + } else { + free(ret); + } + + ESP_EARLY_LOGV(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu); + return ESP_OK; +} + +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle) +{ + /* + As an optimization, we can create a table with the possible interrupt status registers and masks for every single + source there is. We can then add code here to look up an applicable value and pass that to the + esp_intr_alloc_intrstatus function. + */ + return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); +} + +esp_err_t IRAM_ATTR esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + vector_desc_t *vd = handle->vector_desc; + if (vd->flags & VECDESC_FL_SHARED) { + return ESP_ERR_INVALID_ARG; + } + portENTER_CRITICAL(&spinlock); + uint32_t mask = (1 << vd->intno); + if (is_in_iram) { + vd->flags |= VECDESC_FL_INIRAM; + non_iram_int_mask[vd->cpu] &= ~mask; + } else { + vd->flags &= ~VECDESC_FL_INIRAM; + non_iram_int_mask[vd->cpu] |= mask; + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + +esp_err_t esp_intr_free(intr_handle_t handle) +{ + bool free_shared_vector=false; + if (!handle) return ESP_ERR_INVALID_ARG; + //This routine should be called from the interrupt the task is scheduled on. + if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + esp_intr_disable(handle); + if (handle->vector_desc->flags&VECDESC_FL_SHARED) { + //Find and kill the shared int + shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; + shared_vector_desc_t *prevsvd=NULL; + assert(svd); //should be something in there for a shared int + while (svd!=NULL) { + if (svd==handle->shared_vector_desc) { + //Found it. Now kill it. + if (prevsvd) { + prevsvd->next=svd->next; + } else { + handle->vector_desc->shared_vec_info=svd->next; + } + free(svd); + break; + } + prevsvd=svd; + svd=svd->next; + } + //If nothing left, disable interrupt. + if (handle->vector_desc->shared_vec_info==NULL) free_shared_vector=true; + ESP_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd?"not found or last one":"deleted", free_shared_vector?"empty now.":"still in use"); + } + + if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) { + ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); +#if CONFIG_SYSVIEW_ENABLE + if (!free_shared_vector) { + void *isr_arg = xt_get_interrupt_handler_arg(handle->vector_desc->intno); + if (isr_arg) { + free(isr_arg); + } + } +#endif + //Reset to normal handler + xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)((int)handle->vector_desc->intno)); + //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory + //we save.(We can also not use the same exit path for empty shared ints anymore if we delete + //the desc.) For now, just mark it as free. + handle->vector_desc->flags&=!(VECDESC_FL_NONSHARED|VECDESC_FL_RESERVED); + //Also kill non_iram mask bit. + non_iram_int_mask[handle->vector_desc->cpu]&=~(1<<(handle->vector_desc->intno)); + } + portEXIT_CRITICAL(&spinlock); + free(handle); + return ESP_OK; +} + +int esp_intr_get_intno(intr_handle_t handle) +{ + return handle->vector_desc->intno; +} + +int esp_intr_get_cpu(intr_handle_t handle) +{ + return handle->vector_desc->cpu; +} + +/* + Interrupt disabling strategy: + If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a non-connected + interrupt. If the source is <0 (meaning an internal, per-cpu interrupt), we disable it using ESP_INTR_DISABLE. + This allows us to, for the muxed CPUs, disable an int from the other core. It also allows disabling shared + interrupts. + */ + +//Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled. +#define INT_MUX_DISABLED_INTNO 6 + +esp_err_t IRAM_ATTR esp_intr_enable(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + portENTER_CRITICAL(&spinlock); + int source; + if (handle->shared_vector_desc) { + handle->shared_vector_desc->disabled=0; + source=handle->shared_vector_desc->source; + } else { + source=handle->vector_desc->source; + } + if (source >= 0) { + //Disabled using int matrix; re-connect to enable + intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno); + } else { + //Re-enable using cpu int ena reg + if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu + ESP_INTR_ENABLE(handle->vector_desc->intno); + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + +esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + portENTER_CRITICAL(&spinlock); + int source; + bool disabled = 1; + if (handle->shared_vector_desc) { + handle->shared_vector_desc->disabled=1; + source=handle->shared_vector_desc->source; + + shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; + assert( svd != NULL ); + while( svd ) { + if ( svd->source == source && svd->disabled == 0 ) { + disabled = 0; + break; + } + svd = svd->next; + } + } else { + source=handle->vector_desc->source; + } + + if (source >= 0) { + if ( disabled ) { + //Disable using int matrix + intr_matrix_set(handle->vector_desc->cpu, source, INT_MUX_DISABLED_INTNO); + } + } else { + //Disable using per-cpu regs + if (handle->vector_desc->cpu!=xPortGetCoreID()) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu + } + ESP_INTR_DISABLE(handle->vector_desc->intno); + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + + +void IRAM_ATTR esp_intr_noniram_disable() +{ + int oldint; + int cpu=xPortGetCoreID(); + int intmask=~non_iram_int_mask[cpu]; + if (non_iram_int_disabled_flag[cpu]) abort(); + non_iram_int_disabled_flag[cpu]=true; + asm volatile ( + "movi %0,0\n" + "xsr %0,INTENABLE\n" //disable all ints first + "rsync\n" + "and a3,%0,%1\n" //mask ints that need disabling + "wsr a3,INTENABLE\n" //write back + "rsync\n" + :"=&r"(oldint):"r"(intmask):"a3"); + //Save which ints we did disable + non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu]; +} + +void IRAM_ATTR esp_intr_noniram_enable() +{ + int cpu=xPortGetCoreID(); + int intmask=non_iram_int_disabled[cpu]; + if (!non_iram_int_disabled_flag[cpu]) abort(); + non_iram_int_disabled_flag[cpu]=false; + asm volatile ( + "movi a3,0\n" + "xsr a3,INTENABLE\n" + "rsync\n" + "or a3,a3,%0\n" + "wsr a3,INTENABLE\n" + "rsync\n" + ::"r"(intmask):"a3"); +} + +//These functions are provided in ROM, but the ROM-based functions use non-multicore-capable +//virtualized interrupt levels. Thus, we disable them in the ld file and provide working +//equivalents here. + + +void IRAM_ATTR ets_isr_unmask(unsigned int mask) { + xt_ints_on(mask); +} + +void IRAM_ATTR ets_isr_mask(unsigned int mask) { + xt_ints_off(mask); +} + + + + diff --git a/Tools/esp_idf_patches/components/lwip/apps/sntp/sntp.c b/Tools/esp_idf_patches/components/lwip/apps/sntp/sntp.c new file mode 100644 index 00000000..b133b985 --- /dev/null +++ b/Tools/esp_idf_patches/components/lwip/apps/sntp/sntp.c @@ -0,0 +1,719 @@ +/** + * @file + * SNTP client module + * + * This is simple "SNTP" client for the lwIP raw API. + * It is a minimal implementation of SNTPv4 as specified in RFC 4330. + * + * For a list of some public NTP servers, see this link : + * http://support.ntp.org/bin/view/Servers/NTPPoolServers + * + * @todo: + * - set/change servers at runtime + * - complete SNTP_CHECK_RESPONSE checks 3 and 4 + * - support broadcast/multicast mode? + */ + +/* + * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Frédéric Bernon, Simon Goldschmidt + */ + +#include "apps/sntp/sntp.h" + +#include "lwip/opt.h" +#include "lwip/timers.h" +#include "lwip/udp.h" +#include "lwip/dns.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" +#include "lwip/dhcp.h" + +#include +#include + +#if LWIP_UDP + +/* Handle support for more than one server via SNTP_MAX_SERVERS */ +#if SNTP_MAX_SERVERS > 1 +#define SNTP_SUPPORT_MULTIPLE_SERVERS 1 +#else /* NTP_MAX_SERVERS > 1 */ +#define SNTP_SUPPORT_MULTIPLE_SERVERS 0 +#endif /* NTP_MAX_SERVERS > 1 */ + +#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK) +#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!" +#endif + +/* Configure behaviour depending on microsecond or second precision */ +#ifdef SNTP_SET_SYSTEM_TIME_US +#define SNTP_CALC_TIME_US 1 +#define SNTP_RECEIVE_TIME_SIZE 2 +#else +#define SNTP_SET_SYSTEM_TIME_US(sec, us) +#define SNTP_CALC_TIME_US 0 +#define SNTP_RECEIVE_TIME_SIZE 1 +#endif + + +/* the various debug levels for this file */ +#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) +#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) +#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +#define SNTP_ERR_KOD 1 + +/* SNTP protocol defines */ +#define SNTP_MSG_LEN 48 + +#define SNTP_OFFSET_LI_VN_MODE 0 +#define SNTP_LI_MASK 0xC0 +#define SNTP_LI_NO_WARNING 0x00 +#define SNTP_LI_LAST_MINUTE_61_SEC 0x01 +#define SNTP_LI_LAST_MINUTE_59_SEC 0x02 +#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */ + +#define SNTP_VERSION_MASK 0x38 +#define SNTP_VERSION (4/* NTP Version 4*/<<3) + +#define SNTP_MODE_MASK 0x07 +#define SNTP_MODE_CLIENT 0x03 +#define SNTP_MODE_SERVER 0x04 +#define SNTP_MODE_BROADCAST 0x05 + +#define SNTP_OFFSET_STRATUM 1 +#define SNTP_STRATUM_KOD 0x00 + +#define SNTP_OFFSET_ORIGINATE_TIME 24 +#define SNTP_OFFSET_RECEIVE_TIME 32 +#define SNTP_OFFSET_TRANSMIT_TIME 40 + +/* number of seconds between 1900 and 1970 (MSB=1)*/ +#define DIFF_SEC_1900_1970 (2208988800UL) +/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */ +#define DIFF_SEC_1970_2036 (2085978496UL) + +/** + * SNTP packet format (without optional fields) + * Timestamps are coded as 64 bits: + * - 32 bits seconds since Jan 01, 1970, 00:00 + * - 32 bits seconds fraction (0-padded) + * For future use, if the MSB in the seconds part is set, seconds are based + * on Feb 07, 2036, 06:28:16. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct sntp_msg { + PACK_STRUCT_FLD_8(u8_t li_vn_mode); + PACK_STRUCT_FLD_8(u8_t stratum); + PACK_STRUCT_FLD_8(u8_t poll); + PACK_STRUCT_FLD_8(u8_t precision); + PACK_STRUCT_FIELD(u32_t root_delay); + PACK_STRUCT_FIELD(u32_t root_dispersion); + PACK_STRUCT_FIELD(u32_t reference_identifier); + PACK_STRUCT_FIELD(u32_t reference_timestamp[2]); + PACK_STRUCT_FIELD(u32_t originate_timestamp[2]); + PACK_STRUCT_FIELD(u32_t receive_timestamp[2]); + PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +bool sntp_is_synced = false; + +/* function prototypes */ +static void sntp_request(void *arg); + +/** The operating mode */ +static u8_t sntp_opmode; + +/** The UDP pcb used by the SNTP client */ +static struct udp_pcb* sntp_pcb; +/** Names/Addresses of servers */ +struct sntp_server { +#if SNTP_SERVER_DNS + char* name; +#endif /* SNTP_SERVER_DNS */ + ip_addr_t addr; +}; +static struct sntp_server sntp_servers[SNTP_MAX_SERVERS]; + +#if SNTP_GET_SERVERS_FROM_DHCP +static u8_t sntp_set_servers_from_dhcp; +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** The currently used server (initialized to 0) */ +static u8_t sntp_current_server; +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +#define sntp_current_server 0 +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +#if SNTP_RETRY_TIMEOUT_EXP +#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT +/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */ +static u32_t sntp_retry_timeout; +#else /* SNTP_RETRY_TIMEOUT_EXP */ +#define SNTP_RESET_RETRY_TIMEOUT() +#define sntp_retry_timeout SNTP_RETRY_TIMEOUT +#endif /* SNTP_RETRY_TIMEOUT_EXP */ + +#if SNTP_CHECK_RESPONSE >= 1 +/** Saves the last server address to compare with response */ +static ip_addr_t sntp_last_server_address; +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + +#if SNTP_CHECK_RESPONSE >= 2 +/** Saves the last timestamp sent (which is sent back by the server) + * to compare against in response */ +static u32_t sntp_last_timestamp_sent[2]; +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + +/** + * SNTP processing of received timestamp + */ +static void +sntp_process(u32_t *receive_timestamp) +{ + /* convert SNTP time (1900-based) to unix GMT time (1970-based) + * if MSB is 0, SNTP time is 2036-based! + */ + u32_t rx_secs = ntohl(receive_timestamp[0]); + int is_1900_based = ((rx_secs & 0x80000000) != 0); + u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036); + time_t tim = t; + sntp_is_synced = true; + +#if SNTP_CALC_TIME_US + u32_t us = ntohl(receive_timestamp[1]) / 4295; + SNTP_SET_SYSTEM_TIME_US(t, us); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us)); + +#else /* SNTP_CALC_TIME_US */ + + /* change system time and/or the update the RTC clock */ + SNTP_SET_SYSTEM_TIME(t); + /* display local time from GMT time */ + + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim))); +#endif /* SNTP_CALC_TIME_US */ + LWIP_UNUSED_ARG(tim); +} + +/** + * Initialize request struct to be sent to server. + */ +static void +sntp_initialize_request(struct sntp_msg *req) +{ + memset(req, 0, SNTP_MSG_LEN); + req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT; + +#if SNTP_CHECK_RESPONSE >= 2 + { + u32_t sntp_time_sec, sntp_time_us; + /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */ + SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us); + sntp_last_timestamp_sent[0] = htonl(sntp_time_sec + DIFF_SEC_1900_1970); + req->transmit_timestamp[0] = sntp_last_timestamp_sent[0]; + /* we send/save us instead of fraction to be faster... */ + sntp_last_timestamp_sent[1] = htonl(sntp_time_us); + req->transmit_timestamp[1] = sntp_last_timestamp_sent[1]; + } +#endif /* SNTP_CHECK_RESPONSE >= 2 */ +} + +/** + * Retry: send a new request (and increase retry timeout). + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_retry(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n", + sntp_retry_timeout)); + + /* set up a timer to send a retry and increase the retry delay */ + sys_timeout(sntp_retry_timeout, sntp_request, NULL); + +#if SNTP_RETRY_TIMEOUT_EXP + { + u32_t new_retry_timeout; + /* increase the timeout for next retry */ + new_retry_timeout = sntp_retry_timeout << 1; + /* limit to maximum timeout and prevent overflow */ + if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) && + (new_retry_timeout > sntp_retry_timeout)) { + sntp_retry_timeout = new_retry_timeout; + } + } +#endif /* SNTP_RETRY_TIMEOUT_EXP */ +} + +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** + * If Kiss-of-Death is received (or another packet parsing error), + * try the next server or retry the current server and increase the retry + * timeout if only one server is available. + * (implicitly, SNTP_MAX_SERVERS > 1) + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_try_next_server(void* arg) +{ + u8_t old_server, i; + LWIP_UNUSED_ARG(arg); + + old_server = sntp_current_server; + for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) { + sntp_current_server++; + if (sntp_current_server >= SNTP_MAX_SERVERS) { + sntp_current_server = 0; + } + if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr) +#if SNTP_SERVER_DNS + || (sntp_servers[sntp_current_server].name != NULL) +#endif + ) { + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n", + (u16_t)sntp_current_server)); + /* new server: reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + /* instantly send a request to the next server */ + sntp_request(NULL); + return; + } + } + /* no other valid server found */ + sntp_current_server = old_server; + sntp_retry(NULL); +} +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +/* Always retry on error if only one server is supported */ +#define sntp_try_next_server sntp_retry +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +/** UDP recv callback for the sntp pcb */ +static void +sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + u8_t mode; + u8_t stratum; + u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE]; + err_t err; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + + /* packet received: stop retry timeout */ + sys_untimeout(sntp_try_next_server, NULL); + sys_untimeout(sntp_request, NULL); + + err = ERR_ARG; +#if SNTP_CHECK_RESPONSE >= 1 + /* check server address and port */ + if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) && + (port == SNTP_PORT)) +#else /* SNTP_CHECK_RESPONSE >= 1 */ + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + { + /* process the response */ + if (p->tot_len == SNTP_MSG_LEN) { + pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE); + mode &= SNTP_MODE_MASK; + /* if this is a SNTP response... */ + if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) || + ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) { + pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM); + if (stratum == SNTP_STRATUM_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + err = SNTP_ERR_KOD; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n")); + } else { +#if SNTP_CHECK_RESPONSE >= 2 + /* check originate_timetamp against sntp_last_timestamp_sent */ + u32_t originate_timestamp[2]; + pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME); + if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) || + (originate_timestamp[1] != sntp_last_timestamp_sent[1])) + { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n")); + } else +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */ + { + /* correct answer */ + err = ERR_OK; + pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME); + } + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode)); + /* wait for correct response */ + err = ERR_TIMEOUT; + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len)); + } + } +#if SNTP_CHECK_RESPONSE >= 1 + else { + /* packet from wrong remote address or port, wait for correct response */ + err = ERR_TIMEOUT; + } +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + pbuf_free(p); + if (err == ERR_OK) { + sntp_process(receive_timestamp); + + /* Set up timeout for next request (only if poll response was received)*/ + if (sntp_opmode == SNTP_OPMODE_POLL) { + /* Correct response, reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + + sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL); + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n", + (u32_t)SNTP_UPDATE_DELAY)); + } + } else if (err != ERR_TIMEOUT) { + /* Errors are only processed in case of an explicit poll response */ + if (sntp_opmode == SNTP_OPMODE_POLL) { + if (err == SNTP_ERR_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + sntp_try_next_server(NULL); + } else { + /* another error, try the same server again */ + sntp_retry(NULL); + } + } + } +} + +/** Actually send an sntp request to a server. + * + * @param server_addr resolved IP address of the SNTP server + */ +static void +sntp_send_request(const ip_addr_t *server_addr) +{ + struct pbuf* p; + p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM); + if (p != NULL) { + struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n")); + /* initialize request message */ + sntp_initialize_request(sntpmsg); + /* send request */ + udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT); + /* free the pbuf after sending it */ + pbuf_free(p); + /* set up receive timeout: try next server or retry on timeout */ + sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL); +#if SNTP_CHECK_RESPONSE >= 1 + /* save server address to verify it in sntp_recv */ + ip_addr_set(&sntp_last_server_address, server_addr); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + } else { + LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n", + (u32_t)SNTP_RETRY_TIMEOUT)); + /* out of memory: set up a timer to send a retry */ + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL); + } +} + +#if SNTP_SERVER_DNS +/** + * DNS found callback when using DNS names as server address. + */ +static void +sntp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg) +{ + LWIP_UNUSED_ARG(hostname); + LWIP_UNUSED_ARG(arg); + + if (ipaddr != NULL) { + /* Address resolved, send request */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n")); + sntp_send_request(ipaddr); + } else { + /* DNS resolving failed -> try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n")); + sntp_try_next_server(NULL); + } +} +#endif /* SNTP_SERVER_DNS */ + +/** + * Send out an sntp request. + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_request(void *arg) +{ + ip_addr_t sntp_server_address; + err_t err; + + LWIP_UNUSED_ARG(arg); + + /* initialize SNTP server address */ +#if SNTP_SERVER_DNS + if (sntp_servers[sntp_current_server].name) { + /* always resolve the name and rely on dns-internal caching & timeout */ + ip_addr_set_zero(&sntp_servers[sntp_current_server].addr); + err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address, + sntp_dns_found, NULL); + if (err == ERR_INPROGRESS) { + /* DNS request sent, wait for sntp_dns_found being called */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n")); + return; + } else if (err == ERR_OK) { + sntp_servers[sntp_current_server].addr = sntp_server_address; + } + } else +#endif /* SNTP_SERVER_DNS */ + { + sntp_server_address = sntp_servers[sntp_current_server].addr; + err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK; + } + + if (err == ERR_OK) { + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n", + ipaddr_ntoa(&sntp_server_address))); + sntp_send_request(&sntp_server_address); + } else { + /* address conversion failed, try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n")); + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL); + } +} + +/** + * Initialize this module. + * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC). + */ +void +sntp_init(void) +{ +#ifdef SNTP_SERVER_ADDRESS +#if SNTP_SERVER_DNS + sntp_setservername(0, SNTP_SERVER_ADDRESS); +#else +#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0 +#endif +#endif /* SNTP_SERVER_ADDRESS */ + + if (sntp_pcb == NULL) { + sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL); + if (sntp_pcb != NULL) { + udp_recv(sntp_pcb, sntp_recv, NULL); + + if (sntp_opmode == SNTP_OPMODE_POLL) { + SNTP_RESET_RETRY_TIMEOUT(); +#if SNTP_STARTUP_DELAY + sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL); +#else + sntp_request(NULL); +#endif + } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) { + ip_set_option(sntp_pcb, SOF_BROADCAST); + udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT); + } + } + } +} + +/** + * Stop this module. + */ +void +sntp_stop(void) +{ + if (sntp_pcb != NULL) { + sys_untimeout(sntp_request, NULL); + udp_remove(sntp_pcb); + sntp_pcb = NULL; + } +} + +/** + * Get enabled state. + */ +u8_t sntp_enabled(void) +{ + return (sntp_pcb != NULL)? 1 : 0; +} + +/** + * Sets the operating mode. + * @param operating_mode one of the available operating modes + */ +void +sntp_setoperatingmode(u8_t operating_mode) +{ + LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY); + LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL); + sntp_opmode = operating_mode; +} + +/** + * Gets the operating mode. + */ +u8_t +sntp_getoperatingmode(void) +{ + return sntp_opmode; +} + +#if SNTP_GET_SERVERS_FROM_DHCP +/** + * Config SNTP server handling by IP address, name, or DHCP; clear table + * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp + */ +void +sntp_servermode_dhcp(int set_servers_from_dhcp) +{ + u8_t new_mode = set_servers_from_dhcp ? 1 : 0; + if (sntp_set_servers_from_dhcp != new_mode) { + sntp_set_servers_from_dhcp = new_mode; + } +} +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * Initialize one of the NTP servers by IP address + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver IP address of the NTP server to set + */ +void +sntp_setserver(u8_t idx, const ip_addr_t *server) +{ + if (idx < SNTP_MAX_SERVERS) { + if (server != NULL) { + sntp_servers[idx].addr = (*server); + } else { + ip_addr_set_zero(&sntp_servers[idx].addr); + } +#if SNTP_SERVER_DNS + sntp_servers[idx].name = NULL; +#endif + } +} + +#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP +/** + * Initialize one of the NTP servers by IP address, required by DHCP + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver IP address of the NTP server to set + */ +void +dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server) +{ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n", + (sntp_set_servers_from_dhcp ? "Got" : "Rejected"), + ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num)); + if (sntp_set_servers_from_dhcp && num) { + u8_t i; + for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) { + ip_addr_t addr; + ip_addr_copy_from_ip4(addr, server[i]); + sntp_setserver(i, &addr); + } + for (i = num; i < SNTP_MAX_SERVERS; i++) { + sntp_setserver(i, NULL); + } + } +} +#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * Obtain one of the currently configured by IP address (or DHCP) NTP servers + * + * @param numdns the index of the NTP server + * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP + * server has not been configured by address (or at all). + */ +ip_addr_t +sntp_getserver(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return sntp_servers[idx].addr; + } + return *IP_ADDR_ANY; +} + +#if SNTP_SERVER_DNS +/** + * Initialize one of the NTP servers by name + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver DNS name of the NTP server to set, to be resolved at contact time + */ +void +sntp_setservername(u8_t idx, char *server) +{ + if (idx < SNTP_MAX_SERVERS) { + sntp_servers[idx].name = server; + } +} + +/** + * Obtain one of the currently configured by name NTP servers. + * + * @param numdns the index of the NTP server + * @return IP address of the indexed NTP server or NULL if the NTP + * server has not been configured by name (or at all) + */ +char * +sntp_getservername(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return sntp_servers[idx].name; + } + return NULL; +} +#endif /* SNTP_SERVER_DNS */ + +#endif /* LWIP_UDP */ + diff --git a/Tools/esp_idf_patches/components/lwip/include/lwip/apps/sntp/sntp.h b/Tools/esp_idf_patches/components/lwip/include/lwip/apps/sntp/sntp.h new file mode 100644 index 00000000..76c0bc5a --- /dev/null +++ b/Tools/esp_idf_patches/components/lwip/include/lwip/apps/sntp/sntp.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Frédéric Bernon, Simon Goldschmidt + * + */ +#ifndef LWIP_HDR_APPS_SNTP_H +#define LWIP_HDR_APPS_SNTP_H + +#include "apps/sntp/sntp_opts.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern bool sntp_is_synced; + +/* SNTP operating modes: default is to poll using unicast. + The mode has to be set before calling sntp_init(). */ +#define SNTP_OPMODE_POLL 0 +#define SNTP_OPMODE_LISTENONLY 1 +void sntp_setoperatingmode(u8_t operating_mode); +u8_t sntp_getoperatingmode(void); + +void sntp_init(void); +void sntp_stop(void); +u8_t sntp_enabled(void); + +void sntp_setserver(u8_t idx, const ip_addr_t *addr); +ip_addr_t sntp_getserver(u8_t idx); + +#if SNTP_SERVER_DNS +void sntp_setservername(u8_t idx, char *server); +char *sntp_getservername(u8_t idx); +#endif /* SNTP_SERVER_DNS */ + +#if SNTP_GET_SERVERS_FROM_DHCP +void sntp_servermode_dhcp(int set_servers_from_dhcp); +#else /* SNTP_GET_SERVERS_FROM_DHCP */ +#define sntp_servermode_dhcp(x) +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_SNTP_H */ diff --git a/Tools/esp_idf_patches/components/spiffs/Kconfig b/Tools/esp_idf_patches/components/spiffs/Kconfig new file mode 100644 index 00000000..ff5cb6ec --- /dev/null +++ b/Tools/esp_idf_patches/components/spiffs/Kconfig @@ -0,0 +1,173 @@ +menu "SPIFFS Configuration" + +config SPIFFS_MAX_PARTITIONS + int "Maximum Number of Partitions" + default 3 + range 1 10 + help + Define maximum number of partitions that can be mounted. + +menu "SPIFFS Cache Configuration" +config SPIFFS_CACHE + bool "Enable SPIFFS Cache" + default "y" + help + Enables/disable memory read caching of nucleus file system + operations. + +config SPIFFS_CACHE_WR + bool "Enable SPIFFS Write Caching" + default "y" + depends on SPIFFS_CACHE + help + Enables memory write caching for file descriptors in hydrogen. + +config SPIFFS_CACHE_STATS + bool "Enable SPIFFS Cache Statistics" + default "n" + depends on SPIFFS_CACHE + help + Enable/disable statistics on caching. Debug/test purpose only. + +endmenu + +config SPIFFS_PAGE_CHECK + bool "Enable SPIFFS Page Check" + default "y" + help + Always check header of each accessed page to ensure consistent state. + If enabled it will increase number of reads from flash, especially + if cache is disabled. + +config SPIFFS_GC_MAX_RUNS + int "Set Maximum GC Runs" + default 10 + range 1 255 + help + Define maximum number of GC runs to perform to reach desired free pages. + +config SPIFFS_GC_STATS + bool "Enable SPIFFS GC Statistics" + default "n" + help + Enable/disable statistics on gc. Debug/test purpose only. + +config SPIFFS_PAGE_SIZE + int "SPIFFS logical page size" + default 256 + range 256 1024 + help + Logical page size of SPIFFS partition, in bytes. Must be multiple + of flash page size (which is usually 256 bytes). + Larger page sizes reduce overhead when storing large files, and + improve filesystem performance when reading large files. + Smaller page sizes reduce overhead when storing small (< page size) + files. + +config SPIFFS_OBJ_NAME_LEN + int "Set SPIFFS Maximum Name Length" + default 32 + range 1 256 + help + Object name maximum length. Note that this length include the + zero-termination character, meaning maximum string of characters + can at most be SPIFFS_OBJ_NAME_LEN - 1. + + SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed + SPIFFS_PAGE_SIZE - 64. + +config SPIFFS_USE_MAGIC + bool "Enable SPIFFS Filesystem Magic" + default "y" + help + Enable this to have an identifiable spiffs filesystem. + This will look for a magic in all sectors to determine if this + is a valid spiffs system or not at mount time. + +config SPIFFS_USE_MAGIC_LENGTH + bool "Enable SPIFFS Filesystem Length Magic" + default "y" + depends on SPIFFS_USE_MAGIC + help + If this option is enabled, the magic will also be dependent + on the length of the filesystem. For example, a filesystem + 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 5 + range 0 64 + 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. + + SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed + SPIFFS_PAGE_SIZE - 64. + +config SPIFFS_USE_MTIME + bool "Save file modification time" + default "y" + depends on (!SPIFFS_USE_DIR && SPIFFS_META_LENGTH >= 4) || (SPIFFS_USE_DIR && SPIFFS_META_LENGTH >= 5) + 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. + +config SPIFFS_USE_DIR + bool "Enable directories" + default "y" + depends on SPIFFS_META_LENGTH >= 1 + help + If enabled, directory support will be enabled in spiffs. + Directory create/remove functions will be accessible through + mkdir/rmdir functions. + One additional byte of per-file metadata will be used + to store file the file type (regular file/directory) + +menu "Debug Configuration" + +config SPIFFS_DBG + bool "Enable general SPIFFS debug" + default "n" + help + Enabling this option will print general debug mesages to the console. + +config SPIFFS_API_DBG + bool "Enable SPIFFS API debug" + default "n" + help + Enabling this option will print API debug mesages to the console. + +config SPIFFS_GC_DBG + bool "Enable SPIFFS Garbage Cleaner debug" + default "n" + help + Enabling this option will print GC debug mesages to the console. + +config SPIFFS_CACHE_DBG + bool "Enable SPIFFS Cache debug" + default "n" + depends on SPIFFS_CACHE + help + Enabling this option will print cache debug mesages to the console. + +config SPIFFS_CHECK_DBG + bool "Enable SPIFFS Filesystem Check debug" + default "n" + help + Enabling this option will print Filesystem Check debug mesages + to the console. + +config SPIFFS_TEST_VISUALISATION + bool "Enable SPIFFS Filesystem Visualization" + default "n" + help + Enable this option to enable SPIFFS_vis function in the API. + +endmenu + +endmenu diff --git a/Tools/esp_idf_patches/components/spiffs/esp_spiffs.c b/Tools/esp_idf_patches/components/spiffs/esp_spiffs.c new file mode 100644 index 00000000..68d8c946 --- /dev/null +++ b/Tools/esp_idf_patches/components/spiffs/esp_spiffs.c @@ -0,0 +1,1050 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_spiffs.h" +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "esp_log.h" +#include "esp_partition.h" +#include "esp_spi_flash.h" +#include "esp_image_format.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include +#include +#include +#include +#include +#include "esp_vfs.h" +#include "esp_err.h" +#include "rom/spi_flash.h" + +static const char * TAG = "SPIFFS"; + +#if defined (CONFIG_SPIFFS_USE_MTIME) && defined (CONFIG_SPIFFS_USE_DIR) +_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t)+sizeof(uint8_t), + "SPIFFS_META_LENGTH size should be >= sizeof(time_t)+sizeof(uint8_t)"); +#elif defined (CONFIG_SPIFFS_USE_MTIME) +_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t), + "SPIFFS_META_LENGTH size should be >= sizeof(time_t)"); +#elif defined (CONFIG_SPIFFS_USE_DIR) +_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(uint8_t), + "SPIFFS_META_LENGTH size should be >= sizeof(uint8_t)"); +#endif + +/** + * @brief SPIFFS definition structure + */ +typedef struct { + spiffs *fs; /*!< Handle to the underlying SPIFFS */ + SemaphoreHandle_t lock; /*!< FS lock */ + const esp_partition_t* partition; /*!< The partition on which SPIFFS is located */ + char base_path[ESP_VFS_PATH_MAX+1]; /*!< Mount point */ + bool by_label; /*!< Partition was mounted by label */ + spiffs_config cfg; /*!< SPIFFS Mount configuration */ + uint8_t *work; /*!< Work Buffer */ + uint8_t *fds; /*!< File Descriptor Buffer */ + uint32_t fds_sz; /*!< File Descriptor Buffer Length */ + uint8_t *cache; /*!< Cache Buffer */ + uint32_t cache_sz; /*!< Cache Buffer Length */ +} esp_spiffs_t; + +/** + * @brief SPIFFS DIR structure + */ +typedef struct { + DIR dir; /*!< VFS DIR struct */ + spiffs_DIR d; /*!< SPIFFS DIR struct */ + struct dirent e; /*!< Last open dirent */ + long offset; /*!< Offset of the current dirent */ + char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */ +} vfs_spiffs_dir_t; + +#if defined (CONFIG_SPIFFS_USE_MTIME) || defined (CONFIG_SPIFFS_USE_DIR) +/** + * @brief SPIFFS metadata structure + */ +typedef struct { +#ifdef CONFIG_SPIFFS_USE_MTIME + time_t mtime; /*!< file modification time */ +#endif +#ifdef CONFIG_SPIFFS_USE_DIR + uint8_t type; /*!< file type */ +#endif +} __attribute__((packed, aligned(1))) vfs_spiffs_meta_t; +#endif + +static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode); +static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size); +static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size); +static int vfs_spiffs_close(void* ctx, int fd); +static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode); +static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st); +static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st); +static int vfs_spiffs_unlink(void* ctx, const char *path); +static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2); +static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst); +static DIR* vfs_spiffs_opendir(void* ctx, const char* name); +static int vfs_spiffs_closedir(void* ctx, DIR* pdir); +static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir); +static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, + struct dirent* entry, struct dirent** out_dirent); +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_meta(spiffs *fs, spiffs_file f, uint8_t type); +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s); + +static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS]; + +void spiffs_api_lock(spiffs *fs) +{ + xSemaphoreTake(((esp_spiffs_t *)(fs->user_data))->lock, portMAX_DELAY); +} + +void spiffs_api_unlock(spiffs *fs) +{ + xSemaphoreGive(((esp_spiffs_t *)(fs->user_data))->lock); +} + +static s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst) +{ + esp_err_t err = esp_partition_read(((esp_spiffs_t *)(fs->user_data))->partition, + addr, dst, size); + if (err) { + ESP_LOGE(TAG, "failed to read addr %08x, size %08x, err %d", addr, size, err); + return -1; + } + return 0; +} + +static s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src) +{ + esp_err_t err = esp_partition_write(((esp_spiffs_t *)(fs->user_data))->partition, + addr, src, size); + if (err) { + ESP_LOGE(TAG, "failed to write addr %08x, size %08x, err %d", addr, size, err); + return -1; + } + return 0; +} + +static s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size) +{ + // Check if the sector is already erased + uint8_t f = 1; + esp_err_t err = 0; + uint8_t *buff = heap_caps_malloc(size, MALLOC_CAP_DMA); + if (buff) { + err = esp_partition_read(((esp_spiffs_t *)(fs->user_data))->partition, addr, buff, size); + if (err == ESP_OK) { + f = 0; + for (int i=0; iuser_data))->partition, + addr, size); + } + if (err) { + ESP_LOGE(TAG, "failed to erase addr %08x, size %08x, err %d", addr, size, err); + return -1; + } + return 0; +} + +static void spiffs_api_check(spiffs *fs, spiffs_check_type type, + spiffs_check_report report, uint32_t arg1, uint32_t arg2) +{ + static const char * spiffs_check_type_str[3] = { + "LOOKUP", + "INDEX", + "PAGE" + }; + + static const char * spiffs_check_report_str[7] = { + "PROGRESS", + "ERROR", + "FIX INDEX", + "FIX LOOKUP", + "DELETE ORPHANED INDEX", + "DELETE PAGE", + "DELETE BAD FILE" + }; + + if (report != SPIFFS_CHECK_PROGRESS) { + ESP_LOGE(TAG, "CHECK: type:%s, report:%s, %x:%x", spiffs_check_type_str[type], + spiffs_check_report_str[report], arg1, arg2); + } else { + ESP_LOGV(TAG, "CHECK PROGRESS: report:%s, %x:%x", + spiffs_check_report_str[report], arg1, arg2); + } +} + +static void esp_spiffs_free(esp_spiffs_t ** efs) +{ + esp_spiffs_t * e = *efs; + if (*efs == NULL) { + return; + } + *efs = NULL; + + if (e->fs) { + SPIFFS_unmount(e->fs); + free(e->fs); + } + vSemaphoreDelete(e->lock); + free(e->fds); + free(e->cache); + free(e->work); + free(e); +} + +static esp_err_t esp_spiffs_by_label(const char* label, int * index){ + int i; + esp_spiffs_t * p; + for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) { + p = _efs[i]; + if (p) { + if (!label && !p->by_label) { + *index = i; + return ESP_OK; + } + if (label && p->by_label && strncmp(label, p->partition->label, 17) == 0) { + *index = i; + return ESP_OK; + } + } + } + return ESP_ERR_NOT_FOUND; +} + +static esp_err_t esp_spiffs_get_empty(int * index){ + int i; + for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) { + if (_efs[i] == NULL) { + *index = i; + return ESP_OK; + } + } + return ESP_ERR_NOT_FOUND; +} + +static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf) +{ + int index; + //find if such partition is already mounted + if (esp_spiffs_by_label(conf->partition_label, &index) == ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + + if (esp_spiffs_get_empty(&index) != ESP_OK) { + ESP_LOGE(TAG, "max mounted partitions reached"); + return ESP_ERR_INVALID_STATE; + } + + uint32_t flash_page_size = g_rom_flashchip.page_size; + uint32_t log_page_size = CONFIG_SPIFFS_PAGE_SIZE; + if (log_page_size % flash_page_size != 0) { + ESP_LOGE(TAG, "SPIFFS_PAGE_SIZE is not multiple of flash chip page size (%d)", + flash_page_size); + return ESP_ERR_INVALID_ARG; + } + + esp_partition_subtype_t subtype = conf->partition_label ? + ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS; + const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, + subtype, conf->partition_label); + if (!partition) { + ESP_LOGE(TAG, "spiffs partition could not be found"); + return ESP_ERR_NOT_FOUND; + } + + if (partition->encrypted) { + ESP_LOGE(TAG, "spiffs can not run on encrypted partition"); + return ESP_ERR_INVALID_STATE; + } + + esp_spiffs_t * efs = malloc(sizeof(esp_spiffs_t)); + if (efs == NULL) { + ESP_LOGE(TAG, "esp_spiffs could not be malloced"); + return ESP_ERR_NO_MEM; + } + memset(efs, 0, sizeof(esp_spiffs_t)); + + efs->cfg.hal_erase_f = spiffs_api_erase; + efs->cfg.hal_read_f = spiffs_api_read; + efs->cfg.hal_write_f = spiffs_api_write; + efs->cfg.log_block_size = g_rom_flashchip.sector_size; + efs->cfg.log_page_size = log_page_size; + efs->cfg.phys_addr = 0; + efs->cfg.phys_erase_block = g_rom_flashchip.sector_size; + efs->cfg.phys_size = partition->size; + + efs->by_label = conf->partition_label != NULL; + + efs->lock = xSemaphoreCreateMutex(); + if (efs->lock == NULL) { + ESP_LOGE(TAG, "mutex lock could not be created"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + + efs->fds_sz = conf->max_files * sizeof(spiffs_fd); + efs->fds = malloc(efs->fds_sz); + if (efs->fds == NULL) { + ESP_LOGE(TAG, "fd buffer could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->fds, 0, efs->fds_sz); + +#if SPIFFS_CACHE + efs->cache_sz = sizeof(spiffs_cache) + conf->max_files * (sizeof(spiffs_cache_page) + + efs->cfg.log_page_size); + efs->cache = malloc(efs->cache_sz); + if (efs->cache == NULL) { + ESP_LOGE(TAG, "cache buffer could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->cache, 0, efs->cache_sz); +#endif + + const uint32_t work_sz = efs->cfg.log_page_size * 2; + efs->work = malloc(work_sz); + if (efs->work == NULL) { + ESP_LOGE(TAG, "work buffer could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->work, 0, work_sz); + + efs->fs = malloc(sizeof(spiffs)); + if (efs->fs == NULL) { + ESP_LOGE(TAG, "spiffs could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->fs, 0, sizeof(spiffs)); + + efs->fs->user_data = (void *)efs; + efs->partition = partition; + + s32_t res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz, + efs->cache, efs->cache_sz, spiffs_api_check); + + if (conf->format_if_mount_failed && res != SPIFFS_OK) { + ESP_LOGW(TAG, "mount failed, %i. formatting...", SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + res = SPIFFS_format(efs->fs); + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + esp_spiffs_free(&efs); + return ESP_FAIL; + } + res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz, + efs->cache, efs->cache_sz, spiffs_api_check); + } + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + esp_spiffs_free(&efs); + return ESP_FAIL; + } + _efs[index] = efs; + return ESP_OK; +} + +bool esp_spiffs_mounted(const char* partition_label) +{ + int index; + if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) { + return false; + } + return (SPIFFS_mounted(_efs[index]->fs)); +} + +esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes) +{ + int index; + if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + SPIFFS_info(_efs[index]->fs, total_bytes, used_bytes); + return ESP_OK; +} + +esp_err_t esp_spiffs_format(const char* partition_label) +{ + bool partition_was_mounted = false; + int index; + /* If the partition is not mounted, need to create SPIFFS structures + * and mount the partition, unmount, format, delete SPIFFS structures. + * See SPIFFS wiki for the reason why. + */ + esp_err_t err = esp_spiffs_by_label(partition_label, &index); + if (err != ESP_OK) { + esp_vfs_spiffs_conf_t conf = { + .format_if_mount_failed = true, + .partition_label = partition_label, + .max_files = 1 + }; + err = esp_spiffs_init(&conf); + if (err != ESP_OK) { + return err; + } + err = esp_spiffs_by_label(partition_label, &index); + assert(err == ESP_OK && "failed to get index of the partition just mounted"); + } else if (SPIFFS_mounted(_efs[index]->fs)) { + partition_was_mounted = true; + } + + SPIFFS_unmount(_efs[index]->fs); + + s32_t res = SPIFFS_format(_efs[index]->fs); + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs)); + SPIFFS_clearerr(_efs[index]->fs); + /* If the partition was previously mounted, but format failed, don't + * try to mount the partition back (it will probably fail). On the + * other hand, if it was not mounted, need to clean up. + */ + if (!partition_was_mounted) { + esp_spiffs_free(&_efs[index]); + } + return ESP_FAIL; + } + + if (partition_was_mounted) { + res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work, + _efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache, + _efs[index]->cache_sz, spiffs_api_check); + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(_efs[index]->fs)); + SPIFFS_clearerr(_efs[index]->fs); + return ESP_FAIL; + } + } else { + esp_spiffs_free(&_efs[index]); + } + return ESP_OK; +} + +esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf) +{ + assert(conf->base_path); + const esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_CONTEXT_PTR, + .write_p = &vfs_spiffs_write, + .lseek_p = &vfs_spiffs_lseek, + .read_p = &vfs_spiffs_read, + .open_p = &vfs_spiffs_open, + .close_p = &vfs_spiffs_close, + .fstat_p = &vfs_spiffs_fstat, + .stat_p = &vfs_spiffs_stat, + .link_p = &vfs_spiffs_link, + .unlink_p = &vfs_spiffs_unlink, + .rename_p = &vfs_spiffs_rename, + .opendir_p = &vfs_spiffs_opendir, + .closedir_p = &vfs_spiffs_closedir, + .readdir_p = &vfs_spiffs_readdir, + .readdir_r_p = &vfs_spiffs_readdir_r, + .seekdir_p = &vfs_spiffs_seekdir, + .telldir_p = &vfs_spiffs_telldir, + .mkdir_p = &vfs_spiffs_mkdir, + .rmdir_p = &vfs_spiffs_rmdir + }; + + esp_err_t err = esp_spiffs_init(conf); + if (err != ESP_OK) { + return err; + } + + int index; + if (esp_spiffs_by_label(conf->partition_label, &index) != ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + + strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1); + err = esp_vfs_register(conf->base_path, &vfs, _efs[index]); + if (err != ESP_OK) { + esp_spiffs_free(&_efs[index]); + return err; + } + + return ESP_OK; +} + +esp_err_t esp_vfs_spiffs_unregister(const char* partition_label) +{ + int index; + if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + esp_err_t err = esp_vfs_unregister(_efs[index]->base_path); + if (err != ESP_OK) { + return err; + } + esp_spiffs_free(&_efs[index]); + return ESP_OK; +} + +static int spiffs_res_to_errno(s32_t fr) +{ + switch(fr) { + case SPIFFS_OK : + return 0; + case SPIFFS_ERR_NOT_MOUNTED : + return ENODEV; + case SPIFFS_ERR_NOT_A_FS : + return ENODEV; + case SPIFFS_ERR_FULL : + return ENOSPC; + case SPIFFS_ERR_BAD_DESCRIPTOR : + return EBADF; + case SPIFFS_ERR_MOUNTED : + return EEXIST; + case SPIFFS_ERR_FILE_EXISTS : + return EEXIST; + case SPIFFS_ERR_NOT_FOUND : + return ENOENT; + case SPIFFS_ERR_NOT_A_FILE : + return ENOENT; + case SPIFFS_ERR_DELETED : + return ENOENT; + case SPIFFS_ERR_FILE_DELETED : + return ENOENT; + case SPIFFS_ERR_NAME_TOO_LONG : + return ENAMETOOLONG; + case SPIFFS_ERR_RO_NOT_IMPL : + return EROFS; + case SPIFFS_ERR_RO_ABORTED_OPERATION : + return EROFS; + default : + return EIO; + } + return ENOTSUP; +} + +static int spiffs_mode_conv(int m) +{ + int res = 0; + int acc_mode = m & O_ACCMODE; + if (acc_mode == O_RDONLY) { + res |= SPIFFS_O_RDONLY; + } else if (acc_mode == O_WRONLY) { + res |= SPIFFS_O_WRONLY; + } else if (acc_mode == O_RDWR) { + res |= SPIFFS_O_RDWR; + } + if ((m & O_CREAT) && (m & O_EXCL)) { + res |= SPIFFS_O_CREAT | SPIFFS_O_EXCL; + } else if ((m & O_CREAT) && (m & O_TRUNC)) { + res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC; + } else if (m & O_APPEND) { + res |= SPIFFS_O_CREAT | SPIFFS_O_APPEND; + } + return res; +} + +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 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; + } +#ifdef CONFIG_SPIFFS_USE_DIR + spiffs_stat s; + int ret = SPIFFS_fstat(efs->fs, fd, &s); + if (ret == SPIFFS_OK) { + vfs_spiffs_meta_t * meta = (vfs_spiffs_meta_t *)&s.meta; + if (meta->type == SPIFFS_TYPE_DIR) { + // It is directory, cannot be opened + errno = EISDIR; + ret = SPIFFS_close(efs->fs, fd); + if (ret < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + } + return -1; + } + } +#endif + if (!(spiffs_flags & SPIFFS_RDONLY)) { + vfs_spiffs_update_meta(efs->fs, fd, SPIFFS_TYPE_FILE); + } + return fd; +} + +static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + ssize_t res = SPIFFS_write(efs->fs, fd, (void *)data, size); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + ssize_t res = SPIFFS_read(efs->fs, fd, dst, size); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static int vfs_spiffs_close(void* ctx, int fd) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + int res = SPIFFS_close(efs->fs, fd); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + off_t res = SPIFFS_lseek(efs->fs, fd, offset, mode); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) +{ + assert(st); + spiffs_stat s; + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + off_t res = SPIFFS_fstat(efs->fs, fd, &s); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + st->st_size = s.size; +#ifdef CONFIG_SPIFFS_USE_DIR + vfs_spiffs_meta_t * meta = (vfs_spiffs_meta_t *)&s.meta; + if (meta->type == SPIFFS_TYPE_DIR) st->st_mode = S_IFDIR; + else st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; +#else + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; +#endif + st->st_mtime = vfs_spiffs_get_mtime(&s); + st->st_atime = 0; + st->st_ctime = 0; + return res; +} + +static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) +{ + assert(path); + assert(st); + spiffs_stat s; + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + off_t res = SPIFFS_stat(efs->fs, path, &s); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + + st->st_size = s.size; +#ifdef CONFIG_SPIFFS_USE_DIR + vfs_spiffs_meta_t * meta = (vfs_spiffs_meta_t *)&s.meta; + if (meta->type == SPIFFS_TYPE_DIR) st->st_mode = S_IFDIR; + else st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; +#else + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; + st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; +#endif + st->st_mtime = vfs_spiffs_get_mtime(&s); + st->st_atime = 0; + st->st_ctime = 0; + return res; +} + +static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst) +{ + assert(src); + assert(dst); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + int res = SPIFFS_rename(efs->fs, src, dst); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static int vfs_spiffs_unlink(void* ctx, const char *path) +{ + assert(path); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; +#ifdef CONFIG_SPIFFS_USE_DIR + spiffs_stat s; + off_t ret = SPIFFS_stat(efs->fs, path, &s); + if (ret < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + vfs_spiffs_meta_t * meta = (vfs_spiffs_meta_t *)&s.meta; + if (meta->type == SPIFFS_TYPE_DIR) { + // Directory cannot be unliked (removed) + errno = EISDIR; + return -1; + } +#endif + int res = SPIFFS_remove(efs->fs, path); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static DIR* vfs_spiffs_opendir(void* ctx, const char* name) +{ + assert(name); +#ifdef CONFIG_SPIFFS_USE_DIR + if (strcmp(name, "/") != 0) { + // If not on root, check if path exists and is a directory + struct stat st; + if (vfs_spiffs_stat(ctx, name, &st)) { + // Not found + errno = ENOENT; + return NULL; + } + if (!S_ISDIR(st.st_mode)) { + // Not a directory, cannot open + errno = ENOTDIR; + return NULL; + } + } +#endif + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = calloc(1, sizeof(vfs_spiffs_dir_t)); + if (!dir) { + errno = ENOMEM; + return NULL; + } + if (!SPIFFS_opendir(efs->fs, name, &dir->d)) { + free(dir); + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return NULL; + } + dir->offset = 0; + strlcpy(dir->path, name, SPIFFS_OBJ_NAME_LEN); + return (DIR*) dir; +} + +static int vfs_spiffs_closedir(void* ctx, DIR* pdir) +{ + assert(pdir); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + int res = SPIFFS_closedir(&dir->d); + free(dir); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + static struct dirent* out_dirent; + + int err = vfs_spiffs_readdir_r(ctx, pdir, &dir->e, &out_dirent); + if (err != 0) { + errno = err; + return NULL; + } + return out_dirent; +} + +static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, struct dirent* entry, + struct dirent** out_dirent) +{ + assert(pdir); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + struct spiffs_dirent out; + + // read directory entry + if (SPIFFS_readdir(&dir->d, &out) == 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + if (!errno) { + *out_dirent = NULL; + } + return errno; + } + const char *item_name = (const char *)out.name; + const char *out_item_name = (const char *)out.name; + size_t plen = strlen(dir->path); // directory path length + + // === skip all entries not belonging to the requested directory path === + if (plen > 1) { + // on subdirectory + while ((strstr(item_name, dir->path) != item_name) || (strlen(item_name) <= plen) || (strchr(item_name+plen+1, '/'))) { + if (SPIFFS_readdir(&dir->d, &out) == 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + if (!errno) { + *out_dirent = NULL; + } + return errno; + } + item_name = (const char *)out.name; + } + out_item_name = item_name + plen + 1; + } + else { + // on root + while ((strlen(item_name) > 2) && (strchr(item_name+1, '/'))) { + if (SPIFFS_readdir(&dir->d, &out) == 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + if (!errno) { + *out_dirent = NULL; + } + return errno; + } + item_name = (const char *)out.name; + } + out_item_name = item_name + plen; + } +#ifdef CONFIG_SPIFFS_USE_DIR + // Get file stat, used for setting file type in dirent entry + spiffs_stat s = {0}; + off_t ret = SPIFFS_stat(efs->fs, item_name, &s); + if (ret < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return errno; + } +#endif + + entry->d_ino = 0; +#ifdef CONFIG_SPIFFS_USE_DIR + vfs_spiffs_meta_t * meta = (vfs_spiffs_meta_t *)&s.meta; + if (meta->type == SPIFFS_TYPE_DIR) entry->d_type = DT_DIR; + else entry->d_type = out.type; +#else + entry->d_type = out.type; +#endif + snprintf(entry->d_name, SPIFFS_OBJ_NAME_LEN, "%s", out_item_name); + dir->offset++; + *out_dirent = entry; + return 0; +} + +static long vfs_spiffs_telldir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + return dir->offset; +} + +static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset) +{ + assert(pdir); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + struct spiffs_dirent tmp; + if (offset < dir->offset) { + //rewind dir + SPIFFS_closedir(&dir->d); + if (!SPIFFS_opendir(efs->fs, NULL, &dir->d)) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return; + } + dir->offset = 0; + } + while (dir->offset < offset) { + if (SPIFFS_readdir(&dir->d, &tmp) == 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return; + } + size_t plen = strlen(dir->path); + if (plen > 1) { + if (strncasecmp(dir->path, (const char *)tmp.name, plen) || tmp.name[plen] != '/' || !tmp.name[plen+1]) { + continue; + } + } + dir->offset++; + } +} + +static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode) +{ +#ifdef CONFIG_SPIFFS_USE_DIR + assert(name); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + + int fd = SPIFFS_open(efs->fs, name, SPIFFS_CREAT | SPIFFS_WRONLY, 0); + if (fd < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + vfs_spiffs_update_meta(efs->fs, fd, SPIFFS_TYPE_DIR); + + if (SPIFFS_close(efs->fs, fd) < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return 0; +#else + errno = ENOTSUP; + return -1; +#endif +} + +static int vfs_spiffs_rmdir(void* ctx, const char* name) +{ +#ifdef CONFIG_SPIFFS_USE_DIR + assert(name); + spiffs_stat s; + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + + off_t ret = SPIFFS_stat(efs->fs, name, &s); + if (ret < 0) { + // Directory name not found + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + // return success, as it is acctualy "removed" + return 0; + } + + vfs_spiffs_meta_t * meta = (vfs_spiffs_meta_t *)&s.meta; + if (meta->type != SPIFFS_TYPE_DIR) { + // not a directory + errno = ENOTDIR; + return -1; + } + + // Check if directory is empty + int nument = 0; + char npath[SPIFFS_OBJ_NAME_LEN+8]; + sprintf(npath, efs->base_path); + strlcat(npath, name, SPIFFS_OBJ_NAME_LEN); + DIR *dir = opendir(npath); + if (dir) { + struct dirent *ent; + // Read directory entries + while ((ent = readdir(dir)) != NULL) { + nument++; + } + } + else { + errno = ENOTDIR; + return -1; + } + closedir(dir); + + if (nument > 0) { + // Directory not empty, cannot remove + errno = ENOTEMPTY; + return -1; + } + + int res = SPIFFS_remove(efs->fs, name); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +#else + errno = ENOTSUP; + return -1; +#endif +} + +static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2) +{ + errno = ENOTSUP; + return -1; +} + +static void vfs_spiffs_update_meta(spiffs *fs, spiffs_file fd, uint8_t type) +{ +#if defined (CONFIG_SPIFFS_USE_MTIME) || defined (CONFIG_SPIFFS_USE_DIR) + vfs_spiffs_meta_t meta; +#ifdef CONFIG_SPIFFS_USE_MTIME + meta.mtime = time(NULL); +#endif //CONFIG_SPIFFS_USE_MTIME +#ifdef CONFIG_SPIFFS_USE_DIR + // Add file type (directory or regular file) to the last byte of metadata + meta.type = type; +#endif + int ret = SPIFFS_fupdate_meta(fs, fd, (uint8_t *)&meta); + if (ret != SPIFFS_OK) { + ESP_LOGW(TAG, "Failed to update metadata (%d)", ret); + } +#endif +} + +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s) +{ + time_t t = 0; +#ifdef CONFIG_SPIFFS_USE_MTIME + vfs_spiffs_meta_t meta; + memcpy(&meta, s->meta, sizeof(meta)); + t = meta.mtime; +#endif + return t; +} diff --git a/Tools/esp_idf_patches/components/spiffs/spiffs/src/spiffs_hydrogen.c b/Tools/esp_idf_patches/components/spiffs/spiffs/src/spiffs_hydrogen.c new file mode 100644 index 00000000..5ac660bb --- /dev/null +++ b/Tools/esp_idf_patches/components/spiffs/spiffs/src/spiffs_hydrogen.c @@ -0,0 +1,1455 @@ +/* + * spiffs_hydrogen.c + * + * Created on: Jun 16, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "esp_task_wdt.h" +#include "mphalport.h" + +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); +#endif + +#if SPIFFS_BUFFER_HELP +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { + return num_descs * sizeof(spiffs_fd); +} +#if SPIFFS_CACHE +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); +} +#endif +#endif + +u8_t SPIFFS_mounted(spiffs *fs) { + return SPIFFS_CHECK_MOUNT(fs); +} + +s32_t SPIFFS_format(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; + } + + s32_t res; + SPIFFS_LOCK(fs); + + mp_hal_set_wdt_tmo(); + spiffs_block_ix bix = 0; + while (bix < fs->block_count) { + mp_hal_reset_wdt(); + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_probe_fs(spiffs_config *config) { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = spiffs_probe(config); + return res; +} + +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f) { + SPIFFS_API_DBG("%s " + " sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi + " addr:"_SPIPRIad + " fdsz:"_SPIPRIi " cachesz:"_SPIPRIi + "\n", + __func__, + SPIFFS_CFG_PHYS_SZ(fs), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + SPIFFS_CFG_LOG_BLOCK_SZ(fs), + SPIFFS_CFG_PHYS_ERASE_SZ(fs), + SPIFFS_CFG_PHYS_ADDR(fs), + fd_space_size, cache_size); + void *user_data; + SPIFFS_LOCK(fs); + user_data = fs->user_data; + memset(fs, 0, sizeof(spiffs)); + _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary + u8_t ptr_size = sizeof(void*); + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1); + if (addr_lsb) { + fd_space += (ptr_size-addr_lsb); + fd_space_size -= (ptr_size-addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1); + if (addr_lsb) { + u8_t *cache_8 = (u8_t *)cache; + cache_8 += (ptr_size-addr_lsb); + cache = cache_8; + cache_size -= (ptr_size-addr_lsb); + } + if (cache_size & (ptr_size-1)) { + cache_size -= (cache_size & (ptr_size-1)); + } + +#if SPIFFS_CACHE + fs->cache = cache; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; + spiffs_cache_init(fs); +#endif + + s32_t res; + +#if SPIFFS_USE_MAGIC + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + fs->config_magic = SPIFFS_CONFIG_MAGIC; + + res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); + + fs->check_cb_f = check_cb_f; + + fs->mounted = 1; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +void SPIFFS_unmount(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; + SPIFFS_LOCK(fs); + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0) { +#if SPIFFS_CACHE + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); +#endif + spiffs_fd_return(fs, cur_fd->file_nbr); + } + } + fs->mounted = 0; + + SPIFFS_UNLOCK(fs); +} + +s32_t SPIFFS_errno(spiffs *fs) { + return fs->err_code; +} + +void SPIFFS_clearerr(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); + fs->err_code = SPIFFS_OK; +} + +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; (void)path; (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags); + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + +#if SPIFFS_READ_ONLY + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); +#endif // SPIFFS_READ_ONLY + + s32_t res = spiffs_fd_find_new(fs, &fd, path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { +#if !SPIFFS_READ_ONLY + spiffs_obj_id obj_id; + // no need to enter conflicting name here, already looked for it above + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_O_TRUNC; +#endif // !SPIFFS_READ_ONLY + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { + res = SPIFFS_ERR_NOT_A_FILE; + } + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_RDONLY) == 0) { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + len = avail; + } + } else { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return len; +} + +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + res = 0; + } + return res; +} + + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; + buf_8 += m_len; + buf = buf_8; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + +} +#endif // !SPIFFS_READ_ONLY + +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)buf; (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + u32_t offset; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((fd->flags & SPIFFS_O_APPEND)) { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } + offset = fd->fdoffset; + +#if SPIFFS_CACHE_WR + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } +#endif + if (fd->flags & SPIFFS_O_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; + } else { + offset = fd->size; + } +#if SPIFFS_CACHE_WR + if (fd->cache_page) { + offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); + } +#endif + } + +#if SPIFFS_CACHE_WR + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->offset || // writing before cache + offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache + offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page + { + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->offset = offset; + fd->cache_page->size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); +#ifdef _SPIFFS_TEST + { + intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage]-(u8_t*)cache; + intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage]+len-(u8_t*)cache; + intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + if (__a1 > __b || __a2 > __b) { + printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); + ERREXIT(); + } + } +#endif + _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below + } + } + } +#endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char* []){"SET","CUR","END","???"}[MIN(whence,3)]); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset+offs; + break; + case SPIFFS_SEEK_END: + offs = file_size + offs; + break; + } + if (offs < 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); + } + if (offs > file_size) { + fd->fdoffset = file_size; + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + spiffs_span_ix data_spix = (offs > 0 ? (offs-1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; + + SPIFFS_UNLOCK(fs); + + return offs; +} + +s32_t SPIFFS_remove(spiffs *fs, const char *path) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0,0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_cache_fd_release(fs, fd->cache_page); +#endif + + res = spiffs_object_truncate(fd, 0, 1); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + (void)fh; + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); + + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); + + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + + return res; +} + +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_page_ix pix; + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_stat_pix(fs, pix, 0, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +// Checks if there are any cached writes for the object id associated with +// given filehandle. If so, these writes are flushed. +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + (void)fs; + (void)fh; + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + if (res < SPIFFS_OK) { + fs->err_code = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } + } +#endif + + return res; +} +#endif + +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + (void)fh; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs,res); + SPIFFS_UNLOCK(fs); +#endif + + return res; +} + +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); +#if SPIFFS_CACHE + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { + SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); +#if SPIFFS_READ_ONLY + (void)fs; (void)old_path; (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_page_ix pix_old, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + } else if (res == SPIFFS_OK) { + res = SPIFFS_ERR_CONFLICTING_NAME; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, + 0, 0, &pix_dummy); +#if SPIFFS_TEMPORAL_FD_CACHE + if (res == SPIFFS_OK) { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } +#endif + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_OBJ_META_LEN +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)name; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} +#endif // SPIFFS_OBJ_META_LEN + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); + (void)name; + + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } + + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; +} + +static s32_t spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + e->pix = pix; +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + return SPIFFS_OK; + } + return SPIFFS_VIS_COUNTINUE; +} + +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_MOUNT(d->fs)) { + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(d->fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + ret = e; + } else { + d->fs->err_code = res; + } + SPIFFS_UNLOCK(d->fs); + return ret; +} + +s32_t SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_API_CHECK_CFG(d->fs); + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; +} + +s32_t SPIFFS_check(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_lookup_consistency_check(fs, 0); + + res = spiffs_object_index_consistency_check(fs); + + res = spiffs_page_consistency_check(fs); + + res = spiffs_obj_lu_scan(fs); + + SPIFFS_UNLOCK(fs); + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + + if (total) { + *total = total_data_pages * data_page_size; + } + + if (used) { + *used = fs->stats_p_allocated * data_page_size; + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, max_free_pages); +#if SPIFFS_READ_ONLY + (void)fs; (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_quick(fs, max_free_pages); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + + +s32_t SPIFFS_gc(spiffs *fs, u32_t size) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size); +#if SPIFFS_READ_ONLY + (void)fs; (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_check(fs, size); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = fd->fdoffset; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; +} + +#if SPIFFS_IX_MAP + +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } + + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; + + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + fd->ix_map = 0; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + spiffs_ix_map *map = fd->ix_map; + + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else if (spix_diff > 0) { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs); +} + +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); +} + +#endif // SPIFFS_IX_MAP + +#if SPIFFS_TEST_VISUALISATION +s32_t SPIFFS_vis(spiffs *fs) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; + + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (cur_entry == 0) { + spiffs_printf(_SPIPRIbl" ", bix); + } else if ((cur_entry & 0x3f) == 0) { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){ + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } else { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id)-1) { + spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); + } else { + spiffs_printf("\tera_cnt: N/A\n"); + } + + bix++; + } // per block + + spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); + spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); + spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); + spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); + spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); + spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); + return res; +} +#endif diff --git a/Tools/esp_idf_patches/components/spiffs/test/test_spiffs.c b/Tools/esp_idf_patches/components/spiffs/test/test_spiffs.c new file mode 100644 index 00000000..292fe7e4 --- /dev/null +++ b/Tools/esp_idf_patches/components/spiffs/test/test_spiffs.c @@ -0,0 +1,613 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include "unity.h" +#include "test_utils.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_vfs.h" +#include "esp_spiffs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "esp_partition.h" + +const char* spiffs_test_hello_str = "Hello, World!\n"; +const char* spiffs_test_partition_label = "flash_test"; + +void test_spiffs_create_file_with_text(const char* name, const char* text) +{ + FILE* f = fopen(name, "wb"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_TRUE(fputs(text, f) != EOF); + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +void test_spiffs_overwrite_append(const char* filename) +{ + /* Create new file with 'aaaa' */ + test_spiffs_create_file_with_text(filename, "aaaa"); + + /* Append 'bbbb' to file */ + FILE *f_a = fopen(filename, "a"); + TEST_ASSERT_NOT_NULL(f_a); + TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a)); + TEST_ASSERT_EQUAL(0, fclose(f_a)); + + /* Read back 8 bytes from file, verify it's 'aaaabbbb' */ + char buf[10] = { 0 }; + FILE *f_r = fopen(filename, "r"); + TEST_ASSERT_NOT_NULL(f_r); + TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r)); + TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8); + + /* Be sure we're at end of file */ + TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r)); + + TEST_ASSERT_EQUAL(0, fclose(f_r)); + + /* Overwrite file with 'cccc' */ + test_spiffs_create_file_with_text(filename, "cccc"); + + /* Verify file now only contains 'cccc' */ + f_r = fopen(filename, "r"); + TEST_ASSERT_NOT_NULL(f_r); + bzero(buf, sizeof(buf)); + TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4 + TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4); + TEST_ASSERT_EQUAL(0, fclose(f_r)); +} + +void test_spiffs_read_file(const char* filename) +{ + FILE* f = fopen(filename, "r"); + TEST_ASSERT_NOT_NULL(f); + char buf[32] = { 0 }; + int cb = fread(buf, 1, sizeof(buf), f); + TEST_ASSERT_EQUAL(strlen(spiffs_test_hello_str), cb); + TEST_ASSERT_EQUAL(0, strcmp(spiffs_test_hello_str, buf)); + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +void test_spiffs_open_max_files(const char* filename_prefix, size_t files_count) +{ + FILE** files = calloc(files_count, sizeof(FILE*)); + for (size_t i = 0; i < files_count; ++i) { + char name[32]; + snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i); + files[i] = fopen(name, "w"); + TEST_ASSERT_NOT_NULL(files[i]); + } + /* close everything and clean up */ + for (size_t i = 0; i < files_count; ++i) { + fclose(files[i]); + } + free(files); +} + +void test_spiffs_lseek(const char* filename) +{ + FILE* f = fopen(filename, "wb+"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n")); + TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR)); + TEST_ASSERT_EQUAL('9', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET)); + TEST_ASSERT_EQUAL('3', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END)); + TEST_ASSERT_EQUAL('8', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END)); + TEST_ASSERT_EQUAL(11, ftell(f)); + TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n")); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END)); + TEST_ASSERT_EQUAL(15, ftell(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET)); + char buf[20]; + TEST_ASSERT_EQUAL(15, fread(buf, 1, sizeof(buf), f)); + const char ref_buf[] = "0123456789\nabc\n"; + TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1); + + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +void test_spiffs_stat(const char* filename) +{ + test_spiffs_create_file_with_text(filename, "foo\n"); + struct stat st; + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + TEST_ASSERT(st.st_mode & S_IFREG); + TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); +} + +void test_spiffs_unlink(const char* filename) +{ + test_spiffs_create_file_with_text(filename, "unlink\n"); + + TEST_ASSERT_EQUAL(0, unlink(filename)); + + TEST_ASSERT_NULL(fopen(filename, "r")); +} + +void test_spiffs_rename(const char* filename_prefix) +{ + char name_dst[64]; + char name_src[64]; + snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix); + snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix); + + unlink(name_dst); + unlink(name_src); + + FILE* f = fopen(name_src, "w+"); + TEST_ASSERT_NOT_NULL(f); + const char* str = "0123456789"; + for (int i = 0; i < 400; ++i) { + TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f)); + } + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ASSERT_EQUAL(0, rename(name_src, name_dst)); + TEST_ASSERT_NULL(fopen(name_src, "r")); + FILE* fdst = fopen(name_dst, "r"); + TEST_ASSERT_NOT_NULL(fdst); + TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END)); + TEST_ASSERT_EQUAL(4000, ftell(fdst)); + TEST_ASSERT_EQUAL(0, fclose(fdst)); +} + +void test_spiffs_can_opendir(const char* path) +{ + char name_dir_file[64]; + const char * file_name = "test_opd.txt"; + snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name); + unlink(name_dir_file); + test_spiffs_create_file_with_text(name_dir_file, "test_opendir\n"); + DIR* dir = opendir(path); + TEST_ASSERT_NOT_NULL(dir); + bool found = false; + while (true) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + if (strcasecmp(de->d_name, file_name) == 0) { + found = true; + break; + } + } + TEST_ASSERT_TRUE(found); + TEST_ASSERT_EQUAL(0, closedir(dir)); + unlink(name_dir_file); +} + +void test_spiffs_opendir_readdir_rewinddir(const char* dir_prefix) +{ +#ifdef CONFIG_SPIFFS_USE_DIR + printf("Directories support enabled\n"); +#else + printf("Directories support disabled\n"); +#endif + char name_dir_inner_file[64]; + char name_dir_inner[64]; + char name_dir_file3[64]; + char name_dir_file2[64]; + char name_dir_file1[64]; + + snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix); + snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix); + snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix); + snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix); + snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix); + + unlink(name_dir_inner_file); +#ifdef CONFIG_SPIFFS_USE_DIR + rmdir(name_dir_inner); + if (rmdir(dir_prefix) < 0) + printf("rmdir [%s] Error %d (%s)\n", name_dir_inner, errno, strerror(errno)); +#endif + unlink(name_dir_file1); + unlink(name_dir_file2); + unlink(name_dir_file3); +#ifdef CONFIG_SPIFFS_USE_DIR + if (rmdir(dir_prefix) < 0) + printf("rmdir [%s] Error %d (%s)\n", dir_prefix, errno, strerror(errno)); + if (mkdir(dir_prefix, 0777) < 0) + printf("mkdir [%s] Error %d (%s)\n", dir_prefix, errno, strerror(errno)); + if (mkdir(name_dir_inner, 0777) < 0) + printf("mkdir [%s] Error %d (%s)\n", name_dir_inner, errno, strerror(errno)); +#endif + test_spiffs_create_file_with_text(name_dir_file1, "1\n"); + test_spiffs_create_file_with_text(name_dir_file2, "2\n"); + test_spiffs_create_file_with_text(name_dir_file3, "\01\02\03"); + test_spiffs_create_file_with_text(name_dir_inner_file, "3\n"); + + printf("opendir %s\n", dir_prefix); + DIR* dir = opendir(dir_prefix); + if (!dir) + printf("opendir Error %d (%s)\n", errno, strerror(errno)); + TEST_ASSERT_NOT_NULL(dir); + int count = 0; + const char* names[4]; + while(count < 4) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + printf("found '%s'\n", de->d_name); + if (strcasecmp(de->d_name, "1.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "1.txt"; + ++count; + } else if (strcasecmp(de->d_name, "2.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "2.txt"; + ++count; +#ifdef CONFIG_SPIFFS_USE_DIR + } else if (strcasecmp(de->d_name, "inner") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_DIR); + names[count] = "inner"; +#else + } else if (strcasecmp(de->d_name, "inner/3.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "inner/3.txt"; +#endif + ++count; + } else if (strcasecmp(de->d_name, "boo.bin") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "boo.bin"; + ++count; + } else { + TEST_FAIL_MESSAGE("unexpected directory entry"); + } + } + TEST_ASSERT_EQUAL(count, 4); + + printf("rewinddir %s\n", dir_prefix); + rewinddir(dir); + struct dirent* de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0])); + seekdir(dir, 3); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3])); + seekdir(dir, 1); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1])); + seekdir(dir, 2); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2])); + + TEST_ASSERT_EQUAL(0, closedir(dir)); +} + + +typedef struct { + const char* filename; + bool write; + size_t word_count; + int seed; + SemaphoreHandle_t done; + int result; +} read_write_test_arg_t; + +#define READ_WRITE_TEST_ARG_INIT(name, seed_) \ + { \ + .filename = name, \ + .seed = seed_, \ + .word_count = 4096, \ + .write = true, \ + .done = xSemaphoreCreateBinary() \ + } + +static void read_write_task(void* param) +{ + read_write_test_arg_t* args = (read_write_test_arg_t*) param; + FILE* f = fopen(args->filename, args->write ? "wb" : "rb"); + if (f == NULL) { + args->result = ESP_ERR_NOT_FOUND; + goto done; + } + + srand(args->seed); + for (size_t i = 0; i < args->word_count; ++i) { + uint32_t val = rand(); + if (args->write) { + int cnt = fwrite(&val, sizeof(val), 1, f); + if (cnt != 1) { + ets_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val); + args->result = ESP_FAIL; + goto close; + } + } else { + uint32_t rval; + int cnt = fread(&rval, sizeof(rval), 1, f); + if (cnt != 1) { + ets_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval); + args->result = ESP_FAIL; + goto close; + } + } + } + args->result = ESP_OK; + +close: + fclose(f); + +done: + xSemaphoreGive(args->done); + vTaskDelay(1); + vTaskDelete(NULL); +} + +void test_spiffs_concurrent(const char* filename_prefix) +{ + char names[4][64]; + for (size_t i = 0; i < 4; ++i) { + snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1); + unlink(names[i]); + } + + read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1); + read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2); + + printf("writing f1 and f2\n"); + const int cpuid_0 = 0; + const int cpuid_1 = portNUM_PROCESSORS - 1; + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + + args1.write = false; + args2.write = false; + read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3); + read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4); + + printf("reading f1 and f2, writing f3 and f4\n"); + + xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, cpuid_1); + xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, cpuid_0); + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + xSemaphoreTake(args3.done, portMAX_DELAY); + printf("f3 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args3.result); + xSemaphoreTake(args4.done, portMAX_DELAY); + printf("f4 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args4.result); + + vSemaphoreDelete(args1.done); + vSemaphoreDelete(args2.done); + vSemaphoreDelete(args3.done); + vSemaphoreDelete(args4.done); +} + + +static void test_setup() +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", + .partition_label = spiffs_test_partition_label, + .max_files = 5, + .format_if_mount_failed = true + }; + + TEST_ESP_OK(esp_vfs_spiffs_register(&conf)); +} + +static void test_teardown() +{ + TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label)); +} + +TEST_CASE("can initialize SPIFFS in erased partition", "[spiffs]") +{ + const esp_partition_t* part = get_test_data_partition(); + TEST_ASSERT_NOT_NULL(part); + TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size)); + test_setup(); + size_t total = 0, used = 0; + TEST_ESP_OK(esp_spiffs_info(spiffs_test_partition_label, &total, &used)); + printf("total: %d, used: %d\n", total, used); + TEST_ASSERT_EQUAL(0, used); + test_teardown(); +} + +TEST_CASE("can format mounted partition", "[spiffs]") +{ + // Mount SPIFFS, create file, format, check that the file does not exist. + const esp_partition_t* part = get_test_data_partition(); + TEST_ASSERT_NOT_NULL(part); + test_setup(); + const char* filename = "/spiffs/hello.txt"; + test_spiffs_create_file_with_text(filename, spiffs_test_hello_str); + esp_spiffs_format(part->label); + FILE* f = fopen(filename, "r"); + TEST_ASSERT_NULL(f); + test_teardown(); +} + +TEST_CASE("can format unmounted partition", "[spiffs]") +{ + // Mount SPIFFS, create file, unmount. Format. Mount again, check that + // the file does not exist. + const esp_partition_t* part = get_test_data_partition(); + TEST_ASSERT_NOT_NULL(part); + test_setup(); + const char* filename = "/spiffs/hello.txt"; + test_spiffs_create_file_with_text(filename, spiffs_test_hello_str); + test_teardown(); + esp_spiffs_format(part->label); + // Don't use test_setup here, need to mount without formatting + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", + .partition_label = spiffs_test_partition_label, + .max_files = 5, + .format_if_mount_failed = false + }; + TEST_ESP_OK(esp_vfs_spiffs_register(&conf)); + FILE* f = fopen(filename, "r"); + TEST_ASSERT_NULL(f); + test_teardown(); +} + +TEST_CASE("can create and write file", "[spiffs]") +{ + test_setup(); + test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str); + test_teardown(); +} + +TEST_CASE("can read file", "[spiffs]") +{ + test_setup(); + test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str); + test_spiffs_read_file("/spiffs/hello.txt"); + test_teardown(); +} + +TEST_CASE("can open maximum number of files", "[spiffs]") +{ + size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */ + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", + .partition_label = spiffs_test_partition_label, + .format_if_mount_failed = true, + .max_files = max_files + }; + TEST_ESP_OK(esp_vfs_spiffs_register(&conf)); + test_spiffs_open_max_files("/spiffs/f", max_files); + TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label)); +} + +TEST_CASE("overwrite and append file", "[spiffs]") +{ + test_setup(); + test_spiffs_overwrite_append("/spiffs/hello.txt"); + test_teardown(); +} + +TEST_CASE("can lseek", "[spiffs]") +{ + test_setup(); + test_spiffs_lseek("/spiffs/seek.txt"); + test_teardown(); +} + + +TEST_CASE("stat returns correct values", "[spiffs]") +{ + test_setup(); + test_spiffs_stat("/spiffs/stat.txt"); + test_teardown(); +} + +TEST_CASE("unlink removes a file", "[spiffs]") +{ + test_setup(); + test_spiffs_unlink("/spiffs/unlink.txt"); + test_teardown(); +} + +TEST_CASE("rename moves a file", "[spiffs]") +{ + test_setup(); + test_spiffs_rename("/spiffs/move"); + test_teardown(); +} + +TEST_CASE("can opendir root directory of FS", "[spiffs]") +{ + test_setup(); + test_spiffs_can_opendir("/spiffs"); + test_teardown(); +} + +TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[spiffs]") +{ + test_setup(); + test_spiffs_opendir_readdir_rewinddir("/spiffs/dir"); + test_teardown(); +} + +TEST_CASE("multiple tasks can use same volume", "[spiffs]") +{ + test_setup(); + 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 diff --git a/Tools/esp_idf_patches/project.mk.patch.not_needed b/Tools/esp_idf_patches/project.mk.patch.not_needed new file mode 100644 index 00000000..06b2660f --- /dev/null +++ b/Tools/esp_idf_patches/project.mk.patch.not_needed @@ -0,0 +1,4 @@ + +# IDF_VER := $(shell cd ${IDF_PATH} && git describe --always --tags --dirty) +IDF_VER := "v3.1-dev-156-g1837a034" + diff --git a/Tools/esp_idf_patches/version.txt b/Tools/esp_idf_patches/version.txt new file mode 100644 index 00000000..da00280e --- /dev/null +++ b/Tools/esp_idf_patches/version.txt @@ -0,0 +1 @@ +v3.1-dev-961-ga2556229