diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index fc06245..24775e5 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,8 +1,8 @@ -idf_component_register(SRCS "controlplane.c" "lib/meshtalos.c" "lib/mongoose.c" "lib/storage.c" "network.c" "lib/log.c" "lib/publish.c" "mesh_if.c" - PRIV_REQUIRES esp_wifi esp_timer mpack lwip esp_partition nvs_flash joltwallet__littlefs esp-tls +idf_component_register(SRCS "controlplane.c" "lib/meshtalos.c" "lib/storage.c" "network.c" "lib/log.c" "lib/picohttpparser.c" + PRIV_REQUIRES esp_wifi esp_timer mpack lwip esp_partition nvs_flash joltwallet__littlefs esp-tls app_update INCLUDE_DIRS ".") idf_component_set_property(main MINIMAL_BUILD ON) target_compile_options(${COMPONENT_LIB} PRIVATE -std=gnu23) -target_compile_definitions(${COMPONENT_LIB} PUBLIC MG_ARCH=MG_ARCH_ESP32 PUBLIC MG_ENABLE_POSIX_FS=0 PUBLIC MG_OTA=MG_OTA_NONE) +target_compile_definitions(${COMPONENT_LIB} PUBLIC MG_ARCH=MG_ARCH_ESP32 PUBLIC MG_ENABLE_POSIX_FS=0 PUBLIC MG_OTA=MG_OTA_NONE PUBLIC MG_IO_SIZE=2048) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 89de139..cb2544e 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -1,106 +1,84 @@ -menu "Example Configuration" +menu "Meshtalos Configuration" - config MESH_CHANNEL - int "channel" - range 0 14 - default 0 - help - mesh network channel. - config MESH_ROUTER_SSID - string "Router SSID" - default "ROUTER_SSID" - help - Router SSID. + menu "SoftAP Configuration" + comment "SoftAP Configuration" - config MESH_ROUTER_PASSWD - string "Router password" - default "ROUTER_PASSWD" - help - Router password. + config ESP_WIFI_AP_SSID + string "WiFi AP SSID" + default "myssid" + help + SSID (network name) of the AP for the example to connect to. - choice - bool "Mesh AP Authentication Mode" - default WIFI_AUTH_WPA2_PSK - help - Authentication mode. + config ESP_WIFI_AP_PASSWORD + string "WiFi AP Password" + default "mypassword" + help + WiFi password of the AP for the example to use. - config WIFI_AUTH_OPEN - bool "WIFI_AUTH_OPEN" - config WIFI_AUTH_WPA_PSK - bool "WIFI_AUTH_WPA_PSK" - config WIFI_AUTH_WPA2_PSK - bool "WIFI_AUTH_WPA2_PSK" - config WIFI_AUTH_WPA_WPA2_PSK - bool "WIFI_AUTH_WPA_WPA2_PSK" - endchoice + config ESP_WIFI_AP_CHANNEL + int "WiFi AP Channel" + range 1 14 + default 1 + help + WiFi channel (network channel) of the AP for the example to use. - config MESH_AP_AUTHMODE - int - default 0 if WIFI_AUTH_OPEN - default 2 if WIFI_AUTH_WPA_PSK - default 3 if WIFI_AUTH_WPA2_PSK - default 4 if WIFI_AUTH_WPA_WPA2_PSK - help - Mesh AP authentication mode. + config ESP_MAX_STA_CONN_AP + int "Maximal STA connections" + default 4 + help + Max number of the STA connects to AP. + endmenu - config MESH_AP_PASSWD - string "Mesh AP Password" - default "MAP_PASSWD" - help - Mesh AP password. + menu "STA Configuration" + comment "STA Configuration" - config MESH_AP_CONNECTIONS - int "Mesh AP Connections" - range 1 10 - default 6 - help - The number of mesh stations allowed to connect in. + config ESP_WIFI_REMOTE_AP_SSID + string "WiFi Remote AP SSID" + default "otherapssid" + help + SSID (network name) for the example's sta to connect to. - config MESH_NON_MESH_AP_CONNECTIONS - int "Mesh Non Mesh AP Connections" - range 0 9 - default 0 - help - The number of non-mesh stations allowed to connect in. + config ESP_WIFI_REMOTE_AP_PASSWORD + string "WiFi Remote AP Password" + default "otherappassword" + help + WiFi password for the example to use. - config MESH_IE_ENCRYPTED - bool "Mesh IE encrypted" - default y - help - The mesh IE is encrypted by default. + config ESP_MAXIMUM_STA_RETRY + int "Maximum retry" + default 5 + help + Set the maximum retry value to prevent the station from continuously + attempting to reconnect to the Access Point (AP) when the AP doesn't exist. - config MESH_MAX_LAYER - int "Mesh Max Layer" - range 1 25 - default 6 - help - Max layer allowed in mesh network. + choice ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD + prompt "WiFi Scan auth mode threshold" + default ESP_WIFI_AUTH_WPA2_PSK + help + The weakest authmode to accept in the scan mode. + This value defaults to ESP_WIFI_AUTH_WPA2_PSK in case password is present + and ESP_WIFI_AUTH_OPEN is used. Please select ESP_WIFI_AUTH_WEP / ESP_WIFI_AUTH_WPA_PSK + in case AP is operating in WEP / WPA mode. - config MESH_ROUTE_TABLE_SIZE - int "Mesh Routing Table Size" - range 1 300 - default 50 - help - The number of devices over the network(max: 300). + config ESP_WIFI_AUTH_OPEN + bool "OPEN" + config ESP_WIFI_AUTH_WEP + bool "WEP" + config ESP_WIFI_AUTH_WPA_PSK + bool "WPA PSK" + config ESP_WIFI_AUTH_WPA2_PSK + bool "WPA2 PSK" + config ESP_WIFI_AUTH_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config ESP_WIFI_AUTH_WPA3_PSK + bool "WPA3 PSK" + config ESP_WIFI_AUTH_WPA2_WPA3_PSK + bool "WPA2/WPA3 PSK" + config ESP_WIFI_AUTH_WAPI_PSK + bool "WAPI PSK" + endchoice - config MESH_USE_GLOBAL_DNS_IP - bool "Use global DNS IP" - default n - help - Use fixed DNS server IP address. If enabled, root node - advertises the specified DNS address in DHCP packets. - By default it uses DNS record received from the router. - - config MESH_GLOBAL_DNS_IP - hex "Global DNS" - depends on MESH_USE_GLOBAL_DNS_IP - default 0x08080808 - help - The IP address of global DNS server that is used - for internal IP subnet formed by the mesh network - if MESH_USE_GLOBAL_DNS_IP is enabled. - Note: The IP address is in platform (not network) - format. + endmenu endmenu diff --git a/main/controlplane.c b/main/controlplane.c index 4ad247f..c6358e1 100644 --- a/main/controlplane.c +++ b/main/controlplane.c @@ -5,18 +5,12 @@ #include #include #include +#include #define BLOCKSIZE 4096 const char *TAG = "main"; -void fatal_err(int code) { - while (1) { - ESP_LOGE(TAG, "fatal err: %d", code); - sleep(5); - } -} - void app_main(void) { /* event initialization */ @@ -40,9 +34,16 @@ void app_main(void) { return; } - struct Meshtalos *control = mtsh_init(); - if (control == NULL) { - fatal_err(1); + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, pdFALSE, portMAX_DELAY); + + if (bits & WIFI_CONNECTED_BIT) { + struct Meshtalos *control = mtsh_init(); + if (control == NULL) { + ESP_LOGE("MESHTALOS", "failed init control"); + abort(); + } + mtsh_listen(control, 3030, 80); } - mtsh_listen(control, "http://0.0.0.0:80"); } diff --git a/main/lib/meshtalos.c b/main/lib/meshtalos.c index 3392e1a..71b2bdd 100644 --- a/main/lib/meshtalos.c +++ b/main/lib/meshtalos.c @@ -1,58 +1,484 @@ #include "meshtalos.h" -#include "freertos/idf_additions.h" -#include "lib/mongoose.h" +#include "./mpack.h" +#include "./picohttpparser.h" +#include "lwip/sockets.h" +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include -uint8_t image_buf[IMAGE_SIZE] = {0}; -bool flag_refresh = true; +#define STB_DS_IMPLEMENTATION +#include "./stb_ds.h" -static void ev_handler(struct mg_connection *c, int ev, void *ev_data) { +#define HTTPVER "HTTP/1.1" +#define IMG_W_SIZE 152 +#define IMG_H_SIZE 296 +#define DISPLAY_SIZE IMG_W_SIZE *IMG_H_SIZE +#define IMG_HEADER_SIZE 15 +#define IMG_SIZE IMG_HEADER_SIZE + DISPLAY_SIZE - // struct Storage *storage = (struct Storage *)(ev_data); +struct client { + int fd; + struct sockaddr_in ip4; + bool invalid; +}; - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received - struct mg_http_message *hm = - (struct mg_http_message *)ev_data; // Parsed HTTP request - if (mg_match(hm->uri, mg_str("/api/tag"), NULL)) { // REST API call? - if (hm->body.len == sizeof(image_buf)) { - memcpy(image_buf, hm->body.buf, sizeof(image_buf)); - struct sync_jobs jb; - xQueueSend(sub_jobs, &jb, 0); - mg_http_reply(c, 200, "", "fin"); - flag_refresh = true; - return; - } - mg_http_reply(c, 400, "", "bad"); +struct conn { + int rootfd; + fd_set master, recv; + struct client *c; + int max_client_fd; + struct timeval interval; +}; +struct http_param { + const char *method, *path; + struct phr_header *headers; + size_t method_len, path_len, headers_len, body_len; + int minor_ver; + char *body_start; +}; + +struct conn workers, http; +char iobuf[2048] = {0}; +char httpbuf[2048] = {0}; +char imgbuf[IMG_SIZE]; +struct phr_header headers[30]; +const size_t header_nums = 30; + +void tsend(); +int natoi(const char *a, int len) { + int ret = 0; + for (int i = 0; i < len; i++) { + if (a[i] >= '0' && a[i] <= '9') { + ret = ret * 10 + a[i] - '0'; } else { - mg_http_reply(c, 404, "", "bad req"); + return 0; + } + } + return ret; +} + +ssize_t client_send(struct client *cli, const char *buf, size_t len) { + if (cli->invalid) { + return -1; + } + int ret = send(cli->fd, buf, len, MSG_NOSIGNAL); + if (ret < 0) { + cli->invalid = true; + } + return ret; +} +ssize_t client_recv(struct client *cli, char *buf, size_t len) { + if (cli->invalid) { + return -1; + } + int ret = recv(cli->fd, buf, len, MSG_NOSIGNAL); + if (ret <= 0) { + cli->invalid = true; + } + return ret; +} +ssize_t client_dprintf(struct client *cli, const char *fmt, ...) { + va_list v; + va_start(v, fmt); + size_t l = vsnprintf(httpbuf, sizeof(httpbuf), fmt, v); + va_end(v); + return client_send(cli, httpbuf, l); +} + +void conn_init(struct conn *c) { + FD_ZERO(&c->recv); + arrsetcap(c->c, 10); +} +void conn_poll_accept(struct conn *c) { + while (1) { + struct client temp; + socklen_t tlen = sizeof(temp.ip4); + temp.fd = accept(c->rootfd, (struct sockaddr *)&temp.ip4, &tlen); + if (temp.fd != -1) { + c->max_client_fd = + c->max_client_fd < temp.fd ? temp.fd : c->max_client_fd; + FD_SET(temp.fd, &c->master); + printf("poll accepted: %s\n", inet_ntoa(temp.ip4.sin_addr)); + temp.invalid = false; + arrput(c->c, temp); + } else { + if (errno != EAGAIN || errno != EWOULDBLOCK) { + perror("accept error"); + } + break; + } + } +} +void conn_poll_clients(struct conn *c, + void (*cb)(struct conn *c, struct client *client, + int client_id)) { + memcpy(&c->recv, &c->master, sizeof(c->master)); + int conn = select(c->max_client_fd + 1, &c->recv, NULL, NULL, &c->interval); + if (conn < 0) { + perror("select error"); + } + for (int i = 0; i < arrlen(c->c) && conn; ++i) { + if (FD_ISSET(c->c[i].fd, &c->recv)) { + cb(c, &c->c[i], i); + conn--; } } } +void conn_collect_bad_client(struct conn *c) { + for (int i = 0; i < arrlen(c->c); i++) { + if (c->c[i].invalid) { + FD_CLR(c->c[i].fd, &c->master); + close(c->c[i].fd); + arrdelswap(c->c, i); + } + } +} + +int app_close = 0; + +/* http path handle */ +#define CLRF "\r\n" +#define HEAD_MATCH(h, g) \ + (strlen(h) == g.name_len && strncmp(h, g.name, g.name_len) == 0) +#define HEAD_VAL_MATCH(h, g) \ + (strlen(h) == g.value_len && strncmp(h, g.value, g.value_len) == 0) +int badreq(struct client *c) { + client_dprintf(c, + HTTPVER " 400 Bad Request" CLRF "Connection: close" CLRF CLRF); + return -1; +} +int fbadreq(struct client *c, const char *s, ...) { + va_list p, tp; + va_start(p, s); + va_copy(tp, p); + size_t body_len = vsnprintf(NULL, 0, s, tp); + client_dprintf(c, + HTTPVER " 400 Bad Request" CLRF "Connection: close" CLRF + "Content-Length: %ld" CLRF CLRF, + body_len); + vsnprintf(httpbuf, sizeof(httpbuf), s, p); + client_send(c, httpbuf, body_len); + va_end(p); + return -1; +} +int api_tag_upload(struct client *c, struct http_param *p) { + + printf("INTO TAG RECV\n"); + int acc = 0; + for (int i = 0; i < p->headers_len; i++) { + if (HEAD_MATCH("Content-Type", p->headers[i]) && + HEAD_VAL_MATCH("image/x-portable-greymap", p->headers[i])) { + acc++; + } + if (HEAD_MATCH("Content-Length", p->headers[i])) { + if (strtol(p->headers[i].value, NULL, 10) == IMG_SIZE) { + acc++; + } + } + } + + if (acc != 2) { + + return fbadreq(c, + "Only accept request with PGM P5 format that height and " + "width is either %03d,%03d", + IMG_W_SIZE, IMG_H_SIZE); + } + + size_t upload_size = p->body_len; + memcpy(imgbuf, p->body_start, p->body_len); + + while (upload_size < IMG_SIZE) { + // upload PGM sanitize + if (upload_size > IMG_HEADER_SIZE) { + if (strncmp(imgbuf, "P5", 2) != 0) { + return badreq(c); + } + int santinize_acc = 0; + int width = 0, height = IMG_W_SIZE ^ IMG_H_SIZE; + int p_start = 3, p_len = 0; + for (int i = 3; i < upload_size; i++) { + if (imgbuf[i] == ' ' || imgbuf[i] == '\r' || imgbuf[i] == '\n') { + p_len = i - p_start; + if (santinize_acc == 0) { + width = natoi(&imgbuf[p_start], p_len); + if (width != IMG_W_SIZE && width != IMG_H_SIZE) { + return badreq(c); + } + height ^= width; + } else if (santinize_acc == 1) { + if (height != natoi(&imgbuf[p_start], p_len)) { + return badreq(c); + } + } + p_start = i + 1; + p_len = 0; + santinize_acc += 1; + } + } + } + + // read MIN(iobuf, left_to_read) + size_t next_size = IMG_SIZE - upload_size > sizeof(httpbuf) + ? sizeof(httpbuf) + : IMG_SIZE - upload_size; + ssize_t rcv_size = client_recv(c, httpbuf, next_size); + memcpy(imgbuf + upload_size, httpbuf, rcv_size); + upload_size += next_size; + } + + client_dprintf(c, HTTPVER " 200 Good" CLRF "Connection: close" CLRF CLRF); + tsend(); + return -1; +} +int nopath(struct client *c, struct http_param *p) { + int bodylen = snprintf(NULL, 0, "No Path: %.*s", p->path_len, p->path); + client_dprintf(c, + HTTPVER " 404 Not Found" CLRF "Connection: close" CLRF + "Content-Length: %d" CLRF + "Content-Type: text/plain" CLRF CLRF, + bodylen); + + client_dprintf(c, "No Path: %.*s", p->path_len, p->path); + return -1; +} +/* http path handle */ + +void http_client_handle(struct conn *c, struct client *client, int client_id) { + printf("handle client\n"); + ssize_t rcv_len; + size_t buflen = 0; + while ((rcv_len = read(client->fd, iobuf + buflen, sizeof(iobuf) - buflen)) == + -1 && + errno == EINTR) + ; + + if (rcv_len < 0) { + perror("failed to recv\n"); + return; + } else if (rcv_len == 0) { + printf("closing request\n"); + client->invalid = true; + return; + } else { + printf("read %d bytes\n", rcv_len); + } + + struct http_param p; + p.headers_len = header_nums; + p.headers = headers; + + int pret = phr_parse_request(iobuf, rcv_len, &p.method, &p.method_len, + &p.path, &p.path_len, &p.minor_ver, p.headers, + &p.headers_len, 0); + if (pret > 0) { + p.body_start = memmem(iobuf, rcv_len, "\r\n\r\n", 4); + if (p.body_start - iobuf + 1 >= rcv_len) { + p.body_start = NULL; + } else { + p.body_start += 4; + p.body_len = rcv_len - (p.body_start - iobuf); + } + printf("PATH: \"%.*s\"\n", (int)p.path_len, p.path); + int handler_ret = 0; +#define PEQ(g) (strlen(g) <= p.path_len && strncmp(g, p.path, strlen(g)) == 0) + if (PEQ("/api/tag") && p.method_len == 3 && + strncmp("PUT", p.method, p.method_len) == 0) { + handler_ret = api_tag_upload(client, &p); + } else { + handler_ret = nopath(client, &p); + } + + if (handler_ret == -1) { + client->invalid = true; + } + } else { + printf("can't handle request\n"); + badreq(client); + client->invalid = true; + } +} + +/** + * @brief Rotates an 8-bit image 90 degrees clockwise IN-PLACE. + * @param data Pointer to the image buffer (1 byte per pixel) + * @param W Original width (e.g., 296) + * @param H Original height (e.g., 152) + * * NOTE: After this function, the logical width is H and height is W. + * The buffer must be large enough to hold W*H bytes. + */ +void rotate90_clockwise_inplace(uint8_t *data, int W, int H) { + int total_pixels = W * H; + + // We need 1 bit per pixel to track if we've moved it. + // For 296x152, this is 44,992 bits = 5,624 bytes. + uint8_t *visited = (uint8_t *)calloc((total_pixels + 7) / 8, 1); + if (!visited) + return; // Handle allocation failure (OOM) + + for (int i = 0; i < total_pixels; i++) { + // Skip if this pixel was already moved as part of a previous cycle + if (visited[i >> 3] & (1 << (i & 7))) + continue; + + int curr_idx = i; + uint8_t val_to_move = data[i]; + + while (!(visited[curr_idx >> 3] & (1 << (curr_idx & 7)))) { + // Mark as visited + visited[curr_idx >> 3] |= (1 << (curr_idx & 7)); + + // Calculate new coordinates for 90-degree clockwise + // (x, y) -> (H - 1 - y, x) + int x = curr_idx % W; + int y = curr_idx / W; + + // The NEW dimensions are Height H and Width W (swapped) + // New 1D Index = new_y * NEW_WIDTH + new_x + // For 90deg CW: new_x = H - 1 - y, new_y = x + int next_idx = x * H + (H - 1 - y); + + // Swap values + uint8_t temp = data[next_idx]; + data[next_idx] = val_to_move; + val_to_move = temp; + + curr_idx = next_idx; + } + } + free(visited); +} +#define IMG_BLK_SIZE 1024 + +void flush_mpack(mpack_writer_t *wr, const char *b, size_t len) { + struct client *c = mpack_writer_context(wr); + printf("written %d bytes\n", len); + if (client_send(c, b, len) <= 0) { + mpack_writer_flag_error(wr, mpack_error_io); + } +} + +void send_image(struct client *c) { + mpack_writer_t wr; + mpack_writer_init(&wr, iobuf, sizeof(iobuf)); + mpack_writer_set_flush(&wr, flush_mpack); + mpack_writer_set_context(&wr, c); + for (uint64_t i = IMG_HEADER_SIZE; i < IMG_SIZE; i += IMG_BLK_SIZE) { + mpack_start_map(&wr, 3); + mpack_write_cstr(&wr, "command"); + mpack_write_cstr(&wr, "update_image"); + mpack_write_cstr(&wr, "index"); + mpack_write_u64(&wr, i); + mpack_write_cstr(&wr, "data"); + mpack_write_bin(&wr, &imgbuf[i], MIN(IMG_BLK_SIZE, IMG_SIZE - i)); + mpack_finish_map(&wr); + mpack_writer_flush_message(&wr); + } + mpack_start_map(&wr, 1); + mpack_write_cstr(&wr, "command"); + mpack_write_cstr(&wr, "push_image"); + mpack_writer_destroy(&wr); +} + +void tsend() { + + int param_acc = 0, param_start = 0, param_len = 0; + int img_w = 0, img_h = 0; + for (int i = 0; i < sizeof(imgbuf); i++) { + if (imgbuf[i] == ' ' || imgbuf[i] == '\r' || imgbuf[i] == '\n') { + param_len = i - param_start; + switch (param_acc) { + case 1: + img_w = natoi(&imgbuf[param_start], param_len); + break; + case 2: + img_h = natoi(&imgbuf[param_start], param_len); + break; + } + + param_start = i + 1; + param_acc += 1; + } + } + printf("img_w: %d, img_h: %d\n", img_w, img_h); + if (img_w == IMG_H_SIZE) { + rotate90_clockwise_inplace((uint8_t *)imgbuf + IMG_HEADER_SIZE, IMG_H_SIZE, + IMG_W_SIZE); + } + for (int i = 0; i < arrlen(workers.c); i++) { + send_image(workers.c + i); + } +} + +int main_listen(const int worker_port, int http_port) { + // workers + int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + struct sockaddr_in b = {0}; + memset(&b, 0, sizeof(struct sockaddr_in)); + b.sin_port = htons(worker_port); + b.sin_addr.s_addr = htonl(INADDR_ANY); + b.sin_family = AF_INET; + int val = 1; + assert(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1); + assert(bind(sfd, (struct sockaddr *)&b, sizeof(b)) != -1); + assert(listen(sfd, 5) != -1); + printf("start listen\n"); + int flags = fcntl(sfd, F_GETFL); + assert(fcntl(sfd, F_SETFL, flags | O_NONBLOCK) != -1); + + // HTTP + + int httpfd = socket(AF_INET, SOCK_STREAM, 0); + memset(&b, 0, sizeof(struct sockaddr_in)); + b.sin_port = htons(http_port); + b.sin_addr.s_addr = htonl(INADDR_ANY); + b.sin_family = AF_INET; + val = 1; + assert(setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1); + assert(bind(httpfd, (struct sockaddr *)&b, sizeof(b)) != -1); + assert(listen(httpfd, 5) != -1); + printf("start listen\n"); + flags = fcntl(httpfd, F_GETFL); + assert(fcntl(httpfd, F_SETFL, flags | O_NONBLOCK) != -1); + + workers.interval.tv_usec = 300; + workers.rootfd = sfd; + conn_init(&workers); + http.interval.tv_usec = 300; + http.rootfd = httpfd; + conn_init(&http); + + // event loop + while (!app_close) { + conn_poll_accept(&workers); + conn_poll_accept(&http); + conn_poll_clients(&http, &http_client_handle); + conn_collect_bad_client(&workers); + conn_collect_bad_client(&http); + + // printf("iter\n"); + // usleep(500000); + } + return 0; +} + +void mtsh_listen(struct Meshtalos *mtsh, const int worker_port, int http_port) { + main_listen(worker_port, http_port); +} + struct Meshtalos *mtsh_init() { - struct Meshtalos *mtsh = calloc(1, sizeof(struct Meshtalos)); - if (mtsh == NULL) { - return NULL; - } - - // if ((mtsh->storage = storage_init()) == NULL) { - // goto lfs_err; - // } - mg_mgr_init(&mtsh->mg); - - return mtsh; - // lfs_err: - // if (mtsh != NULL) { - // free(mtsh); - // } - // return NULL; -} - -void mtsh_listen(struct Meshtalos *mtsh, const char *http_endpoint) { - mg_http_listen(&mtsh->mg, http_endpoint, ev_handler, &mtsh->storage); - mtsh_tcp_sender(&mtsh->mg); - for (;;) { - mg_mgr_poll(&mtsh->mg, 1000); - } + struct Meshtalos *m = calloc(sizeof(struct Meshtalos), 1); + return m; } diff --git a/main/lib/meshtalos.h b/main/lib/meshtalos.h index 5487f1a..70e5bff 100644 --- a/main/lib/meshtalos.h +++ b/main/lib/meshtalos.h @@ -8,32 +8,29 @@ * mpack formatted: [ uint8_t id = 1, char topic[TOPIC_LEN] ] */ #pragma once -#include "./mongoose.h" #include "./storage.h" #define TOPIC_LEN 20 -#define IMAGE_SIZE 152 * 296 -#define IMAGE_BLK_SIZE 1024 +#define IMG_W_SIZE 152 +#define IMG_H_SIZE 296 +#define DISPLAY_SIZE IMG_W_SIZE *IMG_H_SIZE +#define IMG_HEADER_SIZE 15 +#define IMG_SIZE IMG_HEADER_SIZE + DISPLAY_SIZE +#define IMG_BLK_SIZE 300 struct Meshtalos { struct Storage *storage; - struct mg_mgr mg; }; -struct sync_jobs { - // char *tag_name; - char topic[TOPIC_LEN]; -}; -struct sub { - struct mg_connection *c; - char topic[TOPIC_LEN]; - struct sub *next; -}; -extern struct sub *sub_list; -extern QueueHandle_t sub_jobs; -extern SemaphoreHandle_t sub_mutex; -extern uint8_t image_buf[IMAGE_SIZE]; +// struct sync_jobs { +// // char *tag_name; +// char topic[TOPIC_LEN]; +// }; +// struct sub { +// char topic[TOPIC_LEN]; +// struct sub *next; +// }; +// extern struct sub *sub_list; struct Meshtalos *mtsh_init(); -void mtsh_listen(struct Meshtalos *mtsh, const char *http_endpoint); -void mtsh_tcp_sender(struct mg_mgr *mgr); +void mtsh_listen(struct Meshtalos *mtsh, const int worker_port, int http_port); diff --git a/main/lib/mongoose.c b/main/lib/mongoose.c deleted file mode 100644 index 972ba77..0000000 --- a/main/lib/mongoose.c +++ /dev/null @@ -1,25608 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2025 Cesanta Software Limited -// All rights reserved -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see http://www.gnu.org/licenses/ -// -// You are free to use this software under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this software under a commercial -// license, as set out in https://www.mongoose.ws/licensing/ -// -// SPDX-License-Identifier: GPL-2.0-only or commercial - -#include "mongoose.h" - -#ifdef MG_ENABLE_LINES -#line 1 "src/base64.c" -#endif - - -static int mg_base64_encode_single(int c) { - if (c < 26) { - return c + 'A'; - } else if (c < 52) { - return c - 26 + 'a'; - } else if (c < 62) { - return c - 52 + '0'; - } else { - return c == 62 ? '+' : '/'; - } -} - -static int mg_base64_decode_single(int c) { - if (c >= 'A' && c <= 'Z') { - return c - 'A'; - } else if (c >= 'a' && c <= 'z') { - return c + 26 - 'a'; - } else if (c >= '0' && c <= '9') { - return c + 52 - '0'; - } else if (c == '+') { - return 62; - } else if (c == '/') { - return 63; - } else if (c == '=') { - return 64; - } else { - return -1; - } -} - -size_t mg_base64_update(unsigned char ch, char *to, size_t n) { - unsigned long rem = (n & 3) % 3; - if (rem == 0) { - to[n] = (char) mg_base64_encode_single(ch >> 2); - to[++n] = (char) ((ch & 3) << 4); - } else if (rem == 1) { - to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 4)); - to[++n] = (char) ((ch & 15) << 2); - } else { - to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 6)); - to[++n] = (char) mg_base64_encode_single(ch & 63); - n++; - } - return n; -} - -size_t mg_base64_final(char *to, size_t n) { - size_t saved = n; - // printf("---[%.*s]\n", n, to); - if (n & 3) n = mg_base64_update(0, to, n); - if ((saved & 3) == 2) n--; - // printf(" %d[%.*s]\n", n, n, to); - while (n & 3) to[n++] = '='; - to[n] = '\0'; - return n; -} - -size_t mg_base64_encode(const unsigned char *p, size_t n, char *to, size_t dl) { - size_t i, len = 0; - if (dl > 0) to[0] = '\0'; - if (dl < ((n / 3) + (n % 3 ? 1 : 0)) * 4 + 1) return 0; - for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len); - len = mg_base64_final(to, len); - return len; -} - -size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) { - const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL - size_t len = 0; - if (dl < n / 4 * 3 + 1) goto fail; - while (src != NULL && src + 3 < end) { - int a = mg_base64_decode_single(src[0]), - b = mg_base64_decode_single(src[1]), - c = mg_base64_decode_single(src[2]), - d = mg_base64_decode_single(src[3]); - if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) { - goto fail; - } - dst[len++] = (char) ((a << 2) | (b >> 4)); - if (src[2] != '=') { - dst[len++] = (char) ((b << 4) | (c >> 2)); - if (src[3] != '=') dst[len++] = (char) ((c << 6) | d); - } - src += 4; - } - dst[len] = '\0'; - return len; -fail: - if (dl > 0) dst[0] = '\0'; - return 0; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/dns.c" -#endif - - - - - - - - -struct dns_data { - struct dns_data *next; - struct mg_connection *c; - uint64_t expire; - uint16_t txnid; -}; - -static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int, - struct mg_dns *, bool); - -static void mg_dns_free(struct dns_data **head, struct dns_data *d) { - LIST_DELETE(struct dns_data, head, d); - mg_free(d); -} - -void mg_resolve_cancel(struct mg_connection *c) { - struct dns_data *tmp, *d; - struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests; - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - if (d->c == c) mg_dns_free(head, d); - } -} - -static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs, - char *to, size_t tolen, size_t j, - int depth) { - size_t i = 0; - if (tolen > 0 && depth == 0) to[0] = '\0'; - if (depth > 5) return 0; - // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1])); - while (ofs + i + 1 < len) { - size_t n = s[ofs + i]; - if (n == 0) { - i++; - break; - } - if (n & 0xc0) { - size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len - // MG_INFO(("PTR %lx", (unsigned long) ptr)); - if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 && - mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0) - return 0; - i += 2; - break; - } - if (ofs + i + n + 1 >= len) return 0; - if (j > 0) { - if (j < tolen) to[j] = '.'; - j++; - } - if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n); - j += n; - i += n + 1; - if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk - // MG_INFO(("--> [%s]", to)); - } - if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term - return i; -} - -static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs, - char *dst, size_t dstlen) { - return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0); -} - -size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, - bool is_question, struct mg_dns_rr *rr) { - const uint8_t *s = buf + ofs, *e = &buf[len]; - - memset(rr, 0, sizeof(*rr)); - if (len < sizeof(struct mg_dns_header)) return 0; // Too small - if (len > 512) return 0; // Too large, we don't expect that - if (s >= e) return 0; // Overflow - - if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0) - return 0; - s += rr->nlen + 4; - if (s > e) return 0; - rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]); - rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]); - if (is_question) return (size_t) (rr->nlen + 4); - - s += 6; - if (s > e) return 0; - rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]); - if (s + rr->alen > e) return 0; - return (size_t) (rr->nlen + rr->alen + 10); -} - -bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) { - const struct mg_dns_header *h = (struct mg_dns_header *) buf; - struct mg_dns_rr rr; - size_t i, n, num_answers, ofs = sizeof(*h); - bool is_response; - memset(dm, 0, sizeof(*dm)); - - if (len < sizeof(*h)) return 0; // Too small, headers dont fit - if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity - num_answers = mg_ntohs(h->num_answers); - if (num_answers > 10) { - MG_DEBUG(("Got %u answers, ignoring beyond 10th one", num_answers)); - num_answers = 10; // Sanity cap - } - dm->txnid = mg_ntohs(h->txnid); - is_response = mg_ntohs(h->flags) & 0x8000; - - for (i = 0; i < mg_ntohs(h->num_questions); i++) { - if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false; - // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass)); - mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name)); - ofs += n; - } - - if (!is_response) { - // For queries, there is no need to parse the answers. In this way, - // we also ensure the domain name (dm->name) is parsed from - // the question field. - return true; - } - - for (i = 0; i < num_answers; i++) { - if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false; - // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass, - // dm->name)); - mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name)); - ofs += n; - - if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) { - dm->addr.is_ip6 = false; - memcpy(&dm->addr.ip, &buf[ofs - 4], 4); - dm->resolved = true; - break; // Return success - } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) { - dm->addr.is_ip6 = true; - memcpy(&dm->addr.ip, &buf[ofs - 16], 16); - dm->resolved = true; - break; // Return success - } - } - return true; -} - -static void dns_cb(struct mg_connection *c, int ev, void *ev_data) { - struct dns_data *d, *tmp; - struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests; - if (ev == MG_EV_POLL) { - uint64_t now = *(uint64_t *) ev_data; - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - // MG_DEBUG ("%lu %lu dns poll", d->expire, now)); - if (now > d->expire) mg_error(d->c, "DNS timeout"); - } - } else if (ev == MG_EV_READ) { - struct mg_dns_message dm; - int resolved = 0; - if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) { - MG_ERROR(("Unexpected DNS response:")); - mg_hexdump(c->recv.buf, c->recv.len); - } else { - // MG_VERBOSE(("%s %d", dm.name, dm.resolved)); - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid)); - if (dm.txnid != d->txnid) continue; - if (d->c->is_resolving) { - if (dm.resolved) { - dm.addr.port = d->c->rem.port; // Save port - d->c->rem = dm.addr; // Copy resolved address - MG_DEBUG( - ("%lu %s is %M", d->c->id, dm.name, mg_print_ip, &d->c->rem)); - mg_connect_resolved(d->c); -#if MG_ENABLE_IPV6 - } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' && - c->mgr->use_dns6 == false) { - struct mg_str x = mg_str(dm.name); - mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true); -#endif - } else { - mg_error(d->c, "%s DNS lookup failed", dm.name); - } - } else { - MG_ERROR(("%lu already resolved", d->c->id)); - } - mg_dns_free(head, d); - resolved = 1; - } - } - if (!resolved) MG_ERROR(("stray DNS reply")); - c->recv.len = 0; - } else if (ev == MG_EV_CLOSE) { - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - mg_error(d->c, "DNS error"); - mg_dns_free(head, d); - } - } -} - -static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name, - uint16_t txnid, bool ipv6) { - struct { - struct mg_dns_header header; - uint8_t data[256]; - } pkt; - size_t i, n; - memset(&pkt, 0, sizeof(pkt)); - pkt.header.txnid = mg_htons(txnid); - pkt.header.flags = mg_htons(0x100); - pkt.header.num_questions = mg_htons(1); - for (i = n = 0; i < sizeof(pkt.data) - 5; i++) { - if (name->buf[i] == '.' || i >= name->len) { - pkt.data[n] = (uint8_t) (i - n); - memcpy(&pkt.data[n + 1], name->buf + n, i - n); - n = i + 1; - } - if (i >= name->len) break; - } - memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query - n += 5; - if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query - // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query - // n += 6; - return mg_send(c, &pkt, sizeof(pkt.header) + n); -} - -bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc); -bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) { - if (dnsc->url == NULL) { - mg_error(0, "DNS server URL is NULL. Call mg_mgr_init()"); - return false; - } - if (dnsc->c == NULL) { - dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL); - if (dnsc->c == NULL) return false; - dnsc->c->pfn = dns_cb; - } - return true; -} - -static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms, - struct mg_dns *dnsc, bool ipv6) { - struct dns_data *d = NULL; - if (!mg_dnsc_init(c->mgr, dnsc)) { - mg_error(c, "resolver"); - } else if ((d = (struct dns_data *) mg_calloc(1, sizeof(*d))) == NULL) { - mg_error(c, "resolve OOM"); - } else { - struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests; - d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1; - d->next = (struct dns_data *) c->mgr->active_dns_requests; - c->mgr->active_dns_requests = d; - d->expire = mg_millis() + (uint64_t) ms; - d->c = c; - c->is_resolving = 1; - MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu", c->id, (int) name->len, - name->buf, dnsc->url, d->txnid)); - if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) { - mg_error(dnsc->c, "DNS send"); - } - } -} - -void mg_resolve(struct mg_connection *c, const char *url) { - struct mg_str host = mg_url_host(url); - c->rem.port = mg_htons(mg_url_port(url)); - if (mg_aton(host, &c->rem)) { - // host is an IP address, do not fire name resolution - mg_connect_resolved(c); - } else { - // host is not an IP, send DNS resolution request - struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4; - mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6); - } -} - -static const uint8_t mdns_answer[] = { - 0, 1, // 2 bytes - record type, A - 0, 1, // 2 bytes - address class, INET - 0, 0, 0, 120, // 4 bytes - TTL - 0, 4 // 2 bytes - address length -}; - -static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ) { - struct mg_dns_header *qh = (struct mg_dns_header *) c->recv.buf; - if (c->recv.len > 12 && (qh->flags & mg_htons(0xF800)) == 0) { - // flags -> !resp, opcode=0 => query; ignore other opcodes and responses - struct mg_dns_rr rr; // Parse first question, offset 12 is header size - size_t n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr); - MG_VERBOSE(("mDNS request parsed, result=%d", (int) n)); - if (n > 0) { - // RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0 - // buf and h declared here to ease future expansion to DNS-SD - uint8_t buf[sizeof(struct mg_dns_header) + 256 + sizeof(mdns_answer) + 4]; - struct mg_dns_header *h = (struct mg_dns_header *) buf; - char local_name[63 + 7]; // name label + '.' + local label + '\0' - uint8_t name_len = (uint8_t) strlen((char *)c->fn_data); - struct mg_dns_message dm; - bool unicast = (rr.aclass & MG_BIT(15)) != 0; // QU - // uint16_t q = mg_ntohs(qh->num_questions); - rr.aclass &= (uint16_t) ~MG_BIT(15); // remove "QU" (unicast response) - qh->num_questions = mg_htons(1); // parser sanity - mg_dns_parse(c->recv.buf, c->recv.len, &dm); - if (name_len > (sizeof(local_name) - 7)) // leave room for .local\0 - name_len = sizeof(local_name) - 7; - memcpy(local_name, c->fn_data, name_len); - strcpy(local_name + name_len, ".local"); // ensure proper name.local\0 - if (strcmp(local_name, dm.name) == 0) { - uint8_t *p = &buf[sizeof(*h)]; - memset(h, 0, sizeof(*h)); // clear header - h->txnid = unicast ? qh->txnid : 0; // RFC-6762 18.1 - // RFC-6762 6: 0 questions, 1 Answer, 0 Auth, 0 Additional RRs - h->num_answers = mg_htons(1); // only one answer - h->flags = mg_htons(0x8400); // Authoritative response - *p++ = name_len; // label 1 - memcpy(p, c->fn_data, name_len), p += name_len; - *p++ = 5; // label 2 - memcpy(p, "local", 5), p += 5; - *p++ = 0; // no more labels - memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer); -#if MG_ENABLE_TCPIP - memcpy(p, &c->mgr->ifp->ip, 4), p += 4; -#else - memcpy(p, c->data, 4), p += 4; -#endif - if (!unicast) memcpy(&c->rem, &c->loc, sizeof(c->rem)); - mg_send(c, buf, (size_t)(p - buf)); // And send it! - MG_DEBUG(("mDNS %c response sent", unicast ? 'U' : 'M')); - } - } - } - mg_iobuf_del(&c->recv, 0, c->recv.len); - } - (void) ev_data; -} - -void mg_multicast_add(struct mg_connection *c, char *ip); -struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name) { - struct mg_connection *c = - mg_listen(mgr, "udp://224.0.0.251:5353", mdns_cb, name); - if (c != NULL) mg_multicast_add(c, (char *)"224.0.0.251"); - return c; -} - - -#ifdef MG_ENABLE_LINES -#line 1 "src/event.c" -#endif - - - - - - -void mg_call(struct mg_connection *c, int ev, void *ev_data) { -#if MG_ENABLE_PROFILE - const char *names[] = { - "EV_ERROR", "EV_OPEN", "EV_POLL", "EV_RESOLVE", - "EV_CONNECT", "EV_ACCEPT", "EV_TLS_HS", "EV_READ", - "EV_WRITE", "EV_CLOSE", "EV_HTTP_MSG", "EV_HTTP_CHUNK", - "EV_WS_OPEN", "EV_WS_MSG", "EV_WS_CTL", "EV_MQTT_CMD", - "EV_MQTT_MSG", "EV_MQTT_OPEN", "EV_SNTP_TIME", "EV_USER"}; - if (ev != MG_EV_POLL && ev < (int) (sizeof(names) / sizeof(names[0]))) { - MG_PROF_ADD(c, names[ev]); - } -#endif - // Fire protocol handler first, user handler second. See #2559 - if (c->pfn != NULL) c->pfn(c, ev, ev_data); - if (c->fn != NULL) c->fn(c, ev, ev_data); -} - -void mg_error(struct mg_connection *c, const char *fmt, ...) { - char buf[64]; - va_list ap; - va_start(ap, fmt); - mg_vsnprintf(buf, sizeof(buf), fmt, &ap); - va_end(ap); - MG_ERROR(("%lu %ld %s", c->id, c->fd, buf)); - c->is_closing = 1; // Set is_closing before sending MG_EV_CALL - mg_call(c, MG_EV_ERROR, buf); // Let user handler override it -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/flash.c" -#endif - - - - - -#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM - -static char *s_addr; // Current address to write to -static size_t s_size; // Firmware size to flash. In-progress indicator -static uint32_t s_crc32; // Firmware checksum - -bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash) { - bool ok = false; - if (s_size) { - MG_ERROR(("OTA already in progress. Call mg_ota_end()")); - } else { - size_t half = flash->size / 2; - s_crc32 = 0; - s_addr = (char *) flash->start + half; - MG_DEBUG(("FW %lu bytes, max %lu", new_firmware_size, half)); - if (new_firmware_size < half) { - ok = true; - s_size = new_firmware_size; - MG_INFO(("Starting OTA, firmware size %lu", s_size)); - } else { - MG_ERROR(("Firmware %lu is too big to fit %lu", new_firmware_size, half)); - } - } - return ok; -} - -bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash) { - bool ok = false; - if (s_size == 0) { - MG_ERROR(("OTA is not started, call mg_ota_begin()")); - } else { - size_t len_aligned_down = MG_ROUND_DOWN(len, flash->align); - if (len_aligned_down) ok = flash->write_fn(s_addr, buf, len_aligned_down); - if (len_aligned_down < len) { - size_t left = len - len_aligned_down; - char tmp[flash->align]; - memset(tmp, 0xff, sizeof(tmp)); - memcpy(tmp, (char *) buf + len_aligned_down, left); - ok = flash->write_fn(s_addr + len_aligned_down, tmp, sizeof(tmp)); - } - s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC - MG_DEBUG(("%#x %p %lu -> %d", s_addr - len, buf, len, ok)); - s_addr += len; - } - return ok; -} - -bool mg_ota_flash_end(struct mg_flash *flash) { - char *base = (char *) flash->start + flash->size / 2; - bool ok = false; - if (s_size) { - size_t size = (size_t) (s_addr - base); - uint32_t crc32 = mg_crc32(0, base, s_size); - if (size == s_size && crc32 == s_crc32) ok = true; - MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, - size, ok ? "ok" : "fail")); - s_size = 0; - if (ok) ok = flash->swap_fn(); - } - MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); - return ok; -} - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/fmt.c" -#endif - - - - -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static int addexp(char *buf, int e, int sign) { - int n = 0; - buf[n++] = 'e'; - buf[n++] = (char) sign; - if (e > 400) return 0; - if (e < 10) buf[n++] = '0'; - if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100); - if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10); - buf[n++] = (char) (e + '0'); - return n; -} - -static int xisinf(double x) { - union { - double f; - uint64_t u; - } ieee754 = {x}; - return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && - ((unsigned) ieee754.u == 0); -} - -static int xisnan(double x) { - union { - double f; - uint64_t u; - } ieee754 = {x}; - return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) + - ((unsigned) ieee754.u != 0) > - 0x7ff00000; -} - -static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { - char buf[40]; - int i, s = 0, n = 0, e = 0; - double t, mul, saved; - if (d == 0.0) return mg_snprintf(dst, dstlen, "%s", "0"); - if (xisinf(d)) return mg_snprintf(dst, dstlen, "%s", d > 0 ? "inf" : "-inf"); - if (xisnan(d)) return mg_snprintf(dst, dstlen, "%s", "nan"); - if (d < 0.0) d = -d, buf[s++] = '-'; - - // Round - saved = d; - if (tz) { - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0; - } else { - mul = 0.1; - } - - while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0; - for (i = 0, t = mul * 5; i < width; i++) t /= 10.0; - - d += t; - - // Calculate exponent, and 'mul' for scientific representation - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++; - while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--; - // printf(" --> %g %d %g %g\n", saved, e, t, mul); - - if (tz && e >= width && width > 1) { - n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); - // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); - n += addexp(buf + s + n, e, '+'); - return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else if (tz && e <= -width && width > 1) { - n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); - // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); - n += addexp(buf + s + n, -e, '-'); - return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else { - int targ_width = width; - for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) { - int ch = (int) (d / t); - if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0'); - d -= ch * t; - t /= 10.0; - } - // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf); - if (n == 0) buf[s++] = '0'; - while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0; - if (s + n < (int) sizeof(buf)) buf[n + s++] = '.'; - // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf); - if (!tz && n > 0) targ_width = width + n; - for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < targ_width; i++) { - int ch = (int) (d / t); - buf[s + n++] = (char) (ch + '0'); - d -= ch * t; - t /= 10.0; - } - } - - while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes - if (tz && n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot - n += s; - if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1; - buf[n] = '\0'; - return mg_snprintf(dst, dstlen, "%s", buf); -} - -static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) { - const char *letters = "0123456789abcdef"; - uint64_t v = (uint64_t) val; - size_t s = 0, n, i; - if (is_signed && val < 0) buf[s++] = '-', v = (uint64_t) (-val); - // This loop prints a number in reverse order. I guess this is because we - // write numbers from right to left: least significant digit comes last. - // Maybe because we use Arabic numbers, and Arabs write RTL? - if (is_hex) { - for (n = 0; v; v >>= 4) buf[s + n++] = letters[v & 15]; - } else { - for (n = 0; v; v /= 10) buf[s + n++] = letters[v % 10]; - } - // Reverse a string - for (i = 0; i < n / 2; i++) { - char t = buf[s + i]; - buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t; - } - if (val == 0) buf[n++] = '0'; // Handle special case - return n + s; -} - -static size_t scpy(void (*out)(char, void *), void *ptr, char *buf, - size_t len) { - size_t i = 0; - while (i < len && buf[i] != '\0') out(buf[i++], ptr); - return i; -} - -size_t mg_xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) { - size_t len = 0; - va_list ap; - va_start(ap, fmt); - len = mg_vxprintf(out, ptr, fmt, &ap); - va_end(ap); - return len; -} - -size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt, - va_list *ap) { - size_t i = 0, n = 0; - while (fmt[i] != '\0') { - if (fmt[i] == '%') { - size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */; - char pad = ' ', minus = 0, c = fmt[++i]; - if (c == '#') x++, c = fmt[++i]; - if (c == '-') minus++, c = fmt[++i]; - if (c == '0') pad = '0', c = fmt[++i]; - while (is_digit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i]; - if (c == '.') { - c = fmt[++i]; - if (c == '*') { - pr = (size_t) va_arg(*ap, int); - c = fmt[++i]; - } else { - pr = 0; - while (is_digit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i]; - } - } - while (c == 'h') c = fmt[++i]; // Treat h and hh as int - if (c == 'l') { - is_long++, c = fmt[++i]; - if (c == 'l') is_long++, c = fmt[++i]; - } - if (c == 'p') x = 1, is_long = 1; - if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' || - c == 'g' || c == 'f') { - bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p'); - char tmp[40]; - size_t xl = x ? 2 : 0; - if (c == 'g' || c == 'f') { - double v = va_arg(*ap, double); - if (pr == ~0U) pr = 6; - k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr, c == 'g'); - } else if (is_long == 2) { - int64_t v = va_arg(*ap, int64_t); - k = mg_lld(tmp, v, s, h); - } else if (is_long == 1) { - long v = va_arg(*ap, long); - k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned long) v, s, h); - } else { - int v = va_arg(*ap, int); - k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h); - } - for (j = 0; j < xl && w > 0; j++) w--; - for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, (char *) "0x", xl); - for (j = 0; pad == '0' && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, tmp, k); - for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - } else if (c == 'm' || c == 'M') { - mg_pm_t f = va_arg(*ap, mg_pm_t); - if (c == 'm') out('"', param); - n += f(out, param, ap); - if (c == 'm') n += 2, out('"', param); - } else if (c == 'c') { - int ch = va_arg(*ap, int); - out((char) ch, param); - n++; - } else if (c == 's') { - char *p = va_arg(*ap, char *); - if (pr == ~0U) pr = p == NULL ? 0 : strlen(p); - for (j = 0; !minus && pr < w && j + pr < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, p, pr); - for (j = 0; minus && pr < w && j + pr < w; j++) - n += scpy(out, param, &pad, 1); - } else if (c == '%') { - out('%', param); - n++; - } else { - out('%', param); - out(c, param); - n += 2; - } - i++; - } else { - out(fmt[i], param), n++, i++; - } - } - return n; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs.c" -#endif - - - - - -struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) { - struct mg_fd *fd = (struct mg_fd *) mg_calloc(1, sizeof(*fd)); - if (fd != NULL) { - fd->fd = fs->op(path, flags); - fd->fs = fs; - if (fd->fd == NULL) { - mg_free(fd); - fd = NULL; - } - } - return fd; -} - -void mg_fs_close(struct mg_fd *fd) { - if (fd != NULL) { - fd->fs->cl(fd->fd); - mg_free(fd); - } -} - -struct mg_str mg_file_read(struct mg_fs *fs, const char *path) { - struct mg_str result = {NULL, 0}; - void *fp; - fs->st(path, &result.len, NULL); - if ((fp = fs->op(path, MG_FS_READ)) != NULL) { - result.buf = (char *) mg_calloc(1, result.len + 1); - if (result.buf != NULL && - fs->rd(fp, (void *) result.buf, result.len) != result.len) { - mg_free((void *) result.buf); - result.buf = NULL; - } - fs->cl(fp); - } - if (result.buf == NULL) result.len = 0; - return result; -} - -bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, - size_t len) { - bool result = false; - struct mg_fd *fd; - char tmp[MG_PATH_MAX]; - mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand()); - if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) { - result = fs->wr(fd->fd, buf, len) == len; - mg_fs_close(fd); - if (result) { - fs->rm(path); - fs->mv(tmp, path); - } else { - fs->rm(tmp); - } - } - return result; -} - -bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) { - va_list ap; - char *data; - bool result = false; - va_start(ap, fmt); - data = mg_vmprintf(fmt, &ap); - va_end(ap); - result = mg_file_write(fs, path, data, strlen(data)); - mg_free(data); - return result; -} - -// This helper function allows to scan a filesystem in a sequential way, -// without using callback function: -// char buf[100] = ""; -// while (mg_fs_ls(&mg_fs_posix, "./", buf, sizeof(buf))) { -// ... -static void mg_fs_ls_fn(const char *filename, void *param) { - struct mg_str *s = (struct mg_str *) param; - if (s->buf[0] == '\0') { - mg_snprintf((char *) s->buf, s->len, "%s", filename); - } else if (strcmp(s->buf, filename) == 0) { - ((char *) s->buf)[0] = '\0'; // Fetch next file - } -} - -bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len) { - struct mg_str s = {buf, len}; - fs->ls(path, mg_fs_ls_fn, &s); - return buf[0] != '\0'; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_fat.c" -#endif - - - -#if MG_ENABLE_FATFS -#include - -static int mg_days_from_epoch(int y, int m, int d) { - y -= m <= 2; - int era = y / 400; - int yoe = y - era * 400; - int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; - int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - return era * 146097 + doe - 719468; -} - -static time_t mg_timegm(const struct tm *t) { - int year = t->tm_year + 1900; - int month = t->tm_mon; // 0-11 - if (month > 11) { - year += month / 12; - month %= 12; - } else if (month < 0) { - int years_diff = (11 - month) / 12; - year -= years_diff; - month += 12 * years_diff; - } - int x = mg_days_from_epoch(year, month + 1, t->tm_mday); - return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec; -} - -static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) { - struct tm tm; - memset(&tm, 0, sizeof(struct tm)); - tm.tm_sec = (ftime << 1) & 0x3e; - tm.tm_min = ((ftime >> 5) & 0x3f); - tm.tm_hour = ((ftime >> 11) & 0x1f); - tm.tm_mday = (fdate & 0x1f); - tm.tm_mon = ((fdate >> 5) & 0x0f) - 1; - tm.tm_year = ((fdate >> 9) & 0x7f) + 80; - return mg_timegm(&tm); -} - -static int ff_stat(const char *path, size_t *size, time_t *mtime) { - FILINFO fi; - if (path[0] == '\0') { - if (size) *size = 0; - if (mtime) *mtime = 0; - return MG_FS_DIR; - } else if (f_stat(path, &fi) == 0) { - if (size) *size = (size_t) fi.fsize; - if (mtime) *mtime = ff_time_to_epoch(fi.fdate, fi.ftime); - return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0); - } else { - return 0; - } -} - -static void ff_list(const char *dir, void (*fn)(const char *, void *), - void *userdata) { - DIR d; - FILINFO fi; - if (f_opendir(&d, dir) == FR_OK) { - while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') { - if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue; - fn(fi.fname, userdata); - } - f_closedir(&d); - } -} - -static void *ff_open(const char *path, int flags) { - FIL f; - unsigned char mode = FA_READ; - if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND; - if (f_open(&f, path, mode) == 0) { - FIL *fp; - if ((fp = mg_calloc(1, sizeof(*fp))) != NULL) { - memcpy(fp, &f, sizeof(*fp)); - return fp; - } - } - return NULL; -} - -static void ff_close(void *fp) { - if (fp != NULL) { - f_close((FIL *) fp); - mg_free(fp); - } -} - -static size_t ff_read(void *fp, void *buf, size_t len) { - UINT n = 0, misalign = ((size_t) buf) & 3; - if (misalign) { - char aligned[4]; - f_read((FIL *) fp, aligned, len > misalign ? misalign : len, &n); - memcpy(buf, aligned, n); - } else { - f_read((FIL *) fp, buf, len, &n); - } - return n; -} - -static size_t ff_write(void *fp, const void *buf, size_t len) { - UINT n = 0; - return f_write((FIL *) fp, (char *) buf, len, &n) == FR_OK ? n : 0; -} - -static size_t ff_seek(void *fp, size_t offset) { - f_lseek((FIL *) fp, offset); - return offset; -} - -static bool ff_rename(const char *from, const char *to) { - return f_rename(from, to) == FR_OK; -} - -static bool ff_remove(const char *path) { - return f_unlink(path) == FR_OK; -} - -static bool ff_mkdir(const char *path) { - return f_mkdir(path) == FR_OK; -} - -struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read, - ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_packed.c" -#endif - - - - - -struct packed_file { - const char *data; - size_t size; - size_t pos; -}; - -#if MG_ENABLE_PACKED_FS -#else -const char *mg_unpack(const char *path, size_t *size, time_t *mtime) { - if (size != NULL) *size = 0; - if (mtime != NULL) *mtime = 0; - (void) path; - return NULL; -} -const char *mg_unlist(size_t no) { - (void) no; - return NULL; -} -#endif - -struct mg_str mg_unpacked(const char *path) { - size_t len = 0; - const char *buf = mg_unpack(path, &len, NULL); - return mg_str_n(buf, len); -} - -static int is_dir_prefix(const char *prefix, size_t n, const char *path) { - // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n])); - return n < strlen(path) && strncmp(prefix, path, n) == 0 && - (n == 0 || path[n] == '/' || path[n - 1] == '/'); -} - -static int packed_stat(const char *path, size_t *size, time_t *mtime) { - const char *p; - size_t i, n = strlen(path); - if (mg_unpack(path, size, mtime)) return MG_FS_READ; // Regular file - // Scan all files. If `path` is a dir prefix for any of them, it's a dir - for (i = 0; (p = mg_unlist(i)) != NULL; i++) { - if (is_dir_prefix(path, n, p)) return MG_FS_DIR; - } - return 0; -} - -static void packed_list(const char *dir, void (*fn)(const char *, void *), - void *userdata) { - char buf[MG_PATH_MAX], tmp[sizeof(buf)]; - const char *path, *begin, *end; - size_t i, n = strlen(dir); - tmp[0] = '\0'; // Previously listed entry - for (i = 0; (path = mg_unlist(i)) != NULL; i++) { - if (!is_dir_prefix(dir, n, path)) continue; - begin = &path[n + 1]; - end = strchr(begin, '/'); - if (end == NULL) end = begin + strlen(begin); - mg_snprintf(buf, sizeof(buf), "%.*s", (int) (end - begin), begin); - buf[sizeof(buf) - 1] = '\0'; - // If this entry has been already listed, skip - // NOTE: we're assuming that file list is sorted alphabetically - if (strcmp(buf, tmp) == 0) continue; - fn(buf, userdata); // Not yet listed, call user function - strcpy(tmp, buf); // And save this entry as listed - } -} - -static void *packed_open(const char *path, int flags) { - size_t size = 0; - const char *data = mg_unpack(path, &size, NULL); - struct packed_file *fp = NULL; - if (data == NULL) return NULL; - if (flags & MG_FS_WRITE) return NULL; - if ((fp = (struct packed_file *) mg_calloc(1, sizeof(*fp))) != NULL) { - fp->size = size; - fp->data = data; - } - return (void *) fp; -} - -static void packed_close(void *fp) { - if (fp != NULL) mg_free(fp); -} - -static size_t packed_read(void *fd, void *buf, size_t len) { - struct packed_file *fp = (struct packed_file *) fd; - if (fp->pos + len > fp->size) len = fp->size - fp->pos; - memcpy(buf, &fp->data[fp->pos], len); - fp->pos += len; - return len; -} - -static size_t packed_write(void *fd, const void *buf, size_t len) { - (void) fd, (void) buf, (void) len; - return 0; -} - -static size_t packed_seek(void *fd, size_t offset) { - struct packed_file *fp = (struct packed_file *) fd; - fp->pos = offset; - if (fp->pos > fp->size) fp->pos = fp->size; - return fp->pos; -} - -static bool packed_rename(const char *from, const char *to) { - (void) from, (void) to; - return false; -} - -static bool packed_remove(const char *path) { - (void) path; - return false; -} - -static bool packed_mkdir(const char *path) { - (void) path; - return false; -} - -struct mg_fs mg_fs_packed = { - packed_stat, packed_list, packed_open, packed_close, packed_read, - packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir}; - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_posix.c" -#endif - - -#if MG_ENABLE_POSIX_FS - -#ifndef MG_STAT_STRUCT -#define MG_STAT_STRUCT stat -#endif - -#ifndef MG_STAT_FUNC -#define MG_STAT_FUNC stat -#endif - -static int p_stat(const char *path, size_t *size, time_t *mtime) { -#if !defined(S_ISDIR) - MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime)); - return 0; -#else -#if MG_ARCH == MG_ARCH_WIN32 - struct _stati64 st; - wchar_t tmp[MG_PATH_MAX]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0])); - if (_wstati64(tmp, &st) != 0) return 0; - // If path is a symlink, windows reports 0 in st.st_size. - // Get a real file size by opening it and jumping to the end - if (st.st_size == 0 && (st.st_mode & _S_IFREG)) { - FILE *fp = _wfopen(tmp, L"rb"); - if (fp != NULL) { - fseek(fp, 0, SEEK_END); - if (ftell(fp) > 0) st.st_size = ftell(fp); // Use _ftelli64 on win10+ - fclose(fp); - } - } -#else - struct MG_STAT_STRUCT st; - if (MG_STAT_FUNC(path, &st) != 0) return 0; -#endif - if (size) *size = (size_t) st.st_size; - if (mtime) *mtime = st.st_mtime; - return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0); -#endif -} - -#if MG_ARCH == MG_ARCH_WIN32 -struct dirent { - char d_name[MAX_PATH]; -}; - -typedef struct win32_dir { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; - -#if 0 -int gettimeofday(struct timeval *tv, void *tz) { - FILETIME ft; - unsigned __int64 tmpres = 0; - - if (tv != NULL) { - GetSystemTimeAsFileTime(&ft); - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - tmpres /= 10; // convert into microseconds - tmpres -= (int64_t) 11644473600000000; - tv->tv_sec = (long) (tmpres / 1000000UL); - tv->tv_usec = (long) (tmpres % 1000000UL); - } - (void) tz; - return 0; -} -#endif - -static int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { - int ret; - char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p; - strncpy(buf, path, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - // Trim trailing slashes. Leave backslash for paths like "X:\" - p = buf + strlen(buf) - 1; - while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; - memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); - ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); - // Convert back to Unicode. If doubly-converted string does not match the - // original, something is fishy, reject. - WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), - NULL, NULL); - if (strcmp(buf, buf2) != 0) { - wbuf[0] = L'\0'; - ret = 0; - } - return ret; -} - -DIR *opendir(const char *name) { - DIR *d = NULL; - wchar_t wpath[MAX_PATH]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((d = (DIR *) mg_calloc(1, sizeof(*d))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0])); - attrs = GetFileAttributesW(wpath); - if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { - (void) wcscat(wpath, L"\\*"); - d->handle = FindFirstFileW(wpath, &d->info); - d->result.d_name[0] = '\0'; - } else { - mg_free(d); - d = NULL; - } - } - return d; -} - -int closedir(DIR *d) { - int result = 0; - if (d != NULL) { - if (d->handle != INVALID_HANDLE_VALUE) - result = FindClose(d->handle) ? 0 : -1; - mg_free(d); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } - return result; -} - -struct dirent *readdir(DIR *d) { - struct dirent *result = NULL; - if (d != NULL) { - memset(&d->result, 0, sizeof(d->result)); - if (d->handle != INVALID_HANDLE_VALUE) { - result = &d->result; - WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name, - sizeof(result->d_name), NULL, NULL); - if (!FindNextFileW(d->handle, &d->info)) { - FindClose(d->handle); - d->handle = INVALID_HANDLE_VALUE; - } - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } - return result; -} -#endif - -static void p_list(const char *dir, void (*fn)(const char *, void *), - void *userdata) { -#if MG_ENABLE_DIRLIST - struct dirent *dp; - DIR *dirp; - if ((dirp = (opendir(dir))) == NULL) return; - while ((dp = readdir(dirp)) != NULL) { - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; - fn(dp->d_name, userdata); - } - closedir(dirp); -#else - (void) dir, (void) fn, (void) userdata; -#endif -} - -static void *p_open(const char *path, int flags) { -#if MG_ARCH == MG_ARCH_WIN32 - const char *mode = flags == MG_FS_READ ? "rb" : "a+b"; - wchar_t b1[MG_PATH_MAX], b2[10]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0])); - MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0])); - return (void *) _wfopen(b1, b2); -#else - const char *mode = flags == MG_FS_READ ? "rbe" : "a+be"; // e for CLOEXEC - return (void *) fopen(path, mode); -#endif -} - -static void p_close(void *fp) { - fclose((FILE *) fp); -} - -static size_t p_read(void *fp, void *buf, size_t len) { - return fread(buf, 1, len, (FILE *) fp); -} - -static size_t p_write(void *fp, const void *buf, size_t len) { - return fwrite(buf, 1, len, (FILE *) fp); -} - -static size_t p_seek(void *fp, size_t offset) { -#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \ - (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ - (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) - if (fseeko((FILE *) fp, (off_t) offset, SEEK_SET) != 0) (void) 0; -#else - if (fseek((FILE *) fp, (long) offset, SEEK_SET) != 0) (void) 0; -#endif - return (size_t) ftell((FILE *) fp); -} - -static bool p_rename(const char *from, const char *to) { - return rename(from, to) == 0; -} - -static bool p_remove(const char *path) { - return remove(path) == 0; -} - -static bool p_mkdir(const char *path) { - return mkdir(path, 0775) == 0; -} - -#else - -static int p_stat(const char *path, size_t *size, time_t *mtime) { - (void) path, (void) size, (void) mtime; - return 0; -} -static void p_list(const char *path, void (*fn)(const char *, void *), - void *userdata) { - (void) path, (void) fn, (void) userdata; -} -static void *p_open(const char *path, int flags) { - (void) path, (void) flags; - return NULL; -} -static void p_close(void *fp) { - (void) fp; -} -static size_t p_read(void *fd, void *buf, size_t len) { - (void) fd, (void) buf, (void) len; - return 0; -} -static size_t p_write(void *fd, const void *buf, size_t len) { - (void) fd, (void) buf, (void) len; - return 0; -} -static size_t p_seek(void *fd, size_t offset) { - (void) fd, (void) offset; - return (size_t) ~0; -} -static bool p_rename(const char *from, const char *to) { - (void) from, (void) to; - return false; -} -static bool p_remove(const char *path) { - (void) path; - return false; -} -static bool p_mkdir(const char *path) { - (void) path; - return false; -} -#endif - -struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read, - p_write, p_seek, p_rename, p_remove, p_mkdir}; - -#ifdef MG_ENABLE_LINES -#line 1 "src/http.c" -#endif - - - - - - - - - - - - - -static int mg_ncasecmp(const char *s1, const char *s2, size_t len) { - int diff = 0; - if (len > 0) do { - int c = *s1++, d = *s2++; - if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; - if (d >= 'A' && d <= 'Z') d += 'a' - 'A'; - diff = c - d; - } while (diff == 0 && s1[-1] != '\0' && --len > 0); - return diff; -} - -bool mg_to_size_t(struct mg_str str, size_t *val); -bool mg_to_size_t(struct mg_str str, size_t *val) { - size_t i = 0, max = (size_t) -1, max2 = max / 10, result = 0, ndigits = 0; - while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++; - if (i < str.len && str.buf[i] == '-') return false; - while (i < str.len && str.buf[i] >= '0' && str.buf[i] <= '9') { - size_t digit = (size_t) (str.buf[i] - '0'); - if (result > max2) return false; // Overflow - result *= 10; - if (result > max - digit) return false; // Overflow - result += digit; - i++, ndigits++; - } - while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++; - if (ndigits == 0) return false; // #2322: Content-Length = 1 * DIGIT - if (i != str.len) return false; // Ditto - *val = (size_t) result; - return true; -} - -// Chunk deletion marker is the MSB in the "processed" counter -#define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1)) - -// Multipart POST example: -// --xyz -// Content-Disposition: form-data; name="val" -// -// abcdef -// --xyz -// Content-Disposition: form-data; name="foo"; filename="a.txt" -// Content-Type: text/plain -// -// hello world -// -// --xyz-- -size_t mg_http_next_multipart(struct mg_str body, size_t ofs, - struct mg_http_part *part) { - struct mg_str cd = mg_str_n("Content-Disposition", 19); - const char *s = body.buf; - size_t b = ofs, h1, h2, b1, b2, max = body.len; - - // Init part params - if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0); - - // Skip boundary - while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++; - if (b <= ofs || b + 2 >= max) return 0; - // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s)); - - // Skip headers - h1 = h2 = b + 2; - for (;;) { - while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++; - if (h2 == h1) break; - if (h2 + 2 >= max) return 0; - // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1])); - if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' && - mg_ncasecmp(&s[h1], cd.buf, cd.len) == 0) { - struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2)); - part->name = mg_http_get_header_var(v, mg_str_n("name", 4)); - part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8)); - } - h1 = h2 = h2 + 2; - } - b1 = b2 = h2 + 2; - while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' && - memcmp(&s[b2 + 2], s, b - ofs) == 0)) - b2++; - - if (b2 + 2 >= max) return 0; - if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1); - // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1])); - return b2 + 2; -} - -void mg_http_bauth(struct mg_connection *c, const char *user, - const char *pass) { - struct mg_str u = mg_str(user), p = mg_str(pass); - size_t need = c->send.len + 36 + (u.len + p.len) * 2; - if (c->send.size < need) mg_iobuf_resize(&c->send, need); - if (c->send.size >= need) { - size_t i, n = 0; - char *buf = (char *) &c->send.buf[c->send.len]; - memcpy(buf, "Authorization: Basic ", 21); // DON'T use mg_send! - for (i = 0; i < u.len; i++) { - n = mg_base64_update(((unsigned char *) u.buf)[i], buf + 21, n); - } - if (p.len > 0) { - n = mg_base64_update(':', buf + 21, n); - for (i = 0; i < p.len; i++) { - n = mg_base64_update(((unsigned char *) p.buf)[i], buf + 21, n); - } - } - n = mg_base64_final(buf + 21, n); - c->send.len += 21 + (size_t) n + 2; - memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2); - } else { - MG_ERROR(("%lu oom %d->%d ", c->id, (int) c->send.size, (int) need)); - } -} - -struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) { - struct mg_str entry, k, v, result = mg_str_n(NULL, 0); - while (mg_span(buf, &entry, &buf, '&')) { - if (mg_span(entry, &k, &v, '=') && name.len == k.len && - mg_ncasecmp(name.buf, k.buf, k.len) == 0) { - result = v; - break; - } - } - return result; -} - -int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst, - size_t dst_len) { - int len; - if (dst != NULL && dst_len > 0) { - dst[0] = '\0'; // If destination buffer is valid, always nul-terminate it - } - if (dst == NULL || dst_len == 0) { - len = -2; // Bad destination - } else if (buf->buf == NULL || name == NULL || buf->len == 0) { - len = -1; // Bad source - } else { - struct mg_str v = mg_http_var(*buf, mg_str(name)); - if (v.buf == NULL) { - len = -4; // Name does not exist - } else { - len = mg_url_decode(v.buf, v.len, dst, dst_len, 1); - if (len < 0) len = -3; // Failed to decode - } - } - return len; -} - -static bool isx(int c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, - int is_form_url_encoded) { - size_t i, j; - for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) { - if (src[i] == '%') { - // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len - if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) { - mg_str_to_num(mg_str_n(src + i + 1, 2), 16, &dst[j], sizeof(uint8_t)); - i += 2; - } else { - return -1; - } - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination - return i >= src_len && j < dst_len ? (int) j : -1; -} - -static bool isok(uint8_t c) { - return c == '\n' || c == '\r' || c == '\t' || c >= ' '; -} - -int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) { - size_t i; - for (i = 0; i < buf_len; i++) { - if (!isok(buf[i])) return -1; - if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') || - (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n')) - return (int) i + 1; - } - return 0; -} -struct mg_str *mg_http_get_header(struct mg_http_message *h, const char *name) { - size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]); - for (i = 0; i < max && h->headers[i].name.len > 0; i++) { - struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value; - if (n == k->len && mg_ncasecmp(k->buf, name, n) == 0) return v; - } - return NULL; -} - -// Is it a valid utf-8 continuation byte -static bool vcb(uint8_t c) { - return (c & 0xc0) == 0x80; -} - -// Get character length (valid utf-8). Used to parse method, URI, headers -static size_t clen(const char *s, const char *end) { - const unsigned char *u = (unsigned char *) s, c = *u; - long n = (long) (end - s); - if (c > ' ' && c <= '~') return 1; // Usual ascii printed char - if ((c & 0xe0) == 0xc0 && n > 1 && vcb(u[1])) return 2; // 2-byte UTF8 - if ((c & 0xf0) == 0xe0 && n > 2 && vcb(u[1]) && vcb(u[2])) return 3; - if ((c & 0xf8) == 0xf0 && n > 3 && vcb(u[1]) && vcb(u[2]) && vcb(u[3])) - return 4; - return 0; -} - -// Skip until the newline. Return advanced `s`, or NULL on error -static const char *skiptorn(const char *s, const char *end, struct mg_str *v) { - v->buf = (char *) s; - while (s < end && s[0] != '\n' && s[0] != '\r') s++, v->len++; // To newline - if (s >= end || (s[0] == '\r' && s[1] != '\n')) return NULL; // Stray \r - if (s < end && s[0] == '\r') s++; // Skip \r - if (s >= end || *s++ != '\n') return NULL; // Skip \n - return s; -} - -static bool mg_http_parse_headers(const char *s, const char *end, - struct mg_http_header *h, size_t max_hdrs) { - size_t i, n; - for (i = 0; i < max_hdrs; i++) { - struct mg_str k = {NULL, 0}, v = {NULL, 0}; - if (s >= end) return false; - if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n')) break; - k.buf = (char *) s; - while (s < end && s[0] != ':' && (n = clen(s, end)) > 0) s += n, k.len += n; - if (k.len == 0) return false; // Empty name - if (s >= end || clen(s, end) == 0) return false; // Invalid UTF-8 - if (*s++ != ':') return false; // Invalid, not followed by : - // if (clen(s, end) == 0) return false; // Invalid UTF-8 - while (s < end && (s[0] == ' ' || s[0] == '\t')) s++; // Skip spaces - if ((s = skiptorn(s, end, &v)) == NULL) return false; - while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) { - v.len--; // Trim spaces - } - // MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf)); - h[i].name = k, h[i].value = v; // Success. Assign values - } - return true; -} - -int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { - int is_response, req_len = mg_http_get_request_len((unsigned char *) s, len); - const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL - const struct mg_str *cl; - size_t n; - bool version_prefix_valid; - - memset(hm, 0, sizeof(*hm)); - if (req_len <= 0) return req_len; - - hm->message.buf = hm->head.buf = (char *) s; - hm->body.buf = (char *) end; - hm->head.len = (size_t) req_len; - hm->message.len = hm->body.len = (size_t) -1; // Set body length to infinite - - // Parse request line - hm->method.buf = (char *) s; - while (s < end && (n = clen(s, end)) > 0) s += n, hm->method.len += n; - while (s < end && s[0] == ' ') s++; // Skip spaces - hm->uri.buf = (char *) s; - while (s < end && (n = clen(s, end)) > 0) s += n, hm->uri.len += n; - while (s < end && s[0] == ' ') s++; // Skip spaces - is_response = - hm->method.len > 5 && (mg_ncasecmp(hm->method.buf, "HTTP/", 5) == 0); - if ((s = skiptorn(s, end, &hm->proto)) == NULL) return false; - // If we're given a version, check that it is HTTP/x.x - version_prefix_valid = - hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0); - if (!is_response && hm->proto.len > 0 && - (!version_prefix_valid || hm->proto.len != 8 || - (hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') || - (hm->proto.buf[6] != '.') || - (hm->proto.buf[7] < '0' || hm->proto.buf[7] > '9'))) { - return -1; - } - - // If URI contains '?' character, setup query string - if ((qs = (const char *) memchr(hm->uri.buf, '?', hm->uri.len)) != NULL) { - hm->query.buf = (char *) qs + 1; - hm->query.len = (size_t) (&hm->uri.buf[hm->uri.len] - (qs + 1)); - hm->uri.len = (size_t) (qs - hm->uri.buf); - } - - // Sanity check. Allow protocol/reason to be empty - // Do this check after hm->method.len and hm->uri.len are finalised - if (hm->method.len == 0 || hm->uri.len == 0) return -1; - - if (!mg_http_parse_headers(s, end, hm->headers, - sizeof(hm->headers) / sizeof(hm->headers[0]))) - return -1; // error when parsing - if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) { - if (mg_to_size_t(*cl, &hm->body.len) == false) return -1; - hm->message.len = (size_t) req_len + hm->body.len; - } - - // mg_http_parse() is used to parse both HTTP requests and HTTP - // responses. If HTTP response does not have Content-Length set, then - // body is read until socket is closed, i.e. body.len is infinite (~0). - // - // For HTTP requests though, according to - // http://tools.ietf.org/html/rfc7231#section-8.1.3, - // only POST and PUT methods have defined body semantics. - // Therefore, if Content-Length is not specified and methods are - // not one of PUT or POST, set body length to 0. - // - // So, if it is HTTP request, and Content-Length is not set, - // and method is not (PUT or POST) then reset body length to zero. - if (hm->body.len == (size_t) ~0 && !is_response && - mg_strcasecmp(hm->method, mg_str("PUT")) != 0 && - mg_strcasecmp(hm->method, mg_str("POST")) != 0) { - hm->body.len = 0; - hm->message.len = (size_t) req_len; - } - - // The 204 (No content) responses also have 0 body length - if (hm->body.len == (size_t) ~0 && is_response && - mg_strcasecmp(hm->uri, mg_str("204")) == 0) { - hm->body.len = 0; - hm->message.len = (size_t) req_len; - } - if (hm->message.len < (size_t) req_len) return -1; // Overflow protection - - return req_len; -} - -static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, - va_list *ap) { - size_t len = c->send.len; - mg_send(c, " \r\n", 10); - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); - if (c->send.len >= len + 10) { - mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10); - c->send.buf[len + 8] = '\r'; - if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker - } - mg_send(c, "\r\n", 2); -} - -void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_http_vprintf_chunk(c, fmt, &ap); - va_end(ap); -} - -void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { - mg_printf(c, "%lx\r\n", (unsigned long) len); - mg_send(c, buf, len); - mg_send(c, "\r\n", 2); - if (len == 0) c->is_resp = 0; -} - -// clang-format off -static const char *mg_http_status_code_str(int status_code) { - switch (status_code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 208: return "Already Reported"; - case 226: return "IM Used"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 308: return "Permanent Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Payload Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 418: return "I'm a teapot"; - case 421: return "Misdirected Request"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 426: return "Upgrade Required"; - case 428: return "Precondition Required"; - case 429: return "Too Many Requests"; - case 431: return "Request Header Fields Too Large"; - case 444: return "Connection Closed Without Response"; - case 451: return "Unavailable For Legal Reasons"; - case 499: return "Client Closed Request"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - case 506: return "Variant Also Negotiates"; - case 507: return "Insufficient Storage"; - case 508: return "Loop Detected"; - case 510: return "Not Extended"; - case 511: return "Network Authentication Required"; - case 599: return "Network Connect Timeout Error"; - default: return ""; - } -} -// clang-format on - -void mg_http_reply(struct mg_connection *c, int code, const char *headers, - const char *fmt, ...) { - va_list ap; - size_t len; - mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n", code, - mg_http_status_code_str(code), headers == NULL ? "" : headers); - len = c->send.len; - va_start(ap, fmt); - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap); - va_end(ap); - if (c->send.len > 16) { - size_t n = mg_snprintf((char *) &c->send.buf[len - 15], 11, "%-10lu", - (unsigned long) (c->send.len - len)); - c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space - } - c->is_resp = 0; -} - -static void http_cb(struct mg_connection *, int, void *); -static void restore_http_cb(struct mg_connection *c) { - mg_fs_close((struct mg_fd *) c->pfn_data); - c->pfn_data = NULL; - c->pfn = http_cb; - c->is_resp = 0; -} - -char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime); -char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) { - mg_snprintf(buf, len, "\"%lld.%lld\"", (int64_t) mtime, (int64_t) size); - return buf; -} - -static void static_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_WRITE || ev == MG_EV_POLL) { - struct mg_fd *fd = (struct mg_fd *) c->pfn_data; - // Read to send IO buffer directly, avoid extra on-stack buffer - size_t n, max = MG_IO_SIZE, space; - size_t *cl = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) / - sizeof(size_t) * sizeof(size_t)]; - if (c->send.size < max) mg_iobuf_resize(&c->send, max); - if (c->send.len >= c->send.size) return; // Rate limit - if ((space = c->send.size - c->send.len) > *cl) space = *cl; - n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space); - c->send.len += n; - *cl -= n; - if (n == 0) restore_http_cb(c); - } else if (ev == MG_EV_CLOSE) { - restore_http_cb(c); - } - (void) ev_data; -} - -// Known mime types. Keep it outside guess_content_type() function, since -// some environments don't like it defined there. -// clang-format off -#define MG_C_STR(a) { (char *) (a), sizeof(a) - 1 } -static struct mg_str s_known_types[] = { - MG_C_STR("html"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("htm"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("css"), MG_C_STR("text/css; charset=utf-8"), - MG_C_STR("js"), MG_C_STR("text/javascript; charset=utf-8"), - MG_C_STR("mjs"), MG_C_STR("text/javascript; charset=utf-8"), - MG_C_STR("gif"), MG_C_STR("image/gif"), - MG_C_STR("png"), MG_C_STR("image/png"), - MG_C_STR("jpg"), MG_C_STR("image/jpeg"), - MG_C_STR("jpeg"), MG_C_STR("image/jpeg"), - MG_C_STR("woff"), MG_C_STR("font/woff"), - MG_C_STR("ttf"), MG_C_STR("font/ttf"), - MG_C_STR("svg"), MG_C_STR("image/svg+xml"), - MG_C_STR("txt"), MG_C_STR("text/plain; charset=utf-8"), - MG_C_STR("avi"), MG_C_STR("video/x-msvideo"), - MG_C_STR("csv"), MG_C_STR("text/csv"), - MG_C_STR("doc"), MG_C_STR("application/msword"), - MG_C_STR("exe"), MG_C_STR("application/octet-stream"), - MG_C_STR("gz"), MG_C_STR("application/gzip"), - MG_C_STR("ico"), MG_C_STR("image/x-icon"), - MG_C_STR("json"), MG_C_STR("application/json"), - MG_C_STR("mov"), MG_C_STR("video/quicktime"), - MG_C_STR("mp3"), MG_C_STR("audio/mpeg"), - MG_C_STR("mp4"), MG_C_STR("video/mp4"), - MG_C_STR("mpeg"), MG_C_STR("video/mpeg"), - MG_C_STR("pdf"), MG_C_STR("application/pdf"), - MG_C_STR("shtml"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("tgz"), MG_C_STR("application/tar-gz"), - MG_C_STR("wav"), MG_C_STR("audio/wav"), - MG_C_STR("webp"), MG_C_STR("image/webp"), - MG_C_STR("zip"), MG_C_STR("application/zip"), - MG_C_STR("3gp"), MG_C_STR("video/3gpp"), - {0, 0}, -}; -// clang-format on - -static struct mg_str guess_content_type(struct mg_str path, const char *extra) { - struct mg_str entry, k, v, s = mg_str(extra), asterisk = mg_str_n("*", 1); - size_t i = 0; - - // Shrink path to its extension only - while (i < path.len && path.buf[path.len - i - 1] != '.') i++; - path.buf += path.len - i; - path.len = i; - - // Process user-provided mime type overrides, if any - while (mg_span(s, &entry, &s, ',')) { - if (mg_span(entry, &k, &v, '=') && - (mg_strcmp(asterisk, k) == 0 || mg_strcmp(path, k) == 0)) - return v; - } - - // Process built-in mime types - for (i = 0; s_known_types[i].buf != NULL; i += 2) { - if (mg_strcmp(path, s_known_types[i]) == 0) return s_known_types[i + 1]; - } - - return mg_str("text/plain; charset=utf-8"); -} - -static int getrange(struct mg_str *s, size_t *a, size_t *b) { - size_t i, numparsed = 0; - for (i = 0; i + 6 < s->len; i++) { - struct mg_str k, v = mg_str_n(s->buf + i + 6, s->len - i - 6); - if (memcmp(&s->buf[i], "bytes=", 6) != 0) continue; - if (mg_span(v, &k, &v, '-')) { - if (mg_to_size_t(k, a)) numparsed++; - if (v.len > 0 && mg_to_size_t(v, b)) numparsed++; - } else { - if (mg_to_size_t(v, a)) numparsed++; - } - break; - } - return (int) numparsed; -} - -void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, - const char *path, - const struct mg_http_serve_opts *opts) { - char etag[64], tmp[MG_PATH_MAX]; - struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct mg_fd *fd = NULL; - size_t size = 0; - time_t mtime = 0; - struct mg_str *inm = NULL; - struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types); - bool gzip = false; - - if (path != NULL) { - // If a browser sends us "Accept-Encoding: gzip", try to open .gz first - struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding"); - if (ae != NULL) { - if (mg_match(*ae, mg_str("*gzip*"), NULL)) { - mg_snprintf(tmp, sizeof(tmp), "%s.gz", path); - fd = mg_fs_open(fs, tmp, MG_FS_READ); - if (fd != NULL) gzip = true, path = tmp; - } - } - // No luck opening .gz? Open what we've told to open - if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ); - } - - // Failed to open, and page404 is configured? Open it, then - if (fd == NULL && opts->page404 != NULL) { - fd = mg_fs_open(fs, opts->page404, MG_FS_READ); - path = opts->page404; - mime = guess_content_type(mg_str(path), opts->mime_types); - } - - if (fd == NULL || fs->st(path, &size, &mtime) == 0) { - mg_http_reply(c, 404, opts->extra_headers, "Not found\n"); - mg_fs_close(fd); - // NOTE: mg_http_etag() call should go first! - } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL && - (inm = mg_http_get_header(hm, "If-None-Match")) != NULL && - mg_strcasecmp(*inm, mg_str(etag)) == 0) { - mg_fs_close(fd); - mg_http_reply(c, 304, opts->extra_headers, ""); - } else { - int n, status = 200; - char range[100]; - size_t r1 = 0, r2 = 0, cl = size; - - // Handle Range header - struct mg_str *rh = mg_http_get_header(hm, "Range"); - range[0] = '\0'; - if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) { - // If range is specified like "400-", set second limit to content len - if (n == 1) r2 = cl - 1; - if (r1 > r2 || r2 >= cl) { - status = 416; - cl = 0; - mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n", - (int64_t) size); - } else { - status = 206; - cl = r2 - r1 + 1; - mg_snprintf(range, sizeof(range), - "Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t) r1, - (uint64_t) (r1 + cl - 1), (uint64_t) size); - fs->sk(fd->fd, r1); - } - } - mg_printf(c, - "HTTP/1.1 %d %s\r\n" - "Content-Type: %.*s\r\n" - "Etag: %s\r\n" - "Content-Length: %llu\r\n" - "%s%s%s\r\n", - status, mg_http_status_code_str(status), (int) mime.len, mime.buf, - etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "", - range, opts->extra_headers ? opts->extra_headers : ""); - if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) { - c->is_resp = 0; - mg_fs_close(fd); - } else { - // Track to-be-sent content length at the end of c->data, aligned - size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) / - sizeof(size_t) * sizeof(size_t)]; - c->pfn = static_cb; - c->pfn_data = fd; - *clp = cl; - } - } -} - -struct printdirentrydata { - struct mg_connection *c; - struct mg_http_message *hm; - const struct mg_http_serve_opts *opts; - const char *dir; -}; - -#if MG_ENABLE_DIRLIST -static void printdirentry(const char *name, void *userdata) { - struct printdirentrydata *d = (struct printdirentrydata *) userdata; - struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs; - size_t size = 0; - time_t t = 0; - char path[MG_PATH_MAX], sz[40], mod[40]; - int flags, n = 0; - - // MG_DEBUG(("[%s] [%s]", d->dir, name)); - if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) > - sizeof(path)) { - MG_ERROR(("%s truncated", name)); - } else if ((flags = fs->st(path, &size, &t)) == 0) { - MG_ERROR(("%lu stat(%s)", d->c->id, path)); - } else { - const char *slash = flags & MG_FS_DIR ? "/" : ""; - if (flags & MG_FS_DIR) { - mg_snprintf(sz, sizeof(sz), "%s", "[DIR]"); - } else { - mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size); - } -#if defined(MG_HTTP_DIRLIST_TIME_FMT) - { - char time_str[40]; - struct tm *time_info = localtime(&t); - strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info); - mg_snprintf(mod, sizeof(mod), "%s", time_str); - } -#else - mg_snprintf(mod, sizeof(mod), "%lu", (unsigned long) t); -#endif - n = (int) mg_url_encode(name, strlen(name), path, sizeof(path)); - mg_printf(d->c, - " %s%s" - "%s%s\n", - n, path, slash, name, slash, (unsigned long) t, mod, - flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz); - } -} - -static void listdir(struct mg_connection *c, struct mg_http_message *hm, - const struct mg_http_serve_opts *opts, char *dir) { - const char *sort_js_code = - ""; - struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct printdirentrydata d = {c, hm, opts, dir}; - char tmp[10], buf[MG_PATH_MAX]; - size_t off, n; - int len = mg_url_decode(hm->uri.buf, hm->uri.len, buf, sizeof(buf), 0); - struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri; - - mg_printf(c, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html; charset=utf-8\r\n" - "%s" - "Content-Length: \r\n\r\n", - opts->extra_headers == NULL ? "" : opts->extra_headers); - off = c->send.len; // Start of body - mg_printf(c, - "Index of %.*s%s%s" - "" - "

Index of %.*s

" - "" - "" - "" - "" - "\n", - (int) uri.len, uri.buf, sort_js_code, sort_js_code2, (int) uri.len, - uri.buf); - mg_printf(c, "%s", - " " - "\n"); - - fs->ls(dir, printdirentry, &d); - mg_printf(c, - "" - "
Name" - "ModifiedSize

..[DIR]

Mongoose v.%s
\n", - MG_VERSION); - n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off)); - if (n > sizeof(tmp)) n = 0; - memcpy(c->send.buf + off - 12, tmp, n); // Set content length - c->is_resp = 0; // Mark response end -} -#endif - -// Resolve requested file into `path` and return its fs->st() result -static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, struct mg_str url, struct mg_str dir, - char *path, size_t path_size) { - int flags, tmp; - // Append URI to the root_dir, and sanitize it - size_t n = mg_snprintf(path, path_size, "%.*s", (int) dir.len, dir.buf); - if (n + 2 >= path_size) { - mg_http_reply(c, 400, "", "Exceeded path size"); - return -1; - } - path[path_size - 1] = '\0'; - // Terminate root dir with slash - if (n > 0 && path[n - 1] != '/') path[n++] = '/', path[n] = '\0'; - if (url.len < hm->uri.len) { - mg_url_decode(hm->uri.buf + url.len, hm->uri.len - url.len, path + n, - path_size - n, 0); - } - path[path_size - 1] = '\0'; // Double-check - if (!mg_path_is_sane(mg_str_n(path, path_size))) { - mg_http_reply(c, 400, "", "Invalid path"); - return -1; - } - n = strlen(path); - while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes - flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR - : fs->st(path, NULL, NULL); - MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path, - flags)); - if (flags == 0) { - // Do nothing - let's caller decide - } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 && - hm->uri.buf[hm->uri.len - 1] != '/') { - mg_printf(c, - "HTTP/1.1 301 Moved\r\n" - "Location: %.*s/\r\n" - "Content-Length: 0\r\n" - "\r\n", - (int) hm->uri.len, hm->uri.buf); - c->is_resp = 0; - flags = -1; - } else if (flags & MG_FS_DIR) { - if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 && - (tmp = fs->st(path, NULL, NULL)) != 0) || - (mg_snprintf(path + n, path_size - n, "/index.shtml") > 0 && - (tmp = fs->st(path, NULL, NULL)) != 0))) { - flags = tmp; - } else if ((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX ".gz") > - 0 && - (tmp = fs->st(path, NULL, NULL)) != - 0)) { // check for gzipped index - flags = tmp; - path[n + 1 + strlen(MG_HTTP_INDEX)] = - '\0'; // Remove appended .gz in index file name - } else { - path[n] = '\0'; // Remove appended index file name - } - } - return flags; -} - -static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm, - const struct mg_http_serve_opts *opts, char *path, - size_t path_size) { - struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct mg_str k, v, part, s = mg_str(opts->root_dir), u = {NULL, 0}, p = u; - while (mg_span(s, &part, &s, ',')) { - if (!mg_span(part, &k, &v, '=')) k = part, v = mg_str_n(NULL, 0); - if (v.len == 0) v = k, k = mg_str("/"), u = k, p = v; - if (hm->uri.len < k.len) continue; - if (mg_strcmp(k, mg_str_n(hm->uri.buf, k.len)) != 0) continue; - u = k, p = v; - } - return uri_to_path2(c, hm, fs, u, p, path, path_size); -} - -void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, - const struct mg_http_serve_opts *opts) { - char path[MG_PATH_MAX]; - const char *sp = opts->ssi_pattern; - int flags = uri_to_path(c, hm, opts, path, sizeof(path)); - if (flags < 0) { - // Do nothing: the response has already been sent by uri_to_path() - } else if (flags & MG_FS_DIR) { -#if MG_ENABLE_DIRLIST - listdir(c, hm, opts, path); -#else - mg_http_reply(c, 403, "", "Forbidden\n"); -#endif - } else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) { - mg_http_serve_ssi(c, opts->root_dir, path); - } else { - mg_http_serve_file(c, hm, path, opts); - } -} - -static bool mg_is_url_safe(int c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~'; -} - -size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) { - size_t i, n = 0; - for (i = 0; i < sl; i++) { - int c = *(unsigned char *) &s[i]; - if (n + 4 >= len) return 0; - if (mg_is_url_safe(c)) { - buf[n++] = s[i]; - } else { - mg_snprintf(&buf[n], 4, "%%%M", mg_print_hex, 1, &s[i]); - n += 3; - } - } - if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination - if (len > 0) buf[len - 1] = '\0'; // Always. - return n; -} - -void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen, - char *pass, size_t passlen) { - struct mg_str *v = mg_http_get_header(hm, "Authorization"); - user[0] = pass[0] = '\0'; - if (v != NULL && v->len > 6 && memcmp(v->buf, "Basic ", 6) == 0) { - char buf[256]; - size_t n = mg_base64_decode(v->buf + 6, v->len - 6, buf, sizeof(buf)); - const char *p = (const char *) memchr(buf, ':', n > 0 ? n : 0); - if (p != NULL) { - mg_snprintf(user, userlen, "%.*s", p - buf, buf); - mg_snprintf(pass, passlen, "%.*s", n - (size_t) (p - buf) - 1, p + 1); - } - } else if (v != NULL && v->len > 7 && memcmp(v->buf, "Bearer ", 7) == 0) { - mg_snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->buf + 7); - } else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) { - struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12)); - if (t.len > 0) mg_snprintf(pass, passlen, "%.*s", (int) t.len, t.buf); - } else { - mg_http_get_var(&hm->query, "access_token", pass, passlen); - } -} - -static struct mg_str stripquotes(struct mg_str s) { - return s.len > 1 && s.buf[0] == '"' && s.buf[s.len - 1] == '"' - ? mg_str_n(s.buf + 1, s.len - 2) - : s; -} - -struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) { - size_t i; - for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) { - if (s.buf[i + v.len] == '=' && memcmp(&s.buf[i], v.buf, v.len) == 0) { - const char *p = &s.buf[i + v.len + 1], *b = p, *x = &s.buf[s.len]; - int q = p < x && *p == '"' ? 1 : 0; - while (p < x && - (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ',')) - p++; - // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.buf, (int) v.len, - // v.buf, (int) (p - b), b)); - return stripquotes(mg_str_n(b, (size_t) (p - b + q))); - } - } - return mg_str_n(NULL, 0); -} - -long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size) { - char buf[20] = "0", file[MG_PATH_MAX], path[MG_PATH_MAX]; - long res = 0, offset; - mg_http_get_var(&hm->query, "offset", buf, sizeof(buf)); - mg_http_get_var(&hm->query, "file", file, sizeof(file)); - offset = strtol(buf, NULL, 0); - mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file); - if (hm->body.len == 0) { - mg_http_reply(c, 200, "", "%ld", res); // Nothing to write - } else if (file[0] == '\0') { - mg_http_reply(c, 400, "", "file required"); - res = -1; - } else if (mg_path_is_sane(mg_str(file)) == false) { - mg_http_reply(c, 400, "", "%s: invalid file", file); - res = -2; - } else if (offset < 0) { - mg_http_reply(c, 400, "", "offset required"); - res = -3; - } else if ((size_t) offset + hm->body.len > max_size) { - mg_http_reply(c, 400, "", "%s: over max size of %lu", path, - (unsigned long) max_size); - res = -4; - } else { - struct mg_fd *fd; - size_t current_size = 0; - MG_DEBUG(("%s -> %lu bytes @ %ld", path, hm->body.len, offset)); - if (offset == 0) fs->rm(path); // If offset if 0, truncate file - fs->st(path, ¤t_size, NULL); - if (offset > 0 && current_size != (size_t) offset) { - mg_http_reply(c, 400, "", "%s: offset mismatch", path); - res = -5; - } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { - mg_http_reply(c, 400, "", "open(%s)", path); - res = -6; - } else { - res = offset + (long) fs->wr(fd->fd, hm->body.buf, hm->body.len); - mg_fs_close(fd); - mg_http_reply(c, 200, "", "%ld", res); - } - } - return res; -} - -int mg_http_status(const struct mg_http_message *hm) { - return atoi(hm->uri.buf); -} - -static bool is_hex_digit(int c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -static int skip_chunk(const char *buf, int len, int *pl, int *dl) { - int i = 0, n = 0; - if (len < 3) return 0; - while (i < len && is_hex_digit(buf[i])) i++; - if (i == 0) return -1; // Error, no length specified - if (i > (int) sizeof(int) * 2) return -1; // Chunk length is too big - if (len < i + 1 || buf[i] != '\r' || buf[i + 1] != '\n') return -1; // Error - if (mg_str_to_num(mg_str_n(buf, (size_t) i), 16, &n, sizeof(int)) == false) - return -1; // Decode chunk length, overflow - if (n < 0) return -1; // Error. TODO(): some checks now redundant - if (n > len - i - 4) return 0; // Chunk not yet fully buffered - if (buf[i + n + 2] != '\r' || buf[i + n + 3] != '\n') return -1; // Error - *pl = i + 2, *dl = n; - return i + 2 + n + 2; -} - -static void http_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ || ev == MG_EV_CLOSE || - (ev == MG_EV_POLL && c->is_accepted && !c->is_draining && - c->recv.len > 0)) { // see #2796 - struct mg_http_message hm; - size_t ofs = 0; // Parsing offset - while (c->is_resp == 0 && ofs < c->recv.len) { - const char *buf = (char *) c->recv.buf + ofs; - int n = mg_http_parse(buf, c->recv.len - ofs, &hm); - struct mg_str *te; // Transfer - encoding header - bool is_chunked = false; - size_t old_len = c->recv.len; - if (n < 0) { - // We don't use mg_error() here, to avoid closing pipelined requests - // prematurely, see #2592 - MG_ERROR(("HTTP parse, %lu bytes", c->recv.len)); - c->is_draining = 1; - mg_hexdump(buf, c->recv.len - ofs > 16 ? 16 : c->recv.len - ofs); - c->recv.len = 0; - return; - } - if (n == 0) break; // Request is not buffered yet - mg_call(c, MG_EV_HTTP_HDRS, &hm); // Got all HTTP headers - if (c->recv.len != old_len) { - // User manipulated received data. Wash our hands - MG_DEBUG(("%lu detaching HTTP handler", c->id)); - c->pfn = NULL; - return; - } - if (ev == MG_EV_CLOSE) { // If client did not set Content-Length - hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG - hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf); - } - if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) { - if (mg_strcasecmp(*te, mg_str("chunked")) == 0) { - is_chunked = true; - } else { - mg_error(c, "Invalid Transfer-Encoding"); // See #2460 - return; - } - } else if (mg_http_get_header(&hm, "Content-length") == NULL) { - // #2593: HTTP packets must contain either Transfer-Encoding or - // Content-length - bool is_response = mg_ncasecmp(hm.method.buf, "HTTP/", 5) == 0; - bool require_content_len = false; - if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 || - mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) { - // POST and PUT should include an entity body. Therefore, they should - // contain a Content-length header. Other requests can also contain a - // body, but their content has no defined semantics (RFC 7231) - require_content_len = true; - ofs += (size_t) n; // this request has been processed - } else if (is_response) { - // HTTP spec 7.2 Entity body: All other responses must include a body - // or Content-Length header field defined with a value of 0. - int status = mg_http_status(&hm); - require_content_len = status >= 200 && status != 204 && status != 304; - } - if (require_content_len) { - if (!c->is_client) mg_http_reply(c, 411, "", ""); - MG_ERROR(("Content length missing from %s", - is_response ? "response" : "request")); - } - } - - if (is_chunked) { - // For chunked data, strip off prefixes and suffixes from chunks - // and relocate them right after the headers, then report a message - char *s = (char *) c->recv.buf + ofs + n; - int o = 0, pl, dl, cl, len = (int) (c->recv.len - ofs - (size_t) n); - - // Find zero-length chunk (the end of the body) - while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0 && dl) o += cl; - if (cl == 0) break; // No zero-len chunk, buffer more data - if (cl < 0) { - mg_error(c, "Invalid chunk"); - break; - } - - // Zero chunk found. Second pass: strip + relocate - o = 0, hm.body.len = 0, hm.message.len = (size_t) n; - while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0) { - memmove(s + hm.body.len, s + o + pl, (size_t) dl); - o += cl, hm.body.len += (size_t) dl, hm.message.len += (size_t) dl; - if (dl == 0) break; - } - ofs += (size_t) (n + o); - } else { // Normal, non-chunked data - size_t len = c->recv.len - ofs - (size_t) n; - if (hm.body.len > len) break; // Buffer more data - ofs += (size_t) n + hm.body.len; - } - - if (c->is_accepted) c->is_resp = 1; // Start generating response - mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp - if (c->is_accepted && !c->is_resp) { - struct mg_str *cc = mg_http_get_header(&hm, "Connection"); - if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) { - c->is_draining = 1; // honor "Connection: close" - break; - } - } - } - if (ofs > 0) mg_iobuf_del(&c->recv, 0, ofs); // Delete processed data - } - (void) ev_data; -} - -struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - return mg_connect_svc(mgr, url, fn, fn_data, http_cb, NULL); -} - -struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); - if (c != NULL) c->pfn = http_cb; - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/iobuf.c" -#endif - - - - - -static size_t roundup(size_t size, size_t align) { - return align == 0 ? size : (size + align - 1) / align * align; -} - -int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) { - int ok = 1; - new_size = roundup(new_size, io->align); - if (new_size == 0) { - mg_bzero(io->buf, io->size); - mg_free(io->buf); - io->buf = NULL; - io->len = io->size = 0; - } else if (new_size != io->size) { - // NOTE(lsm): do not use realloc here. Use mg_calloc/mg_free only - void *p = mg_calloc(1, new_size); - if (p != NULL) { - size_t len = new_size < io->len ? new_size : io->len; - if (len > 0 && io->buf != NULL) memmove(p, io->buf, len); - mg_bzero(io->buf, io->size); - mg_free(io->buf); - io->buf = (unsigned char *) p; - io->size = new_size; - } else { - ok = 0; - MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size)); - } - } - return ok; -} - -int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) { - io->buf = NULL; - io->align = align; - io->size = io->len = 0; - return mg_iobuf_resize(io, size); -} - -size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf, - size_t len) { - size_t new_size = roundup(io->len + len, io->align); - mg_iobuf_resize(io, new_size); // Attempt to resize - if (new_size != io->size) len = 0; // Resize failure, append nothing - if (ofs < io->len) memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs); - if (buf != NULL) memmove(io->buf + ofs, buf, len); - if (ofs > io->len) io->len += ofs - io->len; - io->len += len; - return len; -} - -size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len) { - if (ofs > io->len) ofs = io->len; - if (ofs + len > io->len) len = io->len - ofs; - if (io->buf) memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len); - if (io->buf) mg_bzero(io->buf + io->len - len, len); - io->len -= len; - return len; -} - -void mg_iobuf_free(struct mg_iobuf *io) { - mg_iobuf_resize(io, 0); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/json.c" -#endif - - - - - -static const char *escapeseq(int esc) { - return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\""; -} - -static char json_esc(int c, int esc) { - const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc); - for (p = esc1; *p != '\0'; p++) { - if (*p == c) return esc2[p - esc1]; - } - return 0; -} - -static int mg_pass_string(const char *s, int len) { - int i; - for (i = 0; i < len; i++) { - if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) { - i++; - } else if (s[i] == '\0') { - return MG_JSON_INVALID; - } else if (s[i] == '"') { - return i; - } - } - return MG_JSON_INVALID; -} - -static double mg_atod(const char *p, int len, int *numlen) { - double d = 0.0; - int i = 0, sign = 1; - - // Sign - if (i < len && *p == '-') { - sign = -1, i++; - } else if (i < len && *p == '+') { - i++; - } - - // Decimal - for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { - d *= 10.0; - d += p[i] - '0'; - } - d *= sign; - - // Fractional - if (i < len && p[i] == '.') { - double frac = 0.0, base = 0.1; - i++; - for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { - frac += base * (p[i] - '0'); - base /= 10.0; - } - d += frac * sign; - } - - // Exponential - if (i < len && (p[i] == 'e' || p[i] == 'E')) { - int j, exp = 0, minus = 0; - i++; - if (i < len && p[i] == '-') minus = 1, i++; - if (i < len && p[i] == '+') i++; - while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308) - exp = exp * 10 + (p[i++] - '0'); - if (minus) exp = -exp; - for (j = 0; j < exp; j++) d *= 10.0; - for (j = 0; j < -exp; j++) d /= 10.0; - } - - if (numlen != NULL) *numlen = i; - return d; -} - -// Iterate over object or array elements -size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key, - struct mg_str *val) { - if (ofs >= obj.len) { - ofs = 0; // Out of boundaries, stop scanning - } else if (obj.len < 2 || (*obj.buf != '{' && *obj.buf != '[')) { - ofs = 0; // Not an array or object, stop - } else { - struct mg_str sub = mg_str_n(obj.buf + ofs, obj.len - ofs); - if (ofs == 0) ofs++, sub.buf++, sub.len--; - if (*obj.buf == '[') { // Iterate over an array - int n = 0, o = mg_json_get(sub, "$", &n); - if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) { - ofs = 0; // Error parsing key, stop scanning - } else { - if (key) *key = mg_str_n(NULL, 0); - if (val) *val = mg_str_n(sub.buf + o, (size_t) n); - ofs = (size_t) (&sub.buf[o + n] - obj.buf); - } - } else { // Iterate over an object - int n = 0, o = mg_json_get(sub, "$", &n); - if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) { - ofs = 0; // Error parsing key, stop scanning - } else { - if (key) *key = mg_str_n(sub.buf + o, (size_t) n); - sub.buf += o + n, sub.len -= (size_t) (o + n); - while (sub.len > 0 && *sub.buf != ':') sub.len--, sub.buf++; - if (sub.len > 0 && *sub.buf == ':') sub.len--, sub.buf++; - n = 0, o = mg_json_get(sub, "$", &n); - if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) { - ofs = 0; // Error parsing value, stop scanning - } else { - if (val) *val = mg_str_n(sub.buf + o, (size_t) n); - ofs = (size_t) (&sub.buf[o + n] - obj.buf); - } - } - } - // MG_INFO(("SUB ofs %u %.*s", ofs, sub.len, sub.buf)); - while (ofs && ofs < obj.len && - (obj.buf[ofs] == ' ' || obj.buf[ofs] == '\t' || - obj.buf[ofs] == '\n' || obj.buf[ofs] == '\r')) { - ofs++; - } - if (ofs && ofs < obj.len && obj.buf[ofs] == ',') ofs++; - if (ofs > obj.len) ofs = 0; - } - return ofs; -} - -int mg_json_get(struct mg_str json, const char *path, int *toklen) { - const char *s = json.buf; - int len = (int) json.len; - enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE; - unsigned char nesting[MG_JSON_MAX_DEPTH]; - int i = 0; // Current offset in `s` - int j = 0; // Offset in `s` we're looking for (return value) - int depth = 0; // Current depth (nesting level) - int ed = 0; // Expected depth - int pos = 1; // Current position in `path` - int ci = -1, ei = -1; // Current and expected index in array - - if (toklen) *toklen = 0; - if (path[0] != '$') return MG_JSON_INVALID; - -#define MG_CHECKRET(x) \ - do { \ - if (depth == ed && path[pos] == '\0' && ci == ei) { \ - if (toklen) *toklen = i - j + 1; \ - return j; \ - } \ - } while (0) - -// In the ascii table, the distance between `[` and `]` is 2. -// Ditto for `{` and `}`. Hence +2 in the code below. -#define MG_EOO(x) \ - do { \ - if (depth == ed && ci != ei) return MG_JSON_NOT_FOUND; \ - if (c != nesting[depth - 1] + 2) return MG_JSON_INVALID; \ - depth--; \ - MG_CHECKRET(x); \ - } while (0) - - for (i = 0; i < len; i++) { - unsigned char c = ((unsigned char *) s)[i]; - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; - switch (expecting) { - case S_VALUE: - // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei); - if (depth == ed) j = i; - if (c == '{') { - if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP; - if (depth == ed && path[pos] == '.' && ci == ei) { - // If we start the object, reset array indices - ed++, pos++, ci = ei = -1; - } - nesting[depth++] = c; - expecting = S_KEY; - break; - } else if (c == '[') { - if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP; - if (depth == ed && path[pos] == '[' && ei == ci) { - ed++, pos++, ci = 0; - for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) { - ei *= 10; - ei += path[pos] - '0'; - } - if (path[pos] != 0) pos++; - } - nesting[depth++] = c; - break; - } else if (c == ']' && depth > 0) { // Empty array - MG_EOO(']'); - } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) { - i += 3; - } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) { - i += 3; - } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) { - i += 4; - } else if (c == '-' || ((c >= '0' && c <= '9'))) { - int numlen = 0; - mg_atod(&s[i], len - i, &numlen); - i += numlen - 1; - } else if (c == '"') { - int n = mg_pass_string(&s[i + 1], len - i - 1); - if (n < 0) return n; - i += n + 1; - } else { - return MG_JSON_INVALID; - } - MG_CHECKRET('V'); - if (depth == ed && ei >= 0) ci++; - expecting = S_COMMA_OR_EOO; - break; - - case S_KEY: - if (c == '"') { - int n = mg_pass_string(&s[i + 1], len - i - 1); - if (n < 0) return n; - if (i + 1 + n >= len) return MG_JSON_NOT_FOUND; - if (depth < ed) return MG_JSON_NOT_FOUND; - if (depth == ed && path[pos - 1] != '.') return MG_JSON_NOT_FOUND; - // printf("K %s [%.*s] [%.*s] %d %d %d %d %d\n", path, pos, path, n, - // &s[i + 1], n, depth, ed, ci, ei); - // NOTE(cpq): in the check sequence below is important. - // strncmp() must go first: it fails fast if the remaining length - // of the path is smaller than `n`. - if (depth == ed && path[pos - 1] == '.' && - strncmp(&s[i + 1], &path[pos], (size_t) n) == 0 && - (path[pos + n] == '\0' || path[pos + n] == '.' || - path[pos + n] == '[')) { - pos += n; - } - i += n + 1; - expecting = S_COLON; - } else if (c == '}') { // Empty object - MG_EOO('}'); - expecting = S_COMMA_OR_EOO; - if (depth == ed && ei >= 0) ci++; - } else { - return MG_JSON_INVALID; - } - break; - - case S_COLON: - if (c == ':') { - expecting = S_VALUE; - } else { - return MG_JSON_INVALID; - } - break; - - case S_COMMA_OR_EOO: - if (depth <= 0) { - return MG_JSON_INVALID; - } else if (c == ',') { - expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE; - } else if (c == ']' || c == '}') { - if (depth == ed && c == '}' && path[pos - 1] == '.') - return MG_JSON_NOT_FOUND; - if (depth == ed && c == ']' && path[pos - 1] == ',') - return MG_JSON_NOT_FOUND; - MG_EOO('O'); - if (depth == ed && ei >= 0) ci++; - } else { - return MG_JSON_INVALID; - } - break; - } - } - return MG_JSON_NOT_FOUND; -} - -struct mg_str mg_json_get_tok(struct mg_str json, const char *path) { - int len = 0, ofs = mg_json_get(json, path, &len); - return mg_str_n(ofs < 0 ? NULL : json.buf + ofs, - (size_t) (len < 0 ? 0 : len)); -} - -bool mg_json_get_num(struct mg_str json, const char *path, double *v) { - int n, toklen, found = 0; - if ((n = mg_json_get(json, path, &toklen)) >= 0 && - (json.buf[n] == '-' || (json.buf[n] >= '0' && json.buf[n] <= '9'))) { - if (v != NULL) *v = mg_atod(json.buf + n, toklen, NULL); - found = 1; - } - return found; -} - -bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) { - int found = 0, off = mg_json_get(json, path, NULL); - if (off >= 0 && (json.buf[off] == 't' || json.buf[off] == 'f')) { - if (v != NULL) *v = json.buf[off] == 't'; - found = 1; - } - return found; -} - -bool mg_json_unescape(struct mg_str s, char *to, size_t n) { - size_t i, j; - for (i = 0, j = 0; i < s.len && j < n; i++, j++) { - if (s.buf[i] == '\\' && i + 5 < s.len && s.buf[i + 1] == 'u') { - // \uXXXX escape. We process simple one-byte chars \u00xx within ASCII - // range. More complex chars would require dragging in a UTF8 library, - // which is too much for us - if (mg_str_to_num(mg_str_n(s.buf + i + 2, 4), 16, &to[j], - sizeof(uint8_t)) == false) - return false; - i += 5; - } else if (s.buf[i] == '\\' && i + 1 < s.len) { - char c = json_esc(s.buf[i + 1], 0); - if (c == 0) return false; - to[j] = c; - i++; - } else { - to[j] = s.buf[i]; - } - } - if (j >= n) return false; - if (n > 0) to[j] = '\0'; - return true; -} - -char *mg_json_get_str(struct mg_str json, const char *path) { - char *result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && len > 1 && json.buf[off] == '"') { - if ((result = (char *) mg_calloc(1, (size_t) len)) != NULL && - !mg_json_unescape(mg_str_n(json.buf + off + 1, (size_t) (len - 2)), - result, (size_t) len)) { - mg_free(result); - result = NULL; - } - } - return result; -} - -char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) { - char *result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && json.buf[off] == '"' && len > 1 && - (result = (char *) mg_calloc(1, (size_t) len)) != NULL) { - size_t k = mg_base64_decode(json.buf + off + 1, (size_t) (len - 2), result, - (size_t) len); - if (slen != NULL) *slen = (int) k; - } - return result; -} - -char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) { - char *result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && json.buf[off] == '"' && len > 1 && - (result = (char *) mg_calloc(1, (size_t) len / 2)) != NULL) { - int i; - for (i = 0; i < len - 2; i += 2) { - mg_str_to_num(mg_str_n(json.buf + off + 1 + i, 2), 16, &result[i >> 1], - sizeof(uint8_t)); - } - result[len / 2 - 1] = '\0'; - if (slen != NULL) *slen = len / 2 - 1; - } - return result; -} - -long mg_json_get_long(struct mg_str json, const char *path, long dflt) { - double dv; - long result = dflt; - if (mg_json_get_num(json, path, &dv)) result = (long) dv; - return result; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/log.c" -#endif - - - - - -int mg_log_level = MG_LL_DEBUG; -static mg_pfn_t s_log_func = mg_pfn_stdout; -static void *s_log_func_param = NULL; - -void mg_log_set_fn(mg_pfn_t fn, void *param) { - s_log_func = fn; - s_log_func_param = param; -} - -static void logc(unsigned char c) { - s_log_func((char) c, s_log_func_param); -} - -static void logs(const char *buf, size_t len) { - size_t i; - for (i = 0; i < len; i++) logc(((unsigned char *) buf)[i]); -} - -#if MG_ENABLE_CUSTOM_LOG -// Let user define their own mg_log_prefix() and mg_log() -#else -void mg_log_prefix(int level, const char *file, int line, const char *fname) { - const char *p = strrchr(file, '/'); - char buf[41]; - size_t n; - if (p == NULL) p = strrchr(file, '\\'); - n = mg_snprintf(buf, sizeof(buf), "%-6llx %d %s:%d:%s", mg_millis(), level, - p == NULL ? file : p + 1, line, fname); - if (n > sizeof(buf) - 2) n = sizeof(buf) - 2; - while (n < sizeof(buf)) buf[n++] = ' '; - logs(buf, n - 1); -} - -void mg_log(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap); - va_end(ap); - logs("\r\n", 2); -} -#endif - -static unsigned char nibble(unsigned c) { - return (unsigned char) (c < 10 ? c + '0' : c + 'W'); -} - -#define ISPRINT(x) ((x) >= ' ' && (x) <= '~') -void mg_hexdump(const void *buf, size_t len) { - const unsigned char *p = (const unsigned char *) buf; - unsigned char ascii[16], alen = 0; - size_t i; - for (i = 0; i < len; i++) { - if ((i % 16) == 0) { - // Print buffered ascii chars - if (i > 0) - logs(" ", 2), logs((char *) ascii, 16), logs("\r\n", 2), alen = 0; - // Print hex address, then \t - logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)), - logc(nibble((i >> 4) & 15)), logc('0'), logs(" ", 3); - } - logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5 - logc(' '); // Space after hex number - ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf - } - while (alen < 16) logs(" ", 3), ascii[alen++] = ' '; - logs(" ", 2), logs((char *) ascii, 16), logs("\r\n", 2); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/md5.c" -#endif - - - -// This code implements the MD5 message-digest algorithm. -// The algorithm is due to Ron Rivest. This code was -// written by Colin Plumb in 1993, no copyright is claimed. -// This code is in the public domain; do with it what you wish. -// -// Equivalent code is available from RSA Data Security, Inc. -// This code has been tested against that, and is equivalent, -// except that you don't need to include two pages of legalese -// with every copy. -// -// To compute the message digest of a chunk of bytes, declare an -// MD5Context structure, pass it to MD5Init, call MD5Update as -// needed on buffers full of bytes, and then call MD5Final, which -// will fill a supplied 16-byte array with the digest. - -#if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5 - -static void mg_byte_reverse(unsigned char *buf, unsigned longs) { - if (MG_BIG_ENDIAN) { - do { - uint32_t t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(uint32_t *) buf = t; - buf += 4; - } while (--longs); - } else { - (void) buf, (void) longs; // Little endian. Do nothing - } -} - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, data, s) \ - (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void mg_md5_init(mg_md5_ctx *ctx) { - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) { - uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -void mg_md5_update(mg_md5_ctx *ctx, const unsigned char *buf, size_t len) { - uint32_t t; - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; - ctx->bits[1] += (uint32_t) len >> 29; - - t = (t >> 3) & 0x3f; - - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - buf += t; - len -= t; - } - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - buf += 64; - len -= 64; - } - - memcpy(ctx->in, buf, len); -} - -void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) { - unsigned count; - unsigned char *p; - uint32_t *a; - - count = (ctx->bits[0] >> 3) & 0x3F; - - p = ctx->in + count; - *p++ = 0x80; - count = 64 - 1 - count; - if (count < 8) { - memset(p, 0, count); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - memset(ctx->in, 0, 56); - } else { - memset(p, 0, count - 8); - } - mg_byte_reverse(ctx->in, 14); - - a = (uint32_t *) ctx->in; - a[14] = ctx->bits[0]; - a[15] = ctx->bits[1]; - - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - mg_byte_reverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char *) ctx, 0, sizeof(*ctx)); -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/mqtt.c" -#endif - - - - - - - - -#define MQTT_CLEAN_SESSION 0x02 -#define MQTT_HAS_WILL 0x04 -#define MQTT_WILL_RETAIN 0x20 -#define MQTT_HAS_PASSWORD 0x40 -#define MQTT_HAS_USER_NAME 0x80 - -struct mg_mqtt_pmap { - uint8_t id; - uint8_t type; -}; - -static const struct mg_mqtt_pmap s_prop_map[] = { - {MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY_DATA}, - {MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARIABLE_INT}, - {MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY_DATA}, - {MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR}, - {MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}}; - -void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags, - uint32_t len) { - uint8_t buf[1 + sizeof(len)], *vlen = &buf[1]; - buf[0] = (uint8_t) ((cmd << 4) | flags); - do { - *vlen = len % 0x80; - len /= 0x80; - if (len > 0) *vlen |= 0x80; - vlen++; - } while (len > 0 && vlen < &buf[sizeof(buf)]); - mg_send(c, buf, (size_t) (vlen - buf)); -} - -static void mg_send_u16(struct mg_connection *c, uint16_t value) { - mg_send(c, &value, sizeof(value)); -} - -static void mg_send_u32(struct mg_connection *c, uint32_t value) { - mg_send(c, &value, sizeof(value)); -} - -static uint8_t varint_size(size_t length) { - uint8_t bytes_needed = 0; - do { - bytes_needed++; - length /= 0x80; - } while (length > 0); - return bytes_needed; -} - -static size_t encode_varint(uint8_t *buf, size_t value) { - size_t len = 0; - - do { - uint8_t b = (uint8_t) (value % 128); - value /= 128; - if (value > 0) b |= 0x80; - buf[len++] = b; - } while (value > 0); - - return len; -} - -static size_t decode_varint(const uint8_t *buf, size_t len, size_t *value) { - size_t multiplier = 1, offset; - *value = 0; - - for (offset = 0; offset < 4 && offset < len; offset++) { - uint8_t encoded_byte = buf[offset]; - *value += (encoded_byte & 0x7f) * multiplier; - multiplier *= 128; - - if ((encoded_byte & 0x80) == 0) return offset + 1; - } - - return 0; -} - -static int mqtt_prop_type_by_id(uint8_t prop_id) { - size_t i, num_properties = sizeof(s_prop_map) / sizeof(s_prop_map[0]); - for (i = 0; i < num_properties; ++i) { - if (s_prop_map[i].id == prop_id) return s_prop_map[i].type; - } - return -1; // Property ID not found -} - -// Returns the size of the properties section, without the -// size of the content's length -static size_t get_properties_length(struct mg_mqtt_prop *props, size_t count) { - size_t i, size = 0; - for (i = 0; i < count; i++) { - size++; // identifier - switch (mqtt_prop_type_by_id(props[i].id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - size += (uint32_t) (props[i].val.len + props[i].key.len + - 2 * sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_STRING: - size += (uint32_t) (props[i].val.len + sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_BINARY_DATA: - size += (uint32_t) (props[i].val.len + sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - size += varint_size((uint32_t) props[i].iv); - break; - case MQTT_PROP_TYPE_INT: - size += (uint32_t) sizeof(uint32_t); - break; - case MQTT_PROP_TYPE_SHORT: - size += (uint32_t) sizeof(uint16_t); - break; - case MQTT_PROP_TYPE_BYTE: - size += (uint32_t) sizeof(uint8_t); - break; - default: - return size; // cannot parse further down - } - } - - return size; -} - -// returns the entire size of the properties section, including the -// size of the variable length of the content -static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) { - size_t size = get_properties_length(props, count); - size += varint_size(size); - return size; -} - -static void mg_send_mqtt_properties(struct mg_connection *c, - struct mg_mqtt_prop *props, size_t nprops) { - size_t total_size = get_properties_length(props, nprops); - uint8_t buf_v[4] = {0, 0, 0, 0}; - uint8_t buf[4] = {0, 0, 0, 0}; - size_t i, len = encode_varint(buf, total_size); - - mg_send(c, buf, (size_t) len); - for (i = 0; i < nprops; i++) { - mg_send(c, &props[i].id, sizeof(props[i].id)); - switch (mqtt_prop_type_by_id(props[i].id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - mg_send_u16(c, mg_htons((uint16_t) props[i].key.len)); - mg_send(c, props[i].key.buf, props[i].key.len); - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); - break; - case MQTT_PROP_TYPE_BYTE: - mg_send(c, &props[i].iv, sizeof(uint8_t)); - break; - case MQTT_PROP_TYPE_SHORT: - mg_send_u16(c, mg_htons((uint16_t) props[i].iv)); - break; - case MQTT_PROP_TYPE_INT: - mg_send_u32(c, mg_htonl((uint32_t) props[i].iv)); - break; - case MQTT_PROP_TYPE_STRING: - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); - break; - case MQTT_PROP_TYPE_BINARY_DATA: - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - len = encode_varint(buf_v, props[i].iv); - mg_send(c, buf_v, (size_t) len); - break; - } - } -} - -size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop, - size_t ofs) { - uint8_t *i = (uint8_t *) msg->dgram.buf + msg->props_start + ofs; - uint8_t *end = (uint8_t *) msg->dgram.buf + msg->dgram.len; - size_t new_pos = ofs, len; - prop->id = i[0]; - - if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size) - return 0; - i++, new_pos++; - - switch (mqtt_prop_type_by_id(prop->id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->key.buf = (char *) i + 2; - i += 2 + prop->key.len; - prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->val.buf = (char *) i + 2; - new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len; - break; - case MQTT_PROP_TYPE_BYTE: - prop->iv = (uint8_t) i[0]; - new_pos++; - break; - case MQTT_PROP_TYPE_SHORT: - prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - new_pos += sizeof(uint16_t); - break; - case MQTT_PROP_TYPE_INT: - prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) | - ((uint32_t) i[2] << 8) | i[3]; - new_pos += sizeof(uint32_t); - break; - case MQTT_PROP_TYPE_STRING: - prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->val.buf = (char *) i + 2; - new_pos += 2 + prop->val.len; - break; - case MQTT_PROP_TYPE_BINARY_DATA: - prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->val.buf = (char *) i + 2; - new_pos += 2 + prop->val.len; - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - len = decode_varint(i, (size_t) (end - i), (size_t *) &prop->iv); - new_pos = (!len) ? 0 : new_pos + len; - break; - default: - new_pos = 0; - } - - return new_pos; -} - -void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) { - char client_id[21]; - struct mg_str cid = opts->client_id; - size_t total_len = 7 + 1 + 2 + 2; - uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0}; - - if (cid.len == 0) { - mg_random_str(client_id, sizeof(client_id) - 1); - client_id[sizeof(client_id) - 1] = '\0'; - cid = mg_str(client_id); - } - - if (hdr[6] == 0) hdr[6] = 4; // If version is not set, use 4 (3.1.1) - c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag - hdr[7] = (uint8_t) ((opts->qos & 3) << 3); // Connection flags - if (opts->user.len > 0) { - total_len += 2 + (uint32_t) opts->user.len; - hdr[7] |= MQTT_HAS_USER_NAME; - } - if (opts->pass.len > 0) { - total_len += 2 + (uint32_t) opts->pass.len; - hdr[7] |= MQTT_HAS_PASSWORD; - } - if (opts->topic.len > 0) { // allow zero-length msgs, message.len is size_t - total_len += 4 + (uint32_t) opts->topic.len + (uint32_t) opts->message.len; - hdr[7] |= MQTT_HAS_WILL; - } - if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION; - if (opts->retain) hdr[7] |= MQTT_WILL_RETAIN; - total_len += (uint32_t) cid.len; - if (c->is_mqtt5) { - total_len += get_props_size(opts->props, opts->num_props); - if (hdr[7] & MQTT_HAS_WILL) - total_len += get_props_size(opts->will_props, opts->num_will_props); - } - - mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len); - mg_send(c, hdr, sizeof(hdr)); - // keepalive == 0 means "do not disconnect us!" - mg_send_u16(c, mg_htons((uint16_t) opts->keepalive)); - - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); - - mg_send_u16(c, mg_htons((uint16_t) cid.len)); - mg_send(c, cid.buf, cid.len); - - if (hdr[7] & MQTT_HAS_WILL) { - if (c->is_mqtt5) - mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props); - - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - mg_send_u16(c, mg_htons((uint16_t) opts->message.len)); - mg_send(c, opts->message.buf, opts->message.len); - } - if (opts->user.len > 0) { - mg_send_u16(c, mg_htons((uint16_t) opts->user.len)); - mg_send(c, opts->user.buf, opts->user.len); - } - if (opts->pass.len > 0) { - mg_send_u16(c, mg_htons((uint16_t) opts->pass.len)); - mg_send(c, opts->pass.buf, opts->pass.len); - } -} - -uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { - uint16_t id = opts->retransmit_id; - uint8_t flags = (uint8_t) (((opts->qos & 3) << 1) | (opts->retain ? 1 : 0)); - size_t len = 2 + opts->topic.len + opts->message.len; - MG_DEBUG(("%lu [%.*s] <- [%.*s%c", c->id, (int) opts->topic.len, - (char *) opts->topic.buf, - (int) (opts->message.len <= 10 ? opts->message.len : 10), - (char *) opts->message.buf, opts->message.len <= 10 ? ']' : ' ')); - if (opts->qos > 0) len += 2; - if (c->is_mqtt5) len += get_props_size(opts->props, opts->num_props); - - if (opts->qos > 0 && id != 0) flags |= 1 << 3; - mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len); - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - if (opts->qos > 0) { // need to send 'id' field - if (id == 0) { // generate new one if not resending - if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; - id = c->mgr->mqtt_id; - } - mg_send_u16(c, mg_htons(id)); - } - - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); - - if (opts->message.len > 0) mg_send(c, opts->message.buf, opts->message.len); - return id; -} - -void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { - uint8_t qos_ = opts->qos & 3; - size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0; - size_t len = 2 + opts->topic.len + 2 + 1 + plen; - - mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len); - if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; - mg_send_u16(c, mg_htons(c->mgr->mqtt_id)); - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); - - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - mg_send(c, &qos_, sizeof(qos_)); -} - -int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version, - struct mg_mqtt_message *m) { - uint8_t lc = 0, *p, *end; - uint32_t n = 0, len_len = 0; - - memset(m, 0, sizeof(*m)); - m->dgram.buf = (char *) buf; - if (len < 2) return MQTT_INCOMPLETE; - m->cmd = (uint8_t) (buf[0] >> 4); - m->qos = (buf[0] >> 1) & 3; - - n = len_len = 0; - p = (uint8_t *) buf + 1; - while ((size_t) (p - buf) < len) { - lc = *((uint8_t *) p++); - n += (uint32_t) ((lc & 0x7f) << 7 * len_len); - len_len++; - if (!(lc & 0x80)) break; - if (len_len >= 4) return MQTT_MALFORMED; - } - end = p + n; - if ((lc & 0x80) || (end > buf + len)) return MQTT_INCOMPLETE; - m->dgram.len = (size_t) (end - buf); - - switch (m->cmd) { - case MQTT_CMD_CONNACK: - if (end - p < 2) return MQTT_MALFORMED; - m->ack = p[1]; - break; - case MQTT_CMD_PUBACK: - case MQTT_CMD_PUBREC: - case MQTT_CMD_PUBREL: - case MQTT_CMD_PUBCOMP: - case MQTT_CMD_SUBSCRIBE: - case MQTT_CMD_SUBACK: - case MQTT_CMD_UNSUBSCRIBE: - case MQTT_CMD_UNSUBACK: - if (p + 2 > end) return MQTT_MALFORMED; - m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); - p += 2; - break; - case MQTT_CMD_PUBLISH: { - if (p + 2 > end) return MQTT_MALFORMED; - m->topic.len = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); - m->topic.buf = (char *) p + 2; - p += 2 + m->topic.len; - if (p > end) return MQTT_MALFORMED; - if (m->qos > 0) { - if (p + 2 > end) return MQTT_MALFORMED; - m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); - p += 2; - } - if (p > end) return MQTT_MALFORMED; - if (version == 5 && p + 2 < end) { - len_len = - (uint32_t) decode_varint(p, (size_t) (end - p), &m->props_size); - if (!len_len) return MQTT_MALFORMED; - m->props_start = (size_t) (p + len_len - buf); - p += len_len + m->props_size; - } - if (p > end) return MQTT_MALFORMED; - m->data.buf = (char *) p; - m->data.len = (size_t) (end - p); - break; - } - default: - break; - } - return MQTT_OK; -} - -static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ) { - for (;;) { - uint8_t version = c->is_mqtt5 ? 5 : 4; - struct mg_mqtt_message mm; - int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm); - if (rc == MQTT_MALFORMED) { - MG_ERROR(("%lu MQTT malformed message", c->id)); - c->is_closing = 1; - break; - } else if (rc == MQTT_OK) { - MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd, - (int) mm.dgram.len, (int) mm.data.len, mm.data.buf)); - switch (mm.cmd) { - case MQTT_CMD_CONNACK: - mg_call(c, MG_EV_MQTT_OPEN, &mm.ack); - if (mm.ack == 0) { - MG_DEBUG(("%lu Connected", c->id)); - } else { - MG_ERROR(("%lu MQTT auth failed, code %d", c->id, mm.ack)); - c->is_closing = 1; - } - break; - case MQTT_CMD_PUBLISH: { - MG_DEBUG(("%lu [%.*s] -> [%.*s%c", c->id, (int) mm.topic.len, - mm.topic.buf, - (int) (mm.data.len <= 10 ? mm.data.len : 10), mm.data.buf, - mm.data.len <= 10 ? ']' : ' ')); - if (mm.qos > 0) { - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); - if (c->is_mqtt5) remaining_len += 2; // 3.4.2 - - mg_mqtt_send_header( - c, - (uint8_t) (mm.qos == 2 ? MQTT_CMD_PUBREC : MQTT_CMD_PUBACK), - 0, remaining_len); - mg_send(c, &id, sizeof(id)); - - if (c->is_mqtt5) { - uint16_t zero = 0; - mg_send(c, &zero, sizeof(zero)); - } - } - mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff - break; - } - case MQTT_CMD_PUBREC: { // MQTT5: 3.5.2-1 TODO(): variable header rc - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBREL, 2, remaining_len); - mg_send(c, &id, sizeof(id)); // MQTT5 3.6.1-1, flags = 2 - break; - } - case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); // MQTT5 3.7.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len); - mg_send(c, &id, sizeof(id)); - break; - } - } - mg_call(c, MG_EV_MQTT_CMD, &mm); - mg_iobuf_del(&c->recv, 0, mm.dgram.len); - } else { - break; - } - } - } - (void) ev_data; -} - -void mg_mqtt_ping(struct mg_connection *nc) { - mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0); -} - -void mg_mqtt_pong(struct mg_connection *nc) { - mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0); -} - -void mg_mqtt_disconnect(struct mg_connection *c, - const struct mg_mqtt_opts *opts) { - size_t len = 0; - if (c->is_mqtt5) len = 1 + get_props_size(opts->props, opts->num_props); - mg_mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len); - - if (c->is_mqtt5) { - uint8_t zero = 0; - mg_send(c, &zero, sizeof(zero)); // reason code - mg_send_mqtt_properties(c, opts->props, opts->num_props); - } -} - -struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url, - const struct mg_mqtt_opts *opts, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_connect_svc(mgr, url, fn, fn_data, mqtt_cb, NULL); - if (c != NULL) { - struct mg_mqtt_opts empty; - memset(&empty, 0, sizeof(empty)); - mg_mqtt_login(c, opts == NULL ? &empty : opts); - } - return c; -} - -struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); - if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr; - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/net.c" -#endif - - - - - - - - - -size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) { - size_t old = c->send.len; - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); - return c->send.len - old; -} - -size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { - size_t len = 0; - va_list ap; - va_start(ap, fmt); - len = mg_vprintf(c, fmt, &ap); - va_end(ap); - return len; -} - -static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { - uint32_t localhost = mg_htonl(0x7f000001); - if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false; - memcpy(addr->ip, &localhost, sizeof(uint32_t)); - addr->is_ip6 = false; - return true; -} - -static bool mg_atone(struct mg_str str, struct mg_addr *addr) { - if (str.len > 0) return false; - memset(addr->ip, 0, sizeof(addr->ip)); - addr->is_ip6 = false; - return true; -} - -static bool mg_aton4(struct mg_str str, struct mg_addr *addr) { - uint8_t data[4] = {0, 0, 0, 0}; - size_t i, num_dots = 0; - for (i = 0; i < str.len; i++) { - if (str.buf[i] >= '0' && str.buf[i] <= '9') { - int octet = data[num_dots] * 10 + (str.buf[i] - '0'); - if (octet > 255) return false; - data[num_dots] = (uint8_t) octet; - } else if (str.buf[i] == '.') { - if (num_dots >= 3 || i == 0 || str.buf[i - 1] == '.') return false; - num_dots++; - } else { - return false; - } - } - if (num_dots != 3 || str.buf[i - 1] == '.') return false; - memcpy(&addr->ip, data, sizeof(data)); - addr->is_ip6 = false; - return true; -} - -static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) { - int i; - uint32_t ipv4; - if (str.len < 14) return false; - if (str.buf[0] != ':' || str.buf[1] != ':' || str.buf[6] != ':') return false; - for (i = 2; i < 6; i++) { - if (str.buf[i] != 'f' && str.buf[i] != 'F') return false; - } - // struct mg_str s = mg_str_n(&str.buf[7], str.len - 7); - if (!mg_aton4(mg_str_n(&str.buf[7], str.len - 7), addr)) return false; - memcpy(&ipv4, addr->ip, sizeof(ipv4)); - memset(addr->ip, 0, sizeof(addr->ip)); - addr->ip[10] = addr->ip[11] = 255; - memcpy(&addr->ip[12], &ipv4, 4); - addr->is_ip6 = true; - return true; -} - -static bool mg_aton6(struct mg_str str, struct mg_addr *addr) { - size_t i, j = 0, n = 0, dc = 42; - addr->scope_id = 0; - if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2; - if (mg_v4mapped(str, addr)) return true; - for (i = 0; i < str.len; i++) { - if ((str.buf[i] >= '0' && str.buf[i] <= '9') || - (str.buf[i] >= 'a' && str.buf[i] <= 'f') || - (str.buf[i] >= 'A' && str.buf[i] <= 'F')) { - unsigned long val = 0; // TODO(): This loops on chars, refactor - if (i > j + 3) return false; - // MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.buf[j])); - mg_str_to_num(mg_str_n(&str.buf[j], i - j + 1), 16, &val, sizeof(val)); - addr->ip[n] = (uint8_t) ((val >> 8) & 255); - addr->ip[n + 1] = (uint8_t) (val & 255); - } else if (str.buf[i] == ':') { - j = i + 1; - if (i > 0 && str.buf[i - 1] == ':') { - dc = n; // Double colon - if (i > 1 && str.buf[i - 2] == ':') return false; - } else if (i > 0) { - n += 2; - } - if (n > 14) return false; - addr->ip[n] = addr->ip[n + 1] = 0; // For trailing :: - } else if (str.buf[i] == '%') { // Scope ID, last in string - return mg_str_to_num(mg_str_n(&str.buf[i + 1], str.len - i - 1), 10, - &addr->scope_id, sizeof(uint8_t)); - } else { - return false; - } - } - if (n < 14 && dc == 42) return false; - if (n < 14) { - memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2); - memset(&addr->ip[dc], 0, 14 - n); - } - - addr->is_ip6 = true; - return true; -} - -bool mg_aton(struct mg_str str, struct mg_addr *addr) { - // MG_INFO(("[%.*s]", (int) str.len, str.buf)); - return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) || - mg_aton6(str, addr); -} - -struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) { - struct mg_connection *c = - (struct mg_connection *) mg_calloc(1, sizeof(*c) + mgr->extraconnsize); - if (c != NULL) { - c->mgr = mgr; - c->send.align = c->recv.align = c->rtls.align = MG_IO_SIZE; - c->id = ++mgr->nextid; - MG_PROF_INIT(c); - } - return c; -} - -void mg_close_conn(struct mg_connection *c) { - mg_resolve_cancel(c); // Close any pending DNS query - LIST_DELETE(struct mg_connection, &c->mgr->conns, c); - if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL; - if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL; - // Order of operations is important. `MG_EV_CLOSE` event must be fired - // before we deallocate received data, see #1331 - mg_call(c, MG_EV_CLOSE, NULL); - MG_DEBUG(("%lu %ld closed", c->id, c->fd)); - MG_PROF_DUMP(c); - MG_PROF_FREE(c); - - mg_tls_free(c); - mg_iobuf_free(&c->recv); - mg_iobuf_free(&c->send); - mg_iobuf_free(&c->rtls); - mg_bzero((unsigned char *) c, sizeof(*c)); - mg_free(c); -} - -struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data, - mg_event_handler_t pfn, void *pfn_data) { - struct mg_connection *c = NULL; - if (url == NULL || url[0] == '\0') { - MG_ERROR(("null url")); - } else if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM")); - } else { - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->is_udp = (strncmp(url, "udp:", 4) == 0); - c->fd = (void *) (size_t) MG_INVALID_SOCKET; - c->fn = fn; - c->is_client = true; - c->fn_data = fn_data; - c->is_tls = (mg_url_is_ssl(url) != 0); - c->pfn = pfn; - c->pfn_data = pfn_data; - mg_call(c, MG_EV_OPEN, (void *) url); - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - mg_resolve(c, url); - } - return c; -} - -struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - return mg_connect_svc(mgr, url, fn, fn_data, NULL, NULL); -} - -struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = NULL; - if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM %s", url)); - } else if (!mg_open_listener(c, url)) { - MG_ERROR(("Failed: %s", url)); - MG_PROF_FREE(c); - mg_free(c); - c = NULL; - } else { - c->is_listening = 1; - c->is_udp = strncmp(url, "udp:", 4) == 0; - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->fn = fn; - c->fn_data = fn_data; - c->is_tls = (mg_url_is_ssl(url) != 0); - mg_call(c, MG_EV_OPEN, NULL); - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - } - return c; -} - -struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_alloc_conn(mgr); - if (c != NULL) { - c->fd = (void *) (size_t) fd; - c->fn = fn; - c->fn_data = fn_data; - MG_EPOLL_ADD(c); - mg_call(c, MG_EV_OPEN, NULL); - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - } - return c; -} - -struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, - unsigned flags, void (*fn)(void *), void *arg) { - struct mg_timer *t = (struct mg_timer *) mg_calloc(1, sizeof(*t)); - if (t != NULL) { - flags |= MG_TIMER_AUTODELETE; // We have alloc'ed it, so autodelete - mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg); - } - return t; -} - -long mg_io_recv(struct mg_connection *c, void *buf, size_t len) { - if (c->rtls.len == 0) return MG_IO_WAIT; - if (len > c->rtls.len) len = c->rtls.len; - memcpy(buf, c->rtls.buf, len); - mg_iobuf_del(&c->rtls, 0, len); - return (long) len; -} - -void mg_mgr_free(struct mg_mgr *mgr) { - struct mg_connection *c; - struct mg_timer *tmp, *t = mgr->timers; - while (t != NULL) tmp = t->next, mg_free(t), t = tmp; - mgr->timers = NULL; // Important. Next call to poll won't touch timers - for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1; - mg_mgr_poll(mgr, 0); -#if MG_ENABLE_FREERTOS_TCP - FreeRTOS_DeleteSocketSet(mgr->ss); -#endif - MG_DEBUG(("All connections closed")); -#if MG_ENABLE_EPOLL - if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1; -#endif - mg_tls_ctx_free(mgr); -#if MG_ENABLE_TCPIP - if (mgr->ifp) mg_tcpip_free(mgr->ifp); -#endif -} - -void mg_mgr_init(struct mg_mgr *mgr) { - memset(mgr, 0, sizeof(*mgr)); -#if MG_ENABLE_EPOLL - if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) - MG_ERROR(("epoll_create1 errno %d", errno)); -#else - mgr->epoll_fd = -1; -#endif -#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK - // clang-format off - { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } - // clang-format on -#elif MG_ENABLE_FREERTOS_TCP - mgr->ss = FreeRTOS_CreateSocketSet(); -#elif MG_ARCH == MG_ARCH_UNIX - // Ignore SIGPIPE signal, so if client cancels the request, it - // won't kill the whole process. - signal(SIGPIPE, SIG_IGN); -#elif MG_ENABLE_TCPIP_DRIVER_INIT && defined(MG_TCPIP_DRIVER_INIT) - MG_TCPIP_DRIVER_INIT(mgr); -#endif - mgr->pipe = MG_INVALID_SOCKET; - mgr->dnstimeout = 3000; - mgr->dns4.url = "udp://8.8.8.8:53"; - mgr->dns6.url = "udp://[2001:4860:4860::8888]:53"; - mg_tls_ctx_init(mgr); - MG_DEBUG(("MG_IO_SIZE: %lu, TLS: %s", MG_IO_SIZE, - MG_TLS == MG_TLS_NONE ? "none" - : MG_TLS == MG_TLS_MBED ? "MbedTLS" - : MG_TLS == MG_TLS_OPENSSL ? "OpenSSL" - : MG_TLS == MG_TLS_BUILTIN ? "builtin" - : MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL" - : "custom")); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/net_builtin.c" -#endif - - - -#if MG_ENABLE_TCPIP -#define MG_EPHEMERAL_PORT_BASE 32768 -#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a)))) - -#ifndef MIP_TCP_KEEPALIVE_MS -#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms -#endif - -#define MIP_TCP_ACK_MS 150 // Timeout for ACKing -#define MIP_ARP_RESP_MS 100 // Timeout for ARP response -#define MIP_TCP_SYN_MS 15000 // Timeout for connection establishment -#define MIP_TCP_FIN_MS 1000 // Timeout for closing connection -#define MIP_TCP_WIN 6000 // TCP window size - -struct connstate { - uint32_t seq, ack; // TCP seq/ack counters - uint64_t timer; // TCP timer (see 'ttype' below) - uint32_t acked; // Last ACK-ed number - size_t unacked; // Not acked bytes - uint16_t dmss; // destination MSS (from TCP opts) - uint8_t mac[6]; // Peer MAC address - uint8_t ttype; // Timer type: -#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive -#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon -#define MIP_TTYPE_ARP 2 // ARP resolve sent, waiting for response -#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response -#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection - uint8_t tmiss; // Number of keep-alive misses - struct mg_iobuf raw; // For TLS only. Incoming raw data - bool fin_rcvd; // We have received FIN from the peer - bool twclosure; // 3-way closure done -}; - -#pragma pack(push, 1) - -struct lcp { - uint8_t addr, ctrl, proto[2], code, id, len[2]; -}; - -struct eth { - uint8_t dst[6]; // Destination MAC address - uint8_t src[6]; // Source MAC address - uint16_t type; // Ethernet type -}; - -struct ip { - uint8_t ver; // Version - uint8_t tos; // Unused - uint16_t len; // Datagram length - uint16_t id; // Unused - uint16_t frag; // Fragmentation -#define IP_FRAG_OFFSET_MSK 0x1fff -#define IP_MORE_FRAGS_MSK 0x2000 - uint8_t ttl; // Time to live - uint8_t proto; // Upper level protocol - uint16_t csum; // Checksum - uint32_t src; // Source IP - uint32_t dst; // Destination IP -}; - -struct ip6 { - uint8_t ver; // Version - uint8_t label[3]; // Flow label - uint16_t plen; // Payload length - uint8_t next; // Upper level protocol - uint8_t hops; // Hop limit - uint8_t src[16]; // Source IP - uint8_t dst[16]; // Destination IP -}; - -struct icmp { - uint8_t type; - uint8_t code; - uint16_t csum; -}; - -struct icmp6 { - uint8_t type; - uint8_t code; - uint16_t csum; -}; - -struct arp { - uint16_t fmt; // Format of hardware address - uint16_t pro; // Format of protocol address - uint8_t hlen; // Length of hardware address - uint8_t plen; // Length of protocol address - uint16_t op; // Operation - uint8_t sha[6]; // Sender hardware address - uint32_t spa; // Sender protocol address - uint8_t tha[6]; // Target hardware address - uint32_t tpa; // Target protocol address -}; - -struct tcp { - uint16_t sport; // Source port - uint16_t dport; // Destination port - uint32_t seq; // Sequence number - uint32_t ack; // Acknowledgement number - uint8_t off; // Data offset - uint8_t flags; // TCP flags -#define TH_FIN 0x01 -#define TH_SYN 0x02 -#define TH_RST 0x04 -#define TH_PUSH 0x08 -#define TH_ACK 0x10 -#define TH_URG 0x20 -#define TH_ECE 0x40 -#define TH_CWR 0x80 - uint16_t win; // Window - uint16_t csum; // Checksum - uint16_t urp; // Urgent pointer -}; - -struct udp { - uint16_t sport; // Source port - uint16_t dport; // Destination port - uint16_t len; // UDP length - uint16_t csum; // UDP checksum -}; - -struct dhcp { - uint8_t op, htype, hlen, hops; - uint32_t xid; - uint16_t secs, flags; - uint32_t ciaddr, yiaddr, siaddr, giaddr; - uint8_t hwaddr[208]; - uint32_t magic; - uint8_t options[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)]; -}; - -struct dhcp6 { - union { - uint8_t type; - uint32_t xid; - }; - uint8_t options[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)]; -}; - -#pragma pack(pop) - -struct pkt { - struct mg_str raw; // Raw packet data - struct mg_str pay; // Payload data - struct eth *eth; - struct llc *llc; - struct arp *arp; - struct ip *ip; - struct ip6 *ip6; - struct icmp *icmp; - struct icmp6 *icmp6; - struct tcp *tcp; - struct udp *udp; - struct dhcp *dhcp; - struct dhcp6 *dhcp6; -}; - -static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) { -#if MG_ENABLE_PROFILE - const char *names[] = { - "TCPIP_EV_ST_CHG", - "TCPIP_EV_DHCP_DNS", - "TCPIP_EV_DHCP_SNTP", - "TCPIP_EV_ARP", - "TCPIP_EV_TIMER_1S", - "TCPIP_EV_WIFI_SCAN_RESULT", - "TCPIP_EV_WIFI_SCAN_END", - "TCPIP_EV_WIFI_CONNECT_ERR", - "TCPIP_EV_DRIVER", - "TCPIP_EV_USER" - }; - if (ev != MG_TCPIP_EV_POLL && ev < (int) (sizeof(names) / sizeof(names[0]))) { - MG_PROF_ADD(c, names[ev]); - } -#endif - // Fire protocol handler first, user handler second. See #2559 - if (ifp->pfn != NULL) ifp->pfn(ifp, ev, ev_data); - if (ifp->fn != NULL) ifp->fn(ifp, ev, ev_data); -} - -static void send_syn(struct mg_connection *c); - -static void mkpay(struct pkt *pkt, void *p) { - pkt->pay = - mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p)); -} - -static uint32_t csumup(uint32_t sum, const void *buf, size_t len) { - size_t i; - const uint8_t *p = (const uint8_t *) buf; - for (i = 0; i < len; i++) sum += i & 1 ? p[i] : ((uint32_t) p[i]) << 8; - return sum; -} - -static uint16_t csumfin(uint32_t sum) { - while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); - return mg_htons((uint16_t) ((uint16_t) ~sum & 0xffff)); -} - -static uint16_t ipcsum(const void *buf, size_t len) { - uint32_t sum = csumup(0, buf, len); - return csumfin(sum); -} - -static void settmout(struct mg_connection *c, uint8_t type) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - struct connstate *s = (struct connstate *) (c + 1); - unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS - : type == MIP_TTYPE_ARP ? MIP_ARP_RESP_MS - : type == MIP_TTYPE_SYN ? MIP_TCP_SYN_MS - : type == MIP_TTYPE_FIN ? MIP_TCP_FIN_MS - : MIP_TCP_KEEPALIVE_MS; - if (s->ttype == MIP_TTYPE_FIN) return; // skip if 3-way closing - s->timer = ifp->now + n; - s->ttype = type; - MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer)); -} - -static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) { - size_t n = ifp->driver->tx(ifp->tx.buf, len, ifp); - if (n == len) ifp->nsent++; - return n; -} - -void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac) { - struct eth *eth = (struct eth *) ifp->tx.buf; - struct arp *arp = (struct arp *) (eth + 1); - memset(eth->dst, 255, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); - eth->type = mg_htons(0x806); - memset(arp, 0, sizeof(*arp)); - arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6, - arp->plen = 4; - arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip; - memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); - if (mac != NULL) memcpy(arp->tha, mac, sizeof(arp->tha)); - ether_output(ifp, PDIFF(eth, arp + 1)); -} - -static void onstatechange(struct mg_tcpip_if *ifp) { - if (ifp->state == MG_TCPIP_STATE_READY) { - MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); - MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); - MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); - } else if (ifp->state == MG_TCPIP_STATE_IP) { - mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request - } else if (ifp->state == MG_TCPIP_STATE_UP) { - srand((unsigned int) mg_millis()); - } else if (ifp->state == MG_TCPIP_STATE_DOWN) { - MG_ERROR(("Link down")); - } - mg_tcpip_call(ifp, MG_TCPIP_EV_ST_CHG, &ifp->state); -} - -static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst, - uint8_t proto, uint32_t ip_src, uint32_t ip_dst, - size_t plen) { - struct eth *eth = (struct eth *) ifp->tx.buf; - struct ip *ip = (struct ip *) (eth + 1); - memcpy(eth->dst, mac_dst, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC - eth->type = mg_htons(0x800); - memset(ip, 0, sizeof(*ip)); - ip->ver = 0x45; // Version 4, header length 5 words - ip->frag = mg_htons(0x4000); // Don't fragment - ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen)); - ip->ttl = 64; - ip->proto = proto; - ip->src = ip_src; - ip->dst = ip_dst; - ip->csum = ipcsum(ip, sizeof(*ip)); - return ip; -} - -static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, - uint16_t sport, uint32_t ip_dst, uint16_t dport, - const void *buf, size_t len) { - struct ip *ip = - tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp)); - struct udp *udp = (struct udp *) (ip + 1); - size_t eth_len; - uint32_t cs; - // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len)); - udp->sport = sport; - udp->dport = dport; - udp->len = mg_htons((uint16_t) (sizeof(*udp) + len)); - udp->csum = 0; - cs = csumup(0, udp, sizeof(*udp)); - cs = csumup(cs, buf, len); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs += (uint32_t) (ip->proto + sizeof(*udp) + len); - udp->csum = csumfin(cs); - memmove(udp + 1, buf, len); - // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len)); - eth_len = sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len; - return (ether_output(ifp, eth_len) == eth_len); -} - -static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, - uint32_t ip_dst, uint8_t *opts, size_t optslen, - bool ciaddr) { - // https://datatracker.ietf.org/doc/html/rfc2132#section-9.6 - struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; - dhcp.magic = mg_htonl(0x63825363); - memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac)); - memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid)); - memcpy(&dhcp.options, opts, optslen); - if (ciaddr) dhcp.ciaddr = ip_src; - tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp, - sizeof(dhcp)); -} - -static const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; - -// RFC-2131 #4.3.6, #4.4.1; RFC-2132 #9.8 -static void tx_dhcp_request_sel(struct mg_tcpip_if *ifp, uint32_t ip_req, - uint32_t ip_srv) { - uint8_t extra = (uint8_t) ((ifp->enable_req_dns ? 1 : 0) + - (ifp->enable_req_sntp ? 1 : 0)); - size_t len = strlen(ifp->dhcp_name); - size_t olen = 21 + len + extra + 2 + 1; // Total length of options -#define OPTS_MAXLEN (21 + sizeof(ifp->dhcp_name) + 2 + 2 + 1) - uint8_t opts[OPTS_MAXLEN]; // Allocate options (max size possible) - uint8_t *p = opts; - assert(olen <= sizeof(opts)); - memset(opts, 0, sizeof(opts)); - *p++ = 53, *p++ = 1, *p++ = 3; // Type: DHCP request - *p++ = 54, *p++ = 4, memcpy(p, &ip_srv, 4), p += 4; // DHCP server ID - *p++ = 50, *p++ = 4, memcpy(p, &ip_req, 4), p += 4; // Requested IP - *p++ = 12, *p++ = (uint8_t) (len & 255); // DHCP host - memcpy(p, ifp->dhcp_name, len), p += len; // name - *p++ = 55, *p++ = 2 + extra, *p++ = 1, *p++ = 3; // GW, MASK - if (ifp->enable_req_dns) *p++ = 6; // DNS - if (ifp->enable_req_sntp) *p++ = 42; // SNTP - *p++ = 255; // End of options - // assert((size_t) (p - opts) < olen); - tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, olen, 0); - MG_DEBUG(("DHCP req sent")); -} - -// RFC-2131 #4.3.6, #4.4.5 (renewing: unicast, rebinding: bcast) -static void tx_dhcp_request_re(struct mg_tcpip_if *ifp, uint8_t *mac_dst, - uint32_t ip_src, uint32_t ip_dst) { - uint8_t opts[] = { - 53, 1, 3, // Type: DHCP request - 255 // End of options - }; - tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts), true); - MG_DEBUG(("DHCP req sent")); -} - -static void tx_dhcp_discover(struct mg_tcpip_if *ifp) { - uint8_t opts[] = { - 53, 1, 1, // Type: DHCP discover - 55, 2, 1, 3, // Parameters: ip, mask - 255 // End of options - }; - tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, sizeof(opts), false); - MG_DEBUG(("DHCP discover sent. Our MAC: %M", mg_print_mac, ifp->mac)); -} - -static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, - bool lsn) { - struct mg_connection *c = NULL; - for (c = mgr->conns; c != NULL; c = c->next) { - if (c->is_arplooking && pkt->arp && - memcmp(&pkt->arp->spa, c->rem.ip, sizeof(pkt->arp->spa)) == 0) - break; - if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break; - if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport && - lsn == (bool) c->is_listening && - (lsn || c->rem.port == pkt->tcp->sport)) - break; - } - return c; -} - -static void mac_resolved(struct mg_connection *c); - -static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) { - // ARP request. Make a response, then send - // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4, - // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa)); - struct eth *eth = (struct eth *) ifp->tx.buf; - struct arp *arp = (struct arp *) (eth + 1); - memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); - eth->type = mg_htons(0x806); - *arp = *pkt->arp; - arp->op = mg_htons(2); - memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha)); - memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha)); - arp->tpa = pkt->arp->spa; - arp->spa = ifp->ip; - MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_mac, - &ifp->mac)); - ether_output(ifp, PDIFF(eth, arp + 1)); - } else if (pkt->arp->op == mg_htons(2)) { - if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return; - if (pkt->arp->spa == ifp->gw) { - // Got response for the GW ARP request. Set ifp->gwmac and IP -> READY - memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac)); - if (ifp->state == MG_TCPIP_STATE_IP) { - ifp->state = MG_TCPIP_STATE_READY; - onstatechange(ifp); - } - } else { - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); - if (c != NULL && c->is_arplooking) { - struct connstate *s = (struct connstate *) (c + 1); - memcpy(s->mac, pkt->arp->sha, sizeof(s->mac)); - MG_DEBUG(("%lu ARP resolved %M -> %M", c->id, mg_print_ip4, c->rem.ip, - mg_print_mac, s->mac)); - c->is_arplooking = 0; - mac_resolved(c); - } - } - } -} - -static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("ICMP %d", (int) len)); - if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) { - size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp); - size_t space = ifp->tx.len - hlen, plen = pkt->pay.len; - struct ip *ip; - struct icmp *icmp; - if (plen > space) plen = space; - ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, - sizeof(*icmp) + plen); - icmp = (struct icmp *) (ip + 1); - memset(icmp, 0, sizeof(*icmp)); // Set csum to 0 - memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX - icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen); - ether_output(ifp, hlen + plen); - } -} - -static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip); - -static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint32_t ip = 0, gw = 0, mask = 0, lease = 0, dns = 0, sntp = 0; - uint8_t msgtype = 0, state = ifp->state; - // perform size check first, then access fields - uint8_t *p = pkt->dhcp->options, - *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len]; - if (end < (uint8_t *) (pkt->dhcp + 1)) return; - if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return; - while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9 - if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask - memcpy(&mask, p + 2, sizeof(mask)); - } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW - memcpy(&gw, p + 2, sizeof(gw)); - ip = pkt->dhcp->yiaddr; - } else if (ifp->enable_req_dns && p[0] == 6 && p[1] == sizeof(dns) && - p + 6 < end) { // DNS - memcpy(&dns, p + 2, sizeof(dns)); - } else if (ifp->enable_req_sntp && p[0] == 42 && p[1] == sizeof(sntp) && - p + 6 < end) { // SNTP - memcpy(&sntp, p + 2, sizeof(sntp)); - } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease - memcpy(&lease, p + 2, sizeof(lease)); - lease = mg_ntohl(lease); - } else if (p[0] == 53 && p[1] == 1 && p + 6 < end) { // Msg Type - msgtype = p[2]; - } - p += p[1] + 2; - } - // Process message type, RFC-1533 (9.4); RFC-2131 (3.1, 4) - if (msgtype == 6 && ifp->ip == ip) { // DHCPNACK, release IP - ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; - } else if (msgtype == 2 && ifp->state == MG_TCPIP_STATE_UP && ip && gw && - lease) { // DHCPOFFER - // select IP, (4.4.1) (fallback to IP source addr on foul play) - tx_dhcp_request_sel(ifp, ip, - pkt->dhcp->siaddr ? pkt->dhcp->siaddr : pkt->ip->src); - ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state - } else if (msgtype == 5) { // DHCPACK - if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP - uint64_t rand; - ifp->lease_expire = ifp->now + lease * 1000; - MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - // assume DHCP server = router until ARP resolves - memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); - ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; - ifp->state = MG_TCPIP_STATE_IP; // BOUND state - mg_random(&rand, sizeof(rand)); - srand((unsigned int) (rand + mg_millis())); - if (ifp->enable_req_dns && dns != 0) { - setdns4(ifp, &dns); - mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns); - } - if (ifp->enable_req_sntp && sntp != 0) - mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_SNTP, &sntp); - } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew - ifp->lease_expire = ifp->now + lease * 1000; - MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - } // TODO(): accept provided T1/T2 and store server IP for renewal (4.4) - } - if (ifp->state != state) onstatechange(ifp); -} - -// Simple DHCP server that assigns a next IP address: ifp->ip + 1 -static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint8_t op = 0, *p = pkt->dhcp->options, - *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len]; - // struct dhcp *req = pkt->dhcp; - struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; - if (end < (uint8_t *) (pkt->dhcp + 1)) return; - res.yiaddr = ifp->ip; - ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1 - while (p + 1 < end && p[0] != 255) { // Parse options - if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type - op = p[2]; - } - p += p[1] + 2; - } - if (op == 1 || op == 3) { // DHCP Discover or DHCP Request - uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK - uint8_t opts[] = { - 53, 1, msg, // Message type - 1, 4, 0, 0, 0, 0, // Subnet mask - 54, 4, 0, 0, 0, 0, // Server ID - 12, 3, 'm', 'i', 'p', // Host name: "mip" - 51, 4, 255, 255, 255, 255, // Lease time - 255 // End of options - }; - memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6); - memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask)); - memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip)); - memcpy(&res.options, opts, sizeof(opts)); - res.magic = pkt->dhcp->magic; - res.xid = pkt->dhcp->xid; - if (ifp->enable_get_gateway) { - ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's - memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); - } - tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67), - op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res)); - } -} - -static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - struct mg_connection *c = getpeer(ifp->mgr, pkt, true); - struct connstate *s; - if (c == NULL) return false; // No UDP listener on this port - s = (struct connstate *) (c + 1); - c->rem.port = pkt->udp->sport; - memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); - if (c->recv.len >= MG_MAX_RECV_SIZE) { - mg_error(c, "max_recv_buf_size reached"); - } else if (c->recv.size - c->recv.len < pkt->pay.len && - !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) { - mg_error(c, "oom"); - } else { - memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); - c->recv.len += pkt->pay.len; - mg_call(c, MG_EV_READ, &pkt->pay.len); - } - return true; -} - -static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip, - uint8_t flags, uint16_t sport, uint16_t dport, - uint32_t seq, uint32_t ack, const void *buf, size_t len) { - struct ip *ip; - struct tcp *tcp; - uint16_t opts[4 / 2]; - if (flags & TH_SYN) { // Send MSS, RFC-9293 3.7.1 - opts[0] = mg_htons(0x0204); // RFC-9293 3.2 - opts[1] = mg_htons((uint16_t) (ifp->mtu - 40)); // RFC-6691 - buf = opts; - len = sizeof(opts); - } - ip = tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len); - tcp = (struct tcp *) (ip + 1); - memset(tcp, 0, sizeof(*tcp)); - if (buf != NULL && len) memmove(tcp + 1, buf, len); - tcp->sport = sport; - tcp->dport = dport; - tcp->seq = seq; - tcp->ack = ack; - tcp->flags = flags; - tcp->win = mg_htons(MIP_TCP_WIN); - tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4); - if (flags & TH_SYN) tcp->off += (uint8_t) (sizeof(opts) / 4 << 4); - { - uint32_t cs = 0; - uint16_t n = (uint16_t) (sizeof(*tcp) + len); - uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)}; - cs = csumup(cs, tcp, n); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs = csumup(cs, pseudo, sizeof(pseudo)); - tcp->csum = csumfin(cs); - } - MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src, - mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, - mg_ntohs(tcp->dport), tcp->flags, len)); - return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len); -} - -static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt, - uint8_t flags, uint32_t seqno) { - uint32_t ackno = mg_htonl(mg_ntohl(pkt->tcp->seq) + (uint32_t) pkt->pay.len + - ((pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0)); - return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport, - pkt->tcp->sport, seqno, ackno, NULL, 0); -} - -static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) { - return tx_tcp_ctrlresp(ifp, pkt, toack ? TH_RST : (TH_RST | TH_ACK), - toack ? pkt->tcp->ack : 0); -} - -static struct mg_connection *accept_conn(struct mg_connection *lsn, - struct pkt *pkt, uint16_t mss) { - struct mg_connection *c = mg_alloc_conn(lsn->mgr); - struct connstate *s; - if (c == NULL) { - MG_ERROR(("OOM")); - return NULL; - } - s = (struct connstate *) (c + 1); - s->dmss = mss; // from options in client SYN - s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); - memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); - settmout(c, MIP_TTYPE_KEEPALIVE); - memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - c->rem.port = pkt->tcp->sport; - MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem)); - LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c); - c->is_accepted = 1; - c->is_hexdumping = lsn->is_hexdumping; - c->pfn = lsn->pfn; - c->loc = lsn->loc; - c->pfn_data = lsn->pfn_data; - c->fn = lsn->fn; - c->fn_data = lsn->fn_data; - c->is_tls = lsn->is_tls; - mg_call(c, MG_EV_OPEN, NULL); - mg_call(c, MG_EV_ACCEPT, NULL); - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() - return c; -} - -static size_t trim_len(struct mg_connection *c, size_t len) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - size_t eth_h_len = 14, ip_max_h_len = 24, tcp_max_h_len = 60, udp_h_len = 8; - size_t max_headers_len = - eth_h_len + ip_max_h_len + (c->is_udp ? udp_h_len : tcp_max_h_len); - size_t min_mtu = c->is_udp ? 68 /* RFC-791 */ : max_headers_len - eth_h_len; - - // If the frame exceeds the available buffer, trim the length - if (len + max_headers_len > ifp->tx.len) { - len = ifp->tx.len - max_headers_len; - } - // Ensure the MTU isn't lower than the minimum allowed value - if (ifp->mtu < min_mtu) { - MG_ERROR(("MTU is lower than minimum, capping to %lu", min_mtu)); - ifp->mtu = (uint16_t) min_mtu; - } - // If the total packet size exceeds the MTU, trim the length - if (len + max_headers_len - eth_h_len > ifp->mtu) { - len = ifp->mtu - max_headers_len + eth_h_len; - if (c->is_udp) { - MG_ERROR(("UDP datagram exceeds MTU. Truncating it.")); - } - } - - return len; -} - -long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - struct connstate *s = (struct connstate *) (c + 1); - uint32_t dst_ip = c->rem.ip4; - len = trim_len(c, len); - if (c->is_udp) { - if (!tx_udp(ifp, s->mac, ifp->ip, c->loc.port, dst_ip, c->rem.port, buf, - len)) - return MG_IO_WAIT; - } else { // TCP, cap to peer's MSS - size_t sent; - if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts - sent = tx_tcp(ifp, s->mac, dst_ip, TH_PUSH | TH_ACK, c->loc.port, - c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), buf, len); - if (sent == 0) { - return MG_IO_WAIT; - } else if (sent == (size_t) -1) { - return MG_IO_ERR; - } else { - s->seq += (uint32_t) len; - if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE); - } - } - return (long) len; -} - -static void handle_tls_recv(struct mg_connection *c) { - size_t avail = mg_tls_pending(c); - size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail; - struct mg_iobuf *io = &c->recv; - if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) { - mg_error(c, "oom"); - } else { - // Decrypt data directly into c->recv - long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf, - io->size - io->len); - if (n == MG_IO_ERR) { - mg_error(c, "TLS recv error"); - } else if (n > 0) { - // Decrypted successfully - trigger MG_EV_READ - io->len += (size_t) n; - mg_call(c, MG_EV_READ, &n); - } // else n < 0: outstanding data to be moved to c->recv - } -} - -static void read_conn(struct mg_connection *c, struct pkt *pkt) { - struct connstate *s = (struct connstate *) (c + 1); - struct mg_iobuf *io = c->is_tls ? &c->rtls : &c->recv; - uint32_t seq = mg_ntohl(pkt->tcp->seq); - uint32_t rem_ip = c->rem.ip4; - if (pkt->tcp->flags & TH_FIN) { - uint8_t flags = TH_ACK; - if (mg_ntohl(pkt->tcp->seq) != s->ack) { - MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - return; - } - // If we initiated the closure, we reply with ACK upon receiving FIN - // If we didn't initiate it, we reply with FIN as part of the normal TCP - // closure process - s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1); - s->fin_rcvd = true; - if (c->is_draining && s->ttype == MIP_TTYPE_FIN) { - if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ? - s->seq++; // Yes. Increment our SEQ - } else { // Otherwise, - s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK - } - s->twclosure = true; - } else { - flags |= TH_FIN; - c->is_draining = 1; - settmout(c, MIP_TTYPE_FIN); - } - tx_tcp(c->mgr->ifp, s->mac, rem_ip, flags, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - if (pkt->pay.len == 0) return; // if no data, we're done - } else if (pkt->pay.len <= 1 && mg_ntohl(pkt->tcp->seq) == s->ack - 1) { - // Keep-Alive (RFC-9293 3.8.4, allow erroneous implementations) - MG_VERBOSE(("%lu keepalive ACK", c->id)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - return; // no data to process - } else if (pkt->pay.len == 0) { // this is an ACK - if (s->fin_rcvd && s->ttype == MIP_TTYPE_FIN) s->twclosure = true; - return; // no data to process - } else if (seq != s->ack) { - uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); - if (s->ack == ack) { - MG_VERBOSE(("ignoring duplicate pkt")); - } else { - MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - } - return; // drop it - } else if (io->size - io->len < pkt->pay.len && - !mg_iobuf_resize(io, io->len + pkt->pay.len)) { - mg_error(c, "oom"); - return; // drop it - } - // Copy TCP payload into the IO buffer. If the connection is plain text, - // we copy to c->recv. If the connection is TLS, this data is encrypted, - // therefore we copy that encrypted data to the c->rtls iobuffer instead, - // and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will - // call back mg_io_recv() which grabs raw data from c->rtls - memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len); - io->len += pkt->pay.len; - MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack)); - // Advance ACK counter - s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); - s->unacked += pkt->pay.len; - // size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack; - if (s->unacked > MIP_TCP_WIN / 2 && s->acked != s->ack) { - // Send ACK immediately - MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - s->unacked = 0; - s->acked = s->ack; - if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE); - } else { - // if not already running, setup a timer to send an ACK later - if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK); - } - if (c->is_tls && c->is_tls_hs) { - mg_tls_handshake(c); - } else if (c->is_tls) { - handle_tls_recv(c); - } else { - // Plain text connection, data is already in c->recv, trigger MG_EV_READ - mg_call(c, MG_EV_READ, &pkt->pay.len); - } -} - -// TCP backlog -struct mg_backlog { - uint16_t port, mss; // use port=0 for available entries - uint8_t age; -}; - -static int backlog_insert(struct mg_connection *c, uint16_t port, - uint16_t mss) { - struct mg_backlog *p = (struct mg_backlog *) c->data; - size_t i; - for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) { - if (p[i].port != 0) continue; - p[i].age = 2; // remove after two calls, average 1.5 call rate - p[i].port = port, p[i].mss = mss; - return (int) i; - } - return -1; -} - -static struct mg_backlog *backlog_retrieve(struct mg_connection *c, - uint16_t key, uint16_t port) { - struct mg_backlog *p = (struct mg_backlog *) c->data; - if (key >= sizeof(c->data) / sizeof(*p)) return NULL; - if (p[key].port != port) return NULL; - p += key; - return p; -} - -static void backlog_remove(struct mg_connection *c, uint16_t key) { - struct mg_backlog *p = (struct mg_backlog *) c->data; - p[key].port = 0; -} - -static void backlog_maintain(struct mg_connection *c) { - struct mg_backlog *p = (struct mg_backlog *) c->data; - size_t i; // dec age and remove those where it reaches 0 - for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) { - if (p[i].port == 0) continue; - if (p[i].age != 0) --p[i].age; - if (p[i].age == 0) p[i].port = 0; - } -} - -static void backlog_poll(struct mg_mgr *mgr) { - struct mg_connection *c = NULL; - for (c = mgr->conns; c != NULL; c = c->next) { - if (!c->is_udp && c->is_listening) backlog_maintain(c); - } -} - -// process options (MSS) -static void handle_opt(struct connstate *s, struct tcp *tcp) { - uint8_t *opts = (uint8_t *) (tcp + 1); - int len = 4 * ((int) (tcp->off >> 4) - ((int) sizeof(*tcp) / 4)); - s->dmss = 536; // assume default, RFC-9293 3.7.1 - while (len > 0) { // RFC-9293 3.1 3.2 - uint8_t kind = opts[0], optlen = 1; - if (kind != 1) { // No-Operation - if (kind == 0) break; // End of Option List - optlen = opts[1]; - if (kind == 2 && optlen == 4) // set received MSS - s->dmss = (uint16_t) (((uint16_t) opts[2] << 8) + opts[3]); - } - MG_VERBOSE(("kind: %u, optlen: %u, len: %d\n", kind, optlen, len)); - opts += optlen; - len -= optlen; - } -} - -static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); - struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1); - // Order is VERY important; RFC-9293 3.5.2 - // - check clients (Group 1) and established connections (Group 3) - if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) { - // client got a server connection accept - handle_opt(s, pkt->tcp); // process options (MSS) - s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; - tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack); - c->is_connecting = 0; // Client connected - settmout(c, MIP_TTYPE_KEEPALIVE); - mg_call(c, MG_EV_CONNECT, NULL); // Let user know - if (c->is_tls_hs) mg_tls_handshake(c); - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() - } else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) { - mg_error(c, "connection refused"); - } else if (c != NULL && pkt->tcp->flags & TH_RST) { - // TODO(): validate RST is within window (and optional with proper ACK) - mg_error(c, "peer RST"); // RFC-1122 4.2.2.13 - } else if (c != NULL) { - // process segment - s->tmiss = 0; // Reset missed keep-alive counter - if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer - settmout(c, - MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending - read_conn(c, pkt); // Override timer with ACK timeout if needed - } else - // - we don't listen on that port; RFC-9293 3.5.2 Group 1 - // - check listening connections; RFC-9293 3.5.2 Group 2 - if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) { - // not listening on that port - if (!(pkt->tcp->flags & TH_RST)) { - tx_tcp_rst(ifp, pkt, pkt->tcp->flags & TH_ACK); - } // else silently discard - } else if (pkt->tcp->flags == TH_SYN) { - // listener receives a connection request - struct connstate cs; // At this point, s = NULL, there is no connection - int key; - uint32_t isn; - if (pkt->tcp->sport != 0) { - handle_opt(&cs, pkt->tcp); // process options (MSS) - key = backlog_insert(c, pkt->tcp->sport, - cs.dmss); // backlog options (MSS) - if (key < 0) return; // no room in backlog, discard SYN, client retries - // Use peer's src port and bl key as ISN, to later identify the - // handshake - isn = (mg_htonl(((uint32_t) key << 16) | mg_ntohs(pkt->tcp->sport))); - tx_tcp_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn); - } // what should we do when port=0 ? Linux takes port 0 as any other - // port - } else if (pkt->tcp->flags == TH_ACK) { - // listener receives an ACK - struct mg_backlog *b = NULL; - if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) == - mg_htons(pkt->tcp->sport)) { - uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16); - b = backlog_retrieve(c, key, pkt->tcp->sport); - if (b != NULL) { // ACK is a response to a SYN+ACK - accept_conn(c, pkt, b->mss); // pass options - backlog_remove(c, key); - } // else not an actual match, reset - } - if (b == NULL) tx_tcp_rst(ifp, pkt, true); - } else if (pkt->tcp->flags & TH_RST) { - // silently discard - } else if (pkt->tcp->flags & TH_ACK) { // ACK + something else != RST - tx_tcp_rst(ifp, pkt, true); - } else if (pkt->tcp->flags & TH_SYN) { // SYN + something else != ACK - tx_tcp_rst(ifp, pkt, false); - } // else silently discard -} - -static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint8_t ihl; - uint16_t frag, len; - if (pkt->pay.len < sizeof(*pkt->ip)) return; // Truncated - if ((pkt->ip->ver >> 4) != 4) return; // Not IP - ihl = pkt->ip->ver & 0x0F; - if (ihl < 5) return; // bad IHL - if (pkt->pay.len < (ihl * 4)) return; // Truncated / malformed - // There can be link padding, take length from IP header - len = mg_ntohs(pkt->ip->len); // IP datagram length - if (len < (ihl * 4) || len > pkt->pay.len) return; // malformed - pkt->pay.len = len; // strip padding - mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts - frag = mg_ntohs(pkt->ip->frag); - if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) { - struct mg_connection *c; - if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf); - if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); - c = getpeer(ifp->mgr, pkt, false); - if (c) mg_error(c, "Received fragmented packet"); - } else if (pkt->ip->proto == 1) { - pkt->icmp = (struct icmp *) (pkt->pay.buf); - if (pkt->pay.len < sizeof(*pkt->icmp)) return; - mkpay(pkt, pkt->icmp + 1); - rx_icmp(ifp, pkt); - } else if (pkt->ip->proto == 17) { - pkt->udp = (struct udp *) (pkt->pay.buf); - if (pkt->pay.len < sizeof(*pkt->udp)) return; // truncated - // Take length from UDP header - len = mg_ntohs(pkt->udp->len); // UDP datagram length - if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed - pkt->pay.len = len; // strip excess data - mkpay(pkt, pkt->udp + 1); - MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, - mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst, - mg_ntohs(pkt->udp->dport), (int) pkt->pay.len)); - if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68)) { - pkt->dhcp = (struct dhcp *) (pkt->udp + 1); - mkpay(pkt, pkt->dhcp + 1); - rx_dhcp_client(ifp, pkt); - } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) { - pkt->dhcp = (struct dhcp *) (pkt->udp + 1); - mkpay(pkt, pkt->dhcp + 1); - rx_dhcp_server(ifp, pkt); - } else if (!rx_udp(ifp, pkt)) { - // Should send ICMP Destination Unreachable for unicasts, but keep silent - } - } else if (pkt->ip->proto == 6) { - uint8_t off; - pkt->tcp = (struct tcp *) (pkt->pay.buf); - if (pkt->pay.len < sizeof(*pkt->tcp)) return; - off = pkt->tcp->off >> 4; // account for opts - if (pkt->pay.len < (4 * off)) return; - mkpay(pkt, (uint32_t *) pkt->tcp + off); - MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, - mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst, - mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len)); - rx_tcp(ifp, pkt); - } else { - MG_DEBUG(("Unknown IP proto %x", mg_htons(pkt->ip->proto))); - if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump(pkt->ip, pkt->pay.len >= 32 ? 32 : pkt->pay.len); - } -} - -static bool ip6_handle_opt(struct ip6 *h) { - switch(h->next) { - case 0: - case 43: - case 44: - case 50: - case 51: - case 60: - case 135: - case 139: - case 140: - case 253: - case 254: - MG_INFO(("IPv6 extension header %d", (int) h->next)); - break; - default: - return false; - } - return true; -} - -static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) { - struct ip6 *hdr; - uint16_t len; - if (pkt->pay.len < sizeof(*pkt->ip6)) return; // Truncated - if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6 - hdr = pkt->ip6; - while (ip6_handle_opt(hdr)) ++hdr; - // There can be link padding, take payload length from IPv6 last header - len = mg_ntohs(hdr->plen); // IPv6 datagram reported payload length - if ((len + (size_t) hdr - (size_t) pkt->ip6) > pkt->pay.len) return; - pkt->pay.buf = (char *)(hdr + 1); - pkt->pay.len = len; // strip padding - if (hdr->next == 58) { - pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf); - if (pkt->pay.len < sizeof(*pkt->icmp6)) return; - mkpay(pkt, pkt->icmp6 + 1); - MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src, - mg_print_ip6, &pkt->ip6->dst, - (int) pkt->pay.len)); - // rx_icmp6(ifp, pkt); - } else if (hdr->next == 17) { - pkt->udp = (struct udp *) (pkt->pay.buf); - if (pkt->pay.len < sizeof(*pkt->udp)) return; - mkpay(pkt, pkt->udp + 1); - MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src, - mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst, - mg_ntohs(pkt->udp->dport), (int) pkt->pay.len)); - if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(546)) { - pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1); - mkpay(pkt, pkt->dhcp6 + 1); - // rx_dhcp6_client(ifp, pkt); - } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(547)) { - pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1); - mkpay(pkt, pkt->dhcp6 + 1); - // rx_dhcp6_server(ifp, pkt); - } else if (!rx_udp(ifp, pkt)) { - // Should send ICMPv6 Destination Unreachable for unicasts, keep silent - } - } else if (hdr->next == 6) { - uint8_t off; - pkt->tcp = (struct tcp *) (pkt->pay.buf); - if (pkt->pay.len < sizeof(*pkt->tcp)) return; - off = pkt->tcp->off >> 4; // account for opts - if (pkt->pay.len < sizeof(*pkt->tcp) + 4 * off) return; - mkpay(pkt, (uint32_t *) pkt->tcp + off); - MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src, - mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst, - mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len)); - rx_tcp(ifp, pkt); - } else { - MG_DEBUG(("Unknown IPv6 next hdr %x", mg_htons(hdr->next))); - if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump(pkt->ip6, pkt->pay.len >= 32 ? 32 : pkt->pay.len); - } -} - -static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) { - struct pkt pkt; - memset(&pkt, 0, sizeof(pkt)); - pkt.pay.buf = pkt.raw.buf = (char *) buf; - pkt.pay.len = pkt.raw.len = len; // payload = raw - pkt.eth = (struct eth *) buf; // Ethernet = raw - if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt? - if (ifp->enable_mac_check && - memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 && - memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) - return; - if (ifp->enable_crc32_check && len > 4) { - uint32_t crc; - len -= 4; // TODO(scaprile): check on bigendian - crc = mg_crc32(0, (const char *) buf, len); - if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return; - pkt.pay.len = len; - } - mkpay(&pkt, pkt.eth + 1); - if (pkt.eth->type == mg_htons(0x806)) { - pkt.arp = (struct arp *) (pkt.pay.buf); - if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.pay.len) return; // Truncated - mg_tcpip_call(ifp, MG_TCPIP_EV_ARP, &pkt.raw); - rx_arp(ifp, &pkt); - } else if (pkt.eth->type == mg_htons(0x86dd)) { - pkt.ip6 = (struct ip6 *) (pkt.pay.buf); - rx_ip6(ifp, &pkt); - } else if (pkt.eth->type == mg_htons(0x800)) { - pkt.ip = (struct ip *) (pkt.pay.buf); - rx_ip(ifp, &pkt); - } else { - MG_DEBUG(("Unknown eth type %x", mg_htons(pkt.eth->type))); - if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump(buf, len >= 32 ? 32 : len); - } -} - -static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { - struct mg_connection *c; - bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, now); - ifp->now = now; - - if (expired_1000ms) { -#if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS - const char *names[] = {"down", "up", "req", "ip", "ready"}; - MG_INFO(("Status: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", - names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, - ifp->ndrop, ifp->nerr)); -#endif - backlog_poll(ifp->mgr); - } - // Handle gw ARP request timeout, order is important - if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) { - ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC - onstatechange(ifp); - } - // poll driver - if (ifp->driver->poll) { - bool up = ifp->driver->poll(ifp, expired_1000ms); - // Handle physical interface up/down status - if (expired_1000ms) { - bool current = ifp->state != MG_TCPIP_STATE_DOWN; - if (!up && ifp->enable_dhcp_client) ifp->ip = 0; - if (up != current) { // link state has changed - ifp->state = up == false ? MG_TCPIP_STATE_DOWN - : ifp->enable_dhcp_client || ifp->ip == 0 - ? MG_TCPIP_STATE_UP - : MG_TCPIP_STATE_IP; - onstatechange(ifp); - } else if (!ifp->enable_dhcp_client && ifp->state == MG_TCPIP_STATE_UP && - ifp->ip) { - ifp->state = MG_TCPIP_STATE_IP; // ifp->fn has set an IP - onstatechange(ifp); - } - if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down")); - mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL); - } - } - if (ifp->state == MG_TCPIP_STATE_DOWN) return; - - // DHCP RFC-2131 (4.4) - if (ifp->enable_dhcp_client && expired_1000ms) { - if (ifp->state == MG_TCPIP_STATE_UP) { - tx_dhcp_discover(ifp); // INIT (4.4.1) - } else if (ifp->state == MG_TCPIP_STATE_READY && - ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING - if (ifp->now >= ifp->lease_expire) { - ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP - onstatechange(ifp); - } else if (ifp->now + 30UL * 60UL * 1000UL > ifp->lease_expire && - ((ifp->now / 1000) % 60) == 0) { - // hack: 30 min before deadline, try to rebind (4.3.6) every min - tx_dhcp_request_re(ifp, (uint8_t *) broadcast, ifp->ip, 0xffffffff); - } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5) - } - } - - // Read data from the network - if (ifp->driver->rx != NULL) { // Simple polling driver, returns one frame - size_t len = - ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp); - if (len > 0) { - ifp->nrecv++; - mg_tcpip_rx(ifp, ifp->recv_queue.buf, len); - } - } else { // Complex poll / Interrupt-based driver. Queues recvd frames - char *buf; - size_t len = mg_queue_next(&ifp->recv_queue, &buf); - if (len > 0) { - mg_tcpip_rx(ifp, buf, len); - mg_queue_del(&ifp->recv_queue, len); - } - } - - // Process timeouts - for (c = ifp->mgr->conns; c != NULL; c = c->next) { - struct connstate *s = (struct connstate *) (c + 1); - uint32_t rem_ip; - if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving) - continue; - rem_ip = c->rem.ip4; - if (ifp->now > s->timer) { - if (s->ttype == MIP_TTYPE_ARP) { - mg_error(c, "ARP timeout"); - } else if (c->is_udp) { - continue; - } else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) { - MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack)); - tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - s->acked = s->ack; - } else if (s->ttype == MIP_TTYPE_SYN) { - mg_error(c, "Connection timeout"); - } else if (s->ttype == MIP_TTYPE_FIN) { - c->is_closing = 1; - continue; - } else { - if (s->tmiss++ > 2) { - mg_error(c, "keepalive"); - } else { - MG_VERBOSE(("%lu keepalive", c->id)); - tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0); - } - } - - settmout(c, MIP_TTYPE_KEEPALIVE); - } - } -} - -// This function executes in interrupt context, thus it should copy data -// somewhere fast. Note that newlib's malloc is not thread safe, thus use -// our lock-free queue with preallocated buffer to copy data and return asap -void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) { - char *p; - if (mg_queue_book(&ifp->recv_queue, &p, len) >= len) { - memcpy(p, buf, len); - mg_queue_add(&ifp->recv_queue, len); - ifp->nrecv++; - } else { - ifp->ndrop++; - } -} - -void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) { - // If MAC address is not set, make a random one - if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 && - ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) { - ifp->mac[0] = 0x02; // Locally administered, unicast - mg_random(&ifp->mac[1], sizeof(ifp->mac) - 1); - MG_INFO(("MAC not set. Generated random: %M", mg_print_mac, ifp->mac)); - } - - // If DHCP name is not set, use "mip" - if (ifp->dhcp_name[0] == '\0') { - memcpy(ifp->dhcp_name, "mip", 4); - } - ifp->dhcp_name[sizeof(ifp->dhcp_name) - 1] = '\0'; // Just in case - - if (ifp->driver->init && !ifp->driver->init(ifp)) { - MG_ERROR(("driver init failed")); - } else { - size_t framesize = 1540; - ifp->tx.buf = (char *) mg_calloc(1, framesize), ifp->tx.len = framesize; - if (ifp->recv_queue.size == 0) - ifp->recv_queue.size = ifp->driver->rx ? framesize : 8192; - ifp->recv_queue.buf = (char *) mg_calloc(1, ifp->recv_queue.size); - ifp->timer_1000ms = mg_millis(); - mgr->ifp = ifp; - ifp->mgr = mgr; - ifp->mtu = MG_TCPIP_MTU_DEFAULT; - mgr->extraconnsize = sizeof(struct connstate); - if (ifp->ip == 0) ifp->enable_dhcp_client = true; - memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set best-effort to bcast - mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535 - ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from - // MG_EPHEMERAL_PORT_BASE to 65535 - if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM")); - } -} - -void mg_tcpip_free(struct mg_tcpip_if *ifp) { - mg_free(ifp->recv_queue.buf); - mg_free(ifp->tx.buf); - mg_free(ifp->dns4_url); -} - -static void send_syn(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); - uint32_t rem_ip = c->rem.ip4; - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, - NULL, 0); -} - -static void mac_resolved(struct mg_connection *c) { - if (c->is_udp) { - c->is_connecting = 0; - mg_call(c, MG_EV_CONNECT, NULL); - } else { - send_syn(c); - settmout(c, MIP_TTYPE_SYN); - } -} - -static void ip4_mcastmac(uint8_t *mac, uint32_t *ip) { - uint8_t mcastp[3] = {0x01, 0x00, 0x5E}; // multicast group MAC - memcpy(mac, mcastp, 3); - memcpy(mac + 3, ((uint8_t *) ip) + 1, 3); // 23 LSb - mac[3] &= 0x7F; -} - -void mg_connect_resolved(struct mg_connection *c) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - uint32_t rem_ip = c->rem.ip4; - c->is_resolving = 0; - if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE; - c->loc.ip4 = ifp->ip; - c->loc.port = mg_htons(ifp->eport++); - MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, - &c->rem)); - mg_call(c, MG_EV_RESOLVE, NULL); - c->is_connecting = 1; - if (c->is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) { - struct connstate *s = (struct connstate *) (c + 1); - memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast - mac_resolved(c); - } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) && - rem_ip != ifp->gw) { // skip if gw (onstatechange -> READY -> ARP) - // If we're in the same LAN, fire an ARP lookup. - MG_DEBUG(("%lu ARP lookup...", c->id)); - mg_tcpip_arp_request(ifp, rem_ip, NULL); - settmout(c, MIP_TTYPE_ARP); - c->is_arplooking = 1; - } else if ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) { - struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF - ip4_mcastmac(s->mac, &rem_ip); // multicast group - mac_resolved(c); - } else { - struct connstate *s = (struct connstate *) (c + 1); - memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac)); - mac_resolved(c); - } -} - -bool mg_open_listener(struct mg_connection *c, const char *url) { - c->loc.port = mg_htons(mg_url_port(url)); - if (!mg_aton(mg_url_host(url), &c->loc)) { - MG_ERROR(("invalid listening URL: %s", url)); - return false; - } - return true; -} - -static void write_conn(struct mg_connection *c) { - long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len) - : mg_io_send(c, c->send.buf, c->send.len); - if (len == MG_IO_ERR) { - mg_error(c, "tx err"); - } else if (len > 0) { - mg_iobuf_del(&c->send, 0, (size_t) len); - mg_call(c, MG_EV_WRITE, &len); - } -} - -static void init_closure(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - if (c->is_udp == false && c->is_listening == false && - c->is_connecting == false) { // For TCP conns, - uint32_t rem_ip = c->rem.ip4; - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, - c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - settmout(c, MIP_TTYPE_FIN); - } -} - -static void close_conn(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - mg_iobuf_free(&s->raw); // For TLS connections, release raw data - mg_close_conn(c); -} - -static bool can_write(struct mg_connection *c) { - return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 && - c->is_tls_hs == 0 && c->is_arplooking == 0; -} - -void mg_mgr_poll(struct mg_mgr *mgr, int ms) { - struct mg_connection *c, *tmp; - uint64_t now = mg_millis(); - mg_timer_poll(&mgr->timers, now); - if (mgr->ifp == NULL || mgr->ifp->driver == NULL) return; - mg_tcpip_poll(mgr->ifp, now); - for (c = mgr->conns; c != NULL; c = tmp) { - struct connstate *s = (struct connstate *) (c + 1); - bool is_tls = c->is_tls && !c->is_resolving && !c->is_arplooking && - !c->is_listening && !c->is_connecting; - tmp = c->next; - mg_call(c, MG_EV_POLL, &now); - MG_VERBOSE(("%lu .. %c%c%c%c%c %lu %lu", c->id, c->is_tls ? 'T' : 't', - c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', - c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c', - mg_tls_pending(c), c->rtls.len)); - // order is important, TLS conn close with > 1 record in buffer (below) - if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0)) - handle_tls_recv(c); - if (can_write(c)) write_conn(c); - if (is_tls && c->send.len == 0) mg_tls_flush(c); - if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN) - init_closure(c); - // For non-TLS, close immediately upon completing the 3-way closure - // For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires - if (s->twclosure && - (!c->is_tls || (c->rtls.len == 0 && mg_tls_pending(c) == 0))) - c->is_closing = 1; - if (c->is_closing) close_conn(c); - } - (void) ms; -} - -bool mg_send(struct mg_connection *c, const void *buf, size_t len) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - bool res = false; - uint32_t rem_ip = c->rem.ip4; - if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { - mg_error(c, "net down"); - } else if (c->is_udp && (c->is_arplooking || c->is_resolving)) { - // Fail to send, no target MAC or IP - MG_VERBOSE(("still resolving...")); - } else if (c->is_udp) { - struct connstate *s = (struct connstate *) (c + 1); - len = trim_len(c, len); // Trimming length if necessary - res = tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, - len); - } else { - res = mg_iobuf_add(&c->send, c->send.len, buf, len); - } - return res; -} - -uint8_t mcast_addr[6] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb}; -void mg_multicast_add(struct mg_connection *c, char *ip) { - (void) ip; // ip4_mcastmac(mcast_mac, &ip); - // TODO(): actual IP -> MAC; check database, update - c->mgr->ifp->update_mac_hash_table = true; // mark dirty -} - -bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc); - -static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) { - struct mg_dns *dnsc; - mg_free(ifp->dns4_url); - ifp->dns4_url = mg_mprintf("udp://%M:53", mg_print_ip4, ip); - dnsc = &ifp->mgr->dns4; - dnsc->url = (const char *) ifp->dns4_url; - MG_DEBUG(("Set DNS URL to %s", dnsc->url)); - if (ifp->mgr->use_dns6) return; - if (dnsc->c != NULL) mg_close_conn(dnsc->c); - if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection - MG_ERROR(("DNS connection creation failed")); -} - -#endif // MG_ENABLE_TCPIP - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_ch32v307.c" -#endif - - - - -#if MG_OTA == MG_OTA_CH32V307 -// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html - -static bool mg_ch32v307_write(void *, const void *, size_t); -static bool mg_ch32v307_swap(void); - -static struct mg_flash s_mg_flash_ch32v307 = { - (void *) 0x08000000, // Start - 480 * 1024, // Size, first 320k is 0-wait - 4 * 1024, // Sector size, 4k - 4, // Align, 32 bit - mg_ch32v307_write, - mg_ch32v307_swap, -}; - -#define FLASH_BASE 0x40022000 -#define FLASH_ACTLR (FLASH_BASE + 0) -#define FLASH_KEYR (FLASH_BASE + 4) -#define FLASH_OBKEYR (FLASH_BASE + 8) -#define FLASH_STATR (FLASH_BASE + 12) -#define FLASH_CTLR (FLASH_BASE + 16) -#define FLASH_ADDR (FLASH_BASE + 20) -#define FLASH_OBR (FLASH_BASE + 28) -#define FLASH_WPR (FLASH_BASE + 32) - -MG_IRAM static void flash_unlock(void) { - static bool unlocked; - if (unlocked == false) { - MG_REG(FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_KEYR) = 0xcdef89ab; - unlocked = true; - } -} - -MG_IRAM static void flash_wait(void) { - while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0; -} - -MG_IRAM static void mg_ch32v307_erase(void *addr) { - // MG_INFO(("%p", addr)); - flash_unlock(); - flash_wait(); - MG_REG(FLASH_ADDR) = (uint32_t) addr; - MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT; - flash_wait(); -} - -MG_IRAM static bool is_page_boundary(const void *addr) { - uint32_t val = (uint32_t) addr; - return (val & (s_mg_flash_ch32v307.secsz - 1)) == 0; -} - -MG_IRAM static bool mg_ch32v307_write(void *addr, const void *buf, size_t len) { - // MG_INFO(("%p %p %lu", addr, buf, len)); - // mg_hexdump(buf, len); - flash_unlock(); - const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2]; - uint16_t *dst = (uint16_t *) addr; - MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG - // MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR))); - while (src < end) { - if (is_page_boundary(dst)) mg_ch32v307_erase(dst); - *dst++ = *src++; - flash_wait(); - } - MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG - return true; -} - -MG_IRAM bool mg_ch32v307_swap(void) { - return true; -} - -// just overwrite instead of swap -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - mg_ch32v307_write(p1 + ofs, p2 + ofs, ss); - } - *((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset() -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_ch32v307); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_ch32v307); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_ch32v307)) { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_ch32v307.size, - s_mg_flash_ch32v307.size / s_mg_flash_ch32v307.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - // TODO() disable IRQ, s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_ch32v307.start, - (char *) s_mg_flash_ch32v307.start + s_mg_flash_ch32v307.size / 2, - s_mg_flash_ch32v307.size / 2, s_mg_flash_ch32v307.secsz); - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_dummy.c" -#endif - - - -#if MG_OTA == MG_OTA_NONE -bool mg_ota_begin(size_t new_firmware_size) { - (void) new_firmware_size; - return true; -} -bool mg_ota_write(const void *buf, size_t len) { - (void) buf, (void) len; - return true; -} -bool mg_ota_end(void) { - return true; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_esp32.c" -#endif - - -#if MG_ARCH == MG_ARCH_ESP32 && MG_OTA == MG_OTA_ESP32 - -static const esp_partition_t *s_ota_update_partition; -static esp_ota_handle_t s_ota_update_handle; -static bool s_ota_success; - -// Those empty macros do nothing, but mark places in the code which could -// potentially trigger a watchdog reboot due to the log flash erase operation -#define disable_wdt() -#define enable_wdt() - -bool mg_ota_begin(size_t new_firmware_size) { - if (s_ota_update_partition != NULL) { - MG_ERROR(("Update in progress. Call mg_ota_end() ?")); - return false; - } else { - s_ota_success = false; - disable_wdt(); - s_ota_update_partition = esp_ota_get_next_update_partition(NULL); - esp_err_t err = esp_ota_begin(s_ota_update_partition, new_firmware_size, - &s_ota_update_handle); - enable_wdt(); - MG_DEBUG(("esp_ota_begin(): %d", err)); - s_ota_success = (err == ESP_OK); - } - return s_ota_success; -} - -bool mg_ota_write(const void *buf, size_t len) { - disable_wdt(); - esp_err_t err = esp_ota_write(s_ota_update_handle, buf, len); - enable_wdt(); - MG_INFO(("esp_ota_write(): %d", err)); - s_ota_success = err == ESP_OK; - return s_ota_success; -} - -bool mg_ota_end(void) { - esp_err_t err = esp_ota_end(s_ota_update_handle); - MG_DEBUG(("esp_ota_end(%p): %d", s_ota_update_handle, err)); - if (s_ota_success && err == ESP_OK) { - err = esp_ota_set_boot_partition(s_ota_update_partition); - s_ota_success = (err == ESP_OK); - } - MG_DEBUG(("Finished ESP32 OTA, success: %d", s_ota_success)); - s_ota_update_partition = NULL; - return s_ota_success; -} - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_frdm.c" -#endif - - - - -#if MG_OTA == MG_OTA_FRDM - -MG_IRAM static bool mg_frdm_write(void *, const void *, size_t); -static bool mg_frdm_swap(void); - -static struct mg_flash s_mg_flash_frdm = {(void *) 0x08000000, // Start, - 0x200000, // Size - 0x1000, // Sector size - 0x100, // Align - mg_frdm_write, - mg_frdm_swap}; - -struct mg_flexspi_lut_seq { - uint8_t seqNum; - uint8_t seqId; - uint16_t reserved; -}; - -struct mg_flexspi_mem_config { - uint32_t tag; - uint32_t version; - uint32_t reserved0; - uint8_t readSampleClkSrc; - uint8_t csHoldTime; - uint8_t csSetupTime; - uint8_t columnAddressWidth; - uint8_t deviceModeCfgEnable; - uint8_t deviceModeType; - uint16_t waitTimeCfgCommands; - struct mg_flexspi_lut_seq deviceModeSeq; - uint32_t deviceModeArg; - uint8_t configCmdEnable; - uint8_t configModeType[3]; - struct mg_flexspi_lut_seq configCmdSeqs[3]; - uint32_t reserved1; - uint32_t configCmdArgs[3]; - uint32_t reserved2; - uint32_t controllerMiscOption; - uint8_t deviceType; - uint8_t sflashPadType; - uint8_t serialClkFreq; - uint8_t lutCustomSeqEnable; - uint32_t reserved3[2]; - uint32_t sflashA1Size; - uint32_t sflashA2Size; - uint32_t sflashB1Size; - uint32_t sflashB2Size; - uint32_t csPadSettingOverride; - uint32_t sclkPadSettingOverride; - uint32_t dataPadSettingOverride; - uint32_t dqsPadSettingOverride; - uint32_t timeoutInMs; - uint32_t commandInterval; - uint16_t dataValidTime[2]; - uint16_t busyOffset; - uint16_t busyBitPolarity; - uint32_t lookupTable[64]; - struct mg_flexspi_lut_seq lutCustomSeq[12]; - uint32_t reserved4[4]; -}; - -struct mg_flexspi_nor_config { - struct mg_flexspi_mem_config memConfig; - uint32_t pageSize; - uint32_t sectorSize; - uint8_t ipcmdSerialClkFreq; - uint8_t isUniformBlockSize; - uint8_t isDataOrderSwapped; - uint8_t reserved0[1]; - uint8_t serialNorType; - uint8_t needExitNoCmdMode; - uint8_t halfClkForNonReadCmd; - uint8_t needRestoreNoCmdMode; - uint32_t blockSize; - uint32_t flashStateCtx; - uint32_t reserve2[10]; -}; - -struct mg_flexspi_nor_driver_interface { - uint32_t version; - uint32_t (*init)(uint32_t instance, struct mg_flexspi_nor_config *config); - uint32_t (*wait_busy)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t address, bool keepState); - uint32_t (*page_program)(uint32_t instance, - struct mg_flexspi_nor_config *config, - uint32_t dstAddr, const uint32_t *src, - bool keepState); - uint32_t (*erase_all)(uint32_t instance, - struct mg_flexspi_nor_config *config); - uint32_t (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t start, uint32_t length); - uint32_t (*erase_sector)(uint32_t instance, - struct mg_flexspi_nor_config *config, - uint32_t address); - uint32_t (*erase_block)(uint32_t instance, - struct mg_flexspi_nor_config *config, - uint32_t address); - uint32_t (*read)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *dst, uint32_t start, uint32_t bytes); - void (*config_clock)(uint32_t instance, uint32_t freqOption, - uint32_t sampleClkMode); - uint32_t (*set_clock_source)(uint32_t clockSrc); - uint32_t (*get_config)(uint32_t instance, - struct mg_flexspi_nor_config *config, - uint32_t *option); - void (*hw_reset)(uint32_t instance, uint32_t reset_logic); - uint32_t (*xfer)(uint32_t instance, char *xfer); - uint32_t (*update_lut)(uint32_t instance, uint32_t seqIndex, - const uint32_t *lutBase, uint32_t numberOfSeq); - uint32_t (*partial_program)(uint32_t instance, - struct mg_flexspi_nor_config *config, - uint32_t dstAddr, const uint32_t *src, - uint32_t length, bool keepState); -}; - -#define MG_FLEXSPI_CFG_BLK_TAG (0x42464346UL) -#define MG_FLEXSPI_BASE 0x40134000UL - -#define MG_CMD_SDR 0x01 -#define MG_RADDR_SDR 0x02 -#define MG_WRITE_SDR 0x08 -#define MG_READ_SDR 0x09 -#define MG_DUMMY_SDR 0x0C -#define MG_STOP_EXE 0 - -#define MG_FLEXSPI_1PAD 0 -#define MG_FLEXSPI_4PAD 2 - -#define MG_FLEXSPI_LUT_OPERAND0(x) (((x) &0xFF) << 0) -#define MG_FLEXSPI_LUT_NUM_PADS0(x) (((x) &0x3) << 8) -#define MG_FLEXSPI_LUT_OPCODE0(x) (((x) &0x3F) << 10) -#define MG_FLEXSPI_LUT_OPERAND1(x) (((x) &0xFF) << 16) -#define MG_FLEXSPI_LUT_NUM_PADS1(x) (((x) &0x3) << 24) -#define MG_FLEXSPI_LUT_OPCODE1(x) (((x) &0x3F) << 26) - -#define MG_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ - (MG_FLEXSPI_LUT_OPERAND0(op0) | MG_FLEXSPI_LUT_NUM_PADS0(pad0) | \ - MG_FLEXSPI_LUT_OPCODE0(cmd0) | MG_FLEXSPI_LUT_OPERAND1(op1) | \ - MG_FLEXSPI_LUT_NUM_PADS1(pad1) | MG_FLEXSPI_LUT_OPCODE1(cmd1)) - -struct mg_flexspi_nor_config default_config = { - .memConfig = - { - .tag = MG_FLEXSPI_CFG_BLK_TAG, - .version = 0, - .readSampleClkSrc = 1, - .csHoldTime = 3, - .csSetupTime = 3, - .deviceModeCfgEnable = 1, - .deviceModeSeq = {.seqNum = 1, .seqId = 2}, - .deviceModeArg = 0x0740, - .configCmdEnable = 0, - .deviceType = 0x1, - .sflashPadType = 4, - .serialClkFreq = 4, - .sflashA1Size = 0x4000000U, - .sflashA2Size = 0, - .sflashB1Size = 0, - .sflashB2Size = 0, - .lookupTable = - { - [0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xEB, - MG_RADDR_SDR, MG_FLEXSPI_4PAD, 0x18), - [1] = - MG_FLEXSPI_LUT_SEQ(MG_DUMMY_SDR, MG_FLEXSPI_4PAD, 0x06, - MG_READ_SDR, MG_FLEXSPI_4PAD, 0x04), - [4 * 1 + 0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x05, - MG_READ_SDR, MG_FLEXSPI_1PAD, 0x04), - [4 * 2 + 0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x01, - MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x02), - [4 * 3 + 0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x06, - MG_STOP_EXE, MG_FLEXSPI_1PAD, 0x00), - [4 * 5 + 0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x20, - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), - [4 * 8 + 0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x52, - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), - [4 * 9 + 0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x02, - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), - [4 * 9 + 1] = - MG_FLEXSPI_LUT_SEQ(MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x00, - MG_STOP_EXE, MG_FLEXSPI_1PAD, 0x00), - [4 * 11 + 0] = - MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x60, - MG_STOP_EXE, MG_FLEXSPI_1PAD, 0x00), - }, - }, - .pageSize = 0x100, - .sectorSize = 0x1000, - .ipcmdSerialClkFreq = 0, - .blockSize = 0x8000, -}; - -#define MG_FLEXSPI_NOR_INSTANCE 0 -#define MG_ROMAPI_ADDRESS 0x13030000U -#define flexspi_nor \ - ((struct mg_flexspi_nor_driver_interface *) (( \ - (uint32_t *) MG_ROMAPI_ADDRESS)[5])) - -MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_frdm.start, - *end = base + s_mg_flash_frdm.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_frdm.secsz) == 0; -} - -MG_IRAM static int flexspi_nor_get_config( - struct mg_flexspi_nor_config *config) { - uint32_t option = 0xc0000004; - return flexspi_nor->get_config(MG_FLEXSPI_NOR_INSTANCE, config, &option); -} - -MG_IRAM static int flash_init(void) { - static bool initialized = false; - if (!initialized) { - struct mg_flexspi_nor_config config; - memset(&config, 0, sizeof(config)); - flexspi_nor->set_clock_source(0); - flexspi_nor->config_clock(MG_FLEXSPI_NOR_INSTANCE, 1, 0); - if (flexspi_nor->init(MG_FLEXSPI_NOR_INSTANCE, &default_config)) { - return 1; - } - flexspi_nor_get_config(&config); - if (flexspi_nor->init(MG_FLEXSPI_NOR_INSTANCE, &config)) { - return 1; - } - initialized = true; - } - return 0; -} - -MG_IRAM static bool flash_erase(struct mg_flexspi_nor_config *config, - void *addr) { - if (flash_page_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - return false; - } - - void *dst = (void *) ((char *) addr - (char *) s_mg_flash_frdm.start); - bool ok = (flexspi_nor->erase_sector(MG_FLEXSPI_NOR_INSTANCE, config, - (uint32_t) dst) == 0); - MG_INFO(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); - return ok; -} - -MG_IRAM bool mg_frdm_swap(void) { - return true; -} - -MG_IRAM static void flash_wait(void) { - while ((*((volatile uint32_t *) (MG_FLEXSPI_BASE + 0xE0)) & MG_BIT(1)) == 0) - (void) 0; -} - -static bool s_flash_irq_disabled; - -MG_IRAM static bool mg_frdm_write(void *addr, const void *buf, size_t len) { - struct mg_flexspi_nor_config config; - bool ok = false; - MG_ARM_DISABLE_IRQ(); - if (flash_init() != 0) goto fwxit; - if (flexspi_nor_get_config(&config) != 0) goto fwxit; - if ((len % s_mg_flash_frdm.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_frdm.align)); - goto fwxit; - } - if ((char *) addr < (char *) s_mg_flash_frdm.start) { - MG_ERROR(("Invalid flash write address: %p", addr)); - goto fwxit; - } - - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - ok = true; - - while (ok && src < end) { - if (flash_page_start(dst) && flash_erase(&config, dst) == false) { - ok = false; - break; - } - uint32_t status; - uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_frdm.start; - if ((char *) buf >= (char *) s_mg_flash_frdm.start && - (char *) buf < - (char *) (s_mg_flash_frdm.start + s_mg_flash_frdm.size)) { - // If we copy from FLASH to FLASH, then we first need to copy the source - // to RAM - size_t tmp_buf_size = s_mg_flash_frdm.align / sizeof(uint32_t); - uint32_t tmp[tmp_buf_size]; - - for (size_t i = 0; i < tmp_buf_size; i++) { - flash_wait(); - tmp[i] = src[i]; - } - status = flexspi_nor->page_program(MG_FLEXSPI_NOR_INSTANCE, &config, - (uint32_t) dst_ofs, tmp, false); - } else { - status = flexspi_nor->page_program(MG_FLEXSPI_NOR_INSTANCE, &config, - (uint32_t) dst_ofs, src, false); - } - src = (uint32_t *) ((char *) src + s_mg_flash_frdm.align); - dst = (uint32_t *) ((char *) dst + s_mg_flash_frdm.align); - if (status != 0) { - ok = false; - } - } - MG_INFO(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail")); -fwxit: - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - return ok; -} - -// just overwrite instead of swap -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - mg_frdm_write(p1 + ofs, p2 + ofs, ss); - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_frdm); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_frdm); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_frdm)) { - if (0) { // is_dualbank() - // TODO(): no devices so far - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - } else { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_frdm.size, - s_mg_flash_frdm.size / s_mg_flash_frdm.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_frdm.start, - (char *) s_mg_flash_frdm.start + s_mg_flash_frdm.size / 2, - s_mg_flash_frdm.size / 2, s_mg_flash_frdm.secsz); - } - } - return false; -} - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_imxrt.c" -#endif - - - - -#if MG_OTA >= MG_OTA_RT1020 && MG_OTA <= MG_OTA_RT1170 - -static bool mg_imxrt_write(void *, const void *, size_t); -static bool mg_imxrt_swap(void); - -#if MG_OTA <= MG_OTA_RT1060 -#define MG_IMXRT_FLASH_START 0x60000000 -#define FLEXSPI_NOR_INSTANCE 0 -#elif MG_OTA == MG_OTA_RT1064 -#define MG_IMXRT_FLASH_START 0x70000000 -#define FLEXSPI_NOR_INSTANCE 1 -#else // RT1170 -#define MG_IMXRT_FLASH_START 0x30000000 -#define FLEXSPI_NOR_INSTANCE 1 -#endif - -#if MG_OTA == MG_OTA_RT1050 -#define MG_IMXRT_SECTOR_SIZE (256 * 1024) -#define MG_IMXRT_PAGE_SIZE 512 -#else -#define MG_IMXRT_SECTOR_SIZE (4 * 1024) -#define MG_IMXRT_PAGE_SIZE 256 -#endif - -// TODO(): fill at init, support more devices in a dynamic way -// TODO(): then, check alignment is <= 256, see Wizard's #251 -static struct mg_flash s_mg_flash_imxrt = { - (void *) MG_IMXRT_FLASH_START, // Start, - 4 * 1024 * 1024, // Size, 4mb - MG_IMXRT_SECTOR_SIZE, // Sector size - MG_IMXRT_PAGE_SIZE, // Align - mg_imxrt_write, - mg_imxrt_swap, -}; - -struct mg_flexspi_lut_seq { - uint8_t seqNum; - uint8_t seqId; - uint16_t reserved; -}; - -struct mg_flexspi_mem_config { - uint32_t tag; - uint32_t version; - uint32_t reserved0; - uint8_t readSampleClkSrc; - uint8_t csHoldTime; - uint8_t csSetupTime; - uint8_t columnAddressWidth; - uint8_t deviceModeCfgEnable; - uint8_t deviceModeType; - uint16_t waitTimeCfgCommands; - struct mg_flexspi_lut_seq deviceModeSeq; - uint32_t deviceModeArg; - uint8_t configCmdEnable; - uint8_t configModeType[3]; - struct mg_flexspi_lut_seq configCmdSeqs[3]; - uint32_t reserved1; - uint32_t configCmdArgs[3]; - uint32_t reserved2; - uint32_t controllerMiscOption; - uint8_t deviceType; - uint8_t sflashPadType; - uint8_t serialClkFreq; - uint8_t lutCustomSeqEnable; - uint32_t reserved3[2]; - uint32_t sflashA1Size; - uint32_t sflashA2Size; - uint32_t sflashB1Size; - uint32_t sflashB2Size; - uint32_t csPadSettingOverride; - uint32_t sclkPadSettingOverride; - uint32_t dataPadSettingOverride; - uint32_t dqsPadSettingOverride; - uint32_t timeoutInMs; - uint32_t commandInterval; - uint16_t dataValidTime[2]; - uint16_t busyOffset; - uint16_t busyBitPolarity; - uint32_t lookupTable[64]; - struct mg_flexspi_lut_seq lutCustomSeq[12]; - uint32_t reserved4[4]; -}; - -struct mg_flexspi_nor_config { - struct mg_flexspi_mem_config memConfig; - uint32_t pageSize; - uint32_t sectorSize; - uint8_t ipcmdSerialClkFreq; - uint8_t isUniformBlockSize; - uint8_t reserved0[2]; - uint8_t serialNorType; - uint8_t needExitNoCmdMode; - uint8_t halfClkForNonReadCmd; - uint8_t needRestoreNoCmdMode; - uint32_t blockSize; - uint32_t reserve2[11]; -}; - -/* FLEXSPI memory config block related defintions */ -#define MG_FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian -#define MG_FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 - -#define MG_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ - (MG_FLEXSPI_LUT_OPERAND0(op0) | MG_FLEXSPI_LUT_NUM_PADS0(pad0) | \ - MG_FLEXSPI_LUT_OPCODE0(cmd0) | MG_FLEXSPI_LUT_OPERAND1(op1) | \ - MG_FLEXSPI_LUT_NUM_PADS1(pad1) | MG_FLEXSPI_LUT_OPCODE1(cmd1)) - -#define MG_CMD_SDR 0x01 -#define MG_CMD_DDR 0x21 -#define MG_DUMMY_SDR 0x0C -#define MG_DUMMY_DDR 0x2C -#define MG_DUMMY_RWDS_DDR 0x2D -#define MG_RADDR_SDR 0x02 -#define MG_RADDR_DDR 0x22 -#define MG_CADDR_DDR 0x23 -#define MG_READ_SDR 0x09 -#define MG_READ_DDR 0x29 -#define MG_WRITE_SDR 0x08 -#define MG_WRITE_DDR 0x28 -#define MG_STOP 0 - -#define MG_FLEXSPI_1PAD 0 -#define MG_FLEXSPI_2PAD 1 -#define MG_FLEXSPI_4PAD 2 -#define MG_FLEXSPI_8PAD 3 - -#define MG_FLEXSPI_QSPI_LUT \ - { \ - [0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xEB, MG_RADDR_SDR, \ - MG_FLEXSPI_4PAD, 0x18), \ - [1] = MG_FLEXSPI_LUT_SEQ(MG_DUMMY_SDR, MG_FLEXSPI_4PAD, 0x06, MG_READ_SDR, \ - MG_FLEXSPI_4PAD, 0x04), \ - [4 * 1 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x05, \ - MG_READ_SDR, MG_FLEXSPI_1PAD, 0x04), \ - [4 * 3 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x06, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - [4 * 5 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x20, \ - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \ - [4 * 8 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xD8, \ - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \ - [4 * 9 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x02, \ - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \ - [4 * 9 + 1] = MG_FLEXSPI_LUT_SEQ(MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x04, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - [4 * 11 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x60, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - } - -#define MG_FLEXSPI_HYPER_LUT \ - { \ - [0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xA0, MG_RADDR_DDR, \ - MG_FLEXSPI_8PAD, 0x18), \ - [1] = MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \ - MG_DUMMY_DDR, MG_FLEXSPI_8PAD, 0x0C), \ - [2] = MG_FLEXSPI_LUT_SEQ(MG_READ_DDR, MG_FLEXSPI_8PAD, 0x04, MG_STOP, \ - MG_FLEXSPI_1PAD, 0x0), \ - [4 * 1 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 1 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 1 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 1 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x70), \ - [4 * 2 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xA0, \ - MG_RADDR_DDR, MG_FLEXSPI_8PAD, 0x18), \ - [4 * 2 + 1] = \ - MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \ - MG_DUMMY_RWDS_DDR, MG_FLEXSPI_8PAD, 0x0B), \ - [4 * 2 + 2] = MG_FLEXSPI_LUT_SEQ(MG_READ_DDR, MG_FLEXSPI_8PAD, 0x4, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - [4 * 3 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 3 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 3 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 3 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 4 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 4 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \ - [4 * 4 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x02), \ - [4 * 4 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \ - [4 * 5 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 5 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 5 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 5 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x80), \ - [4 * 6 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 6 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 6 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 6 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 7 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 7 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \ - [4 * 7 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x02), \ - [4 * 7 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \ - [4 * 8 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_RADDR_DDR, MG_FLEXSPI_8PAD, 0x18), \ - [4 * 8 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 8 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x30, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - [4 * 9 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 9 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 9 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 9 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xA0), \ - [4 * 10 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_RADDR_DDR, MG_FLEXSPI_8PAD, 0x18), \ - [4 * 10 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \ - MG_WRITE_DDR, MG_FLEXSPI_8PAD, 0x80), \ - [4 * 11 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 11 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 11 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 11 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x80), \ - [4 * 12 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 12 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 12 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 12 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 13 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 13 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \ - [4 * 13 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x02), \ - [4 * 13 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \ - [4 * 14 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \ - [4 * 14 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \ - [4 * 14 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \ - [4 * 14 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \ - MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x10), \ - } - -#define MG_LUT_CUSTOM_SEQ \ - { \ - {.seqNum = 0, .seqId = 0, .reserved = 0}, \ - {.seqNum = 2, .seqId = 1, .reserved = 0}, \ - {.seqNum = 2, .seqId = 3, .reserved = 0}, \ - {.seqNum = 4, .seqId = 5, .reserved = 0}, \ - {.seqNum = 2, .seqId = 9, .reserved = 0}, \ - {.seqNum = 4, .seqId = 11, .reserved = 0}, \ - } - -#define MG_FLEXSPI_LUT_OPERAND0(x) (((uint32_t) (((uint32_t) (x)))) & 0xFFU) -#define MG_FLEXSPI_LUT_NUM_PADS0(x) \ - (((uint32_t) (((uint32_t) (x)) << 8U)) & 0x300U) -#define MG_FLEXSPI_LUT_OPCODE0(x) \ - (((uint32_t) (((uint32_t) (x)) << 10U)) & 0xFC00U) -#define MG_FLEXSPI_LUT_OPERAND1(x) \ - (((uint32_t) (((uint32_t) (x)) << 16U)) & 0xFF0000U) -#define MG_FLEXSPI_LUT_NUM_PADS1(x) \ - (((uint32_t) (((uint32_t) (x)) << 24U)) & 0x3000000U) -#define MG_FLEXSPI_LUT_OPCODE1(x) \ - (((uint32_t) (((uint32_t) (x)) << 26U)) & 0xFC000000U) - -#if MG_OTA == MG_OTA_RT1020 || MG_OTA == MG_OTA_RT1050 -// RT102X and RT105x boards support ROM API version 1.4 -struct mg_flexspi_nor_driver_interface { - uint32_t version; - int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t dst_addr, const uint32_t *src); - uint32_t reserved; - int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t start, uint32_t lengthInBytes); - uint32_t reserved2; - int (*update_lut)(uint32_t instance, uint32_t seqIndex, - const uint32_t *lutBase, uint32_t seqNumber); - int (*xfer)(uint32_t instance, char *xfer); - void (*clear_cache)(uint32_t instance); -}; -#elif MG_OTA <= MG_OTA_RT1064 -// RT104x and RT106x support ROM API version 1.5 -struct mg_flexspi_nor_driver_interface { - uint32_t version; - int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t dst_addr, const uint32_t *src); - int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t start, uint32_t lengthInBytes); - int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *dst, uint32_t addr, uint32_t lengthInBytes); - void (*clear_cache)(uint32_t instance); - int (*xfer)(uint32_t instance, char *xfer); - int (*update_lut)(uint32_t instance, uint32_t seqIndex, - const uint32_t *lutBase, uint32_t seqNumber); - int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *option); -}; -#else -// RT117x support ROM API version 1.7 -struct mg_flexspi_nor_driver_interface { - uint32_t version; - int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t dst_addr, const uint32_t *src); - int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t start, uint32_t lengthInBytes); - int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *dst, uint32_t addr, uint32_t lengthInBytes); - uint32_t reserved; - int (*xfer)(uint32_t instance, char *xfer); - int (*update_lut)(uint32_t instance, uint32_t seqIndex, - const uint32_t *lutBase, uint32_t seqNumber); - int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *option); - int (*erase_sector)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t address); - int (*erase_block)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t address); - void (*hw_reset)(uint32_t instance, uint32_t resetLogic); - int (*wait_busy)(uint32_t instance, struct mg_flexspi_nor_config *config, - bool isParallelMode, uint32_t address); - int (*set_clock_source)(uint32_t instance, uint32_t clockSrc); - void (*config_clock)(uint32_t instance, uint32_t freqOption, - uint32_t sampleClkMode); -}; -#endif - -#if MG_OTA <= MG_OTA_RT1064 -#define MG_FLEXSPI_BASE 0x402A8000 -#define flexspi_nor \ - (*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0020001c + \ - 16))) -#else -#define MG_FLEXSPI_BASE 0x400CC000 -#define flexspi_nor \ - (*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0021001c + \ - 12))) -#endif - -static bool s_flash_irq_disabled; - -MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_imxrt.start, - *end = base + s_mg_flash_imxrt.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_imxrt.secsz) == 0; -} - -#if MG_OTA == MG_OTA_RT1050 -// Configuration for Hyper flash memory -static struct mg_flexspi_nor_config default_config = { - .memConfig = - { - .tag = MG_FLEXSPI_CFG_BLK_TAG, - .version = MG_FLEXSPI_CFG_BLK_VERSION, - .readSampleClkSrc = 3, // ReadSampleClk_LoopbackFromDqsPad - .csHoldTime = 3, - .csSetupTime = 3, - .columnAddressWidth = 3u, - .controllerMiscOption = - MG_BIT(6) | MG_BIT(4) | MG_BIT(3) | MG_BIT(0), - .deviceType = 1, // serial NOR - .sflashPadType = 8, - .serialClkFreq = 7, // 133MHz - .sflashA1Size = 64 * 1024 * 1024, - .dataValidTime = {15, 0}, - .busyOffset = 15, - .busyBitPolarity = 1, - .lutCustomSeqEnable = 0x1, - .lookupTable = MG_FLEXSPI_HYPER_LUT, - .lutCustomSeq = MG_LUT_CUSTOM_SEQ, - }, - .pageSize = 512, - .sectorSize = 256 * 1024, - .ipcmdSerialClkFreq = 1, - .serialNorType = 1u, - .blockSize = 256 * 1024, - .isUniformBlockSize = true}; -#else -// Note: this QSPI configuration works for RTs supporting QSPI -// Configuration for QSPI memory -static struct mg_flexspi_nor_config default_config = { - .memConfig = {.tag = MG_FLEXSPI_CFG_BLK_TAG, - .version = MG_FLEXSPI_CFG_BLK_VERSION, - .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad - .csHoldTime = 3, - .csSetupTime = 3, - .controllerMiscOption = MG_BIT(4), - .deviceType = 1, // serial NOR - .sflashPadType = 4, - .serialClkFreq = 7, // 133MHz - .sflashA1Size = 8 * 1024 * 1024, - .lookupTable = MG_FLEXSPI_QSPI_LUT}, - .pageSize = 256, - .sectorSize = 4 * 1024, - .ipcmdSerialClkFreq = 1, - .blockSize = 64 * 1024, - .isUniformBlockSize = false}; -#endif - -// must reside in RAM, as flash will be erased -MG_IRAM static int flexspi_nor_get_config( - struct mg_flexspi_nor_config **config) { - *config = &default_config; - return 0; -} - -#if 0 -// ROM API get_config call (ROM version >= 1.5) -MG_IRAM static int flexspi_nor_get_config( - struct mg_flexspi_nor_config **config) { - uint32_t options[] = {0xc0000000, 0x00}; - - MG_ARM_DISABLE_IRQ(); - uint32_t status = - flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, *config, options); - if (!s_flash_irq_disabled) { - MG_ARM_ENABLE_IRQ(); - } - if (status) { - MG_ERROR(("Failed to extract flash configuration: status %u", status)); - } - return status; -} -#endif - -MG_IRAM static void mg_spin(volatile uint32_t count) { - while (count--) (void) 0; -} - -MG_IRAM static void flash_wait(void) { - while ((*((volatile uint32_t *) (MG_FLEXSPI_BASE + 0xE0)) & MG_BIT(1)) == 0) - mg_spin(1); -} - -MG_IRAM static bool flash_erase(struct mg_flexspi_nor_config *config, - void *addr) { - if (flash_page_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - return false; - } - - void *dst = (void *) ((char *) addr - (char *) s_mg_flash_imxrt.start); - - bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, config, (uint32_t) dst, - s_mg_flash_imxrt.secsz) == 0); - MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); - return ok; -} - -#if 0 -// standalone erase call -MG_IRAM static bool mg_imxrt_erase(void *addr) { - struct mg_flexspi_nor_config config, *config_ptr = &config; - bool ret; - // Interrupts must be disabled before calls to ROM API in RT1020 and 1060 - MG_ARM_DISABLE_IRQ(); - ret = (flexspi_nor_get_config(&config_ptr) == 0); - if (ret) ret = flash_erase(config_ptr, addr); - MG_ARM_ENABLE_IRQ(); - return ret; -} -#endif - -MG_IRAM bool mg_imxrt_swap(void) { - return true; -} - -MG_IRAM static bool mg_imxrt_write(void *addr, const void *buf, size_t len) { - struct mg_flexspi_nor_config config, *config_ptr = &config; - bool ok = false; - // Interrupts must be disabled before calls to ROM API in RT1020 and 1060 - MG_ARM_DISABLE_IRQ(); - if (flexspi_nor_get_config(&config_ptr) != 0) goto fwxit; - if ((len % s_mg_flash_imxrt.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_imxrt.align)); - goto fwxit; - } - if ((char *) addr < (char *) s_mg_flash_imxrt.start) { - MG_ERROR(("Invalid flash write address: %p", addr)); - goto fwxit; - } - - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - ok = true; - - while (ok && src < end) { - if (flash_page_start(dst) && flash_erase(config_ptr, dst) == false) { - ok = false; - break; - } - uint32_t status; - uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_imxrt.start; - if ((char *) buf >= (char *) s_mg_flash_imxrt.start) { - // If we copy from FLASH to FLASH, then we first need to copy the source - // to RAM - size_t tmp_buf_size = s_mg_flash_imxrt.align / sizeof(uint32_t); - uint32_t tmp[tmp_buf_size]; - - for (size_t i = 0; i < tmp_buf_size; i++) { - flash_wait(); - tmp[i] = src[i]; - } - status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr, - (uint32_t) dst_ofs, tmp); - } else { - status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr, - (uint32_t) dst_ofs, src); - } - src = (uint32_t *) ((char *) src + s_mg_flash_imxrt.align); - dst = (uint32_t *) ((char *) dst + s_mg_flash_imxrt.align); - if (status != 0) { - ok = false; - } - } - MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail")); -fwxit: - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - return ok; -} - -// just overwrite instead of swap -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - mg_imxrt_write(p1 + ofs, p2 + ofs, ss); - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_imxrt); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_imxrt); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_imxrt)) { - if (0) { // is_dualbank() - // TODO(): no devices so far - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - } else { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_imxrt.size, - s_mg_flash_imxrt.size / s_mg_flash_imxrt.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_imxrt.start, - (char *) s_mg_flash_imxrt.start + s_mg_flash_imxrt.size / 2, - s_mg_flash_imxrt.size / 2, s_mg_flash_imxrt.secsz); - } - } - return false; -} - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_mcxn.c" -#endif - - - - -#if MG_OTA == MG_OTA_MCXN - -// - Flash phrase: 16 bytes; smallest portion programmed in one operation. -// - Flash page: 128 bytes; largest portion programmed in one operation. -// - Flash sector: 8 KB; smallest portion that can be erased in one operation. -// - Flash API mg_flash_driver->program: "start" and "len" must be page-size -// aligned; to use 'phrase', FMU register access is needed. Using ROM - -static bool mg_mcxn_write(void *, const void *, size_t); -static bool mg_mcxn_swap(void); - -static struct mg_flash s_mg_flash_mcxn = { - (void *) 0, // Start, filled at init - 0, // Size, filled at init - 0, // Sector size, filled at init - 0, // Align, filled at init - mg_mcxn_write, - mg_mcxn_swap, -}; - -struct mg_flash_config { - uint32_t addr; - uint32_t size; - uint32_t blocks; - uint32_t page_size; - uint32_t sector_size; - uint32_t ffr[6]; - uint32_t reserved0[5]; - uint32_t *bootctx; - bool useahb; -}; - -struct mg_flash_driver_interface { - uint32_t version; - uint32_t (*init)(struct mg_flash_config *); - uint32_t (*erase)(struct mg_flash_config *, uint32_t start, uint32_t len, - uint32_t key); - uint32_t (*program)(struct mg_flash_config *, uint32_t start, uint8_t *src, - uint32_t len); - uint32_t (*verify_erase)(struct mg_flash_config *, uint32_t start, - uint32_t len); - uint32_t (*verify_program)(struct mg_flash_config *, uint32_t start, - uint32_t len, const uint8_t *expected, - uint32_t *addr, uint32_t *failed); - uint32_t reserved1[12]; - uint32_t (*read)(struct mg_flash_config *, uint32_t start, uint8_t *dest, - uint32_t len); - uint32_t reserved2[4]; - uint32_t (*deinit)(struct mg_flash_config *); -}; -#define mg_flash_driver \ - ((struct mg_flash_driver_interface *) (*((uint32_t *) 0x1303fc00 + 4))) -#define MG_MCXN_FLASK_KEY (('k' << 24) | ('e' << 16) | ('f' << 8) | 'l') - -MG_IRAM static bool flash_sector_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_mcxn.start, - *end = base + s_mg_flash_mcxn.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_mcxn.secsz) == 0; -} - -MG_IRAM static bool flash_erase(struct mg_flash_config *config, void *addr) { - if (flash_sector_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - return false; - } - uint32_t dst = - (uint32_t) addr - (uint32_t) s_mg_flash_mcxn.start; // future-proof - uint32_t status = mg_flash_driver->erase(config, dst, s_mg_flash_mcxn.secsz, - MG_MCXN_FLASK_KEY); - bool ok = (status == 0); - if (!ok) MG_ERROR(("Flash write error: %lu", status)); - MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); - return ok; -} - -#if 0 -// read-while-write, no need to disable IRQs for standalone usage -MG_IRAM static bool mg_mcxn_erase(void *addr) { - uint32_t status; - struct mg_flash_config config; - if ((status = mg_flash_driver->init(&config)) != 0) { - MG_ERROR(("Flash driver init error: %lu", status)); - return false; - } - bool ok = flash_erase(&config, addr); - mg_flash_driver->deinit(&config); - return ok; -} -#endif - -MG_IRAM static bool mg_mcxn_swap(void) { - // TODO(): no devices so far - return true; -} - -static bool s_flash_irq_disabled; - -MG_IRAM static bool mg_mcxn_write(void *addr, const void *buf, size_t len) { - bool ok = false; - uint32_t status; - struct mg_flash_config config; - if ((status = mg_flash_driver->init(&config)) != 0) { - MG_ERROR(("Flash driver init error: %lu", status)); - return false; - } - if ((len % s_mg_flash_mcxn.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_mcxn.align)); - goto fwxit; - } - if ((((size_t) addr - (size_t) s_mg_flash_mcxn.start) % - s_mg_flash_mcxn.align) != 0) { - MG_ERROR(("%p is not on a page boundary", addr)); - goto fwxit; - } - - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - ok = true; - - MG_ARM_DISABLE_IRQ(); - while (ok && src < end) { - if (flash_sector_start(dst) && flash_erase(&config, dst) == false) { - ok = false; - break; - } - uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_mcxn.start; - // assume source is in RAM or in a different bank or read-while-write - status = mg_flash_driver->program(&config, dst_ofs, (uint8_t *) src, - s_mg_flash_mcxn.align); - src = (uint32_t *) ((char *) src + s_mg_flash_mcxn.align); - dst = (uint32_t *) ((char *) dst + s_mg_flash_mcxn.align); - if (status != 0) { - MG_ERROR(("Flash write error: %lu", status)); - ok = false; - } - } - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail")); - -fwxit: - mg_flash_driver->deinit(&config); - return ok; -} - -// try to swap (honor dual image), otherwise just overwrite -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - char *tmp = mg_calloc(1, ss); - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - if (tmp != NULL) - for (size_t i = 0; i < ss; i++) tmp[i] = p1[ofs + i]; - mg_mcxn_write(p1 + ofs, p2 + ofs, ss); - if (tmp != NULL) mg_mcxn_write(p2 + ofs, tmp, ss); - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - uint32_t status; - struct mg_flash_config config; - if ((status = mg_flash_driver->init(&config)) != 0) { - MG_ERROR(("Flash driver init error: %lu", status)); - return false; - } - s_mg_flash_mcxn.start = (void *) config.addr; - s_mg_flash_mcxn.size = config.size; - s_mg_flash_mcxn.secsz = config.sector_size; - s_mg_flash_mcxn.align = config.page_size; - mg_flash_driver->deinit(&config); - MG_DEBUG( - ("%lu-byte flash @%p, using %lu-byte sectors with %lu-byte-aligned pages", - s_mg_flash_mcxn.size, s_mg_flash_mcxn.start, s_mg_flash_mcxn.secsz, - s_mg_flash_mcxn.align)); - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_mcxn); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_mcxn); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_mcxn)) { - if (0) { // is_dualbank() - // TODO(): no devices so far - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - } else { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_mcxn.size, - s_mg_flash_mcxn.size / s_mg_flash_mcxn.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_mcxn.start, - (char *) s_mg_flash_mcxn.start + s_mg_flash_mcxn.size / 2, - s_mg_flash_mcxn.size / 2, s_mg_flash_mcxn.secsz); - } - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_picosdk.c" -#endif - - - - -#if MG_OTA == MG_OTA_PICOSDK - -// Both RP2040 and RP2350 have no flash, low-level flash access support in -// bootrom, and high-level support in Pico-SDK (2.0+ for the RP2350) -// - The RP2350 in RISC-V mode is not tested -// NOTE(): See OTA design notes - -static bool mg_picosdk_write(void *, const void *, size_t); -static bool mg_picosdk_swap(void); - -static struct mg_flash s_mg_flash_picosdk = { - (void *) 0x10000000, // Start; functions handle offset -#ifdef PICO_FLASH_SIZE_BYTES - PICO_FLASH_SIZE_BYTES, // Size, from board definitions -#else - 0x200000, // Size, guess... is 2M enough ? -#endif - FLASH_SECTOR_SIZE, // Sector size, from hardware_flash - FLASH_PAGE_SIZE, // Align, from hardware_flash - mg_picosdk_write, mg_picosdk_swap, -}; - -#define MG_MODULO2(x, m) ((x) & ((m) -1)) - -static bool __no_inline_not_in_flash_func(flash_sector_start)( - volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_picosdk.start, - *end = base + s_mg_flash_picosdk.size; - volatile char *p = (char *) dst; - return p >= base && p < end && - MG_MODULO2(p - base, s_mg_flash_picosdk.secsz) == 0; -} - -static bool __no_inline_not_in_flash_func(flash_erase)(void *addr) { - if (flash_sector_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - return false; - } - void *dst = (void *) ((char *) addr - (char *) s_mg_flash_picosdk.start); - flash_range_erase((uint32_t) dst, s_mg_flash_picosdk.secsz); - MG_DEBUG(("Sector starting at %p erasure", addr)); - return true; -} - -static bool __no_inline_not_in_flash_func(mg_picosdk_swap)(void) { - // TODO(): RP2350 might have some A/B functionality (DS 5.1) - return true; -} - -static bool s_flash_irq_disabled; - -static bool __no_inline_not_in_flash_func(mg_picosdk_write)(void *addr, - const void *buf, - size_t len) { - if ((len % s_mg_flash_picosdk.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_picosdk.align)); - return false; - } - if ((((size_t) addr - (size_t) s_mg_flash_picosdk.start) % - s_mg_flash_picosdk.align) != 0) { - MG_ERROR(("%p is not on a page boundary", addr)); - return false; - } - - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - -#ifndef __riscv - MG_ARM_DISABLE_IRQ(); -#else - asm volatile("csrrc zero, mstatus, %0" : : "i"(1 << 3) : "memory"); -#endif - while (src < end) { - uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_picosdk.start; - if (flash_sector_start(dst) && flash_erase(dst) == false) break; - // flash_range_program() runs in RAM and handles writing up to - // FLASH_PAGE_SIZE bytes. Source must not be in flash - flash_range_program((uint32_t) dst_ofs, (uint8_t *) src, - s_mg_flash_picosdk.align); - src = (uint32_t *) ((char *) src + s_mg_flash_picosdk.align); - dst = (uint32_t *) ((char *) dst + s_mg_flash_picosdk.align); - } - if (!s_flash_irq_disabled) { -#ifndef __riscv - MG_ARM_ENABLE_IRQ(); -#else - asm volatile("csrrs mstatus, %0" : : "i"(1 << 3) : "memory"); -#endif - } - MG_DEBUG(("Flash write %lu bytes @ %p.", len, dst)); - return true; -} - -// just overwrite instead of swap -static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2, - size_t s, - size_t ss) { - char *tmp = mg_calloc(1, ss); - if (tmp == NULL) return; -#if PICO_RP2040 - uint32_t xip[256 / sizeof(uint32_t)]; - void *dst = (void *) ((char *) p1 - (char *) s_mg_flash_picosdk.start); - size_t count = MG_ROUND_UP(s, ss); - // use SDK function calls to get BootROM function pointers - rom_connect_internal_flash_fn connect = (rom_connect_internal_flash_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH); - rom_flash_exit_xip_fn xit = (rom_flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP); - rom_flash_range_program_fn program = (rom_flash_range_program_fn) rom_func_lookup(ROM_FUNC_FLASH_RANGE_PROGRAM); - rom_flash_flush_cache_fn flush = (rom_flash_flush_cache_fn) rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE); - // no stdlib calls here. - MG_ARM_DISABLE_IRQ(); - // 2nd bootloader (XIP) is in flash, SDK functions copy it to RAM on entry - for (size_t i = 0; i < 256 / sizeof(uint32_t); i++) - xip[i] = ((uint32_t *) (s_mg_flash_picosdk.start))[i]; - flash_range_erase((uint32_t) dst, count); - // flash has been erased, no XIP to copy. Only BootROM calls possible - for (uint32_t ofs = 0; ofs < s; ofs += ss) { - for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i]; - __compiler_memory_barrier(); - connect(); - xit(); - program((uint32_t) dst + ofs, tmp, ss); - flush(); - ((void (*)(void))((intptr_t) xip + 1))(); // enter XIP again - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; // AIRCR = SYSRESETREQ -#else - // RP2350 has BootRAM and copies second bootloader there, SDK uses that copy, - // It might also be able to take advantage of partition swapping - rom_reboot_fn reboot = (rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT); - for (size_t ofs = 0; ofs < s; ofs += ss) { - for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i]; - mg_picosdk_write(p1 + ofs, tmp, ss); - } - reboot(BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); // 0x100: NO_RETURN_ON_SUCCESS -#endif -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_picosdk); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_picosdk); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_picosdk)) { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_picosdk.size, - s_mg_flash_picosdk.size / s_mg_flash_picosdk.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished or return on failure - single_bank_swap( - (char *) s_mg_flash_picosdk.start, - (char *) s_mg_flash_picosdk.start + s_mg_flash_picosdk.size / 2, - s_mg_flash_picosdk.size / 2, s_mg_flash_picosdk.secsz); - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_stm32f.c" -#endif - - - - -#if MG_OTA == MG_OTA_STM32F - -static bool mg_stm32f_write(void *, const void *, size_t); -static bool mg_stm32f_swap(void); - -static struct mg_flash s_mg_flash_stm32f = { - (void *) 0x08000000, // Start - 0, // Size, FLASH_SIZE_REG - 0, // Irregular sector size - 32, // Align, 256 bit - mg_stm32f_write, - mg_stm32f_swap, -}; - -#define MG_FLASH_BASE 0x40023c00 -#define MG_FLASH_KEYR 0x04 -#define MG_FLASH_SR 0x0c -#define MG_FLASH_CR 0x10 -#define MG_FLASH_OPTCR 0x14 -#define MG_FLASH_SIZE_REG_F7 0x1FF0F442 -#define MG_FLASH_SIZE_REG_F4 0x1FFF7A22 - -#define STM_DBGMCU_IDCODE 0xE0042000 -#define STM_DEV_ID (MG_REG(STM_DBGMCU_IDCODE) & (MG_BIT(12) - 1)) -#define SYSCFG_MEMRMP 0x40013800 - -#define MG_FLASH_SIZE_REG_LOCATION \ - ((STM_DEV_ID >= 0x449) ? MG_FLASH_SIZE_REG_F7 : MG_FLASH_SIZE_REG_F4) - -static size_t flash_size(void) { - return (MG_REG(MG_FLASH_SIZE_REG_LOCATION) & 0xFFFF) * 1024; -} - -MG_IRAM static int is_dualbank(void) { - // only F42x/F43x series (0x419) support dual bank - return STM_DEV_ID == 0x419; -} - -MG_IRAM static void flash_unlock(void) { - static bool unlocked = false; - if (unlocked == false) { - MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0x45670123; - MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0xcdef89ab; - unlocked = true; - } -} - -#define MG_FLASH_CONFIG_16_64_128 1 // used by STM32F7 -#define MG_FLASH_CONFIG_32_128_256 2 // used by STM32F4 and F2 - -MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_stm32f.start; - char *end = base + s_mg_flash_stm32f.size; - - if (is_dualbank() && dst >= (uint32_t *) (base + (end - base) / 2)) { - dst = (uint32_t *) ((uint32_t) dst - (end - base) / 2); - } - - uint32_t flash_config = MG_FLASH_CONFIG_16_64_128; - if (STM_DEV_ID >= 0x449) { - flash_config = MG_FLASH_CONFIG_32_128_256; - } - - volatile char *p = (char *) dst; - if (p >= base && p < end) { - if (p < base + 16 * 1024 * 4 * flash_config) { - if ((p - base) % (16 * 1024 * flash_config) == 0) return true; - } else if (p == base + 16 * 1024 * 4 * flash_config) { - return true; - } else if ((p - base) % (128 * 1024 * flash_config) == 0) { - return true; - } - } - return false; -} - -MG_IRAM static int flash_sector(volatile uint32_t *addr) { - char *base = (char *) s_mg_flash_stm32f.start; - char *end = base + s_mg_flash_stm32f.size; - bool addr_in_bank_2 = false; - if (is_dualbank() && addr >= (uint32_t *) (base + (end - base) / 2)) { - addr = (uint32_t *) ((uint32_t) addr - (end - base) / 2); - addr_in_bank_2 = true; - } - volatile char *p = (char *) addr; - uint32_t flash_config = MG_FLASH_CONFIG_16_64_128; - if (STM_DEV_ID >= 0x449) { - flash_config = MG_FLASH_CONFIG_32_128_256; - } - int sector = -1; - if (p >= base && p < end) { - if (p < base + 16 * 1024 * 4 * flash_config) { - sector = (p - base) / (16 * 1024 * flash_config); - } else if (p >= base + 64 * 1024 * flash_config && - p < base + 128 * 1024 * flash_config) { - sector = 4; - } else { - sector = (p - base) / (128 * 1024 * flash_config) + 4; - } - } - if (sector == -1) return -1; - if (addr_in_bank_2) sector += 12; // a bank has 12 sectors - return sector; -} - -MG_IRAM static bool flash_is_err(void) { - return MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & ((MG_BIT(7) - 1) << 1); -} - -MG_IRAM static void flash_wait(void) { - while (MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & (MG_BIT(16))) (void) 0; -} - -MG_IRAM static void flash_clear_err(void) { - flash_wait(); // Wait until ready - MG_REG(MG_FLASH_BASE + MG_FLASH_SR) = 0xf2; // Clear all errors -} - -MG_IRAM static bool mg_stm32f_erase(void *addr) { - bool ok = false; - if (flash_page_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - } else { - int sector = flash_sector(addr); - if (sector < 0) return false; - uint32_t sector_reg = sector; - if (is_dualbank() && sector >= 12) { - // 3.9.8 Flash control register (FLASH_CR) for F42xxx and F43xxx - // BITS[7:3] - sector_reg -= 12; - sector_reg |= MG_BIT(4); - } - flash_unlock(); - flash_wait(); - uint32_t cr = MG_BIT(1); // SER - cr |= MG_BIT(16); // STRT - cr |= (sector_reg & 31) << 3; // sector - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = cr; - ok = !flash_is_err(); - MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr, - ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR), - MG_REG(MG_FLASH_BASE + MG_FLASH_SR))); - // After we have erased the sector, set CR flags for programming - // 2 << 8 is word write parallelism, bit(0) is PG. RM0385, section 3.7.5 - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | (2 << 8); - flash_clear_err(); - } - return ok; -} - -MG_IRAM static bool mg_stm32f_swap(void) { - // STM32 F42x/F43x support dual bank, however, the memory mapping - // change will not be carried through a hard reset. Therefore, we will use - // the single bank approach for this family as well. - return true; -} - -static bool s_flash_irq_disabled; - -MG_IRAM static bool mg_stm32f_write(void *addr, const void *buf, size_t len) { - if ((len % s_mg_flash_stm32f.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32f.align)); - return false; - } - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - bool ok = true; - MG_ARM_DISABLE_IRQ(); - flash_unlock(); - flash_clear_err(); - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | MG_BIT(9); // PG, 32-bit - flash_wait(); - MG_DEBUG(("Writing flash @ %p, %lu bytes", addr, len)); - while (ok && src < end) { - if (flash_page_start(dst) && mg_stm32f_erase(dst) == false) break; - *(volatile uint32_t *) dst++ = *src++; - MG_DSB(); // ensure flash is written with no errors - flash_wait(); - if (flash_is_err()) ok = false; - } - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst, - ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR), - MG_REG(MG_FLASH_BASE + MG_FLASH_SR))); - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) &= ~MG_BIT(0); // Clear programming flag - return ok; -} - -// just overwrite instead of swap -MG_IRAM void single_bank_swap(char *p1, char *p2, size_t size) { - // no stdlib calls here - mg_stm32f_write(p1, p2, size); - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - s_mg_flash_stm32f.size = flash_size(); -#ifdef __ZEPHYR__ - *((uint32_t *)0xE000ED94) = 0; - MG_DEBUG(("Jailbreak %s", *((uint32_t *)0xE000ED94) == 0 ? "successful" : "failed")); -#endif - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32f); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_stm32f); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_stm32f)) { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_stm32f.size, STM_DEV_ID == 0x449 ? 8 : 12)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - char *p1 = (char *) s_mg_flash_stm32f.start; - char *p2 = p1 + s_mg_flash_stm32f.size / 2; - size_t size = s_mg_flash_stm32f.size / 2; - // Runs in RAM, will reset when finished - single_bank_swap(p1, p2, size); - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_stm32h5.c" -#endif - - - - -#if MG_OTA == MG_OTA_STM32H5 - -static bool mg_stm32h5_write(void *, const void *, size_t); -static bool mg_stm32h5_swap(void); - -static struct mg_flash s_mg_flash_stm32h5 = { - (void *) 0x08000000, // Start - 2 * 1024 * 1024, // Size, 2Mb - 8 * 1024, // Sector size, 8k - 16, // Align, 128 bit - mg_stm32h5_write, - mg_stm32h5_swap, -}; - -#define MG_FLASH_BASE 0x40022000 // Base address of the flash controller -#define FLASH_KEYR (MG_FLASH_BASE + 0x4) // See RM0481 7.11 -#define FLASH_OPTKEYR (MG_FLASH_BASE + 0xc) -#define FLASH_OPTCR (MG_FLASH_BASE + 0x1c) -#define FLASH_NSSR (MG_FLASH_BASE + 0x20) -#define FLASH_NSCR (MG_FLASH_BASE + 0x28) -#define FLASH_NSCCR (MG_FLASH_BASE + 0x30) -#define FLASH_OPTSR_CUR (MG_FLASH_BASE + 0x50) -#define FLASH_OPTSR_PRG (MG_FLASH_BASE + 0x54) - -static void flash_unlock(void) { - static bool unlocked = false; - if (unlocked == false) { - MG_REG(FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_KEYR) = 0Xcdef89ab; - MG_REG(FLASH_OPTKEYR) = 0x08192a3b; - MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f; - unlocked = true; - } -} - -static int flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_stm32h5.start, - *end = base + s_mg_flash_stm32h5.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_stm32h5.secsz) == 0; -} - -static bool flash_is_err(void) { - return MG_REG(FLASH_NSSR) & ((MG_BIT(8) - 1) << 17); // RM0481 7.11.9 -} - -static void flash_wait(void) { - while ((MG_REG(FLASH_NSSR) & MG_BIT(0)) && - (MG_REG(FLASH_NSSR) & MG_BIT(16)) == 0) { - (void) 0; - } -} - -static void flash_clear_err(void) { - flash_wait(); // Wait until ready - MG_REG(FLASH_NSCCR) = ((MG_BIT(9) - 1) << 16U); // Clear all errors -} - -static bool flash_bank_is_swapped(void) { - return MG_REG(FLASH_OPTCR) & MG_BIT(31); // RM0481 7.11.8 -} - -static bool mg_stm32h5_erase(void *location) { - bool ok = false; - if (flash_page_start(location) == false) { - MG_ERROR(("%p is not on a sector boundary")); - } else { - uintptr_t diff = (char *) location - (char *) s_mg_flash_stm32h5.start; - uint32_t sector = diff / s_mg_flash_stm32h5.secsz; - uint32_t saved_cr = MG_REG(FLASH_NSCR); // Save CR value - flash_unlock(); - flash_clear_err(); - MG_REG(FLASH_NSCR) = 0; - if ((sector < 128 && flash_bank_is_swapped()) || - (sector > 127 && !flash_bank_is_swapped())) { - MG_REG(FLASH_NSCR) |= MG_BIT(31); // Set FLASH_CR_BKSEL - } - if (sector > 127) sector -= 128; - MG_REG(FLASH_NSCR) |= MG_BIT(2) | (sector << 6); // Erase | sector_num - MG_REG(FLASH_NSCR) |= MG_BIT(5); // Start erasing - flash_wait(); - ok = !flash_is_err(); - MG_DEBUG(("Erase sector %lu @ %p: %s. CR %#lx SR %#lx", sector, location, - ok ? "ok" : "fail", MG_REG(FLASH_NSCR), MG_REG(FLASH_NSSR))); - // mg_hexdump(location, 32); - MG_REG(FLASH_NSCR) = saved_cr; // Restore saved CR - } - return ok; -} - -static bool mg_stm32h5_swap(void) { - uint32_t desired = flash_bank_is_swapped() ? 0 : MG_BIT(31); - flash_unlock(); - flash_clear_err(); - // printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG); - MG_SET_BITS(MG_REG(FLASH_OPTSR_PRG), MG_BIT(31), desired); - // printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG); - MG_REG(FLASH_OPTCR) |= MG_BIT(1); // OPTSTART - while ((MG_REG(FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0; - return true; -} - -static bool mg_stm32h5_write(void *addr, const void *buf, size_t len) { - if ((len % s_mg_flash_stm32h5.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32h5.align)); - return false; - } - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - bool ok = true; - MG_ARM_DISABLE_IRQ(); - flash_unlock(); - flash_clear_err(); - MG_REG(FLASH_NSCR) = MG_BIT(1); // Set programming flag - while (ok && src < end) { - if (flash_page_start(dst) && mg_stm32h5_erase(dst) == false) { - ok = false; - break; - } - *(volatile uint32_t *) dst++ = *src++; - flash_wait(); - if (flash_is_err()) ok = false; - } - MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst, - flash_is_err() ? "fail" : "ok", MG_REG(FLASH_NSCR), - MG_REG(FLASH_NSSR))); - MG_REG(FLASH_NSCR) = 0; // Clear flags - return ok; -} - -bool mg_ota_begin(size_t new_firmware_size) { -#ifdef __ZEPHYR__ - *((uint32_t *)0xE000ED94) = 0; - MG_DEBUG(("Jailbreak %s", *((uint32_t *)0xE000ED94) == 0 ? "successful" : "failed")); -#endif - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32h5); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_stm32h5); -} - -// Actual bank swap is deferred until reset, it is safe to execute in flash -bool mg_ota_end(void) { - if(!mg_ota_flash_end(&s_mg_flash_stm32h5)) return false; - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - return true; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_stm32h7.c" -#endif - - - - -#if MG_OTA == MG_OTA_STM32H7 || MG_OTA == MG_OTA_STM32H7_DUAL_CORE - -// - H723/735 RM 4.3.3: Note: The application can simultaneously request a read -// and a write operation through the AXI interface. -// - We only need IRAM for partition swapping in the H723, however, all -// related functions must reside in IRAM for this to be possible. -// - Linker files for other devices won't define a .iram section so there's no -// associated penalty - -static bool mg_stm32h7_write(void *, const void *, size_t); -static bool mg_stm32h7_swap(void); - -static struct mg_flash s_mg_flash_stm32h7 = { - (void *) 0x08000000, // Start - 0, // Size, FLASH_SIZE_REG - 128 * 1024, // Sector size, 128k - 32, // Align, 256 bit - mg_stm32h7_write, - mg_stm32h7_swap, -}; - -#define FLASH_BASE1 0x52002000 // Base address for bank1 -#define FLASH_BASE2 0x52002100 // Base address for bank2 -#define FLASH_KEYR 0x04 // See RM0433 4.9.2 -#define FLASH_OPTKEYR 0x08 -#define FLASH_OPTCR 0x18 -#define FLASH_SR 0x10 -#define FLASH_CR 0x0c -#define FLASH_CCR 0x14 -#define FLASH_OPTSR_CUR 0x1c -#define FLASH_OPTSR_PRG 0x20 -#define FLASH_SIZE_REG 0x1ff1e880 - -#define IS_DUALCORE() (MG_OTA == MG_OTA_STM32H7_DUAL_CORE) - -MG_IRAM static bool is_dualbank(void) { - if (IS_DUALCORE()) { - // H745/H755 and H747/H757 are running on dual core. - // Using only the 1st bank (mapped to CM7), in order not to interfere - // with the 2nd bank (CM4), possibly causing CM4 to boot unexpectedly. - return false; - } - return (s_mg_flash_stm32h7.size < 2 * 1024 * 1024) ? false : true; -} - -MG_IRAM static void flash_unlock(void) { - static bool unlocked = false; - if (unlocked == false) { - MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0xcdef89ab; - if (is_dualbank()) { - MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0xcdef89ab; - } - MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x08192a3b; // opt reg is "shared" - MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x4c5d6e7f; // thus unlock once - unlocked = true; - } -} - -MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_stm32h7.start, - *end = base + s_mg_flash_stm32h7.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_stm32h7.secsz) == 0; -} - -MG_IRAM static bool flash_is_err(uint32_t bank) { - return MG_REG(bank + FLASH_SR) & ((MG_BIT(11) - 1) << 17); // RM0433 4.9.5 -} - -MG_IRAM static void flash_wait(uint32_t bank) { - while (MG_REG(bank + FLASH_SR) & (MG_BIT(0) | MG_BIT(2))) (void) 0; -} - -MG_IRAM static void flash_clear_err(uint32_t bank) { - flash_wait(bank); // Wait until ready - MG_REG(bank + FLASH_CCR) = ((MG_BIT(11) - 1) << 16U); // Clear all errors -} - -MG_IRAM static bool flash_bank_is_swapped(uint32_t bank) { - return MG_REG(bank + FLASH_OPTCR) & MG_BIT(31); // RM0433 4.9.7 -} - -// Figure out flash bank based on the address -MG_IRAM static uint32_t flash_bank(void *addr) { - size_t ofs = (char *) addr - (char *) s_mg_flash_stm32h7.start; - if (!is_dualbank()) return FLASH_BASE1; - return ofs < s_mg_flash_stm32h7.size / 2 ? FLASH_BASE1 : FLASH_BASE2; -} - -// read-while-write, no need to disable IRQs for standalone usage -MG_IRAM static bool mg_stm32h7_erase(void *addr) { - bool ok = false; - if (flash_page_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - } else { - uintptr_t diff = (char *) addr - (char *) s_mg_flash_stm32h7.start; - uint32_t sector = diff / s_mg_flash_stm32h7.secsz; - uint32_t bank = flash_bank(addr); - uint32_t saved_cr = MG_REG(bank + FLASH_CR); // Save CR value - - flash_unlock(); - if (sector > 7) sector -= 8; - - flash_clear_err(bank); - MG_REG(bank + FLASH_CR) = MG_BIT(5); // 32-bit write parallelism - MG_REG(bank + FLASH_CR) |= (sector & 7U) << 8U; // Sector to erase - MG_REG(bank + FLASH_CR) |= MG_BIT(2); // Sector erase bit - MG_REG(bank + FLASH_CR) |= MG_BIT(7); // Start erasing - ok = !flash_is_err(bank); - MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr, - ok ? "ok" : "fail", MG_REG(bank + FLASH_CR), - MG_REG(bank + FLASH_SR))); - MG_REG(bank + FLASH_CR) = saved_cr; // Restore CR - } - return ok; -} - -MG_IRAM static bool mg_stm32h7_swap(void) { - if (!is_dualbank()) return true; - uint32_t bank = FLASH_BASE1; - uint32_t desired = flash_bank_is_swapped(bank) ? 0 : MG_BIT(31); - flash_unlock(); - flash_clear_err(bank); - // printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG); - MG_SET_BITS(MG_REG(bank + FLASH_OPTSR_PRG), MG_BIT(31), desired); - // printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG); - MG_REG(bank + FLASH_OPTCR) |= MG_BIT(1); // OPTSTART - while ((MG_REG(bank + FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0; - return true; -} - -static bool s_flash_irq_disabled; - -MG_IRAM static bool mg_stm32h7_write(void *addr, const void *buf, size_t len) { - if ((len % s_mg_flash_stm32h7.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32h7.align)); - return false; - } - uint32_t bank = flash_bank(addr); - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - bool ok = true; - MG_ARM_DISABLE_IRQ(); - flash_unlock(); - flash_clear_err(bank); - MG_REG(bank + FLASH_CR) = MG_BIT(1); // Set programming flag - MG_REG(bank + FLASH_CR) |= MG_BIT(5); // 32-bit write parallelism - while (ok && src < end) { - if (flash_page_start(dst) && mg_stm32h7_erase(dst) == false) { - ok = false; - break; - } - *(volatile uint32_t *) dst++ = *src++; - flash_wait(bank); - if (flash_is_err(bank)) ok = false; - } - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst, - ok ? "ok" : "fail", MG_REG(bank + FLASH_CR), - MG_REG(bank + FLASH_SR))); - MG_REG(bank + FLASH_CR) &= ~MG_BIT(1); // Clear programming flag - return ok; -} - -// just overwrite instead of swap -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - mg_stm32h7_write(p1 + ofs, p2 + ofs, ss); - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - s_mg_flash_stm32h7.size = MG_REG(FLASH_SIZE_REG) * 1024; - if (IS_DUALCORE()) { - // Using only the 1st bank (mapped to CM7) - s_mg_flash_stm32h7.size /= 2; - } -#ifdef __ZEPHYR__ - *((uint32_t *)0xE000ED94) = 0; - MG_DEBUG(("Jailbreak %s", *((uint32_t *)0xE000ED94) == 0 ? "successful" : "failed")); -#endif - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32h7); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_stm32h7); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_stm32h7)) { - if (is_dualbank()) { - // Bank swap is deferred until reset, been executing in flash, reset - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - } else { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_stm32h7.size, - s_mg_flash_stm32h7.size / s_mg_flash_stm32h7.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_stm32h7.start, - (char *) s_mg_flash_stm32h7.start + s_mg_flash_stm32h7.size / 2, - s_mg_flash_stm32h7.size / 2, s_mg_flash_stm32h7.secsz); - } - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/printf.c" -#endif - - - - -size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) { - size_t len = mg_snprintf(NULL, 0, fmt, ap); - char *buf; - if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) { - len = 0; // Nah. Not enough space - } else { - len = mg_vsnprintf((char *) buf, len + 1, fmt, ap); - mg_queue_add(q, len); - } - return len; -} - -size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) { - va_list ap; - size_t len; - va_start(ap, fmt); - len = mg_queue_vprintf(q, fmt, &ap); - va_end(ap); - return len; -} - -static void mg_pfn_iobuf_private(char ch, void *param, bool expand) { - struct mg_iobuf *io = (struct mg_iobuf *) param; - if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2); - if (io->len + 2 <= io->size) { - io->buf[io->len++] = (uint8_t) ch; - io->buf[io->len] = 0; - } else if (io->len < io->size) { - io->buf[io->len++] = 0; // Guarantee to 0-terminate - } -} - -static void mg_putchar_iobuf_static(char ch, void *param) { - mg_pfn_iobuf_private(ch, param, false); -} - -void mg_pfn_iobuf(char ch, void *param) { - mg_pfn_iobuf_private(ch, param, true); -} - -size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) { - struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0}; - size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap); - if (n < len) buf[n] = '\0'; - return n; -} - -size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...) { - va_list ap; - size_t n; - va_start(ap, fmt); - n = mg_vsnprintf(buf, len, fmt, &ap); - va_end(ap); - return n; -} - -char *mg_vmprintf(const char *fmt, va_list *ap) { - struct mg_iobuf io = {0, 0, 0, 256}; - mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap); - return (char *) io.buf; -} - -char *mg_mprintf(const char *fmt, ...) { - char *s; - va_list ap; - va_start(ap, fmt); - s = mg_vmprintf(fmt, &ap); - va_end(ap); - return s; -} - -void mg_pfn_stdout(char c, void *param) { - putchar(c); - (void) param; -} - -static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) { - return mg_xprintf(out, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); -} - -static size_t print_ip6(void (*out)(char, void *), void *arg, uint16_t *p) { - return mg_xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", mg_ntohs(p[0]), - mg_ntohs(p[1]), mg_ntohs(p[2]), mg_ntohs(p[3]), - mg_ntohs(p[4]), mg_ntohs(p[5]), mg_ntohs(p[6]), - mg_ntohs(p[7])); -} - -size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap) { - uint8_t *p = va_arg(*ap, uint8_t *); - return print_ip4(out, arg, p); -} - -size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap) { - uint16_t *p = va_arg(*ap, uint16_t *); - return print_ip6(out, arg, p); -} - -size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) { - struct mg_addr *addr = va_arg(*ap, struct mg_addr *); - if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->ip); - return print_ip4(out, arg, (uint8_t *) &addr->ip); -} - -size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) { - struct mg_addr *a = va_arg(*ap, struct mg_addr *); - return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port)); -} - -size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) { - uint8_t *p = va_arg(*ap, uint8_t *); - return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], - p[3], p[4], p[5]); -} - -static char mg_esc(int c, bool esc) { - const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\""; - for (p = esc ? esc1 : esc2; *p != '\0'; p++) { - if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2]; - } - return 0; -} - -static char mg_escape(int c) { - return mg_esc(c, true); -} - -static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf, - size_t len) { - size_t i = 0, extra = 0; - for (i = 0; i < len && buf[i] != '\0'; i++) { - char c = mg_escape(buf[i]); - if (c) { - out('\\', ptr), out(c, ptr), extra++; - } else { - out(buf[i], ptr); - } - } - return i + extra; -} - -static size_t bcpy(void (*out)(char, void *), void *arg, uint8_t *buf, - size_t len) { - size_t i, j, n = 0; - const char *t = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - for (i = 0; i < len; i += 3) { - uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0, - c3 = i + 2 < len ? buf[i + 2] : 0; - char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='}; - if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)]; - if (i + 2 < len) tmp[3] = t[c3 & 63]; - for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg); - n += j; - } - return n; -} - -size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap) { - size_t bl = (size_t) va_arg(*ap, int); - uint8_t *p = va_arg(*ap, uint8_t *); - const char *hex = "0123456789abcdef"; - size_t j; - for (j = 0; j < bl; j++) { - out(hex[(p[j] >> 4) & 0x0F], arg); - out(hex[p[j] & 0x0F], arg); - } - return 2 * bl; -} -size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap) { - size_t len = (size_t) va_arg(*ap, int); - uint8_t *buf = va_arg(*ap, uint8_t *); - return bcpy(out, arg, buf, len); -} - -size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap) { - size_t len = (size_t) va_arg(*ap, int); - char *p = va_arg(*ap, char *); - if (len == 0) len = p == NULL ? 0 : strlen(p); - return qcpy(out, arg, p, len); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/queue.c" -#endif - - - -#if (defined(__GNUC__) && (__GNUC__ > 4) || \ - (defined(__GNUC_MINOR__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ - defined(__clang__) -#define MG_MEMORY_BARRIER() __sync_synchronize() -#elif defined(_MSC_VER) && _MSC_VER >= 1700 -#define MG_MEMORY_BARRIER() MemoryBarrier() -#elif !defined(MG_MEMORY_BARRIER) -#define MG_MEMORY_BARRIER() -#endif - -// Every message in a queue is prepended by a 32-bit message length (ML). -// If ML is 0, then it is the end, and reader must wrap to the beginning. -// -// Queue when q->tail <= q->head: -// |----- free -----| ML | message1 | ML | message2 | ----- free ------| -// ^ ^ ^ ^ -// buf tail head len -// -// Queue when q->tail > q->head: -// | ML | message2 |----- free ------| ML | message1 | 0 |---- free ----| -// ^ ^ ^ ^ -// buf head tail len - -void mg_queue_init(struct mg_queue *q, char *buf, size_t size) { - q->size = size; - q->buf = buf; - q->head = q->tail = 0; -} - -static size_t mg_queue_read_len(struct mg_queue *q) { - uint32_t n = 0; - MG_MEMORY_BARRIER(); - memcpy(&n, q->buf + q->tail, sizeof(n)); - assert(q->tail + n + sizeof(n) <= q->size); - return n; -} - -static void mg_queue_write_len(struct mg_queue *q, size_t len) { - uint32_t n = (uint32_t) len; - memcpy(q->buf + q->head, &n, sizeof(n)); - MG_MEMORY_BARRIER(); -} - -size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len) { - size_t space = 0, hs = sizeof(uint32_t) * 2; // *2 is for the 0 marker - if (q->head >= q->tail && q->head + len + hs <= q->size) { - space = q->size - q->head - hs; // There is enough space - } else if (q->head >= q->tail && q->tail > hs) { - mg_queue_write_len(q, 0); // Not enough space ahead - q->head = 0; // Wrap head to the beginning - } - if (q->head + hs + len < q->tail) space = q->tail - q->head - hs; - if (buf != NULL) *buf = q->buf + q->head + sizeof(uint32_t); - return space; -} - -size_t mg_queue_next(struct mg_queue *q, char **buf) { - size_t len = 0; - if (q->tail != q->head) { - len = mg_queue_read_len(q); - if (len == 0) { // Zero (head wrapped) ? - q->tail = 0; // Reset tail to the start - if (q->head > q->tail) len = mg_queue_read_len(q); // Read again - } - } - if (buf != NULL) *buf = q->buf + q->tail + sizeof(uint32_t); - assert(q->tail + len <= q->size); - return len; -} - -void mg_queue_add(struct mg_queue *q, size_t len) { - assert(len > 0); - mg_queue_write_len(q, len); - assert(q->head + sizeof(uint32_t) * 2 + len <= q->size); - q->head += len + sizeof(uint32_t); -} - -void mg_queue_del(struct mg_queue *q, size_t len) { - q->tail += len + sizeof(uint32_t); - assert(q->tail + sizeof(uint32_t) <= q->size); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/rpc.c" -#endif - - - - -void mg_rpc_add(struct mg_rpc **head, struct mg_str method, - void (*fn)(struct mg_rpc_req *), void *fn_data) { - struct mg_rpc *rpc = (struct mg_rpc *) mg_calloc(1, sizeof(*rpc)); - if (rpc != NULL) { - rpc->method = mg_strdup(method); - rpc->fn = fn; - rpc->fn_data = fn_data; - rpc->next = *head, *head = rpc; - } -} - -void mg_rpc_del(struct mg_rpc **head, void (*fn)(struct mg_rpc_req *)) { - struct mg_rpc *r; - while ((r = *head) != NULL) { - if (r->fn == fn || fn == NULL) { - *head = r->next; - mg_free((void *) r->method.buf); - mg_free(r); - } else { - head = &(*head)->next; - } - } -} - -static void mg_rpc_call(struct mg_rpc_req *r, struct mg_str method) { - struct mg_rpc *h = r->head == NULL ? NULL : *r->head; - while (h != NULL && !mg_match(method, h->method, NULL)) h = h->next; - if (h != NULL) { - r->rpc = h; - h->fn(r); - } else { - mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) method.len, method.buf); - } -} - -void mg_rpc_process(struct mg_rpc_req *r) { - int len, off = mg_json_get(r->frame, "$.method", &len); - if (off > 0 && r->frame.buf[off] == '"') { - struct mg_str method = mg_str_n(&r->frame.buf[off + 1], (size_t) len - 2); - mg_rpc_call(r, method); - } else if ((off = mg_json_get(r->frame, "$.result", &len)) > 0 || - (off = mg_json_get(r->frame, "$.error", &len)) > 0) { - mg_rpc_call(r, mg_str("")); // JSON response! call "" method handler - } else { - mg_rpc_err(r, -32700, "%m", mg_print_esc, (int) r->frame.len, - r->frame.buf); // Invalid - } -} - -void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) { - int len, off = mg_json_get(r->frame, "$.id", &len); - if (off > 0) { - mg_xprintf(r->pfn, r->pfn_data, "{%m:%.*s,%m:", mg_print_esc, 0, "id", len, - &r->frame.buf[off], mg_print_esc, 0, "result"); - mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); - mg_xprintf(r->pfn, r->pfn_data, "}"); - } -} - -void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_rpc_vok(r, fmt, &ap); - va_end(ap); -} - -void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) { - int len, off = mg_json_get(r->frame, "$.id", &len); - mg_xprintf(r->pfn, r->pfn_data, "{"); - if (off > 0) { - mg_xprintf(r->pfn, r->pfn_data, "%m:%.*s,", mg_print_esc, 0, "id", len, - &r->frame.buf[off]); - } - mg_xprintf(r->pfn, r->pfn_data, "%m:{%m:%d,%m:", mg_print_esc, 0, "error", - mg_print_esc, 0, "code", code, mg_print_esc, 0, "message"); - mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); - mg_xprintf(r->pfn, r->pfn_data, "}}"); -} - -void mg_rpc_err(struct mg_rpc_req *r, int code, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_rpc_verr(r, code, fmt, &ap); - va_end(ap); -} - -static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) { - struct mg_rpc *h, **head = (struct mg_rpc **) va_arg(*ap, void **); - size_t len = 0; - for (h = *head; h != NULL; h = h->next) { - if (h->method.len == 0) continue; // Ignore response handler - len += mg_xprintf(pfn, pfn_data, "%s%m", h == *head ? "" : ",", - mg_print_esc, (int) h->method.len, h->method.buf); - } - return len; -} - -void mg_rpc_list(struct mg_rpc_req *r) { - mg_rpc_ok(r, "[%M]", print_methods, r->head); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sha1.c" -#endif -/* Copyright(c) By Steve Reid */ -/* 100% Public Domain */ - - - -union char64long16 { - unsigned char c[64]; - uint32_t l[16]; -}; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static uint32_t blk0(union char64long16 *block, int i) { - if (MG_BIG_ENDIAN) { - } else { - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | - (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ -#undef blk -#undef R0 -#undef R1 -#undef R2 -#undef R3 -#undef R4 - -#define blk(i) \ - (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \ - block->l[(i + 2) & 15] ^ block->l[i & 15], \ - 1)) -#define R0(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ - w = rol(w, 30); -#define R3(v, w, x, y, z, i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ - w = rol(w, 30); - -static void mg_sha1_transform(uint32_t state[5], - const unsigned char *buffer) { - uint32_t a, b, c, d, e; - union char64long16 block[1]; - - memcpy(block, buffer, 64); - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - R0(a, b, c, d, e, 0); - R0(e, a, b, c, d, 1); - R0(d, e, a, b, c, 2); - R0(c, d, e, a, b, 3); - R0(b, c, d, e, a, 4); - R0(a, b, c, d, e, 5); - R0(e, a, b, c, d, 6); - R0(d, e, a, b, c, 7); - R0(c, d, e, a, b, 8); - R0(b, c, d, e, a, 9); - R0(a, b, c, d, e, 10); - R0(e, a, b, c, d, 11); - R0(d, e, a, b, c, 12); - R0(c, d, e, a, b, 13); - R0(b, c, d, e, a, 14); - R0(a, b, c, d, e, 15); - R1(e, a, b, c, d, 16); - R1(d, e, a, b, c, 17); - R1(c, d, e, a, b, 18); - R1(b, c, d, e, a, 19); - R2(a, b, c, d, e, 20); - R2(e, a, b, c, d, 21); - R2(d, e, a, b, c, 22); - R2(c, d, e, a, b, 23); - R2(b, c, d, e, a, 24); - R2(a, b, c, d, e, 25); - R2(e, a, b, c, d, 26); - R2(d, e, a, b, c, 27); - R2(c, d, e, a, b, 28); - R2(b, c, d, e, a, 29); - R2(a, b, c, d, e, 30); - R2(e, a, b, c, d, 31); - R2(d, e, a, b, c, 32); - R2(c, d, e, a, b, 33); - R2(b, c, d, e, a, 34); - R2(a, b, c, d, e, 35); - R2(e, a, b, c, d, 36); - R2(d, e, a, b, c, 37); - R2(c, d, e, a, b, 38); - R2(b, c, d, e, a, 39); - R3(a, b, c, d, e, 40); - R3(e, a, b, c, d, 41); - R3(d, e, a, b, c, 42); - R3(c, d, e, a, b, 43); - R3(b, c, d, e, a, 44); - R3(a, b, c, d, e, 45); - R3(e, a, b, c, d, 46); - R3(d, e, a, b, c, 47); - R3(c, d, e, a, b, 48); - R3(b, c, d, e, a, 49); - R3(a, b, c, d, e, 50); - R3(e, a, b, c, d, 51); - R3(d, e, a, b, c, 52); - R3(c, d, e, a, b, 53); - R3(b, c, d, e, a, 54); - R3(a, b, c, d, e, 55); - R3(e, a, b, c, d, 56); - R3(d, e, a, b, c, 57); - R3(c, d, e, a, b, 58); - R3(b, c, d, e, a, 59); - R4(a, b, c, d, e, 60); - R4(e, a, b, c, d, 61); - R4(d, e, a, b, c, 62); - R4(c, d, e, a, b, 63); - R4(b, c, d, e, a, 64); - R4(a, b, c, d, e, 65); - R4(e, a, b, c, d, 66); - R4(d, e, a, b, c, 67); - R4(c, d, e, a, b, 68); - R4(b, c, d, e, a, 69); - R4(a, b, c, d, e, 70); - R4(e, a, b, c, d, 71); - R4(d, e, a, b, c, 72); - R4(c, d, e, a, b, 73); - R4(b, c, d, e, a, 74); - R4(a, b, c, d, e, 75); - R4(e, a, b, c, d, 76); - R4(d, e, a, b, c, 77); - R4(c, d, e, a, b, 78); - R4(b, c, d, e, a, 79); - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Erase working structures. The order of operations is important, - * used to ensure that compiler doesn't optimize those out. */ - memset(block, 0, sizeof(block)); - a = b = c = d = e = 0; - (void) a; - (void) b; - (void) c; - (void) d; - (void) e; -} - -void mg_sha1_init(mg_sha1_ctx *context) { - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -void mg_sha1_update(mg_sha1_ctx *context, const unsigned char *data, - size_t len) { - size_t i, j; - - j = context->count[0]; - if ((context->count[0] += (uint32_t) len << 3) < j) context->count[1]++; - context->count[1] += (uint32_t) (len >> 29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64 - j)); - mg_sha1_transform(context->state, context->buffer); - for (; i + 63 < len; i += 64) { - mg_sha1_transform(context->state, &data[i]); - } - j = 0; - } else - i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) { - unsigned i; - unsigned char finalcount[8], c; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> - ((3 - (i & 3)) * 8)) & - 255); - } - c = 0200; - mg_sha1_update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - mg_sha1_update(context, &c, 1); - } - mg_sha1_update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = - (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); - } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sha256.c" -#endif -// https://github.com/B-Con/crypto-algorithms -// Author: Brad Conte (brad AT bradconte.com) -// Disclaimer: This code is presented "as is" without any guarantees. -// Details: Defines the API for the corresponding SHA1 implementation. -// Copyright: public domain - - - -#define ror(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) -#define ch(x, y, z) (((x) & (y)) ^ (~(x) & (z))) -#define maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define ep0(x) (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22)) -#define ep1(x) (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25)) -#define sig0(x) (ror(x, 7) ^ ror(x, 18) ^ ((x) >> 3)) -#define sig1(x) (ror(x, 17) ^ ror(x, 19) ^ ((x) >> 10)) - -static const uint32_t mg_sha256_k[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; - -void mg_sha256_init(mg_sha256_ctx *ctx) { - ctx->len = 0; - ctx->bits = 0; - ctx->state[0] = 0x6a09e667; - ctx->state[1] = 0xbb67ae85; - ctx->state[2] = 0x3c6ef372; - ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; - ctx->state[5] = 0x9b05688c; - ctx->state[6] = 0x1f83d9ab; - ctx->state[7] = 0x5be0cd19; -} - -static void mg_sha256_chunk(mg_sha256_ctx *ctx) { - int i, j; - uint32_t a, b, c, d, e, f, g, h; - uint32_t m[64]; - for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (uint32_t) (((uint32_t) ctx->buffer[j] << 24) | - ((uint32_t) ctx->buffer[j + 1] << 16) | - ((uint32_t) ctx->buffer[j + 2] << 8) | - ((uint32_t) ctx->buffer[j + 3])); - for (; i < 64; ++i) - m[i] = sig1(m[i - 2]) + m[i - 7] + sig0(m[i - 15]) + m[i - 16]; - - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for (i = 0; i < 64; ++i) { - uint32_t t1 = h + ep1(e) + ch(e, f, g) + mg_sha256_k[i] + m[i]; - uint32_t t2 = ep0(a) + maj(a, b, c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} - -void mg_sha256_update(mg_sha256_ctx *ctx, const unsigned char *data, - size_t len) { - size_t i; - for (i = 0; i < len; i++) { - ctx->buffer[ctx->len] = data[i]; - if ((++ctx->len) == 64) { - mg_sha256_chunk(ctx); - ctx->bits += 512; - ctx->len = 0; - } - } -} - -// TODO: make final reusable (remove side effects) -void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *ctx) { - uint32_t i = ctx->len; - if (i < 56) { - ctx->buffer[i++] = 0x80; - while (i < 56) { - ctx->buffer[i++] = 0x00; - } - } else { - ctx->buffer[i++] = 0x80; - while (i < 64) { - ctx->buffer[i++] = 0x00; - } - mg_sha256_chunk(ctx); - memset(ctx->buffer, 0, 56); - } - - ctx->bits += ctx->len * 8; - ctx->buffer[63] = (uint8_t) ((ctx->bits) & 0xff); - ctx->buffer[62] = (uint8_t) ((ctx->bits >> 8) & 0xff); - ctx->buffer[61] = (uint8_t) ((ctx->bits >> 16) & 0xff); - ctx->buffer[60] = (uint8_t) ((ctx->bits >> 24) & 0xff); - ctx->buffer[59] = (uint8_t) ((ctx->bits >> 32) & 0xff); - ctx->buffer[58] = (uint8_t) ((ctx->bits >> 40) & 0xff); - ctx->buffer[57] = (uint8_t) ((ctx->bits >> 48) & 0xff); - ctx->buffer[56] = (uint8_t) ((ctx->bits >> 56) & 0xff); - mg_sha256_chunk(ctx); - - for (i = 0; i < 4; ++i) { - digest[i] = (uint8_t) ((ctx->state[0] >> (24 - i * 8)) & 0xff); - digest[i + 4] = (uint8_t) ((ctx->state[1] >> (24 - i * 8)) & 0xff); - digest[i + 8] = (uint8_t) ((ctx->state[2] >> (24 - i * 8)) & 0xff); - digest[i + 12] = (uint8_t) ((ctx->state[3] >> (24 - i * 8)) & 0xff); - digest[i + 16] = (uint8_t) ((ctx->state[4] >> (24 - i * 8)) & 0xff); - digest[i + 20] = (uint8_t) ((ctx->state[5] >> (24 - i * 8)) & 0xff); - digest[i + 24] = (uint8_t) ((ctx->state[6] >> (24 - i * 8)) & 0xff); - digest[i + 28] = (uint8_t) ((ctx->state[7] >> (24 - i * 8)) & 0xff); - } -} - -void mg_sha256(uint8_t dst[32], uint8_t *data, size_t datasz) { - mg_sha256_ctx ctx; - mg_sha256_init(&ctx); - mg_sha256_update(&ctx, data, datasz); - mg_sha256_final(dst, &ctx); -} - -void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, - size_t datasz) { - mg_sha256_ctx ctx; - uint8_t k[64] = {0}; - uint8_t o_pad[64], i_pad[64]; - unsigned int i; - memset(i_pad, 0x36, sizeof(i_pad)); - memset(o_pad, 0x5c, sizeof(o_pad)); - if (keysz < 64) { - if (keysz > 0) memmove(k, key, keysz); - } else { - mg_sha256_init(&ctx); - mg_sha256_update(&ctx, key, keysz); - mg_sha256_final(k, &ctx); - } - for (i = 0; i < sizeof(k); i++) { - i_pad[i] ^= k[i]; - o_pad[i] ^= k[i]; - } - mg_sha256_init(&ctx); - mg_sha256_update(&ctx, i_pad, sizeof(i_pad)); - mg_sha256_update(&ctx, data, datasz); - mg_sha256_final(dst, &ctx); - mg_sha256_init(&ctx); - mg_sha256_update(&ctx, o_pad, sizeof(o_pad)); - mg_sha256_update(&ctx, dst, 32); - mg_sha256_final(dst, &ctx); -} - -#define rotr64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) -#define ep064(x) (rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39)) -#define ep164(x) (rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41)) -#define sig064(x) (rotr64(x, 1) ^ rotr64(x, 8) ^ ((x) >> 7)) -#define sig164(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ ((x) >> 6)) - -static const uint64_t mg_sha256_k2[80] = { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, - 0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019, - 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, - 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, - 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, - 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275, - 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, - 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, - 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, - 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, - 0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001, - 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, - 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, - 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, - 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, - 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, - 0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207, - 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, - 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, - 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, - 0x5fcb6fab3ad6faec, 0x6c44198c4a475817}; - -static void mg_sha384_transform(mg_sha384_ctx *ctx, const uint8_t data[]) { - uint64_t m[80]; - uint64_t a, b, c, d, e, f, g, h; - int i, j; - - for (i = 0, j = 0; i < 16; ++i, j += 8) - m[i] = ((uint64_t) data[j] << 56) | ((uint64_t) data[j + 1] << 48) | - ((uint64_t) data[j + 2] << 40) | ((uint64_t) data[j + 3] << 32) | - ((uint64_t) data[j + 4] << 24) | ((uint64_t) data[j + 5] << 16) | - ((uint64_t) data[j + 6] << 8) | ((uint64_t) data[j + 7]); - for (; i < 80; ++i) - m[i] = sig164(m[i - 2]) + m[i - 7] + sig064(m[i - 15]) + m[i - 16]; - - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for (i = 0; i < 80; ++i) { - uint64_t t1 = h + ep164(e) + ch(e, f, g) + mg_sha256_k2[i] + m[i]; - uint64_t t2 = ep064(a) + maj(a, b, c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} - -void mg_sha384_init(mg_sha384_ctx *ctx) { - ctx->datalen = 0; - ctx->bitlen[0] = 0; - ctx->bitlen[1] = 0; - ctx->state[0] = 0xcbbb9d5dc1059ed8; - ctx->state[1] = 0x629a292a367cd507; - ctx->state[2] = 0x9159015a3070dd17; - ctx->state[3] = 0x152fecd8f70e5939; - ctx->state[4] = 0x67332667ffc00b31; - ctx->state[5] = 0x8eb44a8768581511; - ctx->state[6] = 0xdb0c2e0d64f98fa7; - ctx->state[7] = 0x47b5481dbefa4fa4; -} - -void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len) { - size_t i; - for (i = 0; i < len; ++i) { - ctx->buffer[ctx->datalen] = data[i]; - ctx->datalen++; - if (ctx->datalen == 128) { - mg_sha384_transform(ctx, ctx->buffer); - ctx->bitlen[1] += 1024; - if (ctx->bitlen[1] < 1024) ctx->bitlen[0]++; - ctx->datalen = 0; - } - } -} - -void mg_sha384_final(uint8_t hash[48], mg_sha384_ctx *ctx) { - size_t i = ctx->datalen; - - if (ctx->datalen < 112) { - ctx->buffer[i++] = 0x80; - while (i < 112) ctx->buffer[i++] = 0x00; - } else { - ctx->buffer[i++] = 0x80; - while (i < 128) ctx->buffer[i++] = 0x00; - mg_sha384_transform(ctx, ctx->buffer); - memset(ctx->buffer, 0, 112); - } - - ctx->bitlen[1] += ctx->datalen * 8; - if (ctx->bitlen[1] < ctx->datalen * 8) ctx->bitlen[0]++; - ctx->buffer[127] = (uint8_t) (ctx->bitlen[1]); - ctx->buffer[126] = (uint8_t) (ctx->bitlen[1] >> 8); - ctx->buffer[125] = (uint8_t) (ctx->bitlen[1] >> 16); - ctx->buffer[124] = (uint8_t) (ctx->bitlen[1] >> 24); - ctx->buffer[123] = (uint8_t) (ctx->bitlen[1] >> 32); - ctx->buffer[122] = (uint8_t) (ctx->bitlen[1] >> 40); - ctx->buffer[121] = (uint8_t) (ctx->bitlen[1] >> 48); - ctx->buffer[120] = (uint8_t) (ctx->bitlen[1] >> 56); - ctx->buffer[119] = (uint8_t) (ctx->bitlen[0]); - ctx->buffer[118] = (uint8_t) (ctx->bitlen[0] >> 8); - ctx->buffer[117] = (uint8_t) (ctx->bitlen[0] >> 16); - ctx->buffer[116] = (uint8_t) (ctx->bitlen[0] >> 24); - ctx->buffer[115] = (uint8_t) (ctx->bitlen[0] >> 32); - ctx->buffer[114] = (uint8_t) (ctx->bitlen[0] >> 40); - ctx->buffer[113] = (uint8_t) (ctx->bitlen[0] >> 48); - ctx->buffer[112] = (uint8_t) (ctx->bitlen[0] >> 56); - mg_sha384_transform(ctx, ctx->buffer); - - for (i = 0; i < 6; ++i) { - hash[i * 8] = (uint8_t) ((ctx->state[i] >> 56) & 0xff); - hash[i * 8 + 1] = (uint8_t) ((ctx->state[i] >> 48) & 0xff); - hash[i * 8 + 2] = (uint8_t) ((ctx->state[i] >> 40) & 0xff); - hash[i * 8 + 3] = (uint8_t) ((ctx->state[i] >> 32) & 0xff); - hash[i * 8 + 4] = (uint8_t) ((ctx->state[i] >> 24) & 0xff); - hash[i * 8 + 5] = (uint8_t) ((ctx->state[i] >> 16) & 0xff); - hash[i * 8 + 6] = (uint8_t) ((ctx->state[i] >> 8) & 0xff); - hash[i * 8 + 7] = (uint8_t) (ctx->state[i] & 0xff); - } -} - -void mg_sha384(uint8_t dst[48], uint8_t *data, size_t datasz) { - mg_sha384_ctx ctx; - mg_sha384_init(&ctx); - mg_sha384_update(&ctx, data, datasz); - mg_sha384_final(dst, &ctx); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sntp.c" -#endif - - - - - - -#define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds -#define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1 - -static uint64_t s_boot_timestamp = 0; // Updated by SNTP - -uint64_t mg_now(void) { - return mg_millis() + s_boot_timestamp; -} - -static int64_t gettimestamp(const uint32_t *data) { - uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]); - if (sec) sec -= SNTP_TIME_OFFSET; - return ((int64_t) sec) * 1000 + (int64_t) (frac / SNTP_MAX_FRAC * 1000.0); -} - -int64_t mg_sntp_parse(const unsigned char *buf, size_t len) { - int64_t epoch_milliseconds = -1; - int mode = len > 0 ? buf[0] & 7 : 0; - int version = len > 0 ? (buf[0] >> 3) & 7 : 0; - if (len < 48) { - MG_ERROR(("%s", "corrupt packet")); - } else if (mode != 4 && mode != 5) { - MG_ERROR(("%s", "not a server reply")); - } else if (buf[1] == 0) { - MG_ERROR(("%s", "server sent a kiss of death")); - } else if (version == 4 || version == 3) { - // int64_t ref = gettimestamp((uint32_t *) &buf[16]); - int64_t origin_time = gettimestamp((uint32_t *) &buf[24]); - int64_t receive_time = gettimestamp((uint32_t *) &buf[32]); - int64_t transmit_time = gettimestamp((uint32_t *) &buf[40]); - int64_t now = (int64_t) mg_millis(); - int64_t latency = (now - origin_time) - (transmit_time - receive_time); - epoch_milliseconds = transmit_time + latency / 2; - s_boot_timestamp = (uint64_t) (epoch_milliseconds - now); - } else { - MG_ERROR(("unexpected version: %d", version)); - } - return epoch_milliseconds; -} - -static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) { - uint64_t *expiration_time = (uint64_t *) c->data; - if (ev == MG_EV_OPEN) { - *expiration_time = mg_millis() + 3000; // Store expiration time in 3s - } else if (ev == MG_EV_CONNECT) { - mg_sntp_request(c); - } else if (ev == MG_EV_READ) { - int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); - if (milliseconds > 0) { - s_boot_timestamp = (uint64_t) milliseconds - mg_millis(); - mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds); - MG_DEBUG(("%lu got time: %lld ms from epoch", c->id, milliseconds)); - } - // mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer - c->is_closing = 1; - } else if (ev == MG_EV_POLL) { - if (mg_millis() > *expiration_time) c->is_closing = 1; - } else if (ev == MG_EV_CLOSE) { - } - (void) ev_data; -} - -void mg_sntp_request(struct mg_connection *c) { - if (c->is_resolving) { - MG_ERROR(("%lu wait until resolved", c->id)); - } else { - int64_t now = (int64_t) mg_millis(); // Use int64_t, for vc98 - uint8_t buf[48] = {0}; - uint32_t *t = (uint32_t *) &buf[40]; - double frac = ((double) (now % 1000)) / 1000.0 * SNTP_MAX_FRAC; - buf[0] = (0 << 6) | (4 << 3) | 3; - t[0] = mg_htonl((uint32_t) (now / 1000) + SNTP_TIME_OFFSET); - t[1] = mg_htonl((uint32_t) frac); - mg_send(c, buf, sizeof(buf)); - } -} - -struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - if (url == NULL) url = "udp://time.google.com:123"; - return mg_connect_svc(mgr, url, fn, fn_data, sntp_cb, NULL); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sock.c" -#endif - - - - - - - - - - - -#if MG_ENABLE_SOCKET - -#ifndef closesocket -#define closesocket(x) close(x) -#endif - -#define FD(c_) ((MG_SOCKET_TYPE) (size_t) (c_)->fd) -#define S2PTR(s_) ((void *) (size_t) (s_)) - -#ifndef MSG_NONBLOCKING -#define MSG_NONBLOCKING 0 -#endif - -#ifndef AF_INET6 -#define AF_INET6 10 -#endif - -#ifndef MG_SOCK_ERR -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? errno : 0) -#endif - -#ifndef MG_SOCK_INTR -#define MG_SOCK_INTR(fd) (fd == MG_INVALID_SOCKET && MG_SOCK_ERR(-1) == EINTR) -#endif - -#ifndef MG_SOCK_PENDING -#define MG_SOCK_PENDING(errcode) \ - (((errcode) < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK)) -#endif - -#ifndef MG_SOCK_RESET -#define MG_SOCK_RESET(errcode) \ - (((errcode) < 0) && (errno == EPIPE || errno == ECONNRESET)) -#endif - -union usa { - struct sockaddr sa; - struct sockaddr_in sin; -#if MG_ENABLE_IPV6 - struct sockaddr_in6 sin6; -#endif -}; - -static socklen_t tousa(struct mg_addr *a, union usa *usa) { - socklen_t len = sizeof(usa->sin); - memset(usa, 0, sizeof(*usa)); - usa->sin.sin_family = AF_INET; - usa->sin.sin_port = a->port; - memcpy(&usa->sin.sin_addr, a->ip, sizeof(uint32_t)); -#if MG_ENABLE_IPV6 - if (a->is_ip6) { - usa->sin.sin_family = AF_INET6; - usa->sin6.sin6_port = a->port; - usa->sin6.sin6_scope_id = a->scope_id; - memcpy(&usa->sin6.sin6_addr, a->ip, sizeof(a->ip)); - len = sizeof(usa->sin6); - } -#endif - return len; -} - -static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) { - a->is_ip6 = is_ip6; - a->port = usa->sin.sin_port; - memcpy(&a->ip, &usa->sin.sin_addr, sizeof(uint32_t)); -#if MG_ENABLE_IPV6 - if (is_ip6) { - memcpy(a->ip, &usa->sin6.sin6_addr, sizeof(a->ip)); - a->port = usa->sin6.sin6_port; - a->scope_id = (uint8_t) usa->sin6.sin6_scope_id; - } -#endif -} - -static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) { - union usa usa; - socklen_t n = sizeof(usa); - if (getsockname(fd, &usa.sa, &n) == 0) { - tomgaddr(&usa, addr, n != sizeof(usa.sin)); - } -} - -static void iolog(struct mg_connection *c, char *buf, long n, bool r) { - if (n == MG_IO_WAIT) { - // Do nothing - } else if (n <= 0) { - c->is_closing = 1; // Termination. Don't call mg_error(): #1529 - } else if (n > 0) { - if (c->is_hexdumping) { - MG_INFO(("\n-- %lu %M %s %M %ld", c->id, mg_print_ip_port, &c->loc, - r ? "<-" : "->", mg_print_ip_port, &c->rem, n)); - mg_hexdump(buf, (size_t) n); - } - if (r) { - c->recv.len += (size_t) n; - mg_call(c, MG_EV_READ, &n); - } else { - mg_iobuf_del(&c->send, 0, (size_t) n); - // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0); - if (c->send.len == 0) { - MG_EPOLL_MOD(c, 0); - } - mg_call(c, MG_EV_WRITE, &n); - } - } -} - -long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { - long n; - if (c->is_udp) { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen); - if (n > 0) setlocaddr(FD(c), &c->loc); - } else { - n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING); - } - MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n))); - if (MG_SOCK_PENDING(n)) return MG_IO_WAIT; - if (MG_SOCK_RESET(n)) return MG_IO_RESET; // MbedTLS, see #1507 - if (n <= 0) return MG_IO_ERR; - return n; -} - -bool mg_send(struct mg_connection *c, const void *buf, size_t len) { - if (c->is_udp) { - long n = mg_io_send(c, buf, len); - MG_DEBUG(("%lu %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len, - c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n))); - iolog(c, (char *) buf, n, false); - return n > 0; - } else { - return mg_iobuf_add(&c->send, c->send.len, buf, len); - } -} - -static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) { -#if defined(MG_CUSTOM_NONBLOCK) - MG_CUSTOM_NONBLOCK(fd); -#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK - unsigned long on = 1; - ioctlsocket(fd, FIONBIO, &on); -#elif MG_ENABLE_RL - unsigned long on = 1; - ioctlsocket(fd, FIONBIO, &on); -#elif MG_ENABLE_FREERTOS_TCP - const BaseType_t off = 0; - if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) (void) 0; - if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) (void) 0; -#elif MG_ENABLE_LWIP - lwip_fcntl(fd, F_SETFL, O_NONBLOCK); -#elif MG_ARCH == MG_ARCH_THREADX - // NetxDuo fails to send large blocks of data to the non-blocking sockets - (void) fd; - //fcntl(fd, F_SETFL, O_NONBLOCK); -#elif MG_ARCH == MG_ARCH_TIRTOS - int val = 0; - setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val)); - // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT - int sz = sizeof(val); - getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz); - val /= 2; // set send low-water mark at half send buffer size - setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)); -#else - fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode - fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec -#endif -} - -void mg_multicast_add(struct mg_connection *c, char *ip); -void mg_multicast_add(struct mg_connection *c, char *ip) { -#if MG_ENABLE_RL - MG_ERROR(("unsupported")); -#elif MG_ENABLE_FREERTOS_TCP - // TODO(): prvAllowIPPacketIPv4() -#else - // lwIP, Unix, Windows, Zephyr 4+(, AzureRTOS ?) -#if MG_ENABLE_LWIP && !LWIP_IGMP - MG_ERROR(("LWIP_IGMP not defined, no multicast support")); -#else -#if defined(__ZEPHYR__) && ZEPHYR_VERSION_CODE < 0x40000 - MG_ERROR(("struct ip_mreq not defined")); -#else - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = inet_addr(ip); - mreq.imr_interface.s_addr = mg_htonl(INADDR_ANY); - setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); -#endif // !Zephyr -#endif // !lwIP -#endif -} - -bool mg_open_listener(struct mg_connection *c, const char *url) { - MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; - bool success = false; - c->loc.port = mg_htons(mg_url_port(url)); - if (!mg_aton(mg_url_host(url), &c->loc)) { - MG_ERROR(("invalid listening URL: %s", url)); - } else { - union usa usa; - socklen_t slen = tousa(&c->loc, &usa); - int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET; - int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM; - int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; - (void) on; - - if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) { - MG_ERROR(("socket: %d", MG_SOCK_ERR(-1))); -#if defined(SO_EXCLUSIVEADDRUSE) - } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - (char *) &on, sizeof(on))) != 0) { - // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" - MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d", on, MG_SOCK_ERR(rc))); -#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE) - } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, - sizeof(on))) != 0) { - // 1. SO_REUSEADDR semantics on UNIX and Windows is different. On - // Windows, SO_REUSEADDR allows to bind a socket to a port without error - // even if the port is already open by another program. This is not the - // behavior SO_REUSEADDR was designed for, and leads to hard-to-track - // failure scenarios. - // - // 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining - // SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but - // won't work! (setsockopt will return EINVAL) - MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc))); -#endif -#if MG_IPV6_V6ONLY - // Bind only to the V6 address, not V4 address on this port - } else if (c->loc.is_ip6 && - (rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, - sizeof(on))) != 0) { - // See #2089. Allow to bind v4 and v6 sockets on the same port - MG_ERROR(("setsockopt(IPV6_V6ONLY): %d", MG_SOCK_ERR(rc))); -#endif - } else if ((rc = bind(fd, &usa.sa, slen)) != 0) { - MG_ERROR(("bind: %d", MG_SOCK_ERR(rc))); - } else if ((type == SOCK_STREAM && - (rc = listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE)) != 0)) { - // NOTE(lsm): FreeRTOS uses backlog value as a connection limit - // In case port was set to 0, get the real port number - MG_ERROR(("listen: %d", MG_SOCK_ERR(rc))); - } else { - setlocaddr(fd, &c->loc); - mg_set_non_blocking_mode(fd); - c->fd = S2PTR(fd); - MG_EPOLL_ADD(c); - success = true; - } - } - if (success == false && fd != MG_INVALID_SOCKET) closesocket(fd); - return success; -} - -static long recv_raw(struct mg_connection *c, void *buf, size_t len) { - long n = 0; - if (c->is_udp) { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen); - if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin)); - } else { - n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING); - } - MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n))); - if (MG_SOCK_PENDING(n)) return MG_IO_WAIT; - if (MG_SOCK_RESET(n)) return MG_IO_RESET; // MbedTLS, see #1507 - if (n <= 0) return MG_IO_ERR; - return n; -} - -static bool ioalloc(struct mg_connection *c, struct mg_iobuf *io) { - bool res = false; - if (io->len >= MG_MAX_RECV_SIZE) { - mg_error(c, "MG_MAX_RECV_SIZE"); - } else if (io->size <= io->len && - !mg_iobuf_resize(io, io->size + MG_IO_SIZE)) { - mg_error(c, "OOM"); - } else { - res = true; - } - return res; -} - -// NOTE(lsm): do only one iteration of reads, cause some systems -// (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data -static void read_conn(struct mg_connection *c) { - if (ioalloc(c, &c->recv)) { - char *buf = (char *) &c->recv.buf[c->recv.len]; - size_t len = c->recv.size - c->recv.len; - long n = -1; - if (c->is_tls) { - // Do not read to the raw TLS buffer if it already has enough. - // This is to prevent overflowing c->rtls if our reads are slow - long m; - if (c->rtls.len < 16 * 1024 + 40) { // TLS record, header, MAC, padding - if (!ioalloc(c, &c->rtls)) return; - n = recv_raw(c, (char *) &c->rtls.buf[c->rtls.len], - c->rtls.size - c->rtls.len); - if (n > 0) c->rtls.len += (size_t) n; - } - // there can still be > 16K from last iteration, always mg_tls_recv() - m = c->is_tls_hs ? (long) MG_IO_WAIT : mg_tls_recv(c, buf, len); - if (n == MG_IO_ERR || n == MG_IO_RESET) { // Windows, see #3031 - if (c->rtls.len == 0 || m < 0) { - // Close only when we have fully drained both rtls and TLS buffers - c->is_closing = 1; // or there's nothing we can do about it. - if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104 - } else { // see #2885 - // TLS buffer is capped to max record size, even though, there can - // be more than one record, give TLS a chance to process them. - } - } else if (c->is_tls_hs) { - mg_tls_handshake(c); - } - n = m; - } else { - n = recv_raw(c, buf, len); - } - MG_DEBUG(("%lu %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len, - c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n))); - iolog(c, buf, n, true); - } -} - -static void write_conn(struct mg_connection *c) { - char *buf = (char *) c->send.buf; - size_t len = c->send.len; - long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len); - MG_DEBUG(("%lu %ld snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, - (long) c->send.len, (long) c->send.size, (long) c->recv.len, - (long) c->recv.size, n, MG_SOCK_ERR(n))); - iolog(c, buf, n, false); -} - -static void close_conn(struct mg_connection *c) { - if (FD(c) != MG_INVALID_SOCKET) { -#if MG_ENABLE_EPOLL - epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL); -#endif - closesocket(FD(c)); -#if MG_ENABLE_FREERTOS_TCP - FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL); -#endif - } - mg_close_conn(c); -} - -static void connect_conn(struct mg_connection *c) { - union usa usa; - socklen_t n = sizeof(usa); - // Use getpeername() to test whether we have connected - if (getpeername(FD(c), &usa.sa, &n) == 0) { - c->is_connecting = 0; - setlocaddr(FD(c), &c->loc); - mg_call(c, MG_EV_CONNECT, NULL); - MG_EPOLL_MOD(c, 0); - if (c->is_tls_hs) mg_tls_handshake(c); - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() - } else { - mg_error(c, "socket error"); - } -} - -static void setsockopts(struct mg_connection *c) { -#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_THREADX || \ - MG_ARCH == MG_ARCH_TIRTOS - (void) c; -#else - int on = 1; -#if !defined(SOL_TCP) -#define SOL_TCP IPPROTO_TCP -#endif - if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) != 0) - (void) 0; - if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) != - 0) - (void) 0; -#endif -} - -void mg_connect_resolved(struct mg_connection *c) { - int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; - int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; - int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP - c->fd = S2PTR(socket(af, type, proto)); // Create outbound socket - c->is_resolving = 0; // Clear resolving flag - if (FD(c) == MG_INVALID_SOCKET) { - mg_error(c, "socket(): %d", MG_SOCK_ERR(-1)); - } else if (c->is_udp) { - MG_EPOLL_ADD(c); -#if MG_ARCH == MG_ARCH_TIRTOS - union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets - socklen_t slen = tousa(&c->loc, &usa); - if ((rc = bind(c->fd, &usa.sa, slen)) != 0) - MG_ERROR(("bind: %d", MG_SOCK_ERR(rc))); -#endif - setlocaddr(FD(c), &c->loc); - mg_call(c, MG_EV_RESOLVE, NULL); - mg_call(c, MG_EV_CONNECT, NULL); - } else { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - mg_set_non_blocking_mode(FD(c)); - setsockopts(c); - MG_EPOLL_ADD(c); - mg_call(c, MG_EV_RESOLVE, NULL); - rc = connect(FD(c), &usa.sa, slen); // Attempt to connect - if (rc == 0) { // Success - setlocaddr(FD(c), &c->loc); - mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() - } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake - MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem)); - c->is_connecting = 1; - } else { - mg_error(c, "connect: %d", MG_SOCK_ERR(rc)); - } - } -} - -static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa, - socklen_t *len) { - MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; - do { - memset(usa, 0, sizeof(*usa)); - fd = accept(sock, &usa->sa, len); - } while (MG_SOCK_INTR(fd)); - return fd; -} - -static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) { - struct mg_connection *c = NULL; - union usa usa; - socklen_t sa_len = sizeof(usa); - MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len); - if (fd == MG_INVALID_SOCKET) { -#if MG_ARCH == MG_ARCH_THREADX || defined(__ECOS) - // NetxDuo, in non-block socket mode can mark listening socket readable - // even it is not. See comment for 'select' func implementation in - // nx_bsd.c That's not an error, just should try later - if (errno != EAGAIN) -#endif - MG_ERROR(("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERR(-1))); -#if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && \ - (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL && !MG_ENABLE_EPOLL - } else if ((long) fd >= FD_SETSIZE) { - MG_ERROR(("%ld > %ld", (long) fd, (long) FD_SETSIZE)); - closesocket(fd); -#endif - } else if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("%lu OOM", lsn->id)); - closesocket(fd); - } else { - tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin)); - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->fd = S2PTR(fd); - MG_EPOLL_ADD(c); - mg_set_non_blocking_mode(FD(c)); - setsockopts(c); - c->is_accepted = 1; - c->is_hexdumping = lsn->is_hexdumping; - c->loc = lsn->loc; - c->pfn = lsn->pfn; - c->pfn_data = lsn->pfn_data; - c->fn = lsn->fn; - c->fn_data = lsn->fn_data; - c->is_tls = lsn->is_tls; - MG_DEBUG(("%lu %ld accepted %M -> %M", c->id, c->fd, mg_print_ip_port, - &c->rem, mg_print_ip_port, &c->loc)); - mg_call(c, MG_EV_OPEN, NULL); - mg_call(c, MG_EV_ACCEPT, NULL); - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() - } -} - -static bool can_read(const struct mg_connection *c) { - return c->is_full == false; -} - -static bool can_write(const struct mg_connection *c) { - return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0); -} - -static bool skip_iotest(const struct mg_connection *c) { - return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) || - (can_read(c) == false && can_write(c) == false); -} - -static void mg_iotest(struct mg_mgr *mgr, int ms) { -#if MG_ENABLE_FREERTOS_TCP - struct mg_connection *c; - for (c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (skip_iotest(c)) continue; - if (can_read(c)) - FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT); - if (can_write(c)) FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE); - if (c->is_closing) ms = 1; - } - FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms)); - for (c = mgr->conns; c != NULL; c = c->next) { - EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss); - c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0; - c->is_writable = bits & eSELECT_WRITE ? 1U : 0; - if (c->fd != MG_INVALID_SOCKET) - FreeRTOS_FD_CLR(c->fd, mgr->ss, - eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE); - } -#elif MG_ENABLE_EPOLL - size_t max = 1; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1; - if (can_write(c)) MG_EPOLL_MOD(c, 1); - if (c->is_closing) ms = 1; - max++; - } - struct epoll_event *evs = (struct epoll_event *) alloca(max * sizeof(evs[0])); - int n = epoll_wait(mgr->epoll_fd, evs, (int) max, ms); - for (int i = 0; i < n; i++) { - struct mg_connection *c = (struct mg_connection *) evs[i].data.ptr; - if (evs[i].events & EPOLLERR) { - mg_error(c, "socket error"); - } else if (c->is_readable == 0) { - bool rd = evs[i].events & (EPOLLIN | EPOLLHUP); - bool wr = evs[i].events & EPOLLOUT; - c->is_readable = can_read(c) && rd ? 1U : 0; - c->is_writable = can_write(c) && wr ? 1U : 0; - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1; - } - } - (void) skip_iotest; -#elif MG_ENABLE_POLL - nfds_t n = 0; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) n++; - struct pollfd *fds = (struct pollfd *) alloca(n * sizeof(fds[0])); - memset(fds, 0, n * sizeof(fds[0])); - n = 0; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (c->is_closing) ms = 1; - if (skip_iotest(c)) { - // Socket not valid, ignore - } else { - // Don't wait if TLS is ready - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1; - fds[n].fd = FD(c); - if (can_read(c)) fds[n].events |= POLLIN; - if (can_write(c)) fds[n].events |= POLLOUT; - n++; - } - } - - // MG_INFO(("poll n=%d ms=%d", (int) n, ms)); - if (poll(fds, n, ms) < 0) { -#if MG_ARCH == MG_ARCH_WIN32 - if (n == 0) Sleep(ms); // On Windows, poll fails if no sockets -#endif - memset(fds, 0, n * sizeof(fds[0])); - } - n = 0; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { - if (skip_iotest(c)) { - // Socket not valid, ignore - } else { - if (fds[n].revents & POLLERR) { - mg_error(c, "socket error"); - } else { - c->is_readable = - (unsigned) (fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0); - c->is_writable = (unsigned) (fds[n].revents & POLLOUT ? 1 : 0); - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1; - } - n++; - } - } -#else - struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_1ms = {0, 1000}, *tvp; - struct mg_connection *c; - fd_set rset, wset, eset; - MG_SOCKET_TYPE maxfd = 0; - int rc; - - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); - tvp = ms < 0 ? NULL : &tv; - for (c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (skip_iotest(c)) continue; - FD_SET(FD(c), &eset); - if (can_read(c)) FD_SET(FD(c), &rset); - if (can_write(c)) FD_SET(FD(c), &wset); - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) tvp = &tv_1ms; - if (FD(c) > maxfd) maxfd = FD(c); - if (c->is_closing) tvp = &tv_1ms; - } - - if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) <= 0) { -#if MG_ARCH == MG_ARCH_WIN32 - if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets -#else - if (rc < 0) MG_ERROR(("select: %d %d", rc, MG_SOCK_ERR(rc))); -#endif - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); - } - - for (c = mgr->conns; c != NULL; c = c->next) { - if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) { -#if MG_ARCH == MG_ARCH_THREADX - // NetxDuo stack returns exceptions for listening connection after accept - if (c->is_listening == 0) mg_error(c, "socket error"); -#else - mg_error(c, "socket error"); -#endif - } else { - c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset); - c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset); - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1; - } - } -#endif -} - -static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2]) { - socklen_t n = sizeof(usa[0].sin); - bool success = false; - - sp[0] = sp[1] = MG_INVALID_SOCKET; - (void) memset(&usa[0], 0, sizeof(usa[0])); - usa[0].sin.sin_family = AF_INET; - *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1 - usa[1] = usa[0]; - - if ((sp[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && - (sp[1] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && - bind(sp[0], &usa[0].sa, n) == 0 && // - bind(sp[1], &usa[1].sa, n) == 0 && // - getsockname(sp[0], &usa[0].sa, &n) == 0 && // - getsockname(sp[1], &usa[1].sa, &n) == 0 && // - connect(sp[0], &usa[1].sa, n) == 0 && // - connect(sp[1], &usa[0].sa, n) == 0) { // - success = true; - } - if (!success) { - if (sp[0] != MG_INVALID_SOCKET) closesocket(sp[0]); - if (sp[1] != MG_INVALID_SOCKET) closesocket(sp[1]); - sp[0] = sp[1] = MG_INVALID_SOCKET; - } - return success; -} - -// mg_wakeup() event handler -static void wufn(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ) { - unsigned long *id = (unsigned long *) c->recv.buf; - // MG_INFO(("Got data")); - // mg_hexdump(c->recv.buf, c->recv.len); - if (c->recv.len >= sizeof(*id)) { - struct mg_connection *t; - for (t = c->mgr->conns; t != NULL; t = t->next) { - if (t->id == *id) { - struct mg_str data = mg_str_n((char *) c->recv.buf + sizeof(*id), - c->recv.len - sizeof(*id)); - mg_call(t, MG_EV_WAKEUP, &data); - } - } - } - c->recv.len = 0; // Consume received data - } else if (ev == MG_EV_CLOSE) { - closesocket(c->mgr->pipe); // When we're closing, close the other - c->mgr->pipe = MG_INVALID_SOCKET; // side of the socketpair, too - } - (void) ev_data; -} - -bool mg_wakeup_init(struct mg_mgr *mgr) { - bool ok = false; - if (mgr->pipe == MG_INVALID_SOCKET) { - union usa usa[2]; - MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET}; - struct mg_connection *c = NULL; - if (!mg_socketpair(sp, usa)) { - MG_ERROR(("Cannot create socket pair")); - } else if ((c = mg_wrapfd(mgr, (int) sp[1], wufn, NULL)) == NULL) { - closesocket(sp[0]); - closesocket(sp[1]); - sp[0] = sp[1] = MG_INVALID_SOCKET; - } else { - tomgaddr(&usa[0], &c->rem, false); - MG_DEBUG(("%lu %p pipe %lu", c->id, c->fd, (unsigned long) sp[0])); - mgr->pipe = sp[0]; - ok = true; - } - } - return ok; -} - -bool mg_wakeup(struct mg_mgr *mgr, unsigned long conn_id, const void *buf, - size_t len) { - if (mgr->pipe != MG_INVALID_SOCKET && conn_id > 0) { - char *extended_buf = (char *) alloca(len + sizeof(conn_id)); - memcpy(extended_buf, &conn_id, sizeof(conn_id)); - memcpy(extended_buf + sizeof(conn_id), buf, len); - send(mgr->pipe, extended_buf, len + sizeof(conn_id), MSG_NONBLOCKING); - return true; - } - return false; -} - -void mg_mgr_poll(struct mg_mgr *mgr, int ms) { - struct mg_connection *c, *tmp; - uint64_t now; - - mg_iotest(mgr, ms); - now = mg_millis(); - mg_timer_poll(&mgr->timers, now); - - for (c = mgr->conns; c != NULL; c = tmp) { - bool is_resp = c->is_resp; - tmp = c->next; - mg_call(c, MG_EV_POLL, &now); - if (is_resp && !c->is_resp) { - long n = 0; - mg_call(c, MG_EV_READ, &n); - } - MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id, - c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-', - c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', - c->is_tls_hs ? 'H' : 'h', c->is_resolving ? 'R' : 'r', - c->is_closing ? 'C' : 'c', mg_tls_pending(c), c->rtls.len)); - if (c->is_resolving || c->is_closing) { - // Do nothing - } else if (c->is_listening && c->is_udp == 0) { - if (c->is_readable) accept_conn(mgr, c); - } else if (c->is_connecting) { - if (c->is_readable || c->is_writable) connect_conn(c); - } else { - if (c->is_readable) read_conn(c); - if (c->is_writable) write_conn(c); - if (c->is_tls && !c->is_tls_hs && c->send.len == 0) mg_tls_flush(c); - } - - if (c->is_draining && c->send.len == 0) c->is_closing = 1; - if (c->is_closing) close_conn(c); - } -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ssi.c" -#endif - - - - - -#ifndef MG_MAX_SSI_DEPTH -#define MG_MAX_SSI_DEPTH 5 -#endif - -#ifndef MG_SSI_BUFSIZ -#define MG_SSI_BUFSIZ 1024 -#endif - -#if MG_ENABLE_SSI -static char *mg_ssi(const char *path, const char *root, int depth) { - struct mg_iobuf b = {NULL, 0, 0, MG_IO_SIZE}; - FILE *fp = fopen(path, "rb"); - if (fp != NULL) { - char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)]; - int ch, intag = 0; - size_t len = 0; - buf[0] = arg[0] = '\0'; - while ((ch = fgetc(fp)) != EOF) { - if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { - buf[len++] = (char) (ch & 0xff); - buf[len] = '\0'; - if (sscanf(buf, " %#x %#x", s_txdesc[s_txno][1], tsr)); - if (!(s_txdesc[s_txno][1] & MG_BIT(31))) s_txdesc[s_txno][1] |= MG_BIT(31); - } - - GMAC_REGS->GMAC_RSR = rsr; - GMAC_REGS->GMAC_TSR = tsr; -} - -struct mg_tcpip_driver mg_tcpip_driver_same54 = { - mg_tcpip_driver_same54_init, mg_tcpip_driver_same54_tx, NULL, - mg_tcpip_driver_same54_poll}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/sdio.c" -#endif - - -#if MG_ENABLE_TCPIP && \ - (defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO) - -// SDIO 6.9 Table 6-1 CCCR (Common Card Control Registers) -#define MG_SDIO_CCCR_SDIOREV 0x000 -#define MG_SDIO_CCCR_SDREV 0x001 -#define MG_SDIO_CCCR_IOEN 0x002 -#define MG_SDIO_CCCR_IORDY 0x003 -#define MG_SDIO_CCCR_INTEN 0x004 -#define MG_SDIO_CCCR_BIC 0x007 -#define MG_SDIO_CCCR_CCAP 0x008 -#define MG_SDIO_CCCR_CCIS 0x009 // 3 registers -#define MG_SDIO_CCCR_F0BLKSZ 0x010 // 2 registers -#define MG_SDIO_CCCR_HISPD 0x013 -// SDIO 6.10 Table 6-3 FBR (Function Basic Registers) -#define MG_SDIO_FBR_FnBLKSZ(n) (((n) &7) * 0x100 + 0x10) // 2 registers - -// SDIO 5.1 IO_RW_DIRECT Command (CMD52) -#define MG_SDIO_DATA(x) ((x) &0xFF) // bits 0-7 -#define MG_SDIO_ADDR(x) (((x) &0x1FFFF) << 9) // bits 9-25 -#define MG_SDIO_FUNC(x) (((x) &3) << 28) // bits 28-30 (30 unused here) -#define MG_SDIO_WR MG_BIT(31) - -// SDIO 5.3 IO_RW_EXTENDED Command (CMD53) -#define MG_SDIO_LEN(x) ((x) &0x1FF) // bits 0-8 -#define MG_SDIO_OPINC MG_BIT(26) -#define MG_SDIO_BLKMODE MG_BIT(27) - -// - Drivers set blocksize, drivers request transfers. Requesting a read -// transfer > blocksize means block transfer will be used. -// - To simplify the use of DMA transfers and avoid intermediate buffers, -// drivers must have room to accomodate a whole block transfer, e.g.: blocksize -// = 64, read 65 => 2 blocks = 128 bytes -// - Transfers of more than 1 byte assume (uint32_t *) data. 1-byte transfers -// use (uint8_t *) data -// - 'len' is the number of _bytes_ to transfer -bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f, - uint32_t addr, void *data, uint32_t len) { - uint32_t arg, val = 0; - unsigned int blksz = 64; // TODO(): mg_sdio_set_blksz() stores in an array, - // index on f, skip if 0 - if (len == 1) { - arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_FUNC(f) | MG_SDIO_ADDR(addr) | - (write ? MG_SDIO_DATA(*(uint8_t *) data) : 0); - bool res = sdio->txn(sdio, 52, arg, &val); // IO_RW_DIRECT - if (!write) *(uint8_t *) data = (uint8_t) val; - return res; - } - // IO_RW_EXTENDED - arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_OPINC | MG_SDIO_FUNC(f) | - MG_SDIO_ADDR(addr); - if (len > 512 || (blksz != 0 && len > blksz)) { // SDIO 5.3 512 -> len=0 - unsigned int blkcnt; - if (blksz == 0) return false; // > 512 requires block size set - blkcnt = (len + blksz - 1) / blksz; - if (blkcnt > 511) return false; // we don't support "infinite" blocks - arg |= MG_SDIO_BLKMODE | MG_SDIO_LEN(blkcnt); // block transfer - len = blksz * blkcnt; - } else { - arg |= MG_SDIO_LEN(len); // multi-byte transfer - } - return sdio->xfr(sdio, write, arg, - (arg & MG_SDIO_BLKMODE) ? (uint16_t) blksz : 0, - (uint32_t *) data, len, &val); -} - -bool mg_sdio_set_blksz(struct mg_tcpip_sdio *sdio, unsigned int f, - uint16_t blksz) { - uint32_t val = blksz & 0xff; - if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f), &val, 1)) - return false; - val = (blksz >> 8) & 0x0f; // SDIO 6.10 Table 6-4, max 2048 - if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f) + 1, &val, 1)) - return false; - // TODO(): store in an array, index on f. Static 8-element array - MG_VERBOSE(("F%c block size set", (f & 7) + '0')); - return true; -} - -// Enable Fx -bool mg_sdio_enable_f(struct mg_tcpip_sdio *sdio, unsigned int f) { - uint8_t bit = 1U << (f & 7), bits; - uint32_t val = 0; - if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1)) - return false; - bits = (uint8_t) val | bit; - unsigned int times = 501; - while (times--) { - val = bits; /* IOEf */ - ; - if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_IOEN, &val, 1)) - return false; - mg_delayms(1); - val = 0; - if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1)) - return false; - if (val & bit) break; - } - if (times == (unsigned int) ~0) return false; - MG_VERBOSE(("F%c enabled", (f & 7) + '0')); - return true; -} - -// Wait for Fx to be ready -bool mg_sdio_waitready_f(struct mg_tcpip_sdio *sdio, unsigned int f) { - uint8_t bit = 1U << (f & 7); - unsigned int times = 501; - while (times--) { - uint32_t val; - if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IORDY, &val, 1)) - return false; - if (val & bit) break; // IORf - mg_delayms(1); - } - if (times == (unsigned int) ~0) return false; - MG_VERBOSE(("F%c ready", (f & 7) + '0')); - return true; -} - -// SDIO 6.14 Bus State Diagram -bool mg_sdio_init(struct mg_tcpip_sdio *sdio) { - uint32_t val = 0; - if (!sdio->txn(sdio, 0, 0, NULL)) return false; // GO_IDLE_STATE - sdio->txn(sdio, 5, 0, &val); // IO_SEND_OP_COND, no CRC - MG_VERBOSE(("IO Functions: %u, Memory: %c", 1 + ((val >> 28) & 7), - (val & MG_BIT(27)) ? 'Y' : 'N')); - if (!sdio->txn(sdio, 3, 0, &val)) return false; // SEND_RELATIVE_ADDR - val = ((uint32_t) val) >> 16; // RCA - if (!sdio->txn(sdio, 7, val << 16, &val)) - return false; // SELECT/DESELECT_CARD - mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDIOREV, &val, 1); - MG_DEBUG(("CCCR: %u.%u, SDIO: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3, - 1 + ((val >> 6) & 3), (val >> 4) & 3)); - mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDREV, &val, 1); - MG_VERBOSE(("SD: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3)); - mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_BIC, &val, 1); - MG_SET_BITS(val, 3, - MG_BIT(7) | MG_BIT(1)); // SDIO 6.9 Tables 6-1 6-2, 4-bit bus - mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_BIC, &val, 1); - // All Full-Speed SDIO cards support a 4-bit bus. Skip for Low-Speed SDIO - // cards, we don't provide separate low-level functions for width and speed - sdio->cfg(sdio, 0); // set DS; - if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_HISPD, &val, 1)) - return false; - if (val & MG_BIT(0) /* SHS */) { - val = MG_BIT(1); /* EHS */ - if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_HISPD, &val, 1)) - return false; - sdio->cfg(sdio, 1); // set HS; - MG_VERBOSE(("Bus set to 4-bit @50MHz")); - } else { - MG_VERBOSE(("Bus set to 4-bit @25MHz")); - } - return true; -} - -// - 6.11 Card Information Structure (CIS): 0x0001000-0x017FF; for card common -// and all functions -// - 16.5 SDIO Card Metaformat -// - 16.7.2 CISTPL_FUNCE (0x22): Function Extension Tuple, provides standard -// information about the card (common) and each individual function. One -// CISTPL_FUNCE in each function’s CIS, immediately following the CISTPL_FUNCID -// tuple -// - 16.7.3 CISTPL_FUNCE Tuple for Function 0 (common) -// - 16.7.4 CISTPL_FUNCE Tuple for Function 1-7 - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/stm32f.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \ - MG_ENABLE_DRIVER_STM32F -struct stm32f_eth { - volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR, - MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR, - MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR, - MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, - RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR, - RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR, - RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR, - PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564], - DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER, - DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR, - DMACHRBAR; -}; -#undef ETH -#define ETH ((struct stm32f_eth *) (uintptr_t) 0x40028000) - -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static uint8_t s_txno; // Current TX descriptor -static uint8_t s_rxno; // Current RX descriptor - -static struct mg_tcpip_if *s_ifp; // MIP interface - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - ETH->MACMIIAR &= (7 << 2); - ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6); - ETH->MACMIIAR |= MG_BIT(0); - while (ETH->MACMIIAR & MG_BIT(0)) (void) 0; - return ETH->MACMIIDR & 0xffff; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH->MACMIIDR = val; - ETH->MACMIIAR &= (7 << 2); - ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | MG_BIT(1); - ETH->MACMIIAR |= MG_BIT(0); - while (ETH->MACMIIAR & MG_BIT(0)) (void) 0; -} - -static uint32_t get_hclk(void) { - struct rcc { - volatile uint32_t CR, PLLCFGR, CFGR; - } *rcc = (struct rcc *) 0x40023800; - uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */; - - if (rcc->CFGR & (1 << 2)) { - clk = hse; - } else if (rcc->CFGR & (1 << 3)) { - uint32_t vco, m, n, p; - m = (rcc->PLLCFGR & (0x3f << 0)) >> 0; - n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6; - p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2; - clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi; - vco = (uint32_t) ((uint64_t) clk * n / m); - clk = vco / p; - } else { - clk = hsi; - } - uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4; - if (hpre < 8) return clk; - - uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div) - return ((uint32_t) clk) >> ahbptab[hpre - 8]; -} - -// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3, -// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived -// from the HSI (internal RC), and it can go above specs, the datasheets -// specify a range of frequencies and activate one of a series of dividers to -// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on -// HCLK with a +5% drift. If the user uses a different clock from our -// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx -// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift) -static int guess_mdc_cr(void) { - uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values - uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers - uint32_t hclk = get_hclk(); // Guess system HCLK - int result = -1; // Invalid CR value - if (hclk < 25000000) { - MG_ERROR(("HCLK too low")); - } else { - for (int i = 0; i < 6; i++) { - if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { - result = crs[i]; - break; - } - } - if (result < 0) MG_ERROR(("HCLK too high")); - } - MG_DEBUG(("HCLK: %u, CR: %d", hclk, result)); - return result; -} - -static bool mg_tcpip_driver_stm32f_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_stm32f_data *d = - (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - s_ifp = ifp; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = MG_BIT(31); // Own - s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_BIT(14); // 2nd address chained - s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = - (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer - s_txdesc[i][3] = - (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - ETH->DMABMR |= MG_BIT(0); // Software reset - while ((ETH->DMABMR & MG_BIT(0)) != 0) (void) 0; // Wait until done - - // Set MDC clock divider. If user told us the value, use it. Otherwise, guess - int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; - ETH->MACMIIAR = ((uint32_t) cr & 7) << 2; - - // NOTE(cpq): we do not use extended descriptor bit 7, and do not use - // hardware checksum. Therefore, descriptor size is 4, not 8 - // ETH->DMABMR = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) | - // MG_BIT(25); - ETH->MACIMR = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT - ETH->MACFCR = MG_BIT(7); // Disable zero quarta pause - ETH->MACFFR = MG_BIT(10); // Perfect filtering - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, phy_addr, MG_PHY_CLOCKS_MAC); - ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors - ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors - ETH->DMAIER = MG_BIT(6) | MG_BIT(16); // RIE, NISE - ETH->MACCR = - MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast - ETH->DMAOMR = - MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_BIT(25); // SR, ST, TSF, RSF - - // MAC address filtering - ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; - ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) | - ((uint32_t) ifp->mac[2] << 16) | - ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; - return true; -} - -static size_t mg_tcpip_driver_stm32f_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No free descriptors")); - // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][1] = (uint32_t) len; // Set data len - s_txdesc[s_txno][0] = MG_BIT(20) | MG_BIT(28) | MG_BIT(29); // Chain,FS,LS - s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - MG_DSB(); // ensure descriptors have been written - ETH->DMASR = MG_BIT(2) | MG_BIT(5); // Clear any prior TBUS/TUS - ETH->DMATPDR = 0; // and resume - return len; -} - -static void mg_tcpip_driver_stm32f_update_hash_table(struct mg_tcpip_if *ifp) { - // TODO(): read database, rebuild hash table - ETH->MACA1LR = (uint32_t) mcast_addr[3] << 24 | - (uint32_t) mcast_addr[2] << 16 | - (uint32_t) mcast_addr[1] << 8 | (uint32_t) mcast_addr[0]; - ETH->MACA1HR = (uint32_t) mcast_addr[5] << 8 | (uint32_t) mcast_addr[4]; - ETH->MACA1HR |= MG_BIT(31); // AE - (void) ifp; -} - -static bool mg_tcpip_driver_stm32f_poll(struct mg_tcpip_if *ifp, bool s1) { - if (ifp->update_mac_hash_table) { - mg_tcpip_driver_stm32f_update_hash_table(ifp); - ifp->update_mac_hash_table = false; - } - if (!s1) return false; - struct mg_tcpip_driver_stm32f_data *d = - (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex - if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M - if (full_duplex == false) maccr &= ~MG_BIT(11); // Half-duplex - ETH->MACCR = maccr; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10, - maccr & MG_BIT(11) ? "full" : "half")); - } - return up; -} - -#ifdef __riscv -__attribute__((interrupt())) // For RISCV CH32V307, which share the same MAC -#endif -void ETH_IRQHandler(void); -void ETH_IRQHandler(void) { - if (ETH->DMASR & MG_BIT(6)) { // Frame received, loop - ETH->DMASR = MG_BIT(16) | MG_BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done - if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) == - (MG_BIT(8) | MG_BIT(9))) && - !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames - uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_BIT(14) - 1)); - // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], - // ETH->DMASR); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][0] = MG_BIT(31); - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - // Cleanup flags - ETH->DMASR = MG_BIT(16) // NIS, normal interrupt summary - | MG_BIT(7); // Clear possible RBUS while processing - ETH->DMARPDR = 0; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_stm32f = { - mg_tcpip_driver_stm32f_init, mg_tcpip_driver_stm32f_tx, NULL, - mg_tcpip_driver_stm32f_poll}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/stm32h.c" -#endif - - -#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN) -// STM32H: vendor modded single-queue Synopsys v4.2 -// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option -// RT1170 ENET_QOS: quad-queue Synopsys v5.1 -struct synopsys_enet_qos { - volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R, - RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR, - RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR, - MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR, - MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10, - MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR, - RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR, - MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR, - MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR, - RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR, - RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR, - MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R, - RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R, - RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R, - MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR, - MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR, - RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28, - MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR, - RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR, - MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR, - RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR, - MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR, - RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR, - DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR, - DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER, - DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR, - RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2], - DMACMFCR; -}; -#undef ETH -#if MG_ENABLE_DRIVER_STM32H -#define ETH \ - ((struct synopsys_enet_qos *) (uintptr_t) (0x40000000UL + 0x00020000UL + \ - 0x8000UL)) -#elif MG_ENABLE_DRIVER_MCXN -#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL) -#endif - -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static struct mg_tcpip_if *s_ifp; // MIP interface - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - ETH->MACMDIOAR &= (0xF << 8); - ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2; - ETH->MACMDIOAR |= MG_BIT(0); - while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0; - return (uint16_t) ETH->MACMDIODR; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH->MACMDIODR = val; - ETH->MACMDIOAR &= (0xF << 8); - ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2; - ETH->MACMDIOAR |= MG_BIT(0); - while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0; -} - -static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_stm32h_data *d = - (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data; - s_ifp = ifp; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - uint8_t phy_conf = d == NULL ? MG_PHY_CLOCKS_MAC : d->phy_conf; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer - } - - ETH->DMAMR |= MG_BIT(0); // Software reset - for (int i = 0; i < 4; i++) - (void) 0; // wait at least 4 clocks before reading - while ((ETH->DMAMR & MG_BIT(0)) != 0) (void) 0; // Wait until done - - // Set MDC clock divider. Get user value, else, assume max freq - int cr = (d == NULL || d->mdc_cr < 0) ? 7 : d->mdc_cr; - ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8; - - // NOTE(scaprile): We do not use timing facilities so the DMA engine does not - // re-write buffer address - ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value) - ETH->DMASBMR |= MG_BIT(12); // AAL NOTE(scaprile): is this actually needed - ETH->MACIER = 0; // Do not enable additional irq sources (reset value) - ETH->MACTFCR = MG_BIT(7); // Disable zero-quanta pause -#if !MG_ENABLE_DRIVER_MCXN - ETH->MACPFR = MG_BIT(10); // Perfect filtering -#endif - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, phy_addr, phy_conf); - ETH->DMACRDLAR = - (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address - ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length - ETH->DMACRDTPR = - (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - - 1]; // last valid descriptor address - ETH->DMACTDLAR = - (uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address - ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length - ETH->DMACTDTPR = - (uint32_t) (uintptr_t) s_txdesc; // first available descriptor address - ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value) -#if !MG_ENABLE_DRIVER_STM32H - MG_SET_BITS(ETH->DMACTCR, 0x3F << 16, MG_BIT(16)); - MG_SET_BITS(ETH->DMACRCR, 0x3F << 16, MG_BIT(16)); -#endif - ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE - ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) | - MG_BIT(15); // RE, TE, Duplex, Fast, Reserved -#if MG_ENABLE_DRIVER_STM32H - ETH->MTLTQOMR |= MG_BIT(1); // TSF - ETH->MTLRQOMR |= MG_BIT(5); // RSF -#else - ETH->MTLTQOMR |= (7 << 16) | MG_BIT(3) | MG_BIT(1); // 2KB Q0, TSF - ETH->MTLRQOMR |= (7 << 20) | MG_BIT(5); // 2KB Q, RSF - MG_SET_BITS(ETH->RESERVED6[3], 3, 2); // Enable RxQ0 (MAC_RXQ_CTRL0) -#endif - ETH->DMACTCR |= MG_BIT(0); // ST - ETH->DMACRCR |= MG_BIT(0); // SR - - // MAC address filtering - ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; - ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) | - ((uint32_t) ifp->mac[2] << 16) | - ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; - return true; -} - -static uint32_t s_txno; -static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][3] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno, - s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR)); - for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3])); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][2] = (uint32_t) len; // Set data len - s_txdesc[s_txno][3] = MG_BIT(28) | MG_BIT(29); // FD, LD - s_txdesc[s_txno][3] |= MG_BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - ETH->DMACSR |= MG_BIT(2) | MG_BIT(1); // Clear any prior TBU, TPS - ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume - return len; - (void) ifp; -} - -static void mg_tcpip_driver_stm32h_update_hash_table(struct mg_tcpip_if *ifp) { -#if MG_ENABLE_DRIVER_MCXN - ETH->MACPFR = MG_BIT(4); // Pass Multicast (pass all multicast frames) -#else - // TODO(): read database, rebuild hash table - // add mDNS / DNS-SD multicast address - ETH->MACA1LR = (uint32_t) mcast_addr[3] << 24 | - (uint32_t) mcast_addr[2] << 16 | - (uint32_t) mcast_addr[1] << 8 | (uint32_t) mcast_addr[0]; - ETH->MACA1HR = (uint32_t) mcast_addr[5] << 8 | (uint32_t) mcast_addr[4]; - ETH->MACA1HR |= MG_BIT(31); // AE -#endif -(void) ifp; -} - -static bool mg_tcpip_driver_stm32h_poll(struct mg_tcpip_if *ifp, bool s1) { - if (ifp->update_mac_hash_table) { - mg_tcpip_driver_stm32h_update_hash_table(ifp); - ifp->update_mac_hash_table = false; - } - if (!s1) return false; - struct mg_tcpip_driver_stm32h_data *d = - (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(13); // 100M, Full-duplex - if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M - if (full_duplex == false) maccr &= ~MG_BIT(13); // Half-duplex - ETH->MACCR = maccr; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10, - maccr & MG_BIT(13) ? "full" : "half")); - } - return up; -} - -static uint32_t s_rxno; -#if MG_ENABLE_DRIVER_MCXN -void ETHERNET_IRQHandler(void); -void ETHERNET_IRQHandler(void) { -#else -void ETH_IRQHandler(void); -void ETH_IRQHandler(void) { -#endif - if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop - ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][3] & MG_BIT(31)) break; // exit when done - if (((s_rxdesc[s_rxno][3] & (MG_BIT(28) | MG_BIT(29))) == - (MG_BIT(28) | MG_BIT(29))) && - !(s_rxdesc[s_rxno][3] & MG_BIT(15))) { // skip partial/errored frames - uint32_t len = s_rxdesc[s_rxno][3] & (MG_BIT(15) - 1); - // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3], - // ETH->DMACSR)); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][3] = - MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - ETH->DMACSR = - MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing - ETH->DMACRDTPR = - (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_stm32h = { - mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL, - mg_tcpip_driver_stm32h_poll}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/tm4c.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C -struct tm4c_emac { - volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL, - EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS, - EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H, - EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H, - EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL, - EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4, - EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4], - EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR, - EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239], - EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC, - EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD, - EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL, - RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294], - EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR, - EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT, - RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA, - RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS, - EMACEPHYIM, EMACEPHYIMSC; -}; -#undef EMAC -#define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000) - -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static struct mg_tcpip_if *s_ifp; // MIP interface -enum { - EPHY_ADDR = 0, - EPHYBMCR = 0, - EPHYBMSR = 1, - EPHYSTS = 16 -}; // PHY constants - -static inline void tm4cspin(volatile uint32_t count) { - while (count--) (void) 0; -} - -static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) { - EMAC->EMACMIIADDR &= (0xf << 2); - EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6); - EMAC->EMACMIIADDR |= MG_BIT(0); - while (EMAC->EMACMIIADDR & MG_BIT(0)) tm4cspin(1); - return EMAC->EMACMIIDATA; -} - -static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { - EMAC->EMACMIIDATA = val; - EMAC->EMACMIIADDR &= (0xf << 2); - EMAC->EMACMIIADDR |= - ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | MG_BIT(1); - EMAC->EMACMIIADDR |= MG_BIT(0); - while (EMAC->EMACMIIADDR & MG_BIT(0)) tm4cspin(1); -} - -static uint32_t get_sysclk(void) { - struct sysctl { - volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0, - PLLFREQ1; - } *sysctl = (struct sysctl *) 0x400FE000; - uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */; - if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL - uint32_t fin, vco, mdiv, n, q, psysdiv; - uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24; - if (pllsrc == 0) { - clk = piosc; - } else if (pllsrc == 3) { - clk = mosc; - } else { - MG_ERROR(("Unsupported clock source")); - } - q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8; - n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0; - fin = clk / ((q + 1) * (n + 1)); - mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >> - 0; // mint + (mfrac / 1024); MFRAC not supported - psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0; - vco = (uint32_t) ((uint64_t) fin * mdiv); - return vco / (psysdiv + 1); - } - uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20; - if (oscsrc == 0) { - clk = piosc; - } else if (oscsrc == 3) { - clk = mosc; - } else { - MG_ERROR(("Unsupported clock source")); - } - uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16; - return clk / (osysdiv + 1); -} - -// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per -// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be -// derived from the PIOSC (internal RC), and it can go above specs, the -// datasheets specify a range of frequencies and activate one of a series of -// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider -// setting based on SYSCLK with a +5% drift. If the user uses a different clock -// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7) -// (4.5% worst case drift) -// The PHY receives the main oscillator (MOSC) (20.3.1) -static int guess_mdc_cr(void) { - uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values - uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers - uint32_t sysclk = get_sysclk(); // Guess system SYSCLK - int result = -1; // Invalid CR value - if (sysclk < 25000000) { - MG_ERROR(("SYSCLK too low")); - } else { - for (int i = 0; i < 4; i++) { - if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { - result = crs[i]; - break; - } - } - if (result < 0) MG_ERROR(("SYSCLK too high")); - } - MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result)); - return result; -} - -static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_tm4c_data *d = - (struct mg_tcpip_driver_tm4c_data *) ifp->driver_data; - s_ifp = ifp; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = MG_BIT(31); // Own - s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_BIT(14); // 2nd address chained - s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = - (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain - // MG_DEBUG(("%d %p", i, s_rxdesc[i])); - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer - s_txdesc[i][3] = - (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - EMAC->EMACDMABUSMOD |= MG_BIT(0); // Software reset - while ((EMAC->EMACDMABUSMOD & MG_BIT(0)) != 0) - tm4cspin(1); // Wait until done - - // Set MDC clock divider. If user told us the value, use it. Otherwise, guess - int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; - EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2; - - // NOTE(cpq): we do not use extended descriptor bit 7, and do not use - // hardware checksum. Therefore, descriptor size is 4, not 8 - // EMAC->EMACDMABUSMOD = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) | - // MG_BIT(25); - EMAC->EMACIM = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT - EMAC->EMACFLOWCTL = MG_BIT(7); // Disable zero-quanta pause - EMAC->EMACFRAMEFLTR = MG_BIT(10); // Perfect filtering - // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode - emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_BIT(15)); // Reset internal PHY (EPHY) - emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_BIT(12)); // Set autonegotiation - EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors - EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors - EMAC->EMACDMAIM = MG_BIT(6) | MG_BIT(16); // RIE, NIE - EMAC->EMACCFG = - MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast - EMAC->EMACDMAOPMODE = - MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_BIT(25); // SR, ST, TSF, RSF - EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; - EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) | - ((uint32_t) ifp->mac[2] << 16) | - ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; - return true; -} - -static uint32_t s_txno; -static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // fail - } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No descriptors available")); - // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) - // EMAC->EMACDMARIS); - len = 0; // fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][1] = (uint32_t) len; // Set data len - s_txdesc[s_txno][0] = - MG_BIT(20) | MG_BIT(28) | MG_BIT(29) | MG_BIT(30); // Chain,FS,LS,IC - s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - EMAC->EMACDMARIS = MG_BIT(2) | MG_BIT(5); // Clear any prior TU/UNF - EMAC->EMACTXPOLLD = 0; // and resume - return len; - (void) ifp; -} - -static void mg_tcpip_driver_tm4c_update_hash_table(struct mg_tcpip_if *ifp) { - // TODO(): read database, rebuild hash table - // add mDNS / DNS-SD multicast address - EMAC->EMACADDR1L = (uint32_t) mcast_addr[3] << 24 | - (uint32_t) mcast_addr[2] << 16 | - (uint32_t) mcast_addr[1] << 8 | (uint32_t) mcast_addr[0]; - EMAC->EMACADDR1H = (uint32_t) mcast_addr[5] << 8 | (uint32_t) mcast_addr[4]; - EMAC->EMACADDR1H |= MG_BIT(31); // AE - (void) ifp; -} - -static bool mg_tcpip_driver_tm4c_poll(struct mg_tcpip_if *ifp, bool s1) { - if (ifp->update_mac_hash_table) { - mg_tcpip_driver_tm4c_update_hash_table(ifp); - ifp->update_mac_hash_table = false; - } - if (!s1) return false; - uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR); - bool up = (bmsr & MG_BIT(2)) ? 1 : 0; - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS); - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t emaccfg = - EMAC->EMACCFG | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex - if (sts & MG_BIT(1)) emaccfg &= ~MG_BIT(14); // 10M - if ((sts & MG_BIT(2)) == 0) emaccfg &= ~MG_BIT(11); // Half-duplex - EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", emaccfg & MG_BIT(14) ? 100 : 10, - emaccfg & MG_BIT(11) ? "full" : "half")); - } - return up; -} - -void EMAC0_IRQHandler(void); -static uint32_t s_rxno; -void EMAC0_IRQHandler(void) { - if (EMAC->EMACDMARIS & MG_BIT(6)) { // Frame received, loop - EMAC->EMACDMARIS = MG_BIT(16) | MG_BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done - if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) == - (MG_BIT(8) | MG_BIT(9))) && - !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames - uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_BIT(14) - 1)); - // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], - // EMAC->EMACDMARIS); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][0] = MG_BIT(31); - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - EMAC->EMACDMARIS = MG_BIT(7); // Clear possible RU while processing - EMAC->EMACRXPOLLD = 0; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_tm4c = {mg_tcpip_driver_tm4c_init, - mg_tcpip_driver_tm4c_tx, NULL, - mg_tcpip_driver_tm4c_poll}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/tms570.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TMS570) && MG_ENABLE_DRIVER_TMS570 -struct tms570_emac_ctrl { - volatile uint32_t REVID, SOFTRESET, RESERVED1[1], INTCONTROL, C0RXTHRESHEN, - C0RXEN, C0TXEN, C0MISCEN, RESERVED2[8], - C0RXTHRESHSTAT, C0RXSTAT, C0TXSTAT, C0MISCSTAT, - RESERVED3[8], - C0RXIMAX, C0TXIMAX; -}; -struct tms570_emac { - volatile uint32_t TXREVID, TXCONTROL, TXTEARDOWN, RESERVED1[1], RXREVID, - RXCONTROL, RXTEARDOWN, RESERVED2[25], TXINTSTATRAW,TXINTSTATMASKED, - TXINTMASKSET, TXINTMASKCLEAR, MACINVECTOR, MACEOIVECTOR, RESERVED8[2], RXINTSTATRAW, - RXINTSTATMASKED, RXINTMASKSET, RXINTMASKCLEAR, MACINTSTATRAW, MACINTSTATMASKED, - MACINTMASKSET, MACINTMASKCLEAR, RESERVED3[16], RXMBPENABLE, RXUNICASTSET, - RXUNICASTCLEAR, RXMAXLEN, RXBUFFEROFFSET, RXFILTERLOWTHRESH, RESERVED9[2], RXFLOWTHRESH[8], - RXFREEBUFFER[8], MACCONTROL, MACSTATUS, EMCONTROL, FIFOCONTROL, MACCONFIG, - SOFTRESET, RESERVED4[22], MACSRCADDRLO, MACSRCADDRHI, MACHASH1, MACHASH2, - BOFFTEST, TPACETEST, RXPAUSE, TXPAUSE, RESERVED5[4], RXGOODFRAMES, RXBCASTFRAMES, - RXMCASTFRAMES, RXPAUSEFRAMES, RXCRCERRORS, RXALIGNCODEERRORS, RXOVERSIZED, - RXJABBER, RXUNDERSIZED, RXFRAGMENTS, RXFILTERED, RXQOSFILTERED, RXOCTETS, - TXGOODFRAMES, TXBCASTFRAMES, TXMCASTFRAMES, TXPAUSEFRAMES, TXDEFERRED, - TXCOLLISION, TXSINGLECOLL, TXMULTICOLL, TXEXCESSIVECOLL, TXLATECOLL, - TXUNDERRUN, TXCARRIERSENSE, TXOCTETS, FRAME64, FRAME65T127, FRAME128T255, - FRAME256T511, FRAME512T1023, FRAME1024TUP, NETOCTETS, RXSOFOVERRUNS, - RXMOFOVERRUNS, RXDMAOVERRUNS, RESERVED6[156], MACADDRLO, MACADDRHI, - MACINDEX, RESERVED7[61], TXHDP[8], RXHDP[8], TXCP[8], RXCP[8]; -}; -struct tms570_mdio { - volatile uint32_t REVID, CONTROL, ALIVE, LINK, LINKINTRAW, LINKINTMASKED, - RESERVED1[2], USERINTRAW, USERINTMASKED, USERINTMASKSET, USERINTMASKCLEAR, - RESERVED2[20], USERACCESS0, USERPHYSEL0, USERACCESS1, USERPHYSEL1; -}; -#define SWAP32(x) ( (((x) & 0x000000FF) << 24) | \ - (((x) & 0x0000FF00) << 8) | \ - (((x) & 0x00FF0000) >> 8) | \ - (((x) & 0xFF000000) >> 24) ) -#undef EMAC -#undef EMAC_CTRL -#undef MDIO -#define EMAC ((struct tms570_emac *) (uintptr_t) 0xFCF78000) -#define EMAC_CTRL ((struct tms570_emac_ctrl *) (uintptr_t) 0xFCF78800) -#define MDIO ((struct tms570_mdio *) (uintptr_t) 0xFCF78900) -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] - __attribute__((section(".ETH_CPPI"), aligned(4))); // TX descriptors -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] - __attribute__((section(".ETH_CPPI"), aligned(4))); // RX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] - __attribute__((aligned(4))); // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] - __attribute__((aligned(4))); // TX ethernet buffers -static struct mg_tcpip_if *s_ifp; // MIP interface -static uint16_t emac_read_phy(uint8_t addr, uint8_t reg) { - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; - MDIO->USERACCESS0 = MG_BIT(31) | ((reg & 0x1f) << 21) | - ((addr & 0x1f) << 16); - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; - return MDIO->USERACCESS0 & 0xffff; -} -static void emac_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; - MDIO->USERACCESS0 = MG_BIT(31) | MG_BIT(30) | ((reg & 0x1f) << 21) | - ((addr & 0x1f) << 16) | (val & 0xffff); - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; -} -static bool mg_tcpip_driver_tms570_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_tms570_data *d = - (struct mg_tcpip_driver_tms570_data *) ifp->driver_data; - s_ifp = ifp; - EMAC_CTRL->SOFTRESET = MG_BIT(0); // Reset the EMAC Control Module - while(EMAC_CTRL->SOFTRESET & MG_BIT(0)) (void) 0; // wait - EMAC->SOFTRESET = MG_BIT(0); // Reset the EMAC Module - while(EMAC->SOFTRESET & MG_BIT(0)) (void) 0; - EMAC->MACCONTROL = 0; - EMAC->RXCONTROL = 0; - EMAC->TXCONTROL = 0; - // Initialize all the header descriptor pointer registers - uint32_t i; - for(i = 0; i < ETH_DESC_CNT; i++) { - EMAC->RXHDP[i] = 0; - EMAC->TXHDP[i] = 0; - EMAC->RXCP[i] = 0; - EMAC->TXCP[i] = 0; - ///EMAC->RXFREEBUFFER[i] = 0xff; - } - // Clear the interrupt enable for all the channels - EMAC->TXINTMASKCLEAR = 0xff; - EMAC->RXINTMASKCLEAR = 0xff; - EMAC->MACHASH1 = 0; - EMAC->MACHASH2 = 0; - EMAC->RXBUFFEROFFSET = 0; - EMAC->RXUNICASTCLEAR = 0xff; - EMAC->RXUNICASTSET = 0; - EMAC->RXMBPENABLE = 0; - // init MDIO - // MDIO_CLK frequency = VCLK3/(CLKDIV + 1). (MDIO must be between 1.0 - 2.5Mhz) - uint32_t clkdiv = 75; // VCLK is configured to 75Mhz - // CLKDIV, ENABLE, PREAMBLE, FAULTENB - MDIO->CONTROL = (clkdiv - 1) | MG_BIT(30) | MG_BIT(20) | MG_BIT(18); - volatile int delay = 0xfff; - while (delay-- != 0) (void) 0; - struct mg_phy phy = {emac_read_phy, emac_write_phy}; - mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC); - uint32_t channel; - for (channel = 0; channel < 8; channel++) { - EMAC->MACINDEX = channel; - EMAC->MACADDRHI = ifp->mac[0] | (ifp->mac[1] << 8) | (ifp->mac[2] << 16) | - (ifp->mac[3] << 24); - EMAC->MACADDRLO = ifp->mac[4] | (ifp->mac[5] << 8) | MG_BIT(20) | - MG_BIT(19) | (channel << 16); - } - EMAC->RXUNICASTSET = 1; // accept unicast frames; - - EMAC->RXMBPENABLE |= MG_BIT(30) | MG_BIT(13); // CRC, broadcast - - // Initialize the descriptors - for (i = 0; i < ETH_DESC_CNT; i++) { - if (i < ETH_DESC_CNT - 1) { - s_txdesc[i][0] = 0; - s_rxdesc[i][0] = SWAP32(((uint32_t) &s_rxdesc[i + 1][0])); - } - s_txdesc[i][1] = SWAP32(((uint32_t) s_txbuf[i])); - s_rxdesc[i][1] = SWAP32(((uint32_t) s_rxbuf[i])); - s_txdesc[i][2] = 0; - s_rxdesc[i][2] = SWAP32(ETH_PKT_SIZE); - s_txdesc[i][3] = 0; - s_rxdesc[i][3] = SWAP32(MG_BIT(29)); // OWN - } - s_txdesc[ETH_DESC_CNT - 1][0] = 0; - s_rxdesc[ETH_DESC_CNT - 1][0] = 0; - - EMAC->MACCONTROL = MG_BIT(5) | MG_BIT(0); // Enable MII, Full-duplex - //EMAC->TXINTMASKSET = 1; // Enable TX interrupt - EMAC->RXINTMASKSET = 1; // Enable RX interrupt - //EMAC_CTRL->C0TXEN = 1; // TX completion interrupt - EMAC_CTRL->C0RXEN = 1; // RX completion interrupt - EMAC->TXCONTROL = 1; // TXEN - EMAC->RXCONTROL = 1; // RXEN - EMAC->RXHDP[0] = (uint32_t) &s_rxdesc[0][0]; - return true; -} -static uint32_t s_txno; -static size_t mg_tcpip_driver_tms570_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // fail - } else if ((s_txdesc[s_txno][3] & SWAP32(MG_BIT(29)))) { - ifp->nerr++; - MG_ERROR(("No descriptors available")); - len = 0; // fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - if (len < 128) len = 128; - s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len - s_txdesc[s_txno][3] = - SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length - - while(EMAC->TXHDP[0] != 0) (void) 0; - EMAC->TXHDP[0] = (uint32_t) &s_txdesc[s_txno][0]; - if(++s_txno == ETH_DESC_CNT) { - s_txno = 0; - } - } - return len; - (void) ifp; -} - -static void mg_tcpip_driver_tms570_update_hash_table(struct mg_tcpip_if *ifp) { - // TODO(): read database, rebuild hash table - // Setting Hash Index for 01:00:5e:00:00:fb (multicast) - // using TMS570 XOR method (32.5.37). - // computed hash is 55, which means bit 23 (55 - 32) in - // HASH2 register must be set - EMAC->MACHASH2 = MG_BIT(23); - EMAC->RXMBPENABLE = MG_BIT(5); // enable hash filtering - (void) ifp; -} - -static bool mg_tcpip_driver_tms570_poll(struct mg_tcpip_if *ifp, bool s1) { - if (ifp->update_mac_hash_table) { - mg_tcpip_driver_tms570_update_hash_table(ifp); - ifp->update_mac_hash_table = false; - } - if (!s1) return false; - struct mg_tcpip_driver_tms570_data *d = - (struct mg_tcpip_driver_tms570_data *) ifp->driver_data; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {emac_read_phy, emac_write_phy}; - if (!s1) return false; - up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { - // link state just went up - MG_DEBUG(("Link is %uM %s-duplex", speed == MG_PHY_SPEED_10M ? 10 : 100, - full_duplex ? "full" : "half")); - } - return up; -} - -#pragma CODE_STATE(EMAC_TX_IRQHandler, 32) -#pragma INTERRUPT(EMAC_TX_IRQHandler, IRQ) -void EMAC_TX_IRQHandler(void) { - uint32_t status = EMAC_CTRL->C0TXSTAT; - if (status & 1) { // interrupt caused on channel 0 - while(s_txdesc[s_txno][3] & SWAP32(MG_BIT(29))) (void) 0; - EMAC->TXCP[0] = (uint32_t) &s_txdesc[s_txno][0]; - } - //Write the DMA end of interrupt vector - EMAC->MACEOIVECTOR = 2; -} -static uint32_t s_rxno; -#pragma CODE_STATE(EMAC_RX_IRQHandler, 32) -#pragma INTERRUPT(EMAC_RX_IRQHandler, IRQ) -void EMAC_RX_IRQHandler(void) { - uint32_t status = EMAC_CTRL->C0RXSTAT; - if (status & 1) { // Frame received, loop - uint32_t i; - //MG_INFO(("RX interrupt")); - for (i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][3] & SWAP32(MG_BIT(29))) break; - uint32_t len = SWAP32(s_rxdesc[s_rxno][3]) & 0xffff; - //MG_INFO(("recv len: %d", len)); - //mg_hexdump(s_rxbuf[s_rxno], len); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - uint32_t flags = s_rxdesc[s_rxno][3]; - s_rxdesc[s_rxno][3] = SWAP32(MG_BIT(29)); - s_rxdesc[s_rxno][2] = SWAP32(ETH_PKT_SIZE); - EMAC->RXCP[0] = (uint32_t) &s_rxdesc[s_rxno][0]; - if (flags & SWAP32(MG_BIT(28))) { - //MG_INFO(("EOQ detected")); - EMAC->RXHDP[0] = (uint32_t) &s_rxdesc[0][0]; - } - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - //Write the DMA end of interrupt vector - EMAC->MACEOIVECTOR = 1; -} -struct mg_tcpip_driver mg_tcpip_driver_tms570 = {mg_tcpip_driver_tms570_init, - mg_tcpip_driver_tms570_tx, NULL, - mg_tcpip_driver_tms570_poll}; -#endif - - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/w5100.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5100) && MG_ENABLE_DRIVER_W5100 - -static void w5100_txn(struct mg_tcpip_spi *s, uint16_t addr, bool wr, void *buf, - size_t len) { - size_t i; - uint8_t *p = (uint8_t *) buf; - uint8_t control = wr ? 0xF0 : 0x0F; - uint8_t cmd[] = {control, (uint8_t) (addr >> 8), (uint8_t) (addr & 255)}; - s->begin(s->spi); - for (i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]); - for (i = 0; i < len; i++) { - uint8_t r = s->txn(s->spi, p[i]); - if (!wr) p[i] = r; - } - s->end(s->spi); -} - -// clang-format off -static void w5100_wn(struct mg_tcpip_spi *s, uint16_t addr, void *buf, size_t len) { w5100_txn(s, addr, true, buf, len); } -static void w5100_w1(struct mg_tcpip_spi *s, uint16_t addr, uint8_t val) { w5100_wn(s, addr, &val, 1); } -static void w5100_w2(struct mg_tcpip_spi *s, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5100_wn(s, addr, buf, sizeof(buf)); } -static void w5100_rn(struct mg_tcpip_spi *s, uint16_t addr, void *buf, size_t len) { w5100_txn(s, addr, false, buf, len); } -static uint8_t w5100_r1(struct mg_tcpip_spi *s, uint16_t addr) { uint8_t r = 0; w5100_rn(s, addr, &r, 1); return r; } -static uint16_t w5100_r2(struct mg_tcpip_spi *s, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5100_rn(s, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); } -// clang-format on - -static size_t w5100_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len - while ((n2 = w5100_r2(s, 0x426)) > n) n = n2; // Until it is stable - if (n > 0) { - uint16_t ptr = w5100_r2(s, 0x428); // Get read pointer - if (n <= len + 2 && n > 1) { - r = (uint16_t) (n - 2); - } - uint16_t rxbuf_size = (1 << (w5100_r1(s, 0x1a) & 3)) * 1024; - uint16_t rxbuf_addr = 0x6000; - uint16_t ptr_ofs = (ptr + 2) & (rxbuf_size - 1); - if (ptr_ofs + r < rxbuf_size) { - w5100_rn(s, rxbuf_addr + ptr_ofs, buf, r); - } else { - uint16_t remaining_len = rxbuf_size - ptr_ofs; - w5100_rn(s, rxbuf_addr + ptr_ofs, buf, remaining_len); - w5100_rn(s, rxbuf_addr, buf + remaining_len, n - remaining_len); - } - w5100_w2(s, 0x428, (uint16_t) (ptr + n)); - w5100_w1(s, 0x401, 0x40); // Sock0 CR -> RECV - } - return r; -} - -static size_t w5100_tx(const void *buf, size_t buflen, - struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t i, n = 0, ptr = 0, len = (uint16_t) buflen; - while (n < len) n = w5100_r2(s, 0x420); // Wait for space - ptr = w5100_r2(s, 0x424); // Get write pointer - uint16_t txbuf_size = (1 << (w5100_r1(s, 0x1b) & 3)) * 1024; - uint16_t ptr_ofs = ptr & (txbuf_size - 1); - uint16_t txbuf_addr = 0x4000; - if (ptr_ofs + len > txbuf_size) { - uint16_t size = txbuf_size - ptr_ofs; - w5100_wn(s, txbuf_addr + ptr_ofs, (char *) buf, size); - w5100_wn(s, txbuf_addr, (char *) buf + size, len - size); - } else { - w5100_wn(s, txbuf_addr + ptr_ofs, (char *) buf, len); - } - w5100_w2(s, 0x424, (uint16_t) (ptr + len)); // Advance write pointer - w5100_w1(s, 0x401, 0x20); // Sock0 CR -> SEND - for (i = 0; i < 40; i++) { - uint8_t ir = w5100_r1(s, 0x402); // Read S0 IR - if (ir == 0) continue; - // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr); - w5100_w1(s, 0x402, ir); // Write S0 IR: clear it! - if (ir & 8) len = 0; // Timeout. Report error - if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout - } - return len; -} - -static bool w5100_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - s->end(s->spi); - w5100_w1(s, 0, 0x80); // Reset chip: CR -> 0x80 - w5100_w1(s, 0x72, 0x53); // CR PHYLCKR -> unlock PHY - w5100_w1(s, 0x46, 0); // CR PHYCR0 -> autonegotiation - w5100_w1(s, 0x47, 0); // CR PHYCR1 -> reset - w5100_w1(s, 0x72, 0x00); // CR PHYLCKR -> lock PHY - w5100_wn(s, 0x09, ifp->mac, 6); // SHAR - w5100_w1(s, 0x1a, 6); // Sock0 RX buf size - 4KB - w5100_w1(s, 0x1b, 6); // Sock0 TX buf size - 4KB - w5100_w1(s, 0x400, 0x44); // Sock0 MR -> MACRAW, MAC filter - w5100_w1(s, 0x401, 1); // Sock0 CR -> OPEN - return w5100_r1(s, 0x403) == 0x42; // Sock0 SR == MACRAW -} - -static bool w5100_poll(struct mg_tcpip_if *ifp, bool s1) { - struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data; - return s1 ? w5100_r1(spi, 0x3c /* PHYSR */) & 1 - : false; // Bit 0 of PHYSR is LNK (0 - down, 1 - up) -} - -struct mg_tcpip_driver mg_tcpip_driver_w5100 = {w5100_init, w5100_tx, w5100_rx, - w5100_poll}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/w5500.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500 - -enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 }; - -static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, - bool wr, void *buf, size_t len) { - size_t i; - uint8_t *p = (uint8_t *) buf; - uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255), - (uint8_t) ((block << 3) | (wr ? 4 : 0))}; - s->begin(s->spi); - for (i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]); - for (i = 0; i < len; i++) { - uint8_t r = s->txn(s->spi, p[i]); - if (!wr) p[i] = r; - } - s->end(s->spi); -} - -// clang-format off -static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); } -static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); } -static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); } -static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); } -static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; } -static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); } -// clang-format on - -static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len - while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable - // printf("RSR: %d\n", (int) n); - if (n > 0) { - uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer - n = w5500_r2(s, W5500_RX0, ptr); // Read frame length - if (n <= len + 2 && n > 1) { - r = (uint16_t) (n - 2); - w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r); - } - w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer - w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV - // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r); - } - return r; -} - -static size_t w5500_tx(const void *buf, size_t buflen, - struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t i, ptr, n = 0, len = (uint16_t) buflen; - while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space - ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer - w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data - w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer - w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND - for (i = 0; i < 40; i++) { - uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR - if (ir == 0) continue; - // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr); - w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it! - if (ir & 8) len = 0; // Timeout. Report error - if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout - } - return len; -} - -static bool w5500_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - s->end(s->spi); - w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80 - w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset - w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set - // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC - w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size - w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size - w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW - w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN - return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW -} - -static bool w5500_poll(struct mg_tcpip_if *ifp, bool s1) { - struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data; - return s1 ? w5500_r1(spi, W5500_CR, 0x2e /* PHYCFGR */) & 1 - : false; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up) -} - -struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, - w5500_poll}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/xmc.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC - -struct ETH_GLOBAL_TypeDef { - volatile uint32_t MAC_CONFIGURATION, MAC_FRAME_FILTER, HASH_TABLE_HIGH, - HASH_TABLE_LOW, GMII_ADDRESS, GMII_DATA, FLOW_CONTROL, VLAN_TAG, VERSION, - DEBUG, REMOTE_WAKE_UP_FRAME_FILTER, PMT_CONTROL_STATUS, RESERVED[2], - INTERRUPT_STATUS, INTERRUPT_MASK, MAC_ADDRESS0_HIGH, MAC_ADDRESS0_LOW, - MAC_ADDRESS1_HIGH, MAC_ADDRESS1_LOW, MAC_ADDRESS2_HIGH, MAC_ADDRESS2_LOW, - MAC_ADDRESS3_HIGH, MAC_ADDRESS3_LOW, RESERVED1[40], MMC_CONTROL, - MMC_RECEIVE_INTERRUPT, MMC_TRANSMIT_INTERRUPT, MMC_RECEIVE_INTERRUPT_MASK, - MMC_TRANSMIT_INTERRUPT_MASK, TX_STATISTICS[26], RESERVED2, - RX_STATISTICS_1[26], RESERVED3[6], MMC_IPC_RECEIVE_INTERRUPT_MASK, - RESERVED4, MMC_IPC_RECEIVE_INTERRUPT, RESERVED5, RX_STATISTICS_2[30], - RESERVED7[286], TIMESTAMP_CONTROL, SUB_SECOND_INCREMENT, - SYSTEM_TIME_SECONDS, SYSTEM_TIME_NANOSECONDS, SYSTEM_TIME_SECONDS_UPDATE, - SYSTEM_TIME_NANOSECONDS_UPDATE, TIMESTAMP_ADDEND, TARGET_TIME_SECONDS, - TARGET_TIME_NANOSECONDS, SYSTEM_TIME_HIGHER_WORD_SECONDS, - TIMESTAMP_STATUS, PPS_CONTROL, RESERVED8[564], BUS_MODE, - TRANSMIT_POLL_DEMAND, RECEIVE_POLL_DEMAND, - RECEIVE_DESCRIPTOR_LIST_ADDRESS, TRANSMIT_DESCRIPTOR_LIST_ADDRESS, STATUS, - OPERATION_MODE, INTERRUPT_ENABLE, - MISSED_FRAME_AND_BUFFER_OVERFLOW_COUNTER, - RECEIVE_INTERRUPT_WATCHDOG_TIMER, RESERVED9, AHB_STATUS, RESERVED10[6], - CURRENT_HOST_TRANSMIT_DESCRIPTOR, CURRENT_HOST_RECEIVE_DESCRIPTOR, - CURRENT_HOST_TRANSMIT_BUFFER_ADDRESS, CURRENT_HOST_RECEIVE_BUFFER_ADDRESS, - HW_FEATURE; -}; - -#undef ETH0 -#define ETH0 ((struct ETH_GLOBAL_TypeDef *) 0x5000C000UL) - -#define ETH_PKT_SIZE 1536 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -#ifndef ETH_RAM_SECTION -// if no section is specified, then the data will be placed in the default -// bss section -#define ETH_RAM_SECTION -#endif - -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] ETH_RAM_SECTION; -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] ETH_RAM_SECTION; -static uint32_t s_rxdesc[ETH_DESC_CNT] - [ETH_DS] ETH_RAM_SECTION; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT] - [ETH_DS] ETH_RAM_SECTION; // TX descriptors -static uint8_t s_txno; // Current TX descriptor -static uint8_t s_rxno; // Current RX descriptor - -static struct mg_tcpip_if *s_ifp; // MIP interface -enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 }; - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) | ((uint32_t) addr << 11) | - ((uint32_t) reg << 6) | 1; - while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0; - return (uint16_t) (ETH0->GMII_DATA & 0xffff); -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH0->GMII_DATA = val; - ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) | ((uint32_t) addr << 11) | - ((uint32_t) reg << 6) | 3; - while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0; -} - -static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc_data *d) { - if (d->mdc_cr == -1) { - // assume ETH clock is 60MHz by default - // then according to 13.2.8.1, we need to set value 3 - return 3; - } - - return d->mdc_cr; -} - -static bool mg_tcpip_driver_xmc_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_xmc_data *d = - (struct mg_tcpip_driver_xmc_data *) ifp->driver_data; - s_ifp = ifp; - - // reset MAC - ETH0->BUS_MODE |= 1; - while (ETH0->BUS_MODE & 1) (void) 0; - - // set clock rate - ETH0->GMII_ADDRESS = get_clock_rate(d) << 2; - - // init phy - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC); - - // configure MAC: DO, DM, FES, TC - ETH0->MAC_CONFIGURATION = MG_BIT(13) | MG_BIT(11) | MG_BIT(14) | MG_BIT(24); - - // set the MAC address - ETH0->MAC_ADDRESS0_HIGH = MG_U32(0, 0, ifp->mac[5], ifp->mac[4]); - ETH0->MAC_ADDRESS0_LOW = - MG_U32(ifp->mac[3], ifp->mac[2], ifp->mac[1], ifp->mac[0]); - - // Configure the receive filter - ETH0->MAC_FRAME_FILTER = MG_BIT(10); // Perfect filter - // Disable flow control - ETH0->FLOW_CONTROL = 0; - // Enable store and forward mode - ETH0->OPERATION_MODE = MG_BIT(25) | MG_BIT(21); // RSF, TSF - - // Configure DMA bus mode (AAL, USP, RPBL, PBL) - ETH0->BUS_MODE = MG_BIT(25) | MG_BIT(23) | (32 << 17) | (32 << 8); - - // init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = MG_BIT(31); // OWN descriptor - s_rxdesc[i][1] = MG_BIT(14) | ETH_PKT_SIZE; - s_rxdesc[i][2] = (uint32_t) s_rxbuf[i]; - if (i == ETH_DESC_CNT - 1) { - s_rxdesc[i][3] = (uint32_t) &s_rxdesc[0][0]; - } else { - s_rxdesc[i][3] = (uint32_t) &s_rxdesc[i + 1][0]; - } - } - ETH0->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_rxdesc[0][0]; - - // init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][0] = MG_BIT(30) | MG_BIT(20); - s_txdesc[i][2] = (uint32_t) s_txbuf[i]; - if (i == ETH_DESC_CNT - 1) { - s_txdesc[i][3] = (uint32_t) &s_txdesc[0][0]; - } else { - s_txdesc[i][3] = (uint32_t) &s_txdesc[i + 1][0]; - } - } - ETH0->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_txdesc[0][0]; - - // Clear interrupts - ETH0->STATUS = 0xFFFFFFFF; - - // Disable MAC interrupts - ETH0->MMC_TRANSMIT_INTERRUPT_MASK = 0xFFFFFFFF; - ETH0->MMC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF; - ETH0->MMC_IPC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF; - ETH0->INTERRUPT_MASK = MG_BIT(9) | MG_BIT(3); // TSIM, PMTIM - - // Enable interrupts (NIE, RIE, TIE) - ETH0->INTERRUPT_ENABLE = MG_BIT(16) | MG_BIT(6) | MG_BIT(0); - - // Enable MAC transmission and reception (TE, RE) - ETH0->MAC_CONFIGURATION |= MG_BIT(3) | MG_BIT(2); - // Enable DMA transmission and reception (ST, SR) - ETH0->OPERATION_MODE |= MG_BIT(13) | MG_BIT(1); - return true; -} - -static size_t mg_tcpip_driver_xmc_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No free descriptors")); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); - s_txdesc[s_txno][1] = len; - // Table 13-19 Transmit Descriptor Word 0 (IC, LS, FS, TCH) - s_txdesc[s_txno][0] = MG_BIT(30) | MG_BIT(29) | MG_BIT(28) | MG_BIT(20); - s_txdesc[s_txno][0] |= MG_BIT(31); // OWN bit: handle control to DMA - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - - // Resume processing - ETH0->STATUS = MG_BIT(2); // clear Transmit unavailable - ETH0->TRANSMIT_POLL_DEMAND = 0; - return len; -} - -static void mg_tcpip_driver_xmc_update_hash_table(struct mg_tcpip_if *ifp) { - // TODO(): read database, rebuild hash table - // set the multicast address filter - ETH0->MAC_ADDRESS1_HIGH = - MG_U32(0, 0, mcast_addr[5], mcast_addr[4]) | MG_BIT(31); - ETH0->MAC_ADDRESS1_LOW = - MG_U32(mcast_addr[3], mcast_addr[2], mcast_addr[1], mcast_addr[0]); - (void) ifp; -} - -static bool mg_tcpip_driver_xmc_poll(struct mg_tcpip_if *ifp, bool s1) { - if (ifp->update_mac_hash_table) { - mg_tcpip_driver_xmc_update_hash_table(ifp); - ifp->update_mac_hash_table = false; - } - if (!s1) return false; - struct mg_tcpip_driver_xmc_data *d = - (struct mg_tcpip_driver_xmc_data *) ifp->driver_data; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - MG_DEBUG(("Link is %uM %s-duplex", speed == MG_PHY_SPEED_10M ? 10 : 100, - full_duplex ? "full" : "half")); - } - return up; -} - -void ETH0_0_IRQHandler(void); -void ETH0_0_IRQHandler(void) { - uint32_t irq_status = ETH0->STATUS; - - // check if a frame was received - if (irq_status & MG_BIT(6)) { - for (uint8_t i = 0; i < 10; i++) { // read as they arrive, but not forever - if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; - size_t len = (s_rxdesc[s_rxno][0] & 0x3fff0000) >> 16; - mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); - s_rxdesc[s_rxno][0] = MG_BIT(31); // OWN bit: handle control to DMA - // Resume processing - ETH0->STATUS = MG_BIT(7) | MG_BIT(6); // clear RU and RI - ETH0->RECEIVE_POLL_DEMAND = 0; - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - ETH0->STATUS = MG_BIT(6); - } - - // clear Successful transmission interrupt - if (irq_status & 1) { - ETH0->STATUS = 1; - } - - // clear normal interrupt - if (irq_status & MG_BIT(16)) { - ETH0->STATUS = MG_BIT(16); - } -} - -struct mg_tcpip_driver mg_tcpip_driver_xmc = {mg_tcpip_driver_xmc_init, - mg_tcpip_driver_xmc_tx, NULL, - mg_tcpip_driver_xmc_poll}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/xmc7.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7 - -struct ETH_Type { - volatile uint32_t CTL, STATUS, RESERVED[1022], NETWORK_CONTROL, - NETWORK_CONFIG, NETWORK_STATUS, USER_IO_REGISTER, DMA_CONFIG, - TRANSMIT_STATUS, RECEIVE_Q_PTR, TRANSMIT_Q_PTR, RECEIVE_STATUS, - INT_STATUS, INT_ENABLE, INT_DISABLE, INT_MASK, PHY_MANAGEMENT, PAUSE_TIME, - TX_PAUSE_QUANTUM, PBUF_TXCUTTHRU, PBUF_RXCUTTHRU, JUMBO_MAX_LENGTH, - EXTERNAL_FIFO_INTERFACE, RESERVED1, AXI_MAX_PIPELINE, RSC_CONTROL, - INT_MODERATION, SYS_WAKE_TIME, RESERVED2[7], HASH_BOTTOM, HASH_TOP, - SPEC_ADD1_BOTTOM, SPEC_ADD1_TOP, SPEC_ADD2_BOTTOM, SPEC_ADD2_TOP, - SPEC_ADD3_BOTTOM, SPEC_ADD3_TOP, SPEC_ADD4_BOTTOM, SPEC_ADD4_TOP, - SPEC_TYPE1, SPEC_TYPE2, SPEC_TYPE3, SPEC_TYPE4, WOL_REGISTER, - STRETCH_RATIO, STACKED_VLAN, TX_PFC_PAUSE, MASK_ADD1_BOTTOM, - MASK_ADD1_TOP, DMA_ADDR_OR_MASK, RX_PTP_UNICAST, TX_PTP_UNICAST, - TSU_NSEC_CMP, TSU_SEC_CMP, TSU_MSB_SEC_CMP, TSU_PTP_TX_MSB_SEC, - TSU_PTP_RX_MSB_SEC, TSU_PEER_TX_MSB_SEC, TSU_PEER_RX_MSB_SEC, - DPRAM_FILL_DBG, REVISION_REG, OCTETS_TXED_BOTTOM, OCTETS_TXED_TOP, - FRAMES_TXED_OK, BROADCAST_TXED, MULTICAST_TXED, PAUSE_FRAMES_TXED, - FRAMES_TXED_64, FRAMES_TXED_65, FRAMES_TXED_128, FRAMES_TXED_256, - FRAMES_TXED_512, FRAMES_TXED_1024, FRAMES_TXED_1519, TX_UNDERRUNS, - SINGLE_COLLISIONS, MULTIPLE_COLLISIONS, EXCESSIVE_COLLISIONS, - LATE_COLLISIONS, DEFERRED_FRAMES, CRS_ERRORS, OCTETS_RXED_BOTTOM, - OCTETS_RXED_TOP, FRAMES_RXED_OK, BROADCAST_RXED, MULTICAST_RXED, - PAUSE_FRAMES_RXED, FRAMES_RXED_64, FRAMES_RXED_65, FRAMES_RXED_128, - FRAMES_RXED_256, FRAMES_RXED_512, FRAMES_RXED_1024, FRAMES_RXED_1519, - UNDERSIZE_FRAMES, EXCESSIVE_RX_LENGTH, RX_JABBERS, FCS_ERRORS, - RX_LENGTH_ERRORS, RX_SYMBOL_ERRORS, ALIGNMENT_ERRORS, RX_RESOURCE_ERRORS, - RX_OVERRUNS, RX_IP_CK_ERRORS, RX_TCP_CK_ERRORS, RX_UDP_CK_ERRORS, - AUTO_FLUSHED_PKTS, RESERVED3, TSU_TIMER_INCR_SUB_NSEC, TSU_TIMER_MSB_SEC, - TSU_STROBE_MSB_SEC, TSU_STROBE_SEC, TSU_STROBE_NSEC, TSU_TIMER_SEC, - TSU_TIMER_NSEC, TSU_TIMER_ADJUST, TSU_TIMER_INCR, TSU_PTP_TX_SEC, - TSU_PTP_TX_NSEC, TSU_PTP_RX_SEC, TSU_PTP_RX_NSEC, TSU_PEER_TX_SEC, - TSU_PEER_TX_NSEC, TSU_PEER_RX_SEC, TSU_PEER_RX_NSEC, PCS_CONTROL, - PCS_STATUS, RESERVED4[2], PCS_AN_ADV, PCS_AN_LP_BASE, PCS_AN_EXP, - PCS_AN_NP_TX, PCS_AN_LP_NP, RESERVED5[6], PCS_AN_EXT_STATUS, RESERVED6[8], - TX_PAUSE_QUANTUM1, TX_PAUSE_QUANTUM2, TX_PAUSE_QUANTUM3, RESERVED7, - RX_LPI, RX_LPI_TIME, TX_LPI, TX_LPI_TIME, DESIGNCFG_DEBUG1, - DESIGNCFG_DEBUG2, DESIGNCFG_DEBUG3, DESIGNCFG_DEBUG4, DESIGNCFG_DEBUG5, - DESIGNCFG_DEBUG6, DESIGNCFG_DEBUG7, DESIGNCFG_DEBUG8, DESIGNCFG_DEBUG9, - DESIGNCFG_DEBUG10, RESERVED8[22], SPEC_ADD5_BOTTOM, SPEC_ADD5_TOP, - RESERVED9[60], SPEC_ADD36_BOTTOM, SPEC_ADD36_TOP, INT_Q1_STATUS, - INT_Q2_STATUS, INT_Q3_STATUS, RESERVED10[11], INT_Q15_STATUS, RESERVED11, - TRANSMIT_Q1_PTR, TRANSMIT_Q2_PTR, TRANSMIT_Q3_PTR, RESERVED12[11], - TRANSMIT_Q15_PTR, RESERVED13, RECEIVE_Q1_PTR, RECEIVE_Q2_PTR, - RECEIVE_Q3_PTR, RESERVED14[3], RECEIVE_Q7_PTR, RESERVED15, - DMA_RXBUF_SIZE_Q1, DMA_RXBUF_SIZE_Q2, DMA_RXBUF_SIZE_Q3, RESERVED16[3], - DMA_RXBUF_SIZE_Q7, CBS_CONTROL, CBS_IDLESLOPE_Q_A, CBS_IDLESLOPE_Q_B, - UPPER_TX_Q_BASE_ADDR, TX_BD_CONTROL, RX_BD_CONTROL, UPPER_RX_Q_BASE_ADDR, - RESERVED17[2], HIDDEN_REG0, HIDDEN_REG1, HIDDEN_REG2, HIDDEN_REG3, - RESERVED18[2], HIDDEN_REG4, HIDDEN_REG5; -}; - -#define ETH0 ((struct ETH_Type *) 0x40490000) - -#define ETH_PKT_SIZE 1536 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 2 // Descriptor size (words) - -// TODO(): handle these in a portable compiler-independent CMSIS-friendly way -#define MG_8BYTE_ALIGNED __attribute__((aligned((8U)))) - -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED; -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED; -static uint8_t s_txno MG_8BYTE_ALIGNED; // Current TX descriptor -static uint8_t s_rxno MG_8BYTE_ALIGNED; // Current RX descriptor - -static struct mg_tcpip_if *s_ifp; // MIP interface -enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 }; - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - // WRITE1, READ OPERATION, PHY, REG, WRITE10 - ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(29) | ((addr & 0xf) << 24) | - ((reg & 0x1f) << 18) | MG_BIT(17); - while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0; - return ETH0->PHY_MANAGEMENT & 0xffff; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(28) | ((addr & 0xf) << 24) | - ((reg & 0x1f) << 18) | MG_BIT(17) | val; - while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0; -} - -static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc7_data *d) { - // see ETH0 -> NETWORK_CONFIG register - (void) d; - return 3; -} - -static bool mg_tcpip_driver_xmc7_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_xmc7_data *d = - (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data; - s_ifp = ifp; - - // enable controller, set RGMII mode - ETH0->CTL = MG_BIT(31) | (4 << 8) | 2; - - uint32_t cr = get_clock_rate(d); - // set NSP change, ignore RX FCS, data bus width, clock rate - // frame length 1536, full duplex, speed - ETH0->NETWORK_CONFIG = MG_BIT(29) | MG_BIT(26) | MG_BIT(21) | - ((cr & 7) << 18) | MG_BIT(8) | MG_BIT(1) | MG_BIT(0); - - // config DMA settings: Force TX burst, Discard on Error, set RX buffer size - // to 1536, TX_PBUF_SIZE, RX_PBUF_SIZE, AMBA_BURST_LENGTH - ETH0->DMA_CONFIG = - MG_BIT(26) | MG_BIT(24) | (0x18 << 16) | MG_BIT(10) | (3 << 8) | 4; - - // initialize descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = (uint32_t) s_rxbuf[i]; - if (i == ETH_DESC_CNT - 1) { - s_rxdesc[i][0] |= MG_BIT(1); // mark last descriptor - } - - s_txdesc[i][0] = (uint32_t) s_txbuf[i]; - s_txdesc[i][1] = MG_BIT(31); // OWN descriptor - if (i == ETH_DESC_CNT - 1) { - s_txdesc[i][1] |= MG_BIT(30); // mark last descriptor - } - } - ETH0->RECEIVE_Q_PTR = (uint32_t) s_rxdesc; - ETH0->TRANSMIT_Q_PTR = (uint32_t) s_txdesc; - - // disable other queues - ETH0->TRANSMIT_Q2_PTR = 1; - ETH0->TRANSMIT_Q1_PTR = 1; - ETH0->RECEIVE_Q2_PTR = 1; - ETH0->RECEIVE_Q1_PTR = 1; - - // enable interrupts (RX complete) - ETH0->INT_ENABLE = MG_BIT(1); - - // set MAC address - ETH0->SPEC_ADD1_BOTTOM = - ifp->mac[3] << 24 | ifp->mac[2] << 16 | ifp->mac[1] << 8 | ifp->mac[0]; - ETH0->SPEC_ADD1_TOP = ifp->mac[5] << 8 | ifp->mac[4]; - - // enable MDIO, TX, RX - ETH0->NETWORK_CONTROL = MG_BIT(4) | MG_BIT(3) | MG_BIT(2); - - // start transmission - ETH0->NETWORK_CONTROL |= MG_BIT(9); - - // init phy - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC); - - (void) d; - return true; -} - -static size_t mg_tcpip_driver_xmc7_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if (((s_txdesc[s_txno][1] & MG_BIT(31)) == 0)) { - ifp->nerr++; - MG_ERROR(("No free descriptors")); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); - s_txdesc[s_txno][1] = (s_txno == ETH_DESC_CNT - 1 ? MG_BIT(30) : 0) | - MG_BIT(15) | len; // Last buffer and length - - ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - - MG_DSB(); - ETH0->TRANSMIT_STATUS = ETH0->TRANSMIT_STATUS; - ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission - - return len; -} - -static void mg_tcpip_driver_xmc7_update_hash_table(struct mg_tcpip_if *ifp) { - // TODO(): read database, rebuild hash table - // set multicast MAC address - ETH0->SPEC_ADD2_BOTTOM = mcast_addr[3] << 24 | mcast_addr[2] << 16 | - mcast_addr[1] << 8 | mcast_addr[0]; - ETH0->SPEC_ADD2_TOP = mcast_addr[5] << 8 | mcast_addr[4]; - (void) ifp; -} - -static bool mg_tcpip_driver_xmc7_poll(struct mg_tcpip_if *ifp, bool s1) { - if (ifp->update_mac_hash_table) { - mg_tcpip_driver_xmc7_update_hash_table(ifp); - ifp->update_mac_hash_table = false; - } - if (!s1) return false; - struct mg_tcpip_driver_xmc7_data *d = - (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t netconf = ETH0->NETWORK_CONFIG; - MG_SET_BITS(netconf, MG_BIT(10), - MG_BIT(1) | MG_BIT(0)); // 100M, Full-duplex - uint32_t ctl = ETH0->CTL; - MG_SET_BITS(ctl, 0xFF00, 4 << 8); // /5 for 25M clock - if (speed == MG_PHY_SPEED_1000M) { - netconf |= MG_BIT(10); // 1000M - MG_SET_BITS(ctl, 0xFF00, 0); // /1 for 125M clock TODO() IS THIS NEEDED ? - } else if (speed == MG_PHY_SPEED_10M) { - netconf &= ~MG_BIT(0); // 10M - MG_SET_BITS(ctl, 0xFF00, 49); // /50 for 2.5M clock - } - if (full_duplex == false) netconf &= ~MG_BIT(1); // Half-duplex - ETH0->NETWORK_CONFIG = netconf; // IRQ handler does not fiddle with these - ETH0->CTL = ctl; - MG_DEBUG(("Link is %uM %s-duplex", - speed == MG_PHY_SPEED_10M - ? 10 - : (speed == MG_PHY_SPEED_100M ? 100 : 1000), - full_duplex ? "full" : "half")); - } - return up; -} - -void ETH_IRQHandler(void) { - uint32_t irq_status = ETH0->INT_STATUS; - if (irq_status & MG_BIT(1)) { - for (uint8_t i = 0; i < 10; i++) { // read as they arrive, but not forever - if ((s_rxdesc[s_rxno][0] & MG_BIT(0)) == 0) break; - size_t len = s_rxdesc[s_rxno][1] & (MG_BIT(13) - 1); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); - s_rxdesc[s_rxno][0] &= ~MG_BIT(0); // OWN bit: handle control to DMA - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - - ETH0->INT_STATUS = irq_status; -} - -struct mg_tcpip_driver mg_tcpip_driver_xmc7 = {mg_tcpip_driver_xmc7_init, - mg_tcpip_driver_xmc7_tx, NULL, - mg_tcpip_driver_xmc7_poll}; -#endif diff --git a/main/lib/mongoose.h b/main/lib/mongoose.h deleted file mode 100644 index 2e18b06..0000000 --- a/main/lib/mongoose.h +++ /dev/null @@ -1,3781 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2025 Cesanta Software Limited -// All rights reserved -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see http://www.gnu.org/licenses/ -// -// You are free to use this software under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this software under a commercial -// license, as set out in https://www.mongoose.ws/licensing/ -// -// SPDX-License-Identifier: GPL-2.0-only or commercial - -#ifndef MONGOOSE_H -#define MONGOOSE_H - -#define MG_VERSION "7.19" - -#ifdef __cplusplus -extern "C" { -#endif - - -#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_config.h -#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ... -#define MG_ARCH_WIN32 2 // Windows -#define MG_ARCH_ESP32 3 // ESP32 -#define MG_ARCH_ESP8266 4 // ESP8266 -#define MG_ARCH_FREERTOS 5 // FreeRTOS -#define MG_ARCH_THREADX 6 // Eclipse ThreadX (former MS Azure RTOS) -#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS -#define MG_ARCH_ARMGCC 8 // Plain ARM GCC -#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX) -#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS -#define MG_ARCH_PICOSDK 11 // Raspberry Pi Pico-SDK (RP2040, RP2350) -#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard -#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS) -#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS -#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT -#define MG_ARCH_CUBE 16 // STM32Cube environment - -#define MG_ARCH_NEWLIB MG_ARCH_ARMGCC // Alias, deprecate in 2025 - -#if !defined(MG_ARCH) -#if defined(__unix__) || defined(__APPLE__) -#define MG_ARCH MG_ARCH_UNIX -#elif defined(_WIN32) -#define MG_ARCH MG_ARCH_WIN32 -#endif -#endif // !defined(MG_ARCH) - -#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM) -#include "mongoose_config.h" // keep this include -#endif - -#if !defined(MG_ARCH) -#error "MG_ARCH is not specified and we couldn't guess it. Define MG_ARCH=... in mongoose_config.h" -#endif - -// http://esr.ibiblio.org/?p=5095 -#define MG_BIG_ENDIAN (*(uint16_t *) "\0\xff" < 0x100) - - - - - - - - - - - - - - - - - -#if MG_ARCH == MG_ARCH_ARMCGT - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MG_PATH_MAX 100 -#define MG_ENABLE_SOCKET 0 -#define MG_ENABLE_DIRLIST 0 - -#endif - - -#if MG_ARCH == MG_ARCH_ARMGCC -#define _POSIX_TIMERS - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MG_PATH_MAX 100 -#define MG_ENABLE_SOCKET 0 -#define MG_ENABLE_DIRLIST 0 - -#endif - - -#if MG_ARCH == MG_ARCH_CUBE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Cube-generated header, includes ST Cube HAL -// NOTE: use angle brackets to prevent amalgamator ditching it -#include - -#ifndef MG_PATH_MAX -#define MG_PATH_MAX 100 -#endif - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 0 -#endif - -#ifndef MG_ENABLE_SOCKET -#define MG_ENABLE_SOCKET 0 -#endif - -#ifndef MG_ENABLE_TCPIP -#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack -#endif - -#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && !defined(MG_ENABLE_DRIVER_STM32H) -#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7) -#define MG_ENABLE_DRIVER_STM32F 1 -#elif defined(STM32H5) || defined(STM32H7) -#define MG_ENABLE_DRIVER_STM32H 1 -#else -#error Select a driver in mongoose_config.h -#endif -#endif - -#ifndef MG_TLS -#define MG_TLS MG_TLS_BUILTIN -#endif - -#if !defined(MG_OTA) && defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7) -#define MG_OTA MG_OTA_STM32F -#elif !defined(MG_OTA) && defined(STM32H5) -#define MG_OTA MG_OTA_STM32H5 -#elif !defined(MG_OTA) && defined(STM32H7) -#define MG_OTA MG_OTA_STM32H7 -#endif -// use HAL-defined execute-in-ram section -#define MG_IRAM __attribute__((section(".RamFunc"))) - -#ifndef HAL_ICACHE_MODULE_ENABLED -#define HAL_ICACHE_IsEnabled() 0 -#define HAL_ICACHE_Enable() (void) 0 -#define HAL_ICACHE_Disable() (void) 0 -#endif - -#ifndef MG_SET_MAC_ADDRESS -// Construct MAC address from UUID -#define MGUID ((uint32_t *) UID_BASE) // Unique 96-bit chip ID -#define MG_SET_MAC_ADDRESS(mac) \ - do { \ - int icache_enabled_ = HAL_ICACHE_IsEnabled(); \ - if (icache_enabled_) HAL_ICACHE_Disable(); \ - mac[0] = 42; \ - mac[1] = ((MGUID[0] >> 0) & 255) ^ ((MGUID[2] >> 19) & 255); \ - mac[2] = ((MGUID[0] >> 10) & 255) ^ ((MGUID[1] >> 10) & 255); \ - mac[3] = (MGUID[0] >> 19) & 255; \ - mac[4] = ((MGUID[1] >> 0) & 255) ^ ((MGUID[2] >> 10) & 255); \ - mac[5] = ((MGUID[2] >> 0) & 255) ^ ((MGUID[1] >> 19) & 255); \ - if (icache_enabled_) HAL_ICACHE_Enable(); \ - } while (0) -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_ESP32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#include // Use angle brackets to avoid -#include // amalgamation ditching them - -#define MG_PATH_MAX 128 - -#ifndef MG_ENABLE_POSIX_FS -#define MG_ENABLE_POSIX_FS 1 -#endif - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 1 -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_ESP8266 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MG_PATH_MAX 128 - -#endif - - -#if MG_ARCH == MG_ARCH_FREERTOS - -#include -#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP -#include -#endif -#include -#include -#include -#include -#include -#include // rand(), strtol(), atoi() -#include -#if defined(__ARMCC_VERSION) -#define mode_t size_t -#include -#include -#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) -#elif defined(__CCRH__) -#else -#include -#endif - -#include -#include - -#define MG_ENABLE_CUSTOM_CALLOC 1 - -static inline void mg_free(void *ptr) { - vPortFree(ptr); -} - -// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib -static inline void *mg_calloc(size_t cnt, size_t size) { - void *p = pvPortMalloc(cnt * size); - if (p != NULL) memset(p, 0, size * cnt); - return p; -} - -#if !defined(MG_ENABLE_POSIX_FS) || !MG_ENABLE_POSIX_FS -#else -#define mkdir(a, b) mg_mkdir(a, b) -static inline int mg_mkdir(const char *path, mode_t mode) { - (void) path, (void) mode; - return -1; -} -#endif - -#endif // MG_ARCH == MG_ARCH_FREERTOS - - -#if MG_ARCH == MG_ARCH_PICOSDK -#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include -#include -int mkdir(const char *, mode_t); - -#if MG_OTA == MG_OTA_PICOSDK -#include -#include -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_RTTHREAD - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 1460 -#endif - -#endif // MG_ARCH == MG_ARCH_RTTHREAD - - -#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \ - MG_ARCH == MG_ARCH_CMSIS_RTOS2 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if MG_ARCH == MG_ARCH_CMSIS_RTOS1 -#include "cmsis_os.h" // keep this include -// https://developer.arm.com/documentation/ka003821/latest -extern uint32_t rt_time_get(void); -#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2 -#include "cmsis_os2.h" // keep this include -#endif - -#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) - -#if defined(__ARMCC_VERSION) -#define mode_t size_t -#define mkdir(a, b) mg_mkdir(a, b) -static inline int mg_mkdir(const char *path, mode_t mode) { - (void) path, (void) mode; - return -1; -} -#endif - -#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \ - !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \ - (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP) -#define MG_ENABLE_RL 1 -#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE -#define MG_SOCK_LISTEN_BACKLOG_SIZE 3 -#endif -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_THREADX - -#include -#include -#include -#include - -// Do not include time.h and stdlib.h, since they conflict with nxd_bsd.h -// extern time_t time(time_t *); -#include - -#define MG_DIRSEP '\\' -#undef FOPEN_MAX - -#ifndef MG_PATH_MAX -#define MG_PATH_MAX 32 -#endif - -#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE -#define MG_SOCK_LISTEN_BACKLOG_SIZE 3 -#endif - -#ifndef MG_ENABLE_IPV6 -#define MG_ENABLE_IPV6 0 -#endif - -#define socklen_t int -#define closesocket(x) soc_close(x) - -// In order to enable BSD support in NetxDuo, do the following (assuming Cube): -// 1. Add nxd_bsd.h and nxd_bsd.c to the repo: -// https://github.com/eclipse-threadx/netxduo/blob/v6.1.12_rel/addons/BSD/nxd_bsd.c -// https://github.com/eclipse-threadx/netxduo/blob/v6.1.12_rel/addons/BSD/nxd_bsd.h -// 2. Add to tx_user.h -// #define TX_THREAD_USER_EXTENSION int bsd_errno; -// 3. Add to nx_user.h -// #define NX_ENABLE_EXTENDED_NOTIFY_SUPPORT -// 4. Add __CCRX__ build preprocessor constant -// Project -> Properties -> C/C++ -> Settings -> MCU Compiler -> Preprocessor - -#endif - - -#if MG_ARCH == MG_ARCH_TIRTOS - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#endif - - -#if MG_ARCH == MG_ARCH_UNIX - -#define _DARWIN_UNLIMITED_SELECT 1 // No limit on file descriptors - -#if defined(__APPLE__) -#include -#endif - -#if !defined(MG_ENABLE_EPOLL) && defined(__linux__) -#define MG_ENABLE_EPOLL 1 -#elif !defined(MG_ENABLE_POLL) -#define MG_ENABLE_POLL 1 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL -#include -#elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 1 -#endif - -#ifndef MG_PATH_MAX -#define MG_PATH_MAX FILENAME_MAX -#endif - -#ifndef MG_ENABLE_POSIX_FS -#define MG_ENABLE_POSIX_FS 1 -#endif - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 16384 -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_WIN32 - -// Avoid name clashing; (macro expansion producing 'defined' has undefined -// behaviour). See config.h for user options -#ifndef MG_ENABLE_WINSOCK -#if (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP) && \ - (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \ - (!defined(MG_ENABLE_FREERTOS_TCP) || !MG_ENABLE_FREERTOS_TCP) -#define MG_ENABLE_WINSOCK 1 -#else -#define MG_ENABLE_WINSOCK 0 -#endif -#endif - -#ifndef _CRT_RAND_S -#define _CRT_RAND_S -#endif - -#ifndef _WIN32_WINNT -#if defined(_MSC_VER) && _MSC_VER < 1700 -#define _WIN32_WINNT 0x0400 // Let vc98 pick up wincrypt.h -#else -#define _WIN32_WINNT 0x0600 -#endif -#endif -#ifndef WINVER -#define WINVER _WIN32_WINNT -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // fix missing macros and types - -#if defined(_MSC_VER) && _MSC_VER < 1700 -#define __func__ "" -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -typedef unsigned char uint8_t; -typedef char int8_t; -typedef unsigned short uint16_t; -typedef short int16_t; -typedef unsigned int uint32_t; -typedef int int32_t; -typedef enum { false = 0, true = 1 } bool; -#else -#include -#include -#if MG_ENABLE_WINSOCK -#include -#endif -#endif - -#include -#include - -// For mg_random() -#if defined(_MSC_VER) && _MSC_VER < 1700 -#include -#pragma comment(lib, "advapi32.lib") -#endif - -#if defined(_MSC_VER) && _MSC_VER <= 1200 - #ifndef IPPROTO_IP - #define IPPROTO_IP 0 - #endif - - #ifndef IP_ADD_MEMBERSHIP - struct ip_mreq { - struct in_addr imr_multiaddr; - struct in_addr imr_interface; - }; - #define IP_ADD_MEMBERSHIP 12 - #endif -#endif - -// Protect from calls like std::snprintf in app code -// See https://github.com/cesanta/mongoose/issues/1047 -#ifndef __cplusplus -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#ifndef strdup // For MSVC with _DEBUG, see #1359 -#define strdup(x) _strdup(x) -#endif -#endif - -typedef unsigned long nfds_t; -#if defined(_MSC_VER) -#if MG_ENABLE_WINSOCK -#pragma comment(lib, "ws2_32.lib") -#endif -#ifndef alloca -#define alloca(a) _alloca(a) -#endif -#endif - -#define MG_DIRSEP '\\' - -#ifndef MG_PATH_MAX -#define MG_PATH_MAX FILENAME_MAX -#endif - -#if MG_ENABLE_WINSOCK - -#define MG_INVALID_SOCKET INVALID_SOCKET -#define MG_SOCKET_TYPE SOCKET -#define poll(a, b, c) WSAPoll((a), (b), (c)) -#define closesocket(x) closesocket(x) -typedef int socklen_t; - -#ifndef SO_EXCLUSIVEADDRUSE -#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR)) -#endif - -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? WSAGetLastError() : 0) - -#define MG_SOCK_PENDING(errcode) \ - (((errcode) < 0) && \ - (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || \ - WSAGetLastError() == WSAEWOULDBLOCK)) - -#define MG_SOCK_RESET(errcode) \ - (((errcode) < 0) && (WSAGetLastError() == WSAECONNRESET)) - -#endif // MG_ENABLE_WINSOCK - -#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX) -#define sleep(x) Sleep((x) *1000) -#define mkdir(a, b) _mkdir(a) -#define timegm(x) _mkgmtime(x) - -#ifndef S_ISDIR -#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) -#endif - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 1 -#endif - -#ifndef SIGPIPE -#define SIGPIPE 0 -#endif - -#ifndef MG_ENABLE_POSIX_FS -#define MG_ENABLE_POSIX_FS 1 -#endif - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 16384 -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_ZEPHYR - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MG_PUTCHAR(x) printk("%c", x) -#ifndef strdup -#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) -#endif -#define strerror(x) zsock_gai_strerror(x) - -#ifndef FD_CLOEXEC -#define FD_CLOEXEC 0 -#endif - -#ifndef F_SETFD -#define F_SETFD 0 -#endif - -#define MG_ENABLE_SSI 0 - -int rand(void); -int sscanf(const char *, const char *, ...); - -#endif - - -#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP - -#include -#include - -#include -#include - -#define MG_SOCKET_TYPE Socket_t -#define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET - -// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing -// with FreeRTOS_ prefix, is beyond me -#define IPPROTO_TCP FREERTOS_IPPROTO_TCP -#define IPPROTO_UDP FREERTOS_IPPROTO_UDP -#define AF_INET FREERTOS_AF_INET -#define SOCK_STREAM FREERTOS_SOCK_STREAM -#define SOCK_DGRAM FREERTOS_SOCK_DGRAM -#define SO_BROADCAST 0 -#define SO_ERROR 0 -#define SOL_SOCKET 0 -#define SO_REUSEADDR 0 - -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0) - -#define MG_SOCK_PENDING(errcode) \ - ((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \ - (errcode) == -pdFREERTOS_ERRNO_EISCONN || \ - (errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \ - (errcode) == -pdFREERTOS_ERRNO_EAGAIN) - -#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN) - -// actually only if optional timeout is enabled -#define MG_SOCK_INTR(fd) (fd == NULL) - -#define sockaddr_in freertos_sockaddr -#define sockaddr freertos_sockaddr -#if ipFR_TCP_VERSION_MAJOR >= 4 -#define sin_addr sin_address.ulIP_IPv4 -#endif -#define accept(a, b, c) FreeRTOS_accept((a), (b), (c)) -#define connect(a, b, c) FreeRTOS_connect((a), (b), (c)) -#define bind(a, b, c) FreeRTOS_bind((a), (b), (c)) -#define listen(a, b) FreeRTOS_listen((a), (b)) -#define socket(a, b, c) FreeRTOS_socket((a), (b), (c)) -#define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d)) -#define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d)) -#define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e)) -#define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f)) -#define recvfrom(a, b, c, d, e, f) \ - FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f)) -#define closesocket(x) FreeRTOS_closesocket(x) -#define gethostbyname(x) FreeRTOS_gethostbyname(x) -#define getsockname(a, b, c) mg_getsockname((a), (b), (c)) -#define getpeername(a, b, c) mg_getpeername((a), (b), (c)) - -static inline int mg_getsockname(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) { - (void) fd, (void) buf, (void) len; - return -1; -} - -static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) { - (void) fd, (void) buf, (void) len; - return 0; -} -#endif - - -#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP - -#if defined(__GNUC__) && !defined(__ARMCC_VERSION) -#include -#endif - -struct timeval; - -#include - -#if !LWIP_TIMEVAL_PRIVATE -#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both -#include -#else -struct timeval { - time_t tv_sec; - long tv_usec; -}; -#endif -#endif - -#if LWIP_SOCKET != 1 -// Sockets support disabled in LWIP by default -#error Set LWIP_SOCKET variable to 1 (in lwipopts.h) -#endif -#endif - - -#if defined(MG_ENABLE_RL) && MG_ENABLE_RL -#include - -#define closesocket(x) closesocket(x) - -#define TCP_NODELAY SO_KEEPALIVE - -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0) - -#define MG_SOCK_PENDING(errcode) \ - ((errcode) == BSD_EWOULDBLOCK || (errcode) == BSD_EALREADY || \ - (errcode) == BSD_EINPROGRESS) - -#define MG_SOCK_RESET(errcode) \ - ((errcode) == BSD_ECONNABORTED || (errcode) == BSD_ECONNRESET) - -// In blocking mode, which is enabled by default, accept() waits for a -// connection request. In non blocking mode, you must call accept() -// again if the error code BSD_EWOULDBLOCK is returned. -#define MG_SOCK_INTR(fd) (fd == BSD_EWOULDBLOCK) - -#define socklen_t int -#endif - - -#ifndef MG_ENABLE_LOG -#define MG_ENABLE_LOG 1 -#endif - -#ifndef MG_ENABLE_CUSTOM_CALLOC -#define MG_ENABLE_CUSTOM_CALLOC 0 -#endif - -#ifndef MG_ENABLE_CUSTOM_LOG -#define MG_ENABLE_CUSTOM_LOG 0 // Let user define their own MG_LOG -#endif - -#ifndef MG_ENABLE_TCPIP -#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack -#endif - -#ifndef MG_ENABLE_LWIP -#define MG_ENABLE_LWIP 0 // lWIP network stack -#endif - -#ifndef MG_ENABLE_FREERTOS_TCP -#define MG_ENABLE_FREERTOS_TCP 0 // Amazon FreeRTOS-TCP network stack -#endif - -#ifndef MG_ENABLE_RL -#define MG_ENABLE_RL 0 // ARM MDK network stack -#endif - -#ifndef MG_ENABLE_SOCKET -#define MG_ENABLE_SOCKET !MG_ENABLE_TCPIP -#endif - -#ifndef MG_ENABLE_POLL -#define MG_ENABLE_POLL 0 -#endif - -#ifndef MG_ENABLE_EPOLL -#define MG_ENABLE_EPOLL 0 -#endif - -#ifndef MG_ENABLE_FATFS -#define MG_ENABLE_FATFS 0 -#endif - -#ifndef MG_ENABLE_SSI -#define MG_ENABLE_SSI 0 -#endif - -#ifndef MG_ENABLE_IPV6 -#define MG_ENABLE_IPV6 0 -#endif - -#ifndef MG_IPV6_V6ONLY -#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address -#endif - -#ifndef MG_ENABLE_MD5 -#define MG_ENABLE_MD5 1 -#endif - -// Set MG_ENABLE_WINSOCK=0 for Win32 builds with other external IP stack not -// mentioned in arch_win32.h -#ifndef MG_ENABLE_WINSOCK -#define MG_ENABLE_WINSOCK 1 -#endif - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 0 -#endif - -#ifndef MG_ENABLE_CUSTOM_RANDOM -#define MG_ENABLE_CUSTOM_RANDOM 0 -#endif - -#ifndef MG_ENABLE_CUSTOM_MILLIS -#define MG_ENABLE_CUSTOM_MILLIS 0 -#endif - -#ifndef MG_ENABLE_PACKED_FS -#define MG_ENABLE_PACKED_FS 0 -#endif - -#ifndef MG_ENABLE_ASSERT -#define MG_ENABLE_ASSERT 0 -#endif - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 512 // Granularity of the send/recv IO buffer growth -#endif - -#ifndef MG_MAX_RECV_SIZE -#define MG_MAX_RECV_SIZE (3UL * 1024UL * 1024UL) // Maximum recv IO buffer size -#endif - -#ifndef MG_DATA_SIZE -#define MG_DATA_SIZE 32 // struct mg_connection :: data size -#endif - -#ifndef MG_MAX_HTTP_HEADERS -#define MG_MAX_HTTP_HEADERS 30 -#endif - -#ifndef MG_HTTP_INDEX -#define MG_HTTP_INDEX "index.html" -#endif - -#ifndef MG_PATH_MAX -#ifdef PATH_MAX -#define MG_PATH_MAX PATH_MAX -#else -#define MG_PATH_MAX 128 -#endif -#endif - -#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE -#define MG_SOCK_LISTEN_BACKLOG_SIZE 128 -#endif - -#ifndef MG_DIRSEP -#define MG_DIRSEP '/' -#endif - -#ifndef MG_ENABLE_POSIX_FS -#define MG_ENABLE_POSIX_FS 0 -#endif - -#ifndef MG_INVALID_SOCKET -#define MG_INVALID_SOCKET (-1) -#endif - -#ifndef MG_SOCKET_TYPE -#define MG_SOCKET_TYPE int -#endif - -#ifndef MG_SOCKET_ERRNO -#define MG_SOCKET_ERRNO errno -#endif - -#if MG_ENABLE_EPOLL -#define MG_EPOLL_ADD(c) \ - do { \ - struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \ - epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_ADD, (int) (size_t) c->fd, &ev); \ - } while (0) -#define MG_EPOLL_MOD(c, wr) \ - do { \ - struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \ - if (wr) ev.events |= EPOLLOUT; \ - epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_MOD, (int) (size_t) c->fd, &ev); \ - } while (0) -#else -#define MG_EPOLL_ADD(c) -#define MG_EPOLL_MOD(c, wr) -#endif - -#ifndef MG_ENABLE_PROFILE -#define MG_ENABLE_PROFILE 0 -#endif - -#ifndef MG_ENABLE_TCPIP_DRIVER_INIT // mg_mgr_init() will also initialize -#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for -#endif // Mongoose built-in network stack - -#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223) -#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) -#endif - -#ifndef MG_TCPIP_MASK -#define MG_TCPIP_MASK MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) -#endif - -#ifndef MG_TCPIP_GW -#define MG_TCPIP_GW MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) -#endif - -#ifndef MG_SET_MAC_ADDRESS -#define MG_SET_MAC_ADDRESS(mac) -#endif - -#ifndef MG_SET_WIFI_CONFIG -#define MG_SET_WIFI_CONFIG(data) -#endif - -#ifndef MG_ENABLE_TCPIP_PRINT_DEBUG_STATS -#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0 -#endif - -#ifndef MG_ENABLE_CHACHA20 -#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN -#endif - - - -// Macros to record timestamped events that happens with a connection. -// They are saved into a c->prof IO buffer, each event is a name and a 32-bit -// timestamp in milliseconds since connection init time. -// -// Test (run in two separate terminals): -// make -C tutorials/http/http-server/ CFLAGS_EXTRA=-DMG_ENABLE_PROFILE=1 -// curl localhost:8000 -// Output: -// 1ea1f1e7 2 net.c:150:mg_close_conn 3 profile: -// 1ea1f1e8 2 net.c:150:mg_close_conn 1ea1f1e6 init -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_OPEN -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_ACCEPT -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_READ -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_HTTP_MSG -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_WRITE -// 1ea1f1e8 2 net.c:150:mg_close_conn 1 EV_CLOSE -// -// Usage: -// Enable profiling by setting MG_ENABLE_PROFILE=1 -// Invoke MG_PROF_ADD(c, "MY_EVENT_1") in the places you'd like to measure - -#if MG_ENABLE_PROFILE -struct mg_profitem { - const char *name; // Event name - uint32_t timestamp; // Milliseconds since connection creation (MG_EV_OPEN) -}; - -#define MG_PROFILE_ALLOC_GRANULARITY 256 // Can save 32 items wih to realloc - -// Adding a profile item to the c->prof. Must be as fast as possible. -// Reallocation of the c->prof iobuf is not desirable here, that's why we -// pre-allocate c->prof with MG_PROFILE_ALLOC_GRANULARITY. -// This macro just inits and copies 8 bytes, and calls mg_millis(), -// which should be fast enough. -#define MG_PROF_ADD(c, name_) \ - do { \ - struct mg_iobuf *io = &c->prof; \ - uint32_t inittime = ((struct mg_profitem *) io->buf)->timestamp; \ - struct mg_profitem item = {name_, (uint32_t) mg_millis() - inittime}; \ - mg_iobuf_add(io, io->len, &item, sizeof(item)); \ - } while (0) - -// Initialising profile for a new connection. Not time sensitive -#define MG_PROF_INIT(c) \ - do { \ - struct mg_profitem first = {"init", (uint32_t) mg_millis()}; \ - mg_iobuf_init(&(c)->prof, 0, MG_PROFILE_ALLOC_GRANULARITY); \ - mg_iobuf_add(&c->prof, c->prof.len, &first, sizeof(first)); \ - } while (0) - -#define MG_PROF_FREE(c) mg_iobuf_free(&(c)->prof) - -// Dumping the profile. Not time sensitive -#define MG_PROF_DUMP(c) \ - do { \ - struct mg_iobuf *io = &c->prof; \ - struct mg_profitem *p = (struct mg_profitem *) io->buf; \ - struct mg_profitem *e = &p[io->len / sizeof(*p)]; \ - MG_INFO(("%lu profile:", c->id)); \ - while (p < e) { \ - MG_INFO(("%5lx %s", (unsigned long) p->timestamp, p->name)); \ - p++; \ - } \ - } while (0) - -#else -#define MG_PROF_INIT(c) -#define MG_PROF_FREE(c) -#define MG_PROF_ADD(c, name) -#define MG_PROF_DUMP(c) -#endif - - - - -// Describes an arbitrary chunk of memory -struct mg_str { - char *buf; // String data - size_t len; // String length -}; - -// Using macro to avoid shadowing C++ struct constructor, see #1298 -#define mg_str(s) mg_str_s(s) - -struct mg_str mg_str(const char *s); -struct mg_str mg_str_n(const char *s, size_t n); -int mg_casecmp(const char *s1, const char *s2); -int mg_strcmp(const struct mg_str str1, const struct mg_str str2); -int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2); -struct mg_str mg_strdup(const struct mg_str s); -bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps); -bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char delim); - -bool mg_str_to_num(struct mg_str, int base, void *val, size_t val_len); - - - - -// Single producer, single consumer non-blocking queue - -struct mg_queue { - char *buf; - size_t size; - volatile size_t tail; - volatile size_t head; -}; - -void mg_queue_init(struct mg_queue *, char *, size_t); // Init queue -size_t mg_queue_book(struct mg_queue *, char **buf, size_t); // Reserve space -void mg_queue_add(struct mg_queue *, size_t); // Add new message -size_t mg_queue_next(struct mg_queue *, char **); // Get oldest message -void mg_queue_del(struct mg_queue *, size_t); // Delete oldest message - - - - -typedef void (*mg_pfn_t)(char, void *); // Output function -typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer - -size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *); -size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...); - - - - - - -// Convenience wrappers around mg_xprintf -size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap); -size_t mg_snprintf(char *, size_t, const char *fmt, ...); -char *mg_vmprintf(const char *fmt, va_list *ap); -char *mg_mprintf(const char *fmt, ...); -size_t mg_queue_vprintf(struct mg_queue *, const char *fmt, va_list *); -size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...); - -// %M print helper functions -size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap); - -// Various output functions -void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * -void mg_pfn_stdout(char c, void *param); // param: ignored - -// A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi")) -#define MG_ESC(str) mg_print_esc, 0, (str) - - - - - - -enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE }; -extern int mg_log_level; // Current log level, one of MG_LL_* - -void mg_log(const char *fmt, ...); -void mg_log_prefix(int ll, const char *file, int line, const char *fname); -// bool mg_log2(int ll, const char *file, int line, const char *fmt, ...); -void mg_hexdump(const void *buf, size_t len); -void mg_log_set_fn(mg_pfn_t fn, void *param); - -#define mg_log_set(level_) mg_log_level = (level_) - -#if MG_ENABLE_LOG -#define MG_LOG(level, args) \ - do { \ - if ((level) <= mg_log_level) { \ - mg_log_prefix((level), __FILE__, __LINE__, __func__); \ - mg_log args; \ - } \ - } while (0) -#else -#define MG_LOG(level, args) \ - do { \ - if (0) mg_log args; \ - } while (0) -#endif - -#define MG_ERROR(args) MG_LOG(MG_LL_ERROR, args) -#define MG_INFO(args) MG_LOG(MG_LL_INFO, args) -#define MG_DEBUG(args) MG_LOG(MG_LL_DEBUG, args) -#define MG_VERBOSE(args) MG_LOG(MG_LL_VERBOSE, args) - - - - -struct mg_timer { - uint64_t period_ms; // Timer period in milliseconds - uint64_t expire; // Expiration timestamp in milliseconds - unsigned flags; // Possible flags values below -#define MG_TIMER_ONCE 0 // Call function once -#define MG_TIMER_REPEAT 1 // Call function periodically -#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set -#define MG_TIMER_CALLED 4 // Timer function was called at least once -#define MG_TIMER_AUTODELETE 8 // mg_free() timer when done - void (*fn)(void *); // Function to call - void *arg; // Function argument - struct mg_timer *next; // Linkage -}; - -void mg_timer_init(struct mg_timer **head, struct mg_timer *timer, - uint64_t milliseconds, unsigned flags, void (*fn)(void *), - void *arg); -void mg_timer_free(struct mg_timer **head, struct mg_timer *); -void mg_timer_poll(struct mg_timer **head, uint64_t new_ms); -bool mg_timer_expired(uint64_t *expiration, uint64_t period, uint64_t now); - - - - - -enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 }; - -// Filesystem API functions -// st() returns MG_FS_* flags and populates file size and modification time -// ls() calls fn() for every directory entry, allowing to list a directory -// -// NOTE: UNIX-style shorthand names for the API functions are deliberately -// chosen to avoid conflicts with some libraries that make macros for e.g. -// stat(), write(), read() calls. -struct mg_fs { - int (*st)(const char *path, size_t *size, time_t *mtime); // stat file - void (*ls)(const char *path, void (*fn)(const char *, void *), - void *); // List directory entries: call fn(file_name, fn_data) - // for each directory entry - void *(*op)(const char *path, int flags); // Open file - void (*cl)(void *fd); // Close file - size_t (*rd)(void *fd, void *buf, size_t len); // Read file - size_t (*wr)(void *fd, const void *buf, size_t len); // Write file - size_t (*sk)(void *fd, size_t offset); // Set file position - bool (*mv)(const char *from, const char *to); // Rename file - bool (*rm)(const char *path); // Delete file - bool (*mkd)(const char *path); // Create directory -}; - -extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek -extern struct mg_fs mg_fs_packed; // see tutorials/core/embedded-filesystem -extern struct mg_fs mg_fs_fat; // FAT FS - -// File descriptor -struct mg_fd { - void *fd; - struct mg_fs *fs; -}; - -struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags); -void mg_fs_close(struct mg_fd *fd); -bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len); -struct mg_str mg_file_read(struct mg_fs *fs, const char *path); -bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t); -bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...); - -// Packed API -const char *mg_unpack(const char *path, size_t *size, time_t *mtime); -const char *mg_unlist(size_t no); // Get no'th packed filename -struct mg_str mg_unpacked(const char *path); // Packed file as mg_str - - - - - - - -#if MG_ENABLE_ASSERT -#include -#elif !defined(assert) -#define assert(x) -#endif - -void *mg_calloc(size_t count, size_t size); -void mg_free(void *ptr); -void mg_bzero(volatile unsigned char *buf, size_t len); -bool mg_random(void *buf, size_t len); -char *mg_random_str(char *buf, size_t len); -uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); -uint64_t mg_millis(void); // Return milliseconds since boot -bool mg_path_is_sane(const struct mg_str path); -void mg_delayms(unsigned int ms); - -#define MG_U32(a, b, c, d) \ - (((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \ - ((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255)) - -#define MG_IPV4(a, b, c, d) mg_htonl(MG_U32(a, b, c, d)) - -// For printing IPv4 addresses: printf("%d.%d.%d.%d\n", MG_IPADDR_PARTS(&ip)) -#define MG_U8P(ADDR) ((uint8_t *) (ADDR)) -#define MG_IPADDR_PARTS(ADDR) \ - MG_U8P(ADDR)[0], MG_U8P(ADDR)[1], MG_U8P(ADDR)[2], MG_U8P(ADDR)[3] - -#define MG_LOAD_BE16(p) \ - ((uint16_t) (((uint16_t) MG_U8P(p)[0] << 8U) | MG_U8P(p)[1])) -#define MG_LOAD_BE24(p) \ - ((uint32_t) (((uint32_t) MG_U8P(p)[0] << 16U) | \ - ((uint32_t) MG_U8P(p)[1] << 8U) | MG_U8P(p)[2])) -#define MG_LOAD_BE32(p) \ - ((uint32_t) (((uint32_t) MG_U8P(p)[0] << 24U) | \ - ((uint32_t) MG_U8P(p)[1] << 16U) | \ - ((uint32_t) MG_U8P(p)[2] << 8U) | MG_U8P(p)[3])) -#define MG_STORE_BE16(p, n) \ - do { \ - MG_U8P(p)[0] = ((n) >> 8U) & 255; \ - MG_U8P(p)[1] = (n) &255; \ - } while (0) -#define MG_STORE_BE24(p, n) \ - do { \ - MG_U8P(p)[0] = ((n) >> 16U) & 255; \ - MG_U8P(p)[1] = ((n) >> 8U) & 255; \ - MG_U8P(p)[2] = (n) &255; \ - } while (0) -#define MG_STORE_BE32(p, n) \ - do { \ - MG_U8P(p)[0] = ((n) >> 24U) & 255; \ - MG_U8P(p)[1] = ((n) >> 16U) & 255; \ - MG_U8P(p)[2] = ((n) >> 8U) & 255; \ - MG_U8P(p)[3] = (n) &255; \ - } while (0) - -uint16_t mg_ntohs(uint16_t net); -uint32_t mg_ntohl(uint32_t net); -#define mg_htons(x) mg_ntohs(x) -#define mg_htonl(x) mg_ntohl(x) - -#define MG_REG(x) ((volatile uint32_t *) (x))[0] -#define MG_BIT(x) (((uint32_t) 1U) << (x)) -#define MG_SET_BITS(R, CLRMASK, SETMASK) (R) = ((R) & ~(CLRMASK)) | (SETMASK) - -#define MG_ROUND_UP(x, a) ((a) == 0 ? (x) : ((((x) + (a) -1) / (a)) * (a))) -#define MG_ROUND_DOWN(x, a) ((a) == 0 ? (x) : (((x) / (a)) * (a))) - -#if defined(__GNUC__) && defined(__arm__) -#ifdef __ZEPHYR__ -#define MG_ARM_DISABLE_IRQ() __asm__ __volatile__("cpsid i" : : : "memory") -#define MG_ARM_ENABLE_IRQ() __asm__ __volatile__("cpsie i" : : : "memory") -#else -#define MG_ARM_DISABLE_IRQ() asm volatile("cpsid i" : : : "memory") -#define MG_ARM_ENABLE_IRQ() asm volatile("cpsie i" : : : "memory") -#endif // !ZEPHYR -#elif defined(__CCRH__) -#define MG_RH850_DISABLE_IRQ() __DI() -#define MG_RH850_ENABLE_IRQ() __EI() -#else -#define MG_ARM_DISABLE_IRQ() -#define MG_ARM_ENABLE_IRQ() -#endif - -#if defined(__CC_ARM) -#define MG_DSB() __dsb(0xf) -#elif defined(__ARMCC_VERSION) -#define MG_DSB() __builtin_arm_dsb(0xf) -#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) -#ifdef __ZEPHYR__ -#define MG_DSB() __asm__("DSB 0xf") -#else -#define MG_DSB() asm("DSB 0xf") -#endif // !ZEPHYR -#elif defined(__ICCARM__) -#define MG_DSB() __iar_builtin_DSB() -#else -#define MG_DSB() -#endif - -struct mg_addr; -int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip); - -// Linked list management macros -#define LIST_ADD_HEAD(type_, head_, elem_) \ - do { \ - (elem_)->next = (*head_); \ - *(head_) = (elem_); \ - } while (0) - -#define LIST_ADD_TAIL(type_, head_, elem_) \ - do { \ - type_ **h = head_; \ - while (*h != NULL) h = &(*h)->next; \ - *h = (elem_); \ - } while (0) - -#define LIST_DELETE(type_, head_, elem_) \ - do { \ - type_ **h = head_; \ - while (*h != (elem_)) h = &(*h)->next; \ - *h = (elem_)->next; \ - } while (0) - - - -unsigned short mg_url_port(const char *url); -int mg_url_is_ssl(const char *url); -struct mg_str mg_url_host(const char *url); -struct mg_str mg_url_user(const char *url); -struct mg_str mg_url_pass(const char *url); -const char *mg_url_uri(const char *url); - - - - -struct mg_iobuf { - unsigned char *buf; // Pointer to stored data - size_t size; // Total size available - size_t len; // Current number of bytes - size_t align; // Alignment during allocation -}; - -int mg_iobuf_init(struct mg_iobuf *, size_t, size_t); -int mg_iobuf_resize(struct mg_iobuf *, size_t); -void mg_iobuf_free(struct mg_iobuf *); -size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t); -size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len); - - -size_t mg_base64_update(unsigned char input_byte, char *buf, size_t len); -size_t mg_base64_final(char *buf, size_t len); -size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t); -size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t); - - - - -typedef struct { - uint32_t buf[4]; - uint32_t bits[2]; - unsigned char in[64]; -} mg_md5_ctx; - -void mg_md5_init(mg_md5_ctx *c); -void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len); -void mg_md5_final(mg_md5_ctx *c, unsigned char[16]); - - - - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} mg_sha1_ctx; - -void mg_sha1_init(mg_sha1_ctx *); -void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len); -void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *); -// https://github.com/B-Con/crypto-algorithms -// Author: Brad Conte (brad AT bradconte.com) -// Disclaimer: This code is presented "as is" without any guarantees. -// Details: Defines the API for the corresponding SHA1 implementation. -// Copyright: public domain - - - - - -typedef struct { - uint32_t state[8]; - uint64_t bits; - uint32_t len; - unsigned char buffer[64]; -} mg_sha256_ctx; - - -void mg_sha256_init(mg_sha256_ctx *); -void mg_sha256_update(mg_sha256_ctx *, const unsigned char *data, size_t len); -void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *); -void mg_sha256(uint8_t dst[32], uint8_t *data, size_t datasz); -void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, - size_t datasz); - -typedef struct { - uint64_t state[8]; - uint8_t buffer[128]; - uint64_t bitlen[2]; - uint32_t datalen; -} mg_sha384_ctx; -void mg_sha384_init(mg_sha384_ctx *ctx); -void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len); -void mg_sha384_final(uint8_t digest[48], mg_sha384_ctx *ctx); -void mg_sha384(uint8_t dst[48], uint8_t *data, size_t datasz); - - - -struct mg_connection; -typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, - void *ev_data); -void mg_call(struct mg_connection *c, int ev, void *ev_data); -void mg_error(struct mg_connection *c, const char *fmt, ...); - -enum { - MG_EV_ERROR, // Error char *error_message - MG_EV_OPEN, // Connection created NULL - MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis - MG_EV_RESOLVE, // Host name is resolved NULL - MG_EV_CONNECT, // Connection established NULL - MG_EV_ACCEPT, // Connection accepted NULL - MG_EV_TLS_HS, // TLS handshake succeeded NULL - MG_EV_READ, // Data received from socket long *bytes_read - MG_EV_WRITE, // Data written to socket long *bytes_written - MG_EV_CLOSE, // Connection closed NULL - MG_EV_HTTP_HDRS, // HTTP headers struct mg_http_message * - MG_EV_HTTP_MSG, // Full HTTP request/response struct mg_http_message * - MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * - MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * - MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * - MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * - MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * - MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code - MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis - MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data - MG_EV_USER // Starting ID for user events -}; - - - - - - - - - - -struct mg_dns { - const char *url; // DNS server URL - struct mg_connection *c; // DNS server connection -}; - -struct mg_addr { - union { // Holds IPv4 or IPv6 address, in network byte order - uint8_t ip[16]; - uint32_t ip4; - uint64_t ip6[2]; - }; - uint16_t port; // TCP or UDP port in network byte order - uint8_t scope_id; // IPv6 scope ID - bool is_ip6; // True when address is IPv6 address -}; - -struct mg_mgr { - struct mg_connection *conns; // List of active connections - struct mg_dns dns4; // DNS for IPv4 - struct mg_dns dns6; // DNS for IPv6 - int dnstimeout; // DNS resolve timeout in milliseconds - bool use_dns6; // Use DNS6 server by default, see #1532 - unsigned long nextid; // Next connection ID - void *userdata; // Arbitrary user data pointer - void *tls_ctx; // TLS context shared by all TLS sessions - uint16_t mqtt_id; // MQTT IDs for pub/sub - void *active_dns_requests; // DNS requests in progress - struct mg_timer *timers; // Active timers - int epoll_fd; // Used when MG_EPOLL_ENABLE=1 - struct mg_tcpip_if *ifp; // Builtin TCP/IP stack only. Interface pointer - size_t extraconnsize; // Builtin TCP/IP stack only. Extra space - MG_SOCKET_TYPE pipe; // Socketpair end for mg_wakeup() -#if MG_ENABLE_FREERTOS_TCP - SocketSet_t ss; // NOTE(lsm): referenced from socket struct -#endif -}; - -struct mg_connection { - struct mg_connection *next; // Linkage in struct mg_mgr :: connections - struct mg_mgr *mgr; // Our container - struct mg_addr loc; // Local address - struct mg_addr rem; // Remote address - void *fd; // Connected socket, or LWIP data - unsigned long id; // Auto-incrementing unique connection ID - struct mg_iobuf recv; // Incoming data - struct mg_iobuf send; // Outgoing data - struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE - struct mg_iobuf rtls; // TLS only. Incoming encrypted data - mg_event_handler_t fn; // User-specified event handler function - void *fn_data; // User-specified function parameter - mg_event_handler_t pfn; // Protocol-specific handler function - void *pfn_data; // Protocol-specific function parameter - char data[MG_DATA_SIZE]; // Arbitrary connection data - void *tls; // TLS specific data - unsigned is_listening : 1; // Listening connection - unsigned is_client : 1; // Outbound (client) connection - unsigned is_accepted : 1; // Accepted (server) connection - unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress - unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress - unsigned is_connecting : 1; // Non-blocking connect is in progress - unsigned is_tls : 1; // TLS-enabled connection - unsigned is_tls_hs : 1; // TLS handshake is in progress - unsigned is_udp : 1; // UDP connection - unsigned is_websocket : 1; // WebSocket connection - unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator - unsigned is_hexdumping : 1; // Hexdump in/out traffic - unsigned is_draining : 1; // Send remaining data, then close and free - unsigned is_closing : 1; // Close and free the connection immediately - unsigned is_full : 1; // Stop reads, until cleared - unsigned is_tls_throttled : 1; // Last TLS write: MG_SOCK_PENDING() was true - unsigned is_resp : 1; // Response is still being generated - unsigned is_readable : 1; // Connection is ready to read - unsigned is_writable : 1; // Connection is ready to write -}; - -void mg_mgr_poll(struct mg_mgr *, int ms); -void mg_mgr_init(struct mg_mgr *); -void mg_mgr_free(struct mg_mgr *); - -struct mg_connection *mg_listen(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_connect(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, - mg_event_handler_t fn, void *fn_data); -void mg_connect_resolved(struct mg_connection *); -bool mg_send(struct mg_connection *, const void *, size_t); -size_t mg_printf(struct mg_connection *, const char *fmt, ...); -size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap); -bool mg_aton(struct mg_str str, struct mg_addr *addr); - -// These functions are used to integrate with custom network stacks -struct mg_connection *mg_alloc_conn(struct mg_mgr *); -void mg_close_conn(struct mg_connection *c); -bool mg_open_listener(struct mg_connection *c, const char *url); - -// Utility functions -bool mg_wakeup(struct mg_mgr *, unsigned long id, const void *buf, size_t len); -bool mg_wakeup_init(struct mg_mgr *); -struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, - unsigned flags, void (*fn)(void *), void *arg); -struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data, - mg_event_handler_t pfn, void *pfn_data); - - - - - - - - -struct mg_http_header { - struct mg_str name; // Header name - struct mg_str value; // Header value -}; - -struct mg_http_message { - struct mg_str method, uri, query, proto; // Request/response line - struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers - struct mg_str body; // Body - struct mg_str head; // Request + headers - struct mg_str message; // Request + headers + body -}; - -// Parameter for mg_http_serve_dir() -struct mg_http_serve_opts { - const char *root_dir; // Web root directory, must be non-NULL - const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml - const char *extra_headers; // Extra HTTP headers to add in responses - const char *mime_types; // Extra mime types, ext1=type1,ext2=type2,.. - const char *page404; // Path to the 404 page, or NULL by default - struct mg_fs *fs; // Filesystem implementation. Use NULL for POSIX -}; - -// Parameter for mg_http_next_multipart -struct mg_http_part { - struct mg_str name; // Form field name - struct mg_str filename; // Filename for file uploads - struct mg_str body; // Part contents -}; - -int mg_http_parse(const char *s, size_t len, struct mg_http_message *); -int mg_http_get_request_len(const unsigned char *buf, size_t buf_len); -void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...); -void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len); -void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm); -struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm, - const struct mg_http_serve_opts *); -void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm, - const char *path, const struct mg_http_serve_opts *); -void mg_http_reply(struct mg_connection *, int status_code, const char *headers, - const char *body_fmt, ...); -struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name); -struct mg_str mg_http_var(struct mg_str buf, struct mg_str name); -int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t); -int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form); -size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len); -void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t); -long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size); -void mg_http_bauth(struct mg_connection *, const char *user, const char *pass); -struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v); -size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *); -int mg_http_status(const struct mg_http_message *hm); - - -void mg_http_serve_ssi(struct mg_connection *c, const char *root, - const char *fullpath); - - -#define MG_TLS_NONE 0 // No TLS support -#define MG_TLS_MBED 1 // mbedTLS -#define MG_TLS_OPENSSL 2 // OpenSSL -#define MG_TLS_WOLFSSL 5 // WolfSSL (based on OpenSSL) -#define MG_TLS_BUILTIN 3 // Built-in -#define MG_TLS_CUSTOM 4 // Custom implementation - -#ifndef MG_TLS -#define MG_TLS MG_TLS_NONE -#endif - - - - - -struct mg_tls_opts { - struct mg_str ca; // PEM or DER - struct mg_str cert; // PEM or DER - struct mg_str key; // PEM or DER - struct mg_str name; // If not empty, enable host name verification - int skip_verification; // Skip certificate and host name verification -}; - -void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *opts); -void mg_tls_free(struct mg_connection *); -long mg_tls_send(struct mg_connection *, const void *buf, size_t len); -long mg_tls_recv(struct mg_connection *, void *buf, size_t len); -size_t mg_tls_pending(struct mg_connection *); -void mg_tls_flush(struct mg_connection *); -void mg_tls_handshake(struct mg_connection *); - -// Private -void mg_tls_ctx_init(struct mg_mgr *); -void mg_tls_ctx_free(struct mg_mgr *); - -// Low-level IO primives used by TLS layer -enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 }; -long mg_io_send(struct mg_connection *c, const void *buf, size_t len); -long mg_io_recv(struct mg_connection *c, void *buf, size_t len); -#ifndef TLS_X15519_H -#define TLS_X15519_H - - - -#define X25519_BYTES 32 -extern const uint8_t X25519_BASE_POINT[X25519_BYTES]; - -int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp); - - -#endif /* TLS_X15519_H */ -/****************************************************************************** - * - * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL - * - * This is a simple and straightforward implementation of AES-GCM authenticated - * encryption. The focus of this work was correctness & accuracy. It is written - * in straight 'C' without any particular focus upon optimization or speed. It - * should be endian (memory byte order) neutral since the few places that care - * are handled explicitly. - * - * This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. - * - * It is intended for general purpose use, but was written in support of GRC's - * reference implementation of the SQRL (Secure Quick Reliable Login) client. - * - * See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf - * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \ - * gcm/gcm-revised-spec.pdf - * - * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE - * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. - * - *******************************************************************************/ -#ifndef TLS_AES128_H -#define TLS_AES128_H - -/****************************************************************************** - * AES_CONTEXT : cipher context / holds inter-call data - ******************************************************************************/ -typedef struct { - int mode; // 1 for Encryption, 0 for Decryption - int rounds; // keysize-based rounds count - uint32_t *rk; // pointer to current round key - uint32_t buf[68]; // key expansion buffer -} aes_context; - - -#define GCM_AUTH_FAILURE 0x55555555 // authentication failure - -/****************************************************************************** - * GCM_CONTEXT : MUST be called once before ANY use of this library - ******************************************************************************/ -int mg_gcm_initialize(void); - -// -// aes-gcm.h -// MKo -// -// Created by Markus Kosmal on 20/11/14. -// -// -int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input, - size_t input_length, const unsigned char *key, - const size_t key_len, const unsigned char *iv, - const size_t iv_len, unsigned char *aead, - size_t aead_len, unsigned char *tag, - const size_t tag_len); - -int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, - size_t input_length, const unsigned char *key, - const size_t key_len, const unsigned char *iv, - const size_t iv_len); - -#endif /* TLS_AES128_H */ - -// End of aes128 PD - - - -#define MG_UECC_SUPPORTS_secp256r1 1 -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_H_ -#define _UECC_H_ - -/* Platform selection options. -If MG_UECC_PLATFORM is not defined, the code will try to guess it based on -compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */ -#define mg_uecc_arch_other 0 -#define mg_uecc_x86 1 -#define mg_uecc_x86_64 2 -#define mg_uecc_arm 3 -#define mg_uecc_arm_thumb 4 -#define mg_uecc_arm_thumb2 5 -#define mg_uecc_arm64 6 -#define mg_uecc_avr 7 - -/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform -(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will -be automatically set based on your platform. */ - -/* Optimization level; trade speed for code size. - Larger values produce code that is faster but larger. - Currently supported values are 0 - 4; 0 is unusably slow for most - applications. Optimization level 4 currently only has an effect ARM platforms - where more than one curve is enabled. */ -#ifndef MG_UECC_OPTIMIZATION_LEVEL -#define MG_UECC_OPTIMIZATION_LEVEL 2 -#endif - -/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a -specific function to be used for (scalar) squaring instead of the generic -multiplication function. This can make things faster somewhat faster, but -increases the code size. */ -#ifndef MG_UECC_SQUARE_FUNC -#define MG_UECC_SQUARE_FUNC 0 -#endif - -/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will -switch to native little-endian format for *all* arrays passed in and out of the -public API. This includes public and private keys, shared secrets, signatures -and message hashes. Using this switch reduces the amount of call stack memory -used by uECC, since less intermediate translations are required. Note that this -will *only* work on native little-endian processors and it will treat the -uint8_t arrays passed into the public API as word arrays, therefore requiring -the provided byte arrays to be word aligned on architectures that do not support -unaligned accesses. IMPORTANT: Keys and signatures generated with -MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures -generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same -endianness. */ -#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN -#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0 -#endif - -/* Curve support selection. Set to 0 to remove that curve. */ -#ifndef MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_SUPPORTS_secp160r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp192r1 -#define MG_UECC_SUPPORTS_secp192r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp224r1 -#define MG_UECC_SUPPORTS_secp224r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp256r1 -#define MG_UECC_SUPPORTS_secp256r1 1 -#endif -#ifndef MG_UECC_SUPPORTS_secp256k1 -#define MG_UECC_SUPPORTS_secp256k1 0 -#endif - -/* Specifies whether compressed point format is supported. - Set to 0 to disable point compression/decompression functions. */ -#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT -#define MG_UECC_SUPPORT_COMPRESSED_POINT 1 -#endif - -struct MG_UECC_Curve_t; -typedef const struct MG_UECC_Curve_t *MG_UECC_Curve; - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_SUPPORTS_secp160r1 -MG_UECC_Curve mg_uecc_secp160r1(void); -#endif -#if MG_UECC_SUPPORTS_secp192r1 -MG_UECC_Curve mg_uecc_secp192r1(void); -#endif -#if MG_UECC_SUPPORTS_secp224r1 -MG_UECC_Curve mg_uecc_secp224r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256r1 -MG_UECC_Curve mg_uecc_secp256r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256k1 -MG_UECC_Curve mg_uecc_secp256k1(void); -#endif - -/* MG_UECC_RNG_Function type -The RNG function should fill 'size' random bytes into 'dest'. It should return 1 -if 'dest' was filled with random data, or 0 if the random data could not be -generated. The filled-in values should be either truly random, or from a -cryptographically-secure PRNG. - -A correctly functioning RNG function must be set (using mg_uecc_set_rng()) -before calling mg_uecc_make_key() or mg_uecc_sign(). - -Setting a correctly functioning RNG function improves the resistance to -side-channel attacks for mg_uecc_shared_secret() and -mg_uecc_sign_deterministic(). - -A correct RNG function is set by default when building for Windows, Linux, or OS -X. If you are building on another POSIX-compliant system that supports -/dev/random or /dev/urandom, you can define MG_UECC_POSIX to use the predefined -RNG. For embedded platforms there is no predefined RNG function; you must -provide your own. -*/ -typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size); - -/* mg_uecc_set_rng() function. -Set the function that will be used to generate random bytes. The RNG function -should return 1 if the random data was generated, or 0 if the random data could -not be generated. - -On platforms where there is no predefined RNG function (eg embedded platforms), -this must be called before mg_uecc_make_key() or mg_uecc_sign() are used. - -Inputs: - rng_function - The function that will be used to generate random bytes. -*/ -void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function); - -/* mg_uecc_get_rng() function. - -Returns the function that will be used to generate random bytes. -*/ -MG_UECC_RNG_Function mg_uecc_get_rng(void); - -/* mg_uecc_curve_private_key_size() function. - -Returns the size of a private key for the curve in bytes. -*/ -int mg_uecc_curve_private_key_size(MG_UECC_Curve curve); - -/* mg_uecc_curve_public_key_size() function. - -Returns the size of a public key for the curve in bytes. -*/ -int mg_uecc_curve_public_key_size(MG_UECC_Curve curve); - -/* mg_uecc_make_key() function. -Create a public/private key pair. - -Outputs: - public_key - Will be filled in with the public key. Must be at least 2 * -the curve size (in bytes) long. For example, if the curve is secp256r1, -public_key must be 64 bytes long. private_key - Will be filled in with the -private key. Must be as long as the curve order; this is typically the same as -the curve size, except for secp160r1. For example, if the curve is secp256r1, -private_key must be 32 bytes long. - - For secp160r1, private_key must be 21 bytes long! Note that -the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it -being non-zero). - -Returns 1 if the key pair was generated successfully, 0 if an error occurred. -*/ -int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key, - MG_UECC_Curve curve); - -/* mg_uecc_shared_secret() function. -Compute a shared secret given your secret key and someone else's public key. If -the public key is not from a trusted source and has not been previously -verified, you should verify it first using mg_uecc_valid_public_key(). Note: It -is recommended that you hash the result of mg_uecc_shared_secret() before using -it for symmetric encryption or HMAC. - -Inputs: - public_key - The public key of the remote party. - private_key - Your private key. - -Outputs: - secret - Will be filled in with the shared secret value. Must be the same -size as the curve size; for example, if the curve is secp256r1, secret must be -32 bytes long. - -Returns 1 if the shared secret was generated successfully, 0 if an error -occurred. -*/ -int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key, - uint8_t *secret, MG_UECC_Curve curve); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* mg_uecc_compress() function. -Compress a public key. - -Inputs: - public_key - The public key to compress. - -Outputs: - compressed - Will be filled in with the compressed public key. Must be at -least (curve size + 1) bytes long; for example, if the curve is secp256r1, - compressed must be 33 bytes long. -*/ -void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed, - MG_UECC_Curve curve); - -/* mg_uecc_decompress() function. -Decompress a compressed public key. - -Inputs: - compressed - The compressed public key. - -Outputs: - public_key - Will be filled in with the decompressed public key. -*/ -void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key, - MG_UECC_Curve curve); -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -/* mg_uecc_valid_public_key() function. -Check to see if a public key is valid. - -Note that you are not required to check for a valid public key before using any -other uECC functions. However, you may wish to avoid spending CPU time computing -a shared secret or verifying a signature using an invalid public key. - -Inputs: - public_key - The public key to check. - -Returns 1 if the public key is valid, 0 if it is invalid. -*/ -int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve); - -/* mg_uecc_compute_public_key() function. -Compute the corresponding public key for a private key. - -Inputs: - private_key - The private key to compute the public key for - -Outputs: - public_key - Will be filled in with the corresponding public key - -Returns 1 if the key was computed successfully, 0 if an error occurred. -*/ -int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key, - MG_UECC_Curve curve); - -/* mg_uecc_sign() function. -Generate an ECDSA signature for a given hash value. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it in to this function along with your private key. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - -Outputs: - signature - Will be filled in with the signature value. Must be at least 2 * -curve size long. For example, if the curve is secp256r1, signature must be 64 -bytes long. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve); - -/* MG_UECC_HashContext structure. -This is used to pass in an arbitrary hash function to -mg_uecc_sign_deterministic(). The structure will be used for multiple hash -computations; each time a new hash is computed, init_hash() will be called, -followed by one or more calls to update_hash(), and finally a call to -finish_hash() to produce the resulting hash. - -The intention is that you will create a structure that includes -MG_UECC_HashContext followed by any hash-specific data. For example: - -typedef struct SHA256_HashContext { - MG_UECC_HashContext uECC; - SHA256_CTX ctx; -} SHA256_HashContext; - -void init_SHA256(MG_UECC_HashContext *base) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Init(&context->ctx); -} - -void update_SHA256(MG_UECC_HashContext *base, - const uint8_t *message, - unsigned message_size) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Update(&context->ctx, message, message_size); -} - -void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Final(hash_result, &context->ctx); -} - -... when signing ... -{ - uint8_t tmp[32 + 32 + 64]; - SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, -32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature); -} -*/ -typedef struct MG_UECC_HashContext { - void (*init_hash)(const struct MG_UECC_HashContext *context); - void (*update_hash)(const struct MG_UECC_HashContext *context, - const uint8_t *message, unsigned message_size); - void (*finish_hash)(const struct MG_UECC_HashContext *context, - uint8_t *hash_result); - unsigned - block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ - unsigned - result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ - uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + - block_size) bytes. */ -} MG_UECC_HashContext; - -/* mg_uecc_sign_deterministic() function. -Generate an ECDSA signature for a given hash value, using a deterministic -algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng() -before calling this function; however, if the RNG is defined it will improve -resistance to side-channel attacks. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it to this function along with your private key and a hash context. Note -that the message_hash does not need to be computed with the same hash function -used by hash_context. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - hash_context - A hash context to use. - -Outputs: - signature - Will be filled in with the signature value. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, unsigned hash_size, - const MG_UECC_HashContext *hash_context, - uint8_t *signature, MG_UECC_Curve curve); - -/* mg_uecc_verify() function. -Verify an ECDSA signature. - -Usage: Compute the hash of the signed data using the same hash as the signer and -pass it to this function along with the signer's public key and the signature -values (r and s). - -Inputs: - public_key - The signer's public key. - message_hash - The hash of the signed data. - hash_size - The size of message_hash in bytes. - signature - The signature value. - -Returns 1 if the signature is valid, 0 if it is invalid. -*/ -int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *signature, - MG_UECC_Curve curve); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_TYPES_H_ -#define _UECC_TYPES_H_ - -#ifndef MG_UECC_PLATFORM -#if defined(__AVR__) && __AVR__ -#define MG_UECC_PLATFORM mg_uecc_avr -#elif defined(__thumb2__) || \ - defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ -#define MG_UECC_PLATFORM mg_uecc_arm_thumb2 -#elif defined(__thumb__) -#define MG_UECC_PLATFORM mg_uecc_arm_thumb -#elif defined(__arm__) || defined(_M_ARM) -#define MG_UECC_PLATFORM mg_uecc_arm -#elif defined(__aarch64__) -#define MG_UECC_PLATFORM mg_uecc_arm64 -#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \ - defined(__I86__) -#define MG_UECC_PLATFORM mg_uecc_x86 -#elif defined(__amd64__) || defined(_M_X64) -#define MG_UECC_PLATFORM mg_uecc_x86_64 -#else -#define MG_UECC_PLATFORM mg_uecc_arch_other -#endif -#endif - -#ifndef MG_UECC_ARM_USE_UMAAL -#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6) -#define MG_UECC_ARM_USE_UMAAL 1 -#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \ - (!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__) -#define MG_UECC_ARM_USE_UMAAL 1 -#else -#define MG_UECC_ARM_USE_UMAAL 0 -#endif -#endif - -#ifndef MG_UECC_WORD_SIZE -#if MG_UECC_PLATFORM == mg_uecc_avr -#define MG_UECC_WORD_SIZE 1 -#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64) -#define MG_UECC_WORD_SIZE 8 -#else -#define MG_UECC_WORD_SIZE 4 -#endif -#endif - -#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \ - (MG_UECC_WORD_SIZE != 8) -#error "Unsupported value for MG_UECC_WORD_SIZE" -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1)) -#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 1 -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_arm || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \ - (MG_UECC_WORD_SIZE != 4)) -#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 4 -#endif - -typedef int8_t wordcount_t; -typedef int16_t bitcount_t; -typedef int8_t cmpresult_t; - -#if (MG_UECC_WORD_SIZE == 1) - -typedef uint8_t mg_uecc_word_t; -typedef uint16_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80 -#define MG_UECC_WORD_BITS 8 -#define MG_UECC_WORD_BITS_SHIFT 3 -#define MG_UECC_WORD_BITS_MASK 0x07 - -#elif (MG_UECC_WORD_SIZE == 4) - -typedef uint32_t mg_uecc_word_t; -typedef uint64_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80000000 -#define MG_UECC_WORD_BITS 32 -#define MG_UECC_WORD_BITS_SHIFT 5 -#define MG_UECC_WORD_BITS_MASK 0x01F - -#elif (MG_UECC_WORD_SIZE == 8) - -typedef uint64_t mg_uecc_word_t; - -#define HIGH_BIT_SET 0x8000000000000000U -#define MG_UECC_WORD_BITS 64 -#define MG_UECC_WORD_BITS_SHIFT 6 -#define MG_UECC_WORD_BITS_MASK 0x03F - -#endif /* MG_UECC_WORD_SIZE */ - -#endif /* _UECC_TYPES_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_VLI_H_ -#define _UECC_VLI_H_ - -// -// - -/* Functions for raw large-integer manipulation. These are only available - if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */ -#ifndef MG_UECC_ENABLE_VLI_API -#define MG_UECC_ENABLE_VLI_API 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_ENABLE_VLI_API - -void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli, - wordcount_t num_words); - -/* Returns nonzero if bit 'bit' of vli is set. */ -mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit); - -/* Counts the number of bits required to represent vli. */ -bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli, - const wordcount_t max_words); - -/* Sets dest = src. */ -void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise */ -mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns sign of left - right, in constant time. */ -cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes vli = vli >> 1. */ -void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Computes result = left + right, returning carry. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left - right, returning borrow. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left * right. Result must be 2 * num_words long. */ -void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes result = left^2. Result must be 2 * num_words long. */ -void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left, - wordcount_t num_words); - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = product % mod, where product is 2N words long. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Calculates result = product (mod curve->p), where product is up to - 2 * curve->num_words long. */ -void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product, - MG_UECC_Curve curve); - -/* Computes result = (left * right) % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left * right) % curve->p. */ -void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, MG_UECC_Curve curve); - -/* Computes result = left^2 % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Computes result = left^2 % curve->p. */ -void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve); - -/* Computes result = (1 / input) % mod.*/ -void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input, - const mg_uecc_word_t *mod, wordcount_t num_words); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* Calculates a = sqrt(a) (mod curve->p) */ -void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif - -/* Converts an integer in uECC native format to big-endian bytes. */ -void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const mg_uecc_word_t *native); -/* Converts big-endian bytes to an integer in uECC native format. */ -void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes, - int num_bytes); - -unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve); - -const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve); - -int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve); - -/* Multiplies a point by a scalar. Points are represented by the X coordinate - followed by the Y coordinate in the same array, both coordinates are - curve->num_words long. Note that scalar must be curve->num_n_words long (NOT - curve->num_words). */ -void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, MG_UECC_Curve curve); - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -int mg_uecc_generate_random_int(mg_uecc_word_t *random, - const mg_uecc_word_t *top, - wordcount_t num_words); - -#endif /* MG_UECC_ENABLE_VLI_API */ - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_VLI_H_ */ - -// End of uecc BSD-2 -// portable8439 v1.0.1 -// Source: https://github.com/DavyLandman/portable8439 -// Licensed under CC0-1.0 -// Contains poly1305-donna e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781 (Public -// Domain) - - - - - -#if MG_TLS == MG_TLS_BUILTIN -#ifndef __PORTABLE_8439_H -#define __PORTABLE_8439_H -#if defined(__cplusplus) -extern "C" { -#endif - -// provide your own decl specificier like -DPORTABLE_8439_DECL=ICACHE_RAM_ATTR -#ifndef PORTABLE_8439_DECL -#define PORTABLE_8439_DECL -#endif - -/* - This library implements RFC 8439 a.k.a. ChaCha20-Poly1305 AEAD - - You can use this library to avoid attackers mutating or reusing your - encrypted messages. This does assume you never reuse a nonce+key pair and, - if possible, carefully pick your associated data. -*/ - -/* Make sure we are either nested in C++ or running in a C99+ compiler -#if !defined(__cplusplus) && !defined(_MSC_VER) && \ - (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) -#error "C99 or newer required" -#endif */ - -// #if CHAR_BIT > 8 -// # error "Systems without native octals not suppoted" -// #endif - -#if defined(_MSC_VER) || defined(__cplusplus) -// add restrict support is possible -#if (defined(_MSC_VER) && _MSC_VER >= 1900) || defined(__clang__) || \ - defined(__GNUC__) -#define restrict __restrict -#else -#define restrict -#endif -#endif - -#define RFC_8439_TAG_SIZE (16) -#define RFC_8439_KEY_SIZE (32) -#define RFC_8439_NONCE_SIZE (12) - -/* - Encrypt/Seal plain text bytes into a cipher text that can only be - decrypted by knowing the key, nonce and associated data. - - input: - - key: RFC_8439_KEY_SIZE bytes that all parties have agreed - upon beforehand - - nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated - for the same key. A counter or a pseudo-random value are fine. - - ad: associated data to include with calculating the tag of the - cipher text. Can be null for empty. - - plain_text: data to be encrypted, pointer + size should not overlap - with cipher_text pointer - - output: - - cipher_text: encrypted plain_text with a tag appended. Make sure to - allocate at least plain_text_size + RFC_8439_TAG_SIZE - - returns: - - size of bytes written to cipher_text, can be -1 if overlapping - pointers are passed for plain_text and cipher_text -*/ -PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt( - uint8_t *restrict cipher_text, const uint8_t key[RFC_8439_KEY_SIZE], - const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad, - size_t ad_size, const uint8_t *restrict plain_text, size_t plain_text_size); - -/* - Decrypt/unseal cipher text given the right key, nonce, and additional data. - - input: - - key: RFC_8439_KEY_SIZE bytes that all parties have agreed - upon beforehand - - nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated for - the same key. A counter or a pseudo-random value are fine. - - ad: associated data to include with calculating the tag of the - cipher text. Can be null for empty. - - cipher_text: encrypted message. - - output: - - plain_text: data to be encrypted, pointer + size should not overlap - with cipher_text pointer, leave at least enough room for - cipher_text_size - RFC_8439_TAG_SIZE - - returns: - - size of bytes written to plain_text, -1 signals either: - - incorrect key/nonce/ad - - corrupted cipher_text - - overlapping pointers are passed for plain_text and cipher_text -*/ -PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt( - uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE], - const uint8_t nonce[RFC_8439_NONCE_SIZE], - const uint8_t *restrict cipher_text, size_t cipher_text_size); -#if defined(__cplusplus) -} -#endif -#endif -#endif -#ifndef TLS_RSA_H -#define TLS_RSA_H - - -int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz); - -#endif // TLS_RSA_H - - - - - - - -#if MG_TLS == MG_TLS_MBED -#include -#include -#include -#include -#include - -struct mg_tls_ctx { - int dummy; -#ifdef MBEDTLS_SSL_SESSION_TICKETS - mbedtls_ssl_ticket_context tickets; -#endif -}; - -struct mg_tls { - mbedtls_x509_crt ca; // Parsed CA certificate - mbedtls_x509_crt cert; // Parsed certificate - mbedtls_pk_context pk; // Private key context - mbedtls_ssl_context ssl; // SSL/TLS context - mbedtls_ssl_config conf; // SSL-TLS config -#ifdef MBEDTLS_SSL_SESSION_TICKETS - mbedtls_ssl_ticket_context ticket; // Session tickets context -#endif - // https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29 - unsigned char *throttled_buf; // see #3074 - size_t throttled_len; -}; -#endif - - -#if MG_TLS == MG_TLS_OPENSSL || MG_TLS == MG_TLS_WOLFSSL - -#include -#include - -struct mg_tls { - BIO_METHOD *bm; - SSL_CTX *ctx; - SSL *ssl; -}; -#endif - - -#define WEBSOCKET_OP_CONTINUE 0 -#define WEBSOCKET_OP_TEXT 1 -#define WEBSOCKET_OP_BINARY 2 -#define WEBSOCKET_OP_CLOSE 8 -#define WEBSOCKET_OP_PING 9 -#define WEBSOCKET_OP_PONG 10 - - - -struct mg_ws_message { - struct mg_str data; // Websocket message data - uint8_t flags; // Websocket message flags -}; - -struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data, - const char *fmt, ...); -void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *, - const char *fmt, ...); -size_t mg_ws_send(struct mg_connection *, const void *buf, size_t len, int op); -size_t mg_ws_wrap(struct mg_connection *, size_t len, int op); -size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...); -size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt, - va_list *); - - - - -struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data); -void mg_sntp_request(struct mg_connection *c); -int64_t mg_sntp_parse(const unsigned char *buf, size_t len); - -uint64_t mg_now(void); // Return milliseconds since Epoch - - - - - -#define MQTT_CMD_CONNECT 1 -#define MQTT_CMD_CONNACK 2 -#define MQTT_CMD_PUBLISH 3 -#define MQTT_CMD_PUBACK 4 -#define MQTT_CMD_PUBREC 5 -#define MQTT_CMD_PUBREL 6 -#define MQTT_CMD_PUBCOMP 7 -#define MQTT_CMD_SUBSCRIBE 8 -#define MQTT_CMD_SUBACK 9 -#define MQTT_CMD_UNSUBSCRIBE 10 -#define MQTT_CMD_UNSUBACK 11 -#define MQTT_CMD_PINGREQ 12 -#define MQTT_CMD_PINGRESP 13 -#define MQTT_CMD_DISCONNECT 14 -#define MQTT_CMD_AUTH 15 - -#define MQTT_PROP_PAYLOAD_FORMAT_INDICATOR 0x01 -#define MQTT_PROP_MESSAGE_EXPIRY_INTERVAL 0x02 -#define MQTT_PROP_CONTENT_TYPE 0x03 -#define MQTT_PROP_RESPONSE_TOPIC 0x08 -#define MQTT_PROP_CORRELATION_DATA 0x09 -#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER 0x0B -#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11 -#define MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12 -#define MQTT_PROP_SERVER_KEEP_ALIVE 0x13 -#define MQTT_PROP_AUTHENTICATION_METHOD 0x15 -#define MQTT_PROP_AUTHENTICATION_DATA 0x16 -#define MQTT_PROP_REQUEST_PROBLEM_INFORMATION 0x17 -#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18 -#define MQTT_PROP_REQUEST_RESPONSE_INFORMATION 0x19 -#define MQTT_PROP_RESPONSE_INFORMATION 0x1A -#define MQTT_PROP_SERVER_REFERENCE 0x1C -#define MQTT_PROP_REASON_STRING 0x1F -#define MQTT_PROP_RECEIVE_MAXIMUM 0x21 -#define MQTT_PROP_TOPIC_ALIAS_MAXIMUM 0x22 -#define MQTT_PROP_TOPIC_ALIAS 0x23 -#define MQTT_PROP_MAXIMUM_QOS 0x24 -#define MQTT_PROP_RETAIN_AVAILABLE 0x25 -#define MQTT_PROP_USER_PROPERTY 0x26 -#define MQTT_PROP_MAXIMUM_PACKET_SIZE 0x27 -#define MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28 -#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE 0x29 -#define MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE 0x2A - -enum { - MQTT_PROP_TYPE_BYTE, - MQTT_PROP_TYPE_STRING, - MQTT_PROP_TYPE_STRING_PAIR, - MQTT_PROP_TYPE_BINARY_DATA, - MQTT_PROP_TYPE_VARIABLE_INT, - MQTT_PROP_TYPE_INT, - MQTT_PROP_TYPE_SHORT -}; - -enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED }; - -struct mg_mqtt_prop { - uint8_t id; // Enumerated at MQTT5 Reference - uint32_t iv; // Integer value for 8-, 16-, 32-bit integers types - struct mg_str key; // Non-NULL only for user property type - struct mg_str val; // Non-NULL only for UTF-8 types and user properties -}; - -struct mg_mqtt_opts { - struct mg_str user; // Username, can be empty - struct mg_str pass; // Password, can be empty - struct mg_str client_id; // Client ID - struct mg_str topic; // message/subscription topic - struct mg_str message; // message content - uint8_t qos; // message quality of service - uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4 - uint16_t keepalive; // Keep-alive timer in seconds - uint16_t retransmit_id; // For PUBLISH, init to 0 - bool retain; // Retain flag - bool clean; // Clean session flag - struct mg_mqtt_prop *props; // MQTT5 props array - size_t num_props; // number of props - struct mg_mqtt_prop *will_props; // Valid only for CONNECT packet (MQTT5) - size_t num_will_props; // Number of will props -}; - -struct mg_mqtt_message { - struct mg_str topic; // Parsed topic for PUBLISH - struct mg_str data; // Parsed message for PUBLISH - struct mg_str dgram; // Whole MQTT packet, including headers - uint16_t id; // For PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH - uint8_t cmd; // MQTT command, one of MQTT_CMD_* - uint8_t qos; // Quality of service - uint8_t ack; // CONNACK return code, 0 = success - size_t props_start; // Offset to the start of the properties (MQTT5) - size_t props_size; // Length of the properties -}; - -struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url, - const struct mg_mqtt_opts *opts, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data); -void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts); -uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts); -void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts); -int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *); -void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags, - uint32_t len); -void mg_mqtt_ping(struct mg_connection *); -void mg_mqtt_pong(struct mg_connection *); -void mg_mqtt_disconnect(struct mg_connection *, const struct mg_mqtt_opts *); -size_t mg_mqtt_next_prop(struct mg_mqtt_message *, struct mg_mqtt_prop *, - size_t ofs); - - - - - -// Mongoose sends DNS queries that contain only one question: -// either A (IPv4) or AAAA (IPv6) address lookup. -// Therefore, we expect zero or one answer. -// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address. -struct mg_dns_message { - uint16_t txnid; // Transaction ID - bool resolved; // Resolve successful, addr is set - struct mg_addr addr; // Resolved address - char name[256]; // Host name -}; - -struct mg_dns_header { - uint16_t txnid; // Transaction ID - uint16_t flags; - uint16_t num_questions; - uint16_t num_answers; - uint16_t num_authority_prs; - uint16_t num_other_prs; -}; - -// DNS resource record -struct mg_dns_rr { - uint16_t nlen; // Name or pointer length - uint16_t atype; // Address type - uint16_t aclass; // Address class - uint16_t alen; // Address length -}; - -void mg_resolve(struct mg_connection *, const char *url); -void mg_resolve_cancel(struct mg_connection *); -bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *); -size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, - bool is_question, struct mg_dns_rr *); - -struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name); - - - - - -#ifndef MG_JSON_MAX_DEPTH -#define MG_JSON_MAX_DEPTH 30 -#endif - -// Error return values - negative. Successful returns are >= 0 -enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 }; -int mg_json_get(struct mg_str json, const char *path, int *toklen); - -struct mg_str mg_json_get_tok(struct mg_str json, const char *path); -bool mg_json_get_num(struct mg_str json, const char *path, double *v); -bool mg_json_get_bool(struct mg_str json, const char *path, bool *v); -long mg_json_get_long(struct mg_str json, const char *path, long dflt); -char *mg_json_get_str(struct mg_str json, const char *path); -char *mg_json_get_hex(struct mg_str json, const char *path, int *len); -char *mg_json_get_b64(struct mg_str json, const char *path, int *len); - -bool mg_json_unescape(struct mg_str str, char *buf, size_t len); -size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key, - struct mg_str *val); - - - - -// JSON-RPC request descriptor -struct mg_rpc_req { - struct mg_rpc **head; // RPC handlers list head - struct mg_rpc *rpc; // RPC handler being called - mg_pfn_t pfn; // Response printing function - void *pfn_data; // Response printing function data - void *req_data; // Arbitrary request data - struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]} -}; - -// JSON-RPC method handler -struct mg_rpc { - struct mg_rpc *next; // Next in list - struct mg_str method; // Method pattern - void (*fn)(struct mg_rpc_req *); // Handler function - void *fn_data; // Handler function argument -}; - -void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern, - void (*handler)(struct mg_rpc_req *), void *handler_data); -void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *)); -void mg_rpc_process(struct mg_rpc_req *); - -// Helper functions to print result or error frame -void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...); -void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap); -void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...); -void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *); -void mg_rpc_list(struct mg_rpc_req *r); -// Copyright (c) 2023 Cesanta Software Limited -// All rights reserved - - - - - -#define MG_OTA_NONE 0 // No OTA support -#define MG_OTA_STM32H5 1 // STM32 H5 -#define MG_OTA_STM32H7 2 // STM32 H7 -#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core -#define MG_OTA_STM32F 4 // STM32 F7/F4/F2 -#define MG_OTA_CH32V307 100 // WCH CH32V307 -#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6 -#define MG_OTA_RT1020 300 // IMXRT1020 -#define MG_OTA_RT1050 301 // IMXRT1050 -#define MG_OTA_RT1060 302 // IMXRT1060 -#define MG_OTA_RT1064 303 // IMXRT1064 -#define MG_OTA_RT1170 304 // IMXRT1170 -#define MG_OTA_MCXN 310 // MCXN947 -#define MG_OTA_FRDM 320 // FRDM-RW612 -#define MG_OTA_FLASH 900 // OTA via an internal flash -#define MG_OTA_ESP32 910 // ESP32 OTA implementation -#define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash -#define MG_OTA_CUSTOM 1000 // Custom implementation - -#ifndef MG_OTA -#define MG_OTA MG_OTA_NONE -#else -#ifndef MG_IRAM -#if defined(__GNUC__) -#define MG_IRAM __attribute__((noinline, section(".iram"))) -#else -#define MG_IRAM -#endif // compiler -#endif // IRAM -#endif // OTA - -// Firmware update API -bool mg_ota_begin(size_t new_firmware_size); // Start writing -bool mg_ota_write(const void *buf, size_t len); // Write chunk, aligned to 1k -bool mg_ota_end(void); // Stop writing - - - -#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM - -struct mg_flash { - void *start; // Address at which flash starts - size_t size; // Flash size - size_t secsz; // Sector size - size_t align; // Write alignment - bool (*write_fn)(void *, const void *, size_t); // Write function - bool (*swap_fn)(void); // Swap partitions -}; - -bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash); -bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash); -bool mg_ota_flash_end(struct mg_flash *flash); - -#endif - - - - - - -struct mg_wifi_data { - char *ssid, *pass; // STA mode, SSID to connect to - char *apssid, *appass; // AP mode, our SSID - uint32_t apip, apmask; // AP mode, our IP address and mask - uint8_t security; // STA mode, TBD - uint8_t apsecurity; // AP mode, TBD - uint8_t apchannel; // AP mode, channel to use - bool apmode; // start in AP mode; 'false' -> connect to 'ssid' != NULL -}; - -struct mg_wifi_scan_bss_data { - struct mg_str SSID; - char *BSSID; - int16_t RSSI; - uint8_t security; -#define MG_WIFI_SECURITY_OPEN 0 -#define MG_WIFI_SECURITY_WEP MG_BIT(0) -#define MG_WIFI_SECURITY_WPA MG_BIT(1) -#define MG_WIFI_SECURITY_WPA2 MG_BIT(2) -#define MG_WIFI_SECURITY_WPA3 MG_BIT(3) - uint8_t channel; - unsigned band : 2; -#define MG_WIFI_BAND_2G 0 -#define MG_WIFI_BAND_5G 1 - unsigned has_n : 1; -}; - -bool mg_wifi_scan(void); -bool mg_wifi_connect(struct mg_wifi_data *); -bool mg_wifi_disconnect(void); -bool mg_wifi_ap_start(struct mg_wifi_data *); -bool mg_wifi_ap_stop(void); - - - - - - - -#if MG_ENABLE_TCPIP - -struct mg_tcpip_if; // Mongoose TCP/IP network interface - -struct mg_tcpip_driver { - bool (*init)(struct mg_tcpip_if *); // Init driver - size_t (*tx)(const void *, size_t, struct mg_tcpip_if *); // Transmit frame - size_t (*rx)(void *buf, size_t len, struct mg_tcpip_if *); // Receive frame - bool (*poll)(struct mg_tcpip_if *, bool); // Poll, return Up/down status -}; - -typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev, - void *ev_data); - -enum { - MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state) - MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr - MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr - MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str * - MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL - MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi scan results struct mg_wifi_scan_bss_data * - MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL - MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and chip specific - MG_TCPIP_EV_DRIVER, // Driver event driver specific - MG_TCPIP_EV_USER // Starting ID for user events -}; - - -// Network interface -struct mg_tcpip_if { - uint8_t mac[6]; // MAC address. Must be set to a valid MAC - uint32_t ip, mask, gw; // IP address, mask, default gateway - struct mg_str tx; // Output (TX) buffer - bool enable_dhcp_client; // Enable DCHP client - bool enable_dhcp_server; // Enable DCHP server - bool enable_get_gateway; // DCHP server sets client as gateway - bool enable_req_dns; // DCHP client requests DNS server - bool enable_req_sntp; // DCHP client requests SNTP server - bool enable_crc32_check; // Do a CRC check on RX frames and strip it - bool enable_mac_check; // Do a MAC check on RX frames - bool update_mac_hash_table; // Signal drivers to update MAC controller - struct mg_tcpip_driver *driver; // Low level driver - void *driver_data; // Driver-specific data - mg_tcpip_event_handler_t pfn; // Driver-specific event handler function - mg_tcpip_event_handler_t fn; // User-specified event handler function - struct mg_mgr *mgr; // Mongoose event manager - struct mg_queue recv_queue; // Receive queue - char dhcp_name[12]; // Name reported to DHCP, "mip" if unset - uint16_t mtu; // Interface MTU -#define MG_TCPIP_MTU_DEFAULT 1500 - - // Internal state, user can use it but should not change it - uint8_t gwmac[6]; // Router's MAC - char *dns4_url; // DNS server URL - uint64_t now; // Current time - uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state - uint64_t lease_expire; // Lease expiration time, in ms - uint16_t eport; // Next ephemeral port - volatile uint32_t ndrop; // Number of received, but dropped frames - volatile uint32_t nrecv; // Number of received frames - volatile uint32_t nsent; // Number of transmitted frames - volatile uint32_t nerr; // Number of driver errors - uint8_t state; // Current state -#define MG_TCPIP_STATE_DOWN 0 // Interface is down -#define MG_TCPIP_STATE_UP 1 // Interface is up -#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state -#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned -#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work -}; - -void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *); -void mg_tcpip_free(struct mg_tcpip_if *); -void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp); -void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac); - -extern struct mg_tcpip_driver mg_tcpip_driver_stm32f; -extern struct mg_tcpip_driver mg_tcpip_driver_w5500; -extern struct mg_tcpip_driver mg_tcpip_driver_w5100; -extern struct mg_tcpip_driver mg_tcpip_driver_tm4c; -extern struct mg_tcpip_driver mg_tcpip_driver_tms570; -extern struct mg_tcpip_driver mg_tcpip_driver_stm32h; -extern struct mg_tcpip_driver mg_tcpip_driver_imxrt; -extern struct mg_tcpip_driver mg_tcpip_driver_same54; -extern struct mg_tcpip_driver mg_tcpip_driver_cmsis; -extern struct mg_tcpip_driver mg_tcpip_driver_ra; -extern struct mg_tcpip_driver mg_tcpip_driver_xmc; -extern struct mg_tcpip_driver mg_tcpip_driver_xmc7; -extern struct mg_tcpip_driver mg_tcpip_driver_ppp; -extern struct mg_tcpip_driver mg_tcpip_driver_pico_w; -extern struct mg_tcpip_driver mg_tcpip_driver_rw612; -extern struct mg_tcpip_driver mg_tcpip_driver_cyw; - -// Drivers that require SPI, can use this SPI abstraction -struct mg_tcpip_spi { - void *spi; // Opaque SPI bus descriptor - void (*begin)(void *); // SPI begin: slave select low - void (*end)(void *); // SPI end: slave select high - uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply -}; - - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS - -#include "Driver_ETH_MAC.h" // keep this include -#include "Driver_ETH_PHY.h" // keep this include - -#endif - - -#if MG_ENABLE_TCPIP && \ - ((defined(MG_ENABLE_DRIVER_CYW) && MG_ENABLE_DRIVER_CYW) || \ - (defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO)) - -struct mg_tcpip_spi_ { - void *spi; // Opaque SPI bus descriptor - void (*begin)(void *); // SPI begin: slave select low - void (*end)(void *); // SPI end: slave select high - void (*txn)(void *, uint8_t *, uint8_t *, - size_t len); // SPI transaction: write-read len bytes -}; - -struct mg_tcpip_driver_cyw_firmware { - const uint8_t *code_addr; - size_t code_len; - const uint8_t *nvram_addr; - size_t nvram_len; - const uint8_t *clm_addr; - size_t clm_len; -}; - -struct mg_tcpip_driver_cyw_data { - struct mg_wifi_data wifi; - void *bus; - struct mg_tcpip_driver_cyw_firmware *fw; - bool hs; // use chip "high-speed" mode; otherwise SPI CPOL0 CPHA0 (DS 4.2.3 Table 6) -}; - -#if 0 -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_cyw_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - MG_SET_WIFI_CONFIG(&driver_data_); \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_pico_w; \ - mif_.driver_data = &driver_data_; \ - mif_.recv_queue.size = 8192; \ - mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: cyw, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) -#endif - -#endif - - -#if MG_ENABLE_TCPIP && \ - (defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10) || \ - (defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11) - -struct mg_tcpip_driver_imxrt_data { - // MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk), - // must not exceed 2.5MHz. Configuration for clock range 2.36~2.50 MHz - // 37.5.1.8.2, Table 37-46 : f = ipg_clk / (2(mdc_cr + 1)) - // ipg_clk mdc_cr VALUE - // -------------------------- - // -1 <-- TODO() tell driver to guess the value - // 25 MHz 4 - // 33 MHz 6 - // 40 MHz 7 - // 50 MHz 9 - // 66 MHz 13 - int mdc_cr; // Valid values: -1 to 63 - - uint8_t phy_addr; // PHY address -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 2 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 24 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_imxrt_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_imxrt; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: imxrt, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - - - -struct mg_phy { - uint16_t (*read_reg)(uint8_t addr, uint8_t reg); - void (*write_reg)(uint8_t addr, uint8_t reg, uint16_t value); -}; - -// PHY configuration settings, bitmask -enum { - // Set if PHY LEDs are connected to ground - MG_PHY_LEDS_ACTIVE_HIGH = (1 << 0), - // Set when PHY clocks MAC. Otherwise, MAC clocks PHY - MG_PHY_CLOCKS_MAC = (1 << 1) -}; - -enum { MG_PHY_SPEED_10M, MG_PHY_SPEED_100M, MG_PHY_SPEED_1000M }; - -void mg_phy_init(struct mg_phy *, uint8_t addr, uint8_t config); -bool mg_phy_up(struct mg_phy *, uint8_t addr, bool *full_duplex, - uint8_t *speed); - - -#if MG_ENABLE_TCPIP && MG_ARCH == MG_ARCH_PICOSDK && \ - defined(MG_ENABLE_DRIVER_PICO_W) && MG_ENABLE_DRIVER_PICO_W - -#include "cyw43.h" // keep this include -#include "pico/cyw43_arch.h" // keep this include -#include "pico/unique_id.h" // keep this include - -struct mg_tcpip_driver_pico_w_data { - struct mg_wifi_data wifi; -}; - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_pico_w_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - MG_SET_WIFI_CONFIG(&driver_data_); \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_pico_w; \ - mif_.driver_data = &driver_data_; \ - mif_.recv_queue.size = 8192; \ - mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: pico-w, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -struct mg_tcpip_driver_ppp_data { - void *uart; // Opaque UART bus descriptor - void (*reset)(void *); // Modem hardware reset - void (*tx)(void *, uint8_t); // UART transmit single byte - int (*rx)(void *); // UART receive single byte - const char **script; // List of AT commands and expected replies - int script_index; // Index of the current AT command in the list - uint64_t deadline; // AT command deadline in ms -}; - - -#if MG_ENABLE_TCPIP && \ - (defined(MG_ENABLE_DRIVER_RA6) && MG_ENABLE_DRIVER_RA6) || \ - (defined(MG_ENABLE_DRIVER_RA8) && MG_ENABLE_DRIVER_RA8) - -struct mg_tcpip_driver_ra_data { - // MDC clock "divider". MDC clock is software generated, - uint32_t clock; // core clock frequency in Hz - uint16_t irqno; // IRQn, R_ICU->IELSR[irqno] - uint8_t phy_addr; // PHY address -}; - -#ifndef MG_DRIVER_CLK_FREQ -#define MG_DRIVER_CLK_FREQ 100000000UL -#endif - -#ifndef MG_DRIVER_IRQ_NO -#define MG_DRIVER_IRQ_NO 0 -#endif - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_ra_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.clock = MG_DRIVER_CLK_FREQ; \ - driver_data_.irqno = MG_DRIVER_IRQ_NO; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_ra; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: ra, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612) && MG_ENABLE_DRIVER_RW612 - -struct mg_tcpip_driver_rw612_data { - // 38.1.8 MII Speed Control Register (MSCR) - // MDC clock frequency must not exceed 2.5 MHz and is calculated as follows: - // MDC_freq = P_clock / ((mdc_cr + 1) * 2), where P_clock is the - // peripheral bus clock. - // IEEE802.3 clause 22 defines a minimum of 10 ns for the hold time on the - // MDIO output. Depending on the host bus frequency, the setting may need - // to be increased. - int mdc_cr; - int mdc_holdtime; // Valid values: [0-7] - uint8_t phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 2 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 51 -#endif - -#ifndef MG_DRIVER_MDC_HOLDTIME -#define MG_DRIVER_MDC_HOLDTIME 3 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_rw612_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_rw612; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: rw612, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && \ - MG_ENABLE_DRIVER_SAME54 - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 5 -#endif - -#ifndef MG_DRIVER_PHY_ADDR -#define MG_DRIVER_PHY_ADDR 0 -#endif - -struct mg_tcpip_driver_same54_data { - int mdc_cr; - uint8_t phy_addr; -}; - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_same54_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_DRIVER_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_same54; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: same54, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP && \ - (defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO) - -// Specific chip/card driver --> SDIO driver --> HAL --> SDIO hw controller - -// API with HAL for hardware controller -// - Provide a function to init the controller (external) -// - Provide these functions: -struct mg_tcpip_sdio { - void *sdio; // Opaque SDIO bus descriptor - void (*cfg)(void *, uint8_t); // select operating parameters - // SDIO transaction: send cmd with a possible 1-byte read or write - bool (*txn)(void *, uint8_t cmd, uint32_t arg, uint32_t *r); - // SDIO extended transaction: write or read len bytes, using blksz blocks - bool (*xfr)(void *, bool write, uint32_t arg, uint16_t blksz, uint32_t *, - uint32_t len, uint32_t *r); -}; - -// API with driver (e.g.: cyw.c) -// Once the hardware controller has been initialized: -// - Init card: selects the card, sets F0 block size, sets bus width and speed -bool mg_sdio_init(struct mg_tcpip_sdio *sdio); -// - Enable other possible functions (F1 to F7) -bool mg_sdio_enable_f(struct mg_tcpip_sdio *sdio, unsigned int f); -// - Wait for them to be ready -bool mg_sdio_waitready_f(struct mg_tcpip_sdio *sdio, unsigned int f); -// - Set their transfer block length -bool mg_sdio_set_blksz(struct mg_tcpip_sdio *sdio, unsigned int f, - uint16_t blksz); -// - Transfer data to/from a function (abstracts from transaction type) -// - Requesting a read transfer > blocksize means block transfer will be used. -// - Drivers must have room to accomodate a whole block transfer, see sdio.c -// - Transfers of > 1 byte --> (uint32_t *) data. 1-byte --> (uint8_t *) data -bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f, - uint32_t addr, void *data, uint32_t len); - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \ - MG_ENABLE_DRIVER_STM32F - -struct mg_tcpip_driver_stm32f_data { - // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz - // HCLK range DIVIDER mdc_cr VALUE - // ------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz HCLK/42 0 - // 100-150 MHz HCLK/62 1 - // 20-35 MHz HCLK/16 2 - // 35-60 MHz HCLK/26 3 - // 150-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed - // 216-310 MHz HCLK/124 5 - // 110, 111 Reserved - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - - uint8_t phy_addr; // PHY address -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 4 -#endif - -#if MG_ARCH == MG_ARCH_CUBE -#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn) -#else -#define MG_ENABLE_ETH_IRQ() -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_stm32f_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_stm32f; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - MG_ENABLE_ETH_IRQ(); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: stm32f, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP -#if !defined(MG_ENABLE_DRIVER_STM32H) -#define MG_ENABLE_DRIVER_STM32H 0 -#endif -#if !defined(MG_ENABLE_DRIVER_MCXN) -#define MG_ENABLE_DRIVER_MCXN 0 -#endif -#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN - -struct mg_tcpip_driver_stm32h_data { - // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz - // HCLK range DIVIDER mdc_cr VALUE - // ------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz HCLK/42 0 - // 100-150 MHz HCLK/62 1 - // 20-35 MHz HCLK/16 2 - // 35-60 MHz HCLK/26 3 - // 150-250 MHz HCLK/102 4 <-- value for max speed HSI - // 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI - // 300-500 MHz HCLK/204 6 - // 500-800 MHz HCLK/324 7 - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - - uint8_t phy_addr; // PHY address - uint8_t phy_conf; // PHY config -}; - -#ifndef MG_TCPIP_PHY_CONF -#define MG_TCPIP_PHY_CONF MG_PHY_CLOCKS_MAC -#endif - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 4 -#endif - -#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE -#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn) -#else -#define MG_ENABLE_ETH_IRQ() -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_stm32h_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - driver_data_.phy_conf = MG_TCPIP_PHY_CONF; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_stm32h; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_ENABLE_ETH_IRQ(); \ - MG_INFO(("Driver: stm32h, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C - -struct mg_tcpip_driver_tm4c_data { - // MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz - // SYSCLK range DIVIDER mdc_cr VALUE - // ------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz SYSCLK/42 0 - // 100-150 MHz SYSCLK/62 1 <-- value for EK-TM4C129* on max speed - // 20-35 MHz SYSCLK/16 2 - // 35-60 MHz SYSCLK/26 3 - // 0x4-0xF Reserved - int mdc_cr; // Valid values: -1, 0, 1, 2, 3 -}; - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 1 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_tm4c_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_tm4c; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: tm4c, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TMS570) && MG_ENABLE_DRIVER_TMS570 -struct mg_tcpip_driver_tms570_data { - int mdc_cr; - int phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 1 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_tms570_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_tms570; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: tms570, MAC: %M", mg_print_mac, mif_.mac));\ - } while (0) -#endif - - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7 - -struct mg_tcpip_driver_xmc7_data { - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - uint8_t phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 3 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_xmc7_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_xmc7; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC - -struct mg_tcpip_driver_xmc_data { - // 13.2.8.1 Station Management Functions - // MDC clock divider (). MDC clock is derived from ETH MAC clock - // It must not exceed 2.5MHz - // ETH Clock range DIVIDER mdc_cr VALUE - // -------------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz ETH Clock/42 0 - // 100-150 MHz ETH Clock/62 1 - // 20-35 MHz ETH Clock/16 2 - // 35-60 MHz ETH Clock/26 3 - // 150-250 MHz ETH Clock/102 4 - // 250-300 MHz ETH Clock/124 5 - // 110, 111 Reserved - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - uint8_t phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 4 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_xmc_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_xmc; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: xmc, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - -#ifdef __cplusplus -} -#endif -#endif // MONGOOSE_H diff --git a/main/lib/picohttpparser.c b/main/lib/picohttpparser.c new file mode 100644 index 0000000..8260ff4 --- /dev/null +++ b/main/lib/picohttpparser.c @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, + * Shigeo Mitsunari + * + * The software is licensed under either the MIT License (below) or the Perl + * license. + * + * 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 +#include +#ifdef __SSE4_2__ +#ifdef _MSC_VER +#include +#else +#include +#endif +#endif +#include "picohttpparser.h" + +#if __GNUC__ >= 3 +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#ifdef _MSC_VER +#define ALIGNED(n) _declspec(align(n)) +#else +#define ALIGNED(n) __attribute__((aligned(n))) +#endif + +#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u) + +#define CHECK_EOF() \ + if (buf == buf_end) { \ + *ret = -2; \ + return NULL; \ + } + +#define EXPECT_CHAR_NO_CHECK(ch) \ + if (*buf++ != ch) { \ + *ret = -1; \ + return NULL; \ + } + +#define EXPECT_CHAR(ch) \ + CHECK_EOF(); \ + EXPECT_CHAR_NO_CHECK(ch); + +#define ADVANCE_TOKEN(tok, toklen) \ + do { \ + const char *tok_start = buf; \ + static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ + int found2; \ + buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ + if (!found2) { \ + CHECK_EOF(); \ + } \ + while (1) { \ + if (*buf == ' ') { \ + break; \ + } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ + if ((unsigned char)*buf < '\040' || *buf == '\177') { \ + *ret = -1; \ + return NULL; \ + } \ + } \ + ++buf; \ + CHECK_EOF(); \ + } \ + tok = tok_start; \ + toklen = buf - tok_start; \ + } while (0) + +static const char *token_char_map = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" + "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" + "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +static const char *findchar_fast(const char *buf, const char *buf_end, + const char *ranges, size_t ranges_size, + int *found) { + *found = 0; +#if __SSE4_2__ + if (likely(buf_end - buf >= 16)) { + __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); + + size_t left = (buf_end - buf) & ~15; + do { + __m128i b16 = _mm_loadu_si128((const __m128i *)buf); + int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, + _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | + _SIDD_UBYTE_OPS); + if (unlikely(r != 16)) { + buf += r; + *found = 1; + break; + } + buf += 16; + left -= 16; + } while (likely(left != 0)); + } +#else + /* suppress unused parameter warning */ + (void)buf_end; + (void)ranges; + (void)ranges_size; +#endif + return buf; +} + +static const char *get_token_to_eol(const char *buf, const char *buf_end, + const char **token, size_t *token_len, + int *ret) { + const char *token_start = buf; + +#ifdef __SSE4_2__ + static const char ALIGNED(16) ranges1[16] = + "\0\010" /* allow HT */ + "\012\037" /* allow SP and up to but not including DEL */ + "\177\177"; /* allow chars w. MSB set */ + int found; + buf = findchar_fast(buf, buf_end, ranges1, 6, &found); + if (found) + goto FOUND_CTL; +#else + /* find non-printable char within the next 8 bytes, this is the hottest code; + * manually inlined */ + while (likely(buf_end - buf >= 8)) { +#define DOIT() \ + do { \ + if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ + goto NonPrintable; \ + ++buf; \ + } while (0) + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); +#undef DOIT + continue; + NonPrintable: + if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || + unlikely(*buf == '\177')) { + goto FOUND_CTL; + } + ++buf; + } +#endif + for (;; ++buf) { + CHECK_EOF(); + if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { + if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || + unlikely(*buf == '\177')) { + goto FOUND_CTL; + } + } + } +FOUND_CTL: + if (likely(*buf == '\015')) { + ++buf; + EXPECT_CHAR('\012'); + *token_len = buf - 2 - token_start; + } else if (*buf == '\012') { + *token_len = buf - token_start; + ++buf; + } else { + *ret = -1; + return NULL; + } + *token = token_start; + + return buf; +} + +static const char *is_complete(const char *buf, const char *buf_end, + size_t last_len, int *ret) { + int ret_cnt = 0; + buf = last_len < 3 ? buf : buf + last_len - 3; + + while (1) { + CHECK_EOF(); + if (*buf == '\015') { + ++buf; + CHECK_EOF(); + EXPECT_CHAR('\012'); + ++ret_cnt; + } else if (*buf == '\012') { + ++buf; + ++ret_cnt; + } else { + ++buf; + ret_cnt = 0; + } + if (ret_cnt == 2) { + return buf; + } + } + + *ret = -2; + return NULL; +} + +#define PARSE_INT(valp_, mul_) \ + if (*buf < '0' || '9' < *buf) { \ + buf++; \ + *ret = -1; \ + return NULL; \ + } \ + *(valp_) = (mul_) * (*buf++ - '0'); + +#define PARSE_INT_3(valp_) \ + do { \ + int res_ = 0; \ + PARSE_INT(&res_, 100) \ + *valp_ = res_; \ + PARSE_INT(&res_, 10) \ + *valp_ += res_; \ + PARSE_INT(&res_, 1) \ + *valp_ += res_; \ + } while (0) + +/* returned pointer is always within [buf, buf_end), or null */ +static const char *parse_token(const char *buf, const char *buf_end, + const char **token, size_t *token_len, + char next_char, int *ret) { + /* We use pcmpestri to detect non-token characters. This instruction can take + * no more than eight character ranges (8*2*8=128 bits that is the size of a + * SSE register). Due to this restriction, characters `|` and `~` are handled + * in the slow loop. */ + static const char ALIGNED(16) ranges[] = + "\x00 " /* control chars and up to SP */ + "\"\"" /* 0x22 */ + "()" /* 0x28,0x29 */ + ",," /* 0x2c */ + "//" /* 0x2f */ + ":@" /* 0x3a-0x40 */ + "[]" /* 0x5b-0x5d */ + "{\xff"; /* 0x7b-0xff */ + const char *buf_start = buf; + int found; + buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found); + if (!found) { + CHECK_EOF(); + } + while (1) { + if (*buf == next_char) { + break; + } else if (!token_char_map[(unsigned char)*buf]) { + *ret = -1; + return NULL; + } + ++buf; + CHECK_EOF(); + } + *token = buf_start; + *token_len = buf - buf_start; + return buf; +} + +/* returned pointer is always within [buf, buf_end), or null */ +static const char *parse_http_version(const char *buf, const char *buf_end, + int *minor_version, int *ret) { + /* we want at least [HTTP/1.] to try to parse */ + if (buf_end - buf < 9) { + *ret = -2; + return NULL; + } + EXPECT_CHAR_NO_CHECK('H'); + EXPECT_CHAR_NO_CHECK('T'); + EXPECT_CHAR_NO_CHECK('T'); + EXPECT_CHAR_NO_CHECK('P'); + EXPECT_CHAR_NO_CHECK('/'); + EXPECT_CHAR_NO_CHECK('1'); + EXPECT_CHAR_NO_CHECK('.'); + PARSE_INT(minor_version, 1); + return buf; +} + +static const char *parse_headers(const char *buf, const char *buf_end, + struct phr_header *headers, + size_t *num_headers, size_t max_headers, + int *ret) { + for (;; ++*num_headers) { + CHECK_EOF(); + if (*buf == '\015') { + ++buf; + EXPECT_CHAR('\012'); + break; + } else if (*buf == '\012') { + ++buf; + break; + } + if (*num_headers == max_headers) { + *ret = -1; + return NULL; + } + if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { + /* parsing name, but do not discard SP before colon, see + * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ + if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, + &headers[*num_headers].name_len, ':', ret)) == + NULL) { + return NULL; + } + if (headers[*num_headers].name_len == 0) { + *ret = -1; + return NULL; + } + ++buf; + for (;; ++buf) { + CHECK_EOF(); + if (!(*buf == ' ' || *buf == '\t')) { + break; + } + } + } else { + headers[*num_headers].name = NULL; + headers[*num_headers].name_len = 0; + } + const char *value; + size_t value_len; + if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == + NULL) { + return NULL; + } + /* remove trailing SPs and HTABs */ + const char *value_end = value + value_len; + for (; value_end != value; --value_end) { + const char c = *(value_end - 1); + if (!(c == ' ' || c == '\t')) { + break; + } + } + headers[*num_headers].value = value; + headers[*num_headers].value_len = value_end - value; + } + return buf; +} + +static const char *parse_request(const char *buf, const char *buf_end, + const char **method, size_t *method_len, + const char **path, size_t *path_len, + int *minor_version, struct phr_header *headers, + size_t *num_headers, size_t max_headers, + int *ret) { + /* skip first empty line (some clients add CRLF after POST content) */ + CHECK_EOF(); + if (*buf == '\015') { + ++buf; + EXPECT_CHAR('\012'); + } else if (*buf == '\012') { + ++buf; + } + + /* parse request line */ + if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) { + return NULL; + } + do { + ++buf; + CHECK_EOF(); + } while (*buf == ' '); + ADVANCE_TOKEN(*path, *path_len); + do { + ++buf; + CHECK_EOF(); + } while (*buf == ' '); + if (*method_len == 0 || *path_len == 0) { + *ret = -1; + return NULL; + } + if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { + return NULL; + } + if (*buf == '\015') { + ++buf; + EXPECT_CHAR('\012'); + } else if (*buf == '\012') { + ++buf; + } else { + *ret = -1; + return NULL; + } + + return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); +} + +int phr_parse_request(const char *buf_start, size_t len, const char **method, + size_t *method_len, const char **path, size_t *path_len, + int *minor_version, struct phr_header *headers, + size_t *num_headers, size_t last_len) { + const char *buf = buf_start, *buf_end = buf_start + len; + size_t max_headers = *num_headers; + int r; + + *method = NULL; + *method_len = 0; + *path = NULL; + *path_len = 0; + *minor_version = -1; + *num_headers = 0; + + /* if last_len != 0, check if the request is complete (a fast countermeasure + againt slowloris */ + if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { + return r; + } + + if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, + minor_version, headers, num_headers, max_headers, + &r)) == NULL) { + return r; + } + + return (int)(buf - buf_start); +} + +static const char *parse_response(const char *buf, const char *buf_end, + int *minor_version, int *status, + const char **msg, size_t *msg_len, + struct phr_header *headers, + size_t *num_headers, size_t max_headers, + int *ret) { + /* parse "HTTP/1.x" */ + if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { + return NULL; + } + /* skip space */ + if (*buf != ' ') { + *ret = -1; + return NULL; + } + do { + ++buf; + CHECK_EOF(); + } while (*buf == ' '); + /* parse status code, we want at least [:digit:][:digit:][:digit:] + * to try to parse */ + if (buf_end - buf < 4) { + *ret = -2; + return NULL; + } + PARSE_INT_3(status); + + /* get message including preceding space */ + if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { + return NULL; + } + if (*msg_len == 0) { + /* ok */ + } else if (**msg == ' ') { + /* Remove preceding space. Successful return from `get_token_to_eol` + * guarantees that we would hit something other than SP before running past + * the end of the given buffer. */ + do { + ++*msg; + --*msg_len; + } while (**msg == ' '); + } else { + /* garbage found after status code */ + *ret = -1; + return NULL; + } + + return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); +} + +int phr_parse_response(const char *buf_start, size_t len, int *minor_version, + int *status, const char **msg, size_t *msg_len, + struct phr_header *headers, size_t *num_headers, + size_t last_len) { + const char *buf = buf_start, *buf_end = buf + len; + size_t max_headers = *num_headers; + int r; + + *minor_version = -1; + *status = 0; + *msg = NULL; + *msg_len = 0; + *num_headers = 0; + + /* if last_len != 0, check if the response is complete (a fast countermeasure + against slowloris */ + if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { + return r; + } + + if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, + headers, num_headers, max_headers, &r)) == NULL) { + return r; + } + + return (int)(buf - buf_start); +} + +int phr_parse_headers(const char *buf_start, size_t len, + struct phr_header *headers, size_t *num_headers, + size_t last_len) { + const char *buf = buf_start, *buf_end = buf + len; + size_t max_headers = *num_headers; + int r; + + *num_headers = 0; + + /* if last_len != 0, check if the response is complete (a fast countermeasure + against slowloris */ + if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { + return r; + } + + if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, + &r)) == NULL) { + return r; + } + + return (int)(buf - buf_start); +} + +enum { + CHUNKED_IN_CHUNK_SIZE, + CHUNKED_IN_CHUNK_EXT, + CHUNKED_IN_CHUNK_HEADER_EXPECT_LF, + CHUNKED_IN_CHUNK_DATA, + CHUNKED_IN_CHUNK_DATA_EXPECT_CR, + CHUNKED_IN_CHUNK_DATA_EXPECT_LF, + CHUNKED_IN_TRAILERS_LINE_HEAD, + CHUNKED_IN_TRAILERS_LINE_MIDDLE +}; + +static int decode_hex(int ch) { + if ('0' <= ch && ch <= '9') { + return ch - '0'; + } else if ('A' <= ch && ch <= 'F') { + return ch - 'A' + 0xa; + } else if ('a' <= ch && ch <= 'f') { + return ch - 'a' + 0xa; + } else { + return -1; + } +} + +ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, + size_t *_bufsz) { + size_t dst = 0, src = 0, bufsz = *_bufsz; + ssize_t ret = -2; /* incomplete */ + + decoder->_total_read += bufsz; + + while (1) { + switch (decoder->_state) { + case CHUNKED_IN_CHUNK_SIZE: + for (;; ++src) { + int v; + if (src == bufsz) + goto Exit; + if ((v = decode_hex(buf[src])) == -1) { + if (decoder->_hex_count == 0) { + ret = -1; + goto Exit; + } + /* the only characters that may appear after the chunk size are BWS, + * semicolon, or CRLF */ + switch (buf[src]) { + case ' ': + case '\011': + case ';': + case '\012': + case '\015': + break; + default: + ret = -1; + goto Exit; + } + break; + } + if (decoder->_hex_count == sizeof(size_t) * 2) { + ret = -1; + goto Exit; + } + decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; + ++decoder->_hex_count; + } + decoder->_hex_count = 0; + decoder->_state = CHUNKED_IN_CHUNK_EXT; + /* fallthru */ + case CHUNKED_IN_CHUNK_EXT: + /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ + for (;; ++src) { + if (src == bufsz) + goto Exit; + if (buf[src] == '\015') { + break; + } else if (buf[src] == '\012') { + ret = -1; + goto Exit; + } + } + ++src; + decoder->_state = CHUNKED_IN_CHUNK_HEADER_EXPECT_LF; + /* fallthru */ + case CHUNKED_IN_CHUNK_HEADER_EXPECT_LF: + if (src == bufsz) + goto Exit; + if (buf[src] != '\012') { + ret = -1; + goto Exit; + } + ++src; + if (decoder->bytes_left_in_chunk == 0) { + if (decoder->consume_trailer) { + decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; + break; + } else { + goto Complete; + } + } + decoder->_state = CHUNKED_IN_CHUNK_DATA; + /* fallthru */ + case CHUNKED_IN_CHUNK_DATA: { + size_t avail = bufsz - src; + if (avail < decoder->bytes_left_in_chunk) { + if (dst != src) + memmove(buf + dst, buf + src, avail); + src += avail; + dst += avail; + decoder->bytes_left_in_chunk -= avail; + goto Exit; + } + if (dst != src) + memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); + src += decoder->bytes_left_in_chunk; + dst += decoder->bytes_left_in_chunk; + decoder->bytes_left_in_chunk = 0; + decoder->_state = CHUNKED_IN_CHUNK_DATA_EXPECT_CR; + } + /* fallthru */ + case CHUNKED_IN_CHUNK_DATA_EXPECT_CR: + if (src == bufsz) + goto Exit; + if (buf[src] != '\015') { + ret = -1; + goto Exit; + } + ++src; + decoder->_state = CHUNKED_IN_CHUNK_DATA_EXPECT_LF; + /* fallthru */ + case CHUNKED_IN_CHUNK_DATA_EXPECT_LF: + if (src == bufsz) + goto Exit; + if (buf[src] != '\012') { + ret = -1; + goto Exit; + } + ++src; + decoder->_state = CHUNKED_IN_CHUNK_SIZE; + break; + case CHUNKED_IN_TRAILERS_LINE_HEAD: + for (;; ++src) { + if (src == bufsz) + goto Exit; + if (buf[src] != '\015') + break; + } + if (buf[src++] == '\012') + goto Complete; + decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; + /* fallthru */ + case CHUNKED_IN_TRAILERS_LINE_MIDDLE: + for (;; ++src) { + if (src == bufsz) + goto Exit; + if (buf[src] == '\012') + break; + } + ++src; + decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; + break; + default: + assert(!"decoder is corrupt"); + } + } + +Complete: + ret = bufsz - src; +Exit: + if (dst != src) + memmove(buf + dst, buf + src, bufsz - src); + *_bufsz = dst; + /* if incomplete but the overhead of the chunked encoding is >=100KB and >80%, + * signal an error */ + if (ret == -2) { + decoder->_total_overhead += bufsz - dst; + if (decoder->_total_overhead >= 100 * 1024 && + decoder->_total_read - decoder->_total_overhead < + decoder->_total_read / 4) + ret = -1; + } + return ret; +} + +int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) { + return decoder->_state == CHUNKED_IN_CHUNK_DATA; +} + +#undef CHECK_EOF +#undef EXPECT_CHAR +#undef ADVANCE_TOKEN diff --git a/main/lib/picohttpparser.h b/main/lib/picohttpparser.h new file mode 100644 index 0000000..ad23c8d --- /dev/null +++ b/main/lib/picohttpparser.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, + * Shigeo Mitsunari + * + * The software is licensed under either the MIT License (below) or the Perl + * license. + * + * 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 picohttpparser_h +#define picohttpparser_h + +#include +#include + +#ifdef _MSC_VER +#define ssize_t intptr_t +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* contains name and value of a header (name == NULL if is a continuing line + * of a multiline header */ +struct phr_header { + const char *name; + size_t name_len; + const char *value; + size_t value_len; +}; + +/* returns number of bytes consumed if successful, -2 if request is partial, + * -1 if failed */ +int phr_parse_request(const char *buf, size_t len, const char **method, + size_t *method_len, const char **path, size_t *path_len, + int *minor_version, struct phr_header *headers, + size_t *num_headers, size_t last_len); + +/* ditto */ +int phr_parse_response(const char *_buf, size_t len, int *minor_version, + int *status, const char **msg, size_t *msg_len, + struct phr_header *headers, size_t *num_headers, + size_t last_len); + +/* ditto */ +int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, + size_t *num_headers, size_t last_len); + +/* should be zero-filled before start */ +struct phr_chunked_decoder { + size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ + char consume_trailer; /* if trailing headers should be consumed */ + char _hex_count; + char _state; + uint64_t _total_read; + uint64_t _total_overhead; +}; + +/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- + * encoding headers. When the function returns without an error, bufsz is + * updated to the length of the decoded data available. Applications should + * repeatedly call the function while it returns -2 (incomplete) every time + * supplying newly arrived data. If the end of the chunked-encoded data is + * found, the function returns a non-negative number indicating the number of + * octets left undecoded, that starts from the offset returned by `*bufsz`. + * Returns -1 on error. + */ +ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, + size_t *bufsz); + +/* returns if the chunked decoder is in middle of chunked data */ +int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/main/lib/publish.c b/main/lib/publish.c deleted file mode 100644 index 3e54ac7..0000000 --- a/main/lib/publish.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "freertos/idf_additions.h" -#include "freertos/projdefs.h" -#include "meshtalos.h" -#include "mongoose.h" -#include "portmacro.h" -#include -#include -#include - -struct sub *sub_list = NULL; -SemaphoreHandle_t sub_mutex = NULL; -QueueHandle_t sub_jobs = NULL; - -static void send_image(struct mg_connection *c) { - char mp_buffer[IMAGE_BLK_SIZE + 50]; - for (int i = 0; i < IMAGE_SIZE; i += IMAGE_BLK_SIZE) { - mpack_writer_t wr; - mpack_writer_init(&wr, mp_buffer, sizeof(mp_buffer)); - mpack_build_map(&wr); - mpack_write_cstr(&wr, "command"); - mpack_write_cstr(&wr, "update_image"); - mpack_write_cstr(&wr, "index"); - mpack_write_u64(&wr, i); - mpack_write_cstr(&wr, "data"); - mpack_write_bytes(&wr, (const char *)&image_buf[i], - MIN(IMAGE_SIZE, IMAGE_SIZE - i)); - mpack_complete_map(&wr); - size_t used = mpack_writer_buffer_used(&wr); - if (mpack_writer_destroy(&wr) != mpack_ok) { - MG_INFO(("faild build update image mpack")); - return; - } - mg_send(c, mp_buffer, used); - } - mpack_writer_t wr; - mpack_writer_init(&wr, mp_buffer, sizeof(mp_buffer)); - mpack_build_map(&wr); - mpack_write_cstr(&wr, "command"); - mpack_write_cstr(&wr, "push_image"); - size_t used = mpack_writer_buffer_used(&wr); - if (mpack_writer_destroy(&wr) != mpack_ok) { - MG_INFO(("faild build update image mpack")); - return; - } - mg_send(c, mp_buffer, used); -} - -static void sfn(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_OPEN && c->is_listening == 1) { - MG_INFO(("SERVER is listening")); - } else if (ev == MG_EV_ACCEPT) { - MG_INFO(("SERVER accepted a connection")); - struct sub *newsub = calloc(1, sizeof(struct sub)); - for (struct sub **ptr = &sub_list; *ptr != NULL; ptr = &(*ptr)->next) { - if ((*ptr)->next == NULL) { - (*ptr)->next = newsub; - } - } - - } else if (ev == MG_EV_READ) { - } else if (ev == MG_EV_CLOSE) { - MG_INFO(("SERVER disconnected")); - if (sub_list->c == c) { - sub_list = sub_list->next; - } - for (struct sub *ptr = sub_list; ptr != NULL; ptr = ptr->next) { - if (ptr->next && ptr->next->c == c) { - ptr->next = ptr->next->next; - } - } - } else if (ev == MG_EV_ERROR) { - MG_INFO(("SERVER error: %s", (char *)ev_data)); - } else if (ev == MG_EV_POLL) { - struct sync_jobs job; - if (xQueueReceive(sub_jobs, &job, (TickType_t)20) == pdPASS) { - MG_INFO(("SERVER CLEAN QUEUE ")); - for (struct sub *child = sub_list; child != NULL; child = child->next) { - // if (strncmp(child->topic, job.topic, TOPIC_LEN) == 0) { - send_image(child->c); - // } - } - } - } -} - -void mtsh_tcp_sender(struct mg_mgr *mgr) { - sub_mutex = xSemaphoreCreateMutex(); - sub_jobs = xQueueCreate(10, sizeof(struct sync_jobs)); - - if (sub_mutex == NULL || sub_jobs == NULL) { - // errhandle - MG_ERROR(("failed to init tcp jobs")); - return; - } - mg_listen(mgr, "tcp://localhost:3030", &sfn, NULL); -} diff --git a/main/lib/stb_ds.h b/main/lib/stb_ds.h new file mode 100644 index 0000000..a77e4b8 --- /dev/null +++ b/main/lib/stb_ds.h @@ -0,0 +1,2100 @@ +/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019 + + This is a single-header-file library that provides easy-to-use + dynamic arrays and hash tables for C (also works in C++). + + For a gentle introduction: + http://nothings.org/stb_ds + + To use this library, do this in *one* C or C++ file: + #define STB_DS_IMPLEMENTATION + #include "stb_ds.h" + +TABLE OF CONTENTS + + Table of Contents + Compile-time options + License + Documentation + Notes + Notes - Dynamic arrays + Notes - Hash maps + Credits + +COMPILE-TIME OPTIONS + + #define STBDS_NO_SHORT_NAMES + + This flag needs to be set globally. + + By default stb_ds exposes shorter function names that are not qualified + with the "stbds_" prefix. If these names conflict with the names in your + code, define this flag. + + #define STBDS_SIPHASH_2_4 + + This flag only needs to be set in the file containing #define +STB_DS_IMPLEMENTATION. + + By default stb_ds.h hashes using a weaker variant of SipHash and a custom +hash for 4- and 8-byte keys. On 64-bit platforms, you can define the above flag +to force stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing +so makes hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower +on 64-byte keys, and 10% slower on 256-byte keys on my test computer. + + #define STBDS_REALLOC(context,ptr,size) better_realloc + #define STBDS_FREE(context,ptr) better_free + + These defines only need to be set in the file containing #define +STB_DS_IMPLEMENTATION. + + By default stb_ds uses stdlib realloc() and free() for memory management. +You can substitute your own functions instead by defining these symbols. You +must either define both, or neither. Note that at the moment, 'context' will +always be NULL. + @TODO add an array/hash initialization function that takes a memory context +pointer. + + #define STBDS_UNIT_TESTS + + Defines a function stbds_unit_tests() that checks the functioning of the +data structures. + + Note that on older versions of gcc (e.g. 5.x.x) you may need to build with +'-std=c++0x' (or equivalentally '-std=c++11') when using anonymous structures as +seen on the web page or in STBDS_UNIT_TESTS. + +LICENSE + + Placed in the public domain and also MIT licensed. + See end of file for detailed license information. + +DOCUMENTATION + + Dynamic Arrays + + Non-function interface: + + Declare an empty dynamic array of type T + T* foo = NULL; + + Access the i'th item of a dynamic array 'foo' of type T, T* foo: + foo[i] + + Functions (actually macros) + + arrfree: + void arrfree(T*); + Frees the array. + + arrlen: + ptrdiff_t arrlen(T*); + Returns the number of elements in the array. + + arrlenu: + size_t arrlenu(T*); + Returns the number of elements in the array as an unsigned type. + + arrpop: + T arrpop(T* a) + Removes the final element of the array and returns it. + + arrput: + T arrput(T* a, T b); + Appends the item b to the end of array a. Returns b. + + arrins: + T arrins(T* a, int p, T b); + Inserts the item b into the middle of array a, into a[p], + moving the rest of the array over. Returns b. + + arrinsn: + void arrinsn(T* a, int p, int n); + Inserts n uninitialized items into array a starting at a[p], + moving the rest of the array over. + + arraddnptr: + T* arraddnptr(T* a, int n) + Appends n uninitialized items onto array at the end. + Returns a pointer to the first uninitialized item added. + + arraddnindex: + size_t arraddnindex(T* a, int n) + Appends n uninitialized items onto array at the end. + Returns the index of the first uninitialized item added. + + arrdel: + void arrdel(T* a, int p); + Deletes the element at a[p], moving the rest of the array over. + + arrdeln: + void arrdeln(T* a, int p, int n); + Deletes n elements starting at a[p], moving the rest of the array +over. + + arrdelswap: + void arrdelswap(T* a, int p); + Deletes the element at a[p], replacing it with the element from + the end of the array. O(1) performance. + + arrsetlen: + void arrsetlen(T* a, int n); + Changes the length of the array to n. Allocates uninitialized + slots at the end if necessary. + + arrsetcap: + size_t arrsetcap(T* a, int n); + Sets the length of allocated storage to at least n. It will not + change the length of the array. + + arrcap: + size_t arrcap(T* a); + Returns the number of total elements the array can contain without + needing to be reallocated. + + Hash maps & String hash maps + + Given T is a structure type: struct { TK key; TV value; }. Note that some + functions do not require TV value and can have other fields. For string + hash maps, TK must be 'char *'. + + Special interface: + + stbds_rand_seed: + void stbds_rand_seed(size_t seed); + For security against adversarially chosen data, you should seed the + library with a strong random number. Or at least seed it with time(). + + stbds_hash_string: + size_t stbds_hash_string(char *str, size_t seed); + Returns a hash value for a string. + + stbds_hash_bytes: + size_t stbds_hash_bytes(void *p, size_t len, size_t seed); + These functions hash an arbitrary number of bytes. The function + uses a custom hash for 4- and 8-byte data, and a weakened version + of SipHash for everything else. On 64-bit platforms you can get + specification-compliant SipHash-2-4 on all data by defining + STBDS_SIPHASH_2_4, at a significant cost in speed. + + Non-function interface: + + Declare an empty hash map of type T + T* foo = NULL; + + Access the i'th entry in a hash table T* foo: + foo[i] + + Function interface (actually macros): + + hmfree + shfree + void hmfree(T*); + void shfree(T*); + Frees the hashmap and sets the pointer to NULL. + + hmlen + shlen + ptrdiff_t hmlen(T*) + ptrdiff_t shlen(T*) + Returns the number of elements in the hashmap. + + hmlenu + shlenu + size_t hmlenu(T*) + size_t shlenu(T*) + Returns the number of elements in the hashmap. + + hmgeti + shgeti + hmgeti_ts + ptrdiff_t hmgeti(T*, TK key) + ptrdiff_t shgeti(T*, char* key) + ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar) + Returns the index in the hashmap which has the key 'key', or -1 + if the key is not present. + + hmget + hmget_ts + shget + TV hmget(T*, TK key) + TV shget(T*, char* key) + TV hmget_ts(T*, TK key, ptrdiff_t tempvar) + Returns the value corresponding to 'key' in the hashmap. + The structure must have a 'value' field + + hmgets + shgets + T hmgets(T*, TK key) + T shgets(T*, char* key) + Returns the structure corresponding to 'key' in the hashmap. + + hmgetp + shgetp + hmgetp_ts + hmgetp_null + shgetp_null + T* hmgetp(T*, TK key) + T* shgetp(T*, char* key) + T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar) + T* hmgetp_null(T*, TK key) + T* shgetp_null(T*, char *key) + Returns a pointer to the structure corresponding to 'key' in + the hashmap. Functions ending in "_null" return NULL if the key + is not present in the hashmap; the others return a pointer to a + structure holding the default value (but not the searched-for key). + + hmdefault + shdefault + TV hmdefault(T*, TV value) + TV shdefault(T*, TV value) + Sets the default value for the hashmap, the value which will be + returned by hmget/shget if the key is not present. + + hmdefaults + shdefaults + TV hmdefaults(T*, T item) + TV shdefaults(T*, T item) + Sets the default struct for the hashmap, the contents which will be + returned by hmgets/shgets if the key is not present. + + hmput + shput + TV hmput(T*, TK key, TV value) + TV shput(T*, char* key, TV value) + Inserts a pair into the hashmap. If the key is already + present in the hashmap, updates its value. + + hmputs + shputs + T hmputs(T*, T item) + T shputs(T*, T item) + Inserts a struct with T.key into the hashmap. If the struct is already + present in the hashmap, updates it. + + hmdel + shdel + int hmdel(T*, TK key) + int shdel(T*, char* key) + If 'key' is in the hashmap, deletes its entry and returns 1. + Otherwise returns 0. + + Function interface (actually macros) for strings only: + + sh_new_strdup + void sh_new_strdup(T*); + Overwrites the existing pointer with a newly allocated + string hashmap which will automatically allocate and free + each string key using realloc/free + + sh_new_arena + void sh_new_arena(T*); + Overwrites the existing pointer with a newly allocated + string hashmap which will automatically allocate each string + key to a string arena. Every string key ever used by this + hash table remains in the arena until the arena is freed. + Additionally, any key which is deleted and reinserted will + be allocated multiple times in the string arena. + +NOTES + + * These data structures are realloc'd when they grow, and the macro + "functions" write to the provided pointer. This means: (a) the pointer + must be an lvalue, and (b) the pointer to the data structure is not + stable, and you must maintain it the same as you would a realloc'd + pointer. For example, if you pass a pointer to a dynamic array to a + function which updates it, the function must return back the new + pointer to the caller. This is the price of trying to do this in C. + + * The following are the only functions that are thread-safe on a single data + structure, i.e. can be run in multiple threads simultaneously on the same + data structure + hmlen shlen + hmlenu shlenu + hmget_ts shget_ts + hmgeti_ts shgeti_ts + hmgets_ts shgets_ts + + * You iterate over the contents of a dynamic array and a hashmap in exactly + the same way, using arrlen/hmlen/shlen: + + for (i=0; i < arrlen(foo); ++i) + ... foo[i] ... + + * All operations except arrins/arrdel are O(1) amortized, but individual + operations can be slow, so these data structures may not be suitable + for real time use. Dynamic arrays double in capacity as needed, so + elements are copied an average of once. Hash tables double/halve + their size as needed, with appropriate hysteresis to maintain O(1) + performance. + +NOTES - DYNAMIC ARRAY + + * If you know how long a dynamic array is going to be in advance, you can +avoid extra memory allocations by using arrsetlen to allocate it to that length +in advance and use foo[n] while filling it out, or arrsetcap to allocate the +memory for that length and use arrput/arrpush as normal. + + * Unlike some other versions of the dynamic array, this version should + be safe to use with strict-aliasing optimizations. + +NOTES - HASH MAP + + * For compilers other than GCC and clang (e.g. Visual Studio), for +hmput/hmget/hmdel and variants, the key must be an lvalue (so the macro can take +the address of it). Extensions are used that eliminate this requirement if +you're using C99 and later in GCC or clang, or if you're using C++ in GCC. But +note that this can make your code less portable. + + * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'. + + * The iteration order of your data in the hashmap is determined solely by the + order of insertions and deletions. In particular, if you never delete, new + keys are always added at the end of the array. This will be consistent + across all platforms and versions of the library. However, you should not + attempt to serialize the internal hash table, as the hash is not consistent + between different platforms, and may change with future versions of the +library. + + * Use sh_new_arena() for string hashmaps that you never delete from. +Initialize with NULL if you're managing the memory for your strings, or your +strings are never freed (at least until the hashmap is freed). Otherwise, use +sh_new_strdup(). + @TODO: make an arena variant that garbage collects the strings with a +trivial copy collector into a new arena whenever the table shrinks / rebuilds. +Since current arena recommendation is to only use arena if it never deletes, +then this can just replace current arena implementation. + + * If adversarial input is a serious concern and you're on a 64-bit platform, + enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass + a strong random number to stbds_rand_seed. + + * The default value for the hash table is stored in foo[-1], so if you + use code like 'hmget(T,k)->value = 5' you can accidentally overwrite + the value stored by hmdefault if 'k' is not present. + +CREDITS + + Sean Barrett -- library, idea for dynamic array API/implementation + Per Vognsen -- idea for hash table API/implementation + Rafael Sachetto -- arrpop() + github:HeroicKatora -- arraddn() reworking + + Bugfixes: + Andy Durdin + Shane Liesegang + Vinh Truong + Andreas Molzer + github:hashitaku + github:srdjanstipic + Macoy Madson + Andreas Vennstrom + Tobias Mansfield-Williams +*/ + +#ifdef STBDS_UNIT_TESTS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef INCLUDE_STB_DS_H +#define INCLUDE_STB_DS_H + +#include +#include + +#ifndef STBDS_NO_SHORT_NAMES +#define arrlen stbds_arrlen +#define arrlenu stbds_arrlenu +#define arrput stbds_arrput +#define arrpush stbds_arrput +#define arrpop stbds_arrpop +#define arrfree stbds_arrfree +#define arraddn stbds_arraddn // deprecated, use one of the following instead: +#define arraddnptr stbds_arraddnptr +#define arraddnindex stbds_arraddnindex +#define arrsetlen stbds_arrsetlen +#define arrlast stbds_arrlast +#define arrins stbds_arrins +#define arrinsn stbds_arrinsn +#define arrdel stbds_arrdel +#define arrdeln stbds_arrdeln +#define arrdelswap stbds_arrdelswap +#define arrcap stbds_arrcap +#define arrsetcap stbds_arrsetcap + +#define hmput stbds_hmput +#define hmputs stbds_hmputs +#define hmget stbds_hmget +#define hmget_ts stbds_hmget_ts +#define hmgets stbds_hmgets +#define hmgetp stbds_hmgetp +#define hmgetp_ts stbds_hmgetp_ts +#define hmgetp_null stbds_hmgetp_null +#define hmgeti stbds_hmgeti +#define hmgeti_ts stbds_hmgeti_ts +#define hmdel stbds_hmdel +#define hmlen stbds_hmlen +#define hmlenu stbds_hmlenu +#define hmfree stbds_hmfree +#define hmdefault stbds_hmdefault +#define hmdefaults stbds_hmdefaults + +#define shput stbds_shput +#define shputi stbds_shputi +#define shputs stbds_shputs +#define shget stbds_shget +#define shgeti stbds_shgeti +#define shgets stbds_shgets +#define shgetp stbds_shgetp +#define shgetp_null stbds_shgetp_null +#define shdel stbds_shdel +#define shlen stbds_shlen +#define shlenu stbds_shlenu +#define shfree stbds_shfree +#define shdefault stbds_shdefault +#define shdefaults stbds_shdefaults +#define sh_new_arena stbds_sh_new_arena +#define sh_new_strdup stbds_sh_new_strdup + +#define stralloc stbds_stralloc +#define strreset stbds_strreset +#endif + +#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || \ + !defined(STBDS_REALLOC) && defined(STBDS_FREE) +#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither." +#endif +#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE) +#include +#define STBDS_REALLOC(c, p, s) realloc(p, s) +#define STBDS_FREE(c, p) free(p) +#endif + +#ifdef _MSC_VER +#define STBDS_NOTUSED(v) (void)(v) +#else +#define STBDS_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// for security against attackers, seed the library with a random number, at +// least time() but stronger is better +extern void stbds_rand_seed(size_t seed); + +// these are the hash functions used internally if you want to test them or use +// them for other purposes +extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed); +extern size_t stbds_hash_string(char *str, size_t seed); + +// this is a simple string arena allocator, initialize with e.g. +// 'stbds_string_arena my_arena={0}'. +typedef struct stbds_string_arena stbds_string_arena; +extern char *stbds_stralloc(stbds_string_arena *a, char *str); +extern void stbds_strreset(stbds_string_arena *a); + +// have to #define STBDS_UNIT_TESTS to call this +extern void stbds_unit_tests(void); + +/////////////// +// +// Everything below here is implementation details +// + +extern void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, + size_t min_cap); +extern void stbds_arrfreef(void *a); +extern void stbds_hmfree_func(void *p, size_t elemsize); +extern void *stbds_hmget_key(void *a, size_t elemsize, void *key, + size_t keysize, int mode); +extern void *stbds_hmget_key_ts(void *a, size_t elemsize, void *key, + size_t keysize, ptrdiff_t *temp, int mode); +extern void *stbds_hmput_default(void *a, size_t elemsize); +extern void *stbds_hmput_key(void *a, size_t elemsize, void *key, + size_t keysize, int mode); +extern void *stbds_hmdel_key(void *a, size_t elemsize, void *key, + size_t keysize, size_t keyoffset, int mode); +extern void *stbds_shmode_func(size_t elemsize, int mode); + +#ifdef __cplusplus +} +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define STBDS_HAS_TYPEOF +#ifdef __cplusplus +// #define STBDS_HAS_LITERAL_ARRAY // this is currently broken for clang +#endif +#endif + +#if !defined(__cplusplus) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define STBDS_HAS_LITERAL_ARRAY +#endif +#endif + +// this macro takes the address of the argument, but on gcc/clang can accept +// rvalues +#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF) +#if __clang__ +#define STBDS_ADDRESSOF(typevar, value) \ + ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value +#else +#define STBDS_ADDRESSOF(typevar, value) \ + ((typeof(typevar)[1]){value}) // literal array decays to pointer to value +#endif +#else +#define STBDS_ADDRESSOF(typevar, value) &(value) +#endif + +#define STBDS_OFFSETOF(var, field) ((char *)&(var)->field - (char *)(var)) + +#define stbds_header(t) ((stbds_array_header *)(t) - 1) +#define stbds_temp(t) stbds_header(t)->temp +#define stbds_temp_key(t) (*(char **)stbds_header(t)->hash_table) + +#define stbds_arrsetcap(a, n) (stbds_arrgrow(a, 0, n)) +#define stbds_arrsetlen(a, n) \ + ((stbds_arrcap(a) < (size_t)(n) ? stbds_arrsetcap((a), (size_t)(n)), 0 : 0), \ + (a) ? stbds_header(a)->length = (size_t)(n) : 0) +#define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0) +#define stbds_arrlen(a) ((a) ? (ptrdiff_t)stbds_header(a)->length : 0) +#define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0) +#define stbds_arrput(a, v) \ + (stbds_arrmaybegrow(a, 1), (a)[stbds_header(a)->length++] = (v)) +#define stbds_arrpush stbds_arrput // synonym +#define stbds_arrpop(a) \ + (stbds_header(a)->length--, (a)[stbds_header(a)->length]) +#define stbds_arraddn(a, n) \ + ((void)(stbds_arraddnindex( \ + a, n))) // deprecated, use one of the following instead: +#define stbds_arraddnptr(a, n) \ + (stbds_arrmaybegrow(a, n), \ + (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length - (n)]) \ + : (a)) +#define stbds_arraddnindex(a, n) \ + (stbds_arrmaybegrow(a, n), \ + (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length - (n)) \ + : stbds_arrlen(a)) +#define stbds_arraddnoff stbds_arraddnindex +#define stbds_arrlast(a) ((a)[stbds_header(a)->length - 1]) +#define stbds_arrfree(a) \ + ((void)((a) ? STBDS_FREE(NULL, stbds_header(a)) : (void)0), (a) = NULL) +#define stbds_arrdel(a, i) stbds_arrdeln(a, i, 1) +#define stbds_arrdeln(a, i, n) \ + (memmove(&(a)[i], &(a)[(i) + (n)], \ + sizeof *(a) * (stbds_header(a)->length - (n) - (i))), \ + stbds_header(a)->length -= (n)) +#define stbds_arrdelswap(a, i) \ + ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1) +#define stbds_arrinsn(a, i, n) \ + (stbds_arraddn((a), (n)), \ + memmove(&(a)[(i) + (n)], &(a)[i], \ + sizeof *(a) * (stbds_header(a)->length - (n) - (i)))) +#define stbds_arrins(a, i, v) (stbds_arrinsn((a), (i), 1), (a)[i] = (v)) + +#define stbds_arrmaybegrow(a, n) \ + ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \ + ? (stbds_arrgrow(a, n, 0), 0) \ + : 0) + +#define stbds_arrgrow(a, b, c) \ + ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c))) + +#define stbds_hmput(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), \ + (void *)STBDS_ADDRESSOF((t)->key, (k)), \ + sizeof(t)->key, 0), \ + (t)[stbds_temp((t) - 1)].key = (k), (t)[stbds_temp((t) - 1)].value = (v)) + +#define stbds_hmputs(t, s) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof(s).key, \ + STBDS_HM_BINARY), \ + (t)[stbds_temp((t) - 1)] = (s)) + +#define stbds_hmgeti(t, k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), \ + (void *)STBDS_ADDRESSOF((t)->key, (k)), \ + sizeof(t)->key, STBDS_HM_BINARY), \ + stbds_temp((t) - 1)) + +#define stbds_hmgeti_ts(t, k, temp) \ + ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), \ + (void *)STBDS_ADDRESSOF((t)->key, (k)), \ + sizeof(t)->key, &(temp), STBDS_HM_BINARY), \ + (temp)) + +#define stbds_hmgetp(t, k) ((void)stbds_hmgeti(t, k), &(t)[stbds_temp((t) - 1)]) + +#define stbds_hmgetp_ts(t, k, temp) \ + ((void)stbds_hmgeti_ts(t, k, temp), &(t)[temp]) + +#define stbds_hmdel(t, k) \ + (((t) = stbds_hmdel_key_wrapper( \ + (t), sizeof *(t), (void *)STBDS_ADDRESSOF((t)->key, (k)), \ + sizeof(t)->key, STBDS_OFFSETOF((t), key), STBDS_HM_BINARY)), \ + (t) ? stbds_temp((t) - 1) : 0) + +#define stbds_hmdefault(t, v) \ + ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v)) + +#define stbds_hmdefaults(t, s) \ + ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s)) + +#define stbds_hmfree(p) \ + ((void)((p) != NULL ? stbds_hmfree_func((p) - 1, sizeof *(p)), 0 : 0), \ + (p) = NULL) + +#define stbds_hmgets(t, k) (*stbds_hmgetp(t, k)) +#define stbds_hmget(t, k) (stbds_hmgetp(t, k)->value) +#define stbds_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t, k, temp)->value) +#define stbds_hmlen(t) ((t) ? (ptrdiff_t)stbds_header((t) - 1)->length - 1 : 0) +#define stbds_hmlenu(t) ((t) ? stbds_header((t) - 1)->length - 1 : 0) +#define stbds_hmgetp_null(t, k) \ + (stbds_hmgeti(t, k) == -1 ? NULL : &(t)[stbds_temp((t) - 1)]) + +#define stbds_shput(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void *)(k), \ + sizeof(t)->key, STBDS_HM_STRING), \ + (t)[stbds_temp((t) - 1)].value = (v)) + +#define stbds_shputi(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void *)(k), \ + sizeof(t)->key, STBDS_HM_STRING), \ + (t)[stbds_temp((t) - 1)].value = (v), stbds_temp((t) - 1)) + +#define stbds_shputs(t, s) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void *)(s).key, \ + sizeof(s).key, STBDS_HM_STRING), \ + (t)[stbds_temp((t) - 1)] = (s), \ + (t)[stbds_temp((t) - 1)].key = stbds_temp_key( \ + (t) - 1)) // above line overwrites whole structure, so must rewrite key + // here if it was allocated internally + +#define stbds_pshput(t, p) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void *)(p)->key, \ + sizeof(p)->key, STBDS_HM_PTR_TO_STRING), \ + (t)[stbds_temp((t) - 1)] = (p)) + +#define stbds_shgeti(t, k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void *)(k), \ + sizeof(t)->key, STBDS_HM_STRING), \ + stbds_temp((t) - 1)) + +#define stbds_pshgeti(t, k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void *)(k), \ + sizeof(*(t))->key, STBDS_HM_PTR_TO_STRING), \ + stbds_temp((t) - 1)) + +#define stbds_shgetp(t, k) ((void)stbds_shgeti(t, k), &(t)[stbds_temp((t) - 1)]) + +#define stbds_pshget(t, k) ((void)stbds_pshgeti(t, k), (t)[stbds_temp((t) - 1)]) + +#define stbds_shdel(t, k) \ + (((t) = \ + stbds_hmdel_key_wrapper((t), sizeof *(t), (void *)(k), sizeof(t)->key, \ + STBDS_OFFSETOF((t), key), STBDS_HM_STRING)), \ + (t) ? stbds_temp((t) - 1) : 0) +#define stbds_pshdel(t, k) \ + (((t) = stbds_hmdel_key_wrapper( \ + (t), sizeof *(t), (void *)(k), sizeof(*(t))->key, \ + STBDS_OFFSETOF(*(t), key), STBDS_HM_PTR_TO_STRING)), \ + (t) ? stbds_temp((t) - 1) : 0) + +#define stbds_sh_new_arena(t) \ + ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA)) +#define stbds_sh_new_strdup(t) \ + ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP)) + +#define stbds_shdefault(t, v) stbds_hmdefault(t, v) +#define stbds_shdefaults(t, s) stbds_hmdefaults(t, s) + +#define stbds_shfree stbds_hmfree +#define stbds_shlenu stbds_hmlenu + +#define stbds_shgets(t, k) (*stbds_shgetp(t, k)) +#define stbds_shget(t, k) (stbds_shgetp(t, k)->value) +#define stbds_shgetp_null(t, k) \ + (stbds_shgeti(t, k) == -1 ? NULL : &(t)[stbds_temp((t) - 1)]) +#define stbds_shlen stbds_hmlen + +typedef struct { + size_t length; + size_t capacity; + void *hash_table; + ptrdiff_t temp; +} stbds_array_header; + +typedef struct stbds_string_block { + struct stbds_string_block *next; + char storage[8]; +} stbds_string_block; + +struct stbds_string_arena { + stbds_string_block *storage; + size_t remaining; + unsigned char block; + unsigned char mode; // this isn't used by the string arena itself +}; + +#define STBDS_HM_BINARY 0 +#define STBDS_HM_STRING 1 + +enum { STBDS_SH_NONE, STBDS_SH_DEFAULT, STBDS_SH_STRDUP, STBDS_SH_ARENA }; + +#ifdef __cplusplus +// in C we use implicit assignment from these void*-returning functions to T*. +// in C++ these templates make the same code work +template +static T *stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, + size_t min_cap) { + return (T *)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap); +} +template +static T *stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, + size_t keysize, int mode) { + return (T *)stbds_hmget_key((void *)a, elemsize, key, keysize, mode); +} +template +static T *stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, + size_t keysize, ptrdiff_t *temp, + int mode) { + return (T *)stbds_hmget_key_ts((void *)a, elemsize, key, keysize, temp, mode); +} +template +static T *stbds_hmput_default_wrapper(T *a, size_t elemsize) { + return (T *)stbds_hmput_default((void *)a, elemsize); +} +template +static T *stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, + size_t keysize, int mode) { + return (T *)stbds_hmput_key((void *)a, elemsize, key, keysize, mode); +} +template +static T *stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, + size_t keysize, size_t keyoffset, int mode) { + return (T *)stbds_hmdel_key((void *)a, elemsize, key, keysize, keyoffset, + mode); +} +template +static T *stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) { + return (T *)stbds_shmode_func(elemsize, mode); +} +#else +#define stbds_arrgrowf_wrapper stbds_arrgrowf +#define stbds_hmget_key_wrapper stbds_hmget_key +#define stbds_hmget_key_ts_wrapper stbds_hmget_key_ts +#define stbds_hmput_default_wrapper stbds_hmput_default +#define stbds_hmput_key_wrapper stbds_hmput_key +#define stbds_hmdel_key_wrapper stbds_hmdel_key +#define stbds_shmode_func_wrapper(t, e, m) stbds_shmode_func(e, m) +#endif + +#endif // INCLUDE_STB_DS_H + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// + +#ifdef STB_DS_IMPLEMENTATION +#include +#include + +#ifndef STBDS_ASSERT +#define STBDS_ASSERT_WAS_UNDEFINED +#define STBDS_ASSERT(x) ((void)0) +#endif + +#ifdef STBDS_STATISTICS +#define STBDS_STATS(x) x +size_t stbds_array_grow; +size_t stbds_hash_grow; +size_t stbds_hash_shrink; +size_t stbds_hash_rebuild; +size_t stbds_hash_probes; +size_t stbds_hash_alloc; +size_t stbds_rehash_probes; +size_t stbds_rehash_items; +#else +#define STBDS_STATS(x) +#endif + +// +// stbds_arr implementation +// + +// int *prev_allocs[65536]; +// int num_prev; + +void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) { + stbds_array_header temp = {0}; // force debugging + void *b; + size_t min_len = stbds_arrlen(a) + addlen; + (void)sizeof(temp); + + // compute the minimum capacity needed + if (min_len > min_cap) + min_cap = min_len; + + if (min_cap <= stbds_arrcap(a)) + return a; + + // increase needed capacity to guarantee O(1) amortized + if (min_cap < 2 * stbds_arrcap(a)) + min_cap = 2 * stbds_arrcap(a); + else if (min_cap < 4) + min_cap = 4; + + // if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) + // a+1); if (num_prev == 2201) + // num_prev = num_prev; + b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, + elemsize * min_cap + sizeof(stbds_array_header)); + // if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b; + b = (char *)b + sizeof(stbds_array_header); + if (a == NULL) { + stbds_header(b)->length = 0; + stbds_header(b)->hash_table = 0; + stbds_header(b)->temp = 0; + } else { + STBDS_STATS(++stbds_array_grow); + } + stbds_header(b)->capacity = min_cap; + + return b; +} + +void stbds_arrfreef(void *a) { STBDS_FREE(NULL, stbds_header(a)); } + +// +// stbds_hm hash table implementation +// + +#ifdef STBDS_INTERNAL_SMALL_BUCKET +#define STBDS_BUCKET_LENGTH 4 +#else +#define STBDS_BUCKET_LENGTH 8 +#endif + +#define STBDS_BUCKET_SHIFT (STBDS_BUCKET_LENGTH == 8 ? 3 : 2) +#define STBDS_BUCKET_MASK (STBDS_BUCKET_LENGTH - 1) +#define STBDS_CACHE_LINE_SIZE 64 + +#define STBDS_ALIGN_FWD(n, a) (((n) + (a) - 1) & ~((a) - 1)) + +typedef struct { + size_t hash[STBDS_BUCKET_LENGTH]; + ptrdiff_t index[STBDS_BUCKET_LENGTH]; +} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, + // each array is one 64-byte cache line + +typedef struct { + char *temp_key; // this MUST be the first field of the hash table + size_t slot_count; + size_t used_count; + size_t used_count_threshold; + size_t used_count_shrink_threshold; + size_t tombstone_count; + size_t tombstone_count_threshold; + size_t seed; + size_t slot_count_log2; + stbds_string_arena string; + stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned + // storage after this struct +} stbds_hash_index; + +#define STBDS_INDEX_EMPTY -1 +#define STBDS_INDEX_DELETED -2 +#define STBDS_INDEX_IN_USE(x) ((x) >= 0) + +#define STBDS_HASH_EMPTY 0 +#define STBDS_HASH_DELETED 1 + +static size_t stbds_hash_seed = 0x31415926; + +void stbds_rand_seed(size_t seed) { stbds_hash_seed = seed; } + +#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo) \ + temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, \ + temp >>= 16, /* discard if 32-bit */ \ + var = v64_hi, var <<= 16, var <<= 16, /* discard if 32-bit */ \ + var ^= temp ^ v32 + +#define STBDS_SIZE_T_BITS ((sizeof(size_t)) * 8) + +static size_t stbds_probe_position(size_t hash, size_t slot_count, + size_t slot_log2) { + size_t pos; + STBDS_NOTUSED(slot_log2); + pos = hash & (slot_count - 1); +#ifdef STBDS_INTERNAL_BUCKET_START + pos &= ~STBDS_BUCKET_MASK; +#endif + return pos; +} + +static size_t stbds_log2(size_t slot_count) { + size_t n = 0; + while (slot_count > 1) { + slot_count >>= 1; + ++n; + } + return n; +} + +static stbds_hash_index *stbds_make_hash_index(size_t slot_count, + stbds_hash_index *ot) { + stbds_hash_index *t; + t = (stbds_hash_index *)STBDS_REALLOC( + NULL, 0, + (slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE - 1); + t->storage = (stbds_hash_bucket *)STBDS_ALIGN_FWD((size_t)(t + 1), + STBDS_CACHE_LINE_SIZE); + t->slot_count = slot_count; + t->slot_count_log2 = stbds_log2(slot_count); + t->tombstone_count = 0; + t->used_count = 0; + +#if 0 // A1 + t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow + t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink +#elif 1 // A2 + // t->used_count_threshold = slot_count*12/16; // if 12/16th of table + // is occupied, grow t->tombstone_count_threshold = slot_count* 3/16; // if + // tombstones are 3/16th of table, rebuild t->used_count_shrink_threshold = + // slot_count* 4/16; // if table is only 4/16th full, shrink + + // compute without overflowing + t->used_count_threshold = slot_count - (slot_count >> 2); + t->tombstone_count_threshold = (slot_count >> 3) + (slot_count >> 4); + t->used_count_shrink_threshold = slot_count >> 2; + +#elif 0 // B1 + t->used_count_threshold = + slot_count * 13 / 16; // if 13/16th of table is occupied, grow + t->tombstone_count_threshold = + slot_count * 2 / 16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = + slot_count * 5 / 16; // if table is only 5/16th full, shrink +#else // C1 + t->used_count_threshold = + slot_count * 14 / 16; // if 14/16th of table is occupied, grow + t->tombstone_count_threshold = + slot_count * 2 / 16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = + slot_count * 6 / 16; // if table is only 6/16th full, shrink +#endif + // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled + // with clang 7.0.1 -O2 Note that the larger tables have high variance as they + // were run fewer times + // A1 A2 B1 C1 + // 0.10ms : 0.10ms : 0.10ms : 0.11ms : 2,000 inserts + // creating 2K table 0.96ms : 0.95ms : 0.97ms : 1.04ms : 20,000 + // inserts creating 20K table + // 14.48ms : 14.46ms : 10.63ms : 11.00ms : 200,000 inserts + // creating 200K table + // 195.74ms : 196.35ms : 203.69ms : 214.92ms : 2,000,000 inserts + // creating 2M table + // 2193.88ms : 2209.22ms : 2285.54ms : 2437.17ms : 20,000,000 inserts + // creating 20M table + // 65.27ms : 53.77ms : 65.33ms : 65.47ms : 500,000 inserts & + // deletes in 2K table 72.78ms : 62.45ms : 71.95ms : 72.85ms : + // 500,000 inserts & deletes in 20K table 89.47ms : 77.72ms : 96.49ms + // : 96.75ms : 500,000 inserts & deletes in 200K table 97.58ms + // : 98.14ms : 97.18ms : 97.53ms : 500,000 inserts & deletes in 2M + // table + // 118.61ms : 119.62ms : 120.16ms : 118.86ms : 500,000 inserts & + // deletes in 20M table 192.11ms : 194.39ms : 196.38ms : 195.73ms : + // 500,000 inserts & deletes in 200M table + + if (slot_count <= STBDS_BUCKET_LENGTH) + t->used_count_shrink_threshold = 0; + // to avoid infinite loop, we need to guarantee that at least one slot is + // empty and will terminate probes + STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < + t->slot_count); + STBDS_STATS(++stbds_hash_alloc); + if (ot) { + t->string = ot->string; + // reuse old seed so we can reuse old hashes so below "copy out old data" + // doesn't do any hashing + t->seed = ot->seed; + } else { + size_t a, b, temp; + memset(&t->string, 0, sizeof(t->string)); + t->seed = stbds_hash_seed; + // LCG + // in 32-bit, a = 2147001325 b = 715136305 + // in 64-bit, a = 2862933555777941757 b = 3037000493 + stbds_load_32_or_64(a, temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd); + stbds_load_32_or_64(b, temp, 715136305, 0, 0xb504f32d); + stbds_hash_seed = stbds_hash_seed * a + b; + } + + { + size_t i, j; + for (i = 0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *b = &t->storage[i]; + for (j = 0; j < STBDS_BUCKET_LENGTH; ++j) + b->hash[j] = STBDS_HASH_EMPTY; + for (j = 0; j < STBDS_BUCKET_LENGTH; ++j) + b->index[j] = STBDS_INDEX_EMPTY; + } + } + + // copy out the old data, if any + if (ot) { + size_t i, j; + t->used_count = ot->used_count; + for (i = 0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *ob = &ot->storage[i]; + for (j = 0; j < STBDS_BUCKET_LENGTH; ++j) { + if (STBDS_INDEX_IN_USE(ob->index[j])) { + size_t hash = ob->hash[j]; + size_t pos = + stbds_probe_position(hash, t->slot_count, t->slot_count_log2); + size_t step = STBDS_BUCKET_LENGTH; + STBDS_STATS(++stbds_rehash_items); + for (;;) { + size_t limit, z; + stbds_hash_bucket *bucket; + bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT]; + STBDS_STATS(++stbds_rehash_probes); + + for (z = pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) { + if (bucket->hash[z] == 0) { + bucket->hash[z] = hash; + bucket->index[z] = ob->index[j]; + goto done; + } + } + + limit = pos & STBDS_BUCKET_MASK; + for (z = 0; z < limit; ++z) { + if (bucket->hash[z] == 0) { + bucket->hash[z] = hash; + bucket->index[z] = ob->index[j]; + goto done; + } + } + + pos += step; // quadratic probing + step += STBDS_BUCKET_LENGTH; + pos &= (t->slot_count - 1); + } + } + done:; + } + } + } + + return t; +} + +#define STBDS_ROTATE_LEFT(val, n) \ + (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n)))) +#define STBDS_ROTATE_RIGHT(val, n) \ + (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n)))) + +size_t stbds_hash_string(char *str, size_t seed) { + size_t hash = seed; + while (*str) + hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char)*str++; + + // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits + hash ^= seed; + hash = (~hash) + (hash << 18); + hash ^= hash ^ STBDS_ROTATE_RIGHT(hash, 31); + hash = hash * 21; + hash ^= hash ^ STBDS_ROTATE_RIGHT(hash, 11); + hash += (hash << 6); + hash ^= STBDS_ROTATE_RIGHT(hash, 22); + return hash + seed; +} + +#ifdef STBDS_SIPHASH_2_4 +#define STBDS_SIPHASH_C_ROUNDS 2 +#define STBDS_SIPHASH_D_ROUNDS 4 +typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds + [sizeof(size_t) == 8 ? 1 : -1]; +#endif + +#ifndef STBDS_SIPHASH_C_ROUNDS +#define STBDS_SIPHASH_C_ROUNDS 1 +#endif +#ifndef STBDS_SIPHASH_D_ROUNDS +#define STBDS_SIPHASH_D_ROUNDS 1 +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant, for + // do..while(0) and sizeof()== +#endif + +static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) { + unsigned char *d = (unsigned char *)p; + size_t i, j; + size_t v0, v1, v2, v3, data; + + // hash that works on 32- or 64-bit registers without knowing which we have + // (computes different results on 32-bit and 64-bit platform) + // derived from siphash, but on 32-bit platforms very different as it uses 4 + // 32-bit state not 4 64-bit + v0 = ((((size_t)0x736f6d65 << 16) << 16) + 0x70736575) ^ seed; + v1 = ((((size_t)0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed; + v2 = ((((size_t)0x6c796765 << 16) << 16) + 0x6e657261) ^ seed; + v3 = ((((size_t)0x74656462 << 16) << 16) + 0x79746573) ^ ~seed; + +#ifdef STBDS_TEST_SIPHASH_2_4 + // hardcoded with key material in the siphash test vectors + v0 ^= 0x0706050403020100ull ^ seed; + v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; + v2 ^= 0x0706050403020100ull ^ seed; + v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; +#endif + +#define STBDS_SIPROUND() \ + do { \ + v0 += v1; \ + v1 = STBDS_ROTATE_LEFT(v1, 13); \ + v1 ^= v0; \ + v0 = STBDS_ROTATE_LEFT(v0, STBDS_SIZE_T_BITS / 2); \ + v2 += v3; \ + v3 = STBDS_ROTATE_LEFT(v3, 16); \ + v3 ^= v2; \ + v2 += v1; \ + v1 = STBDS_ROTATE_LEFT(v1, 17); \ + v1 ^= v2; \ + v2 = STBDS_ROTATE_LEFT(v2, STBDS_SIZE_T_BITS / 2); \ + v0 += v3; \ + v3 = STBDS_ROTATE_LEFT(v3, 21); \ + v3 ^= v0; \ + } while (0) + + for (i = 0; i + sizeof(size_t) <= len; + i += sizeof(size_t), d += sizeof(size_t)) { + data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + data |= (size_t)(d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) + << 16 << 16; // discarded if size_t == 4 + + v3 ^= data; + for (j = 0; j < STBDS_SIPHASH_C_ROUNDS; ++j) + STBDS_SIPROUND(); + v0 ^= data; + } + data = len << (STBDS_SIZE_T_BITS - 8); + switch (len - i) { + case 7: + data |= ((size_t)d[6] << 24) << 24; // fall through + case 6: + data |= ((size_t)d[5] << 20) << 20; // fall through + case 5: + data |= ((size_t)d[4] << 16) << 16; // fall through + case 4: + data |= (d[3] << 24); // fall through + case 3: + data |= (d[2] << 16); // fall through + case 2: + data |= (d[1] << 8); // fall through + case 1: + data |= d[0]; // fall through + case 0: + break; + } + v3 ^= data; + for (j = 0; j < STBDS_SIPHASH_C_ROUNDS; ++j) + STBDS_SIPROUND(); + v0 ^= data; + v2 ^= 0xff; + for (j = 0; j < STBDS_SIPHASH_D_ROUNDS; ++j) + STBDS_SIPROUND(); + +#ifdef STBDS_SIPHASH_2_4 + return v0 ^ v1 ^ v2 ^ v3; +#else + return v1 ^ v2 ^ v3; // slightly stronger since v0^v3 in above cancels out + // final round operation? I tweeted at the authors of + // SipHash about this but they didn't reply +#endif +} + +size_t stbds_hash_bytes(void *p, size_t len, size_t seed) { +#ifdef STBDS_SIPHASH_2_4 + return stbds_siphash_bytes(p, len, seed); +#else + unsigned char *d = (unsigned char *)p; + + if (len == 4) { + unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); +#if 0 + // HASH32-A Bob Jenkin's hash function w/o large constants + hash ^= seed; + hash -= (hash<<6); + hash ^= (hash>>17); + hash -= (hash<<9); + hash ^= seed; + hash ^= (hash<<4); + hash -= (hash<<3); + hash ^= (hash<<10); + hash ^= (hash>>15); +#elif 1 + // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash + // with rotates turned into shifts. Note that converting these back to + // rotates makes it run a lot slower, presumably due to collisions, so I'm + // not really sure what's going on. + hash ^= seed; + hash = (hash ^ 61) ^ (hash >> 16); + hash = hash + (hash << 3); + hash = hash ^ (hash >> 4); + hash = hash * 0x27d4eb2d; + hash ^= seed; + hash = hash ^ (hash >> 15); +#else // HASH32-C - Murmur3 + hash ^= seed; + hash *= 0xcc9e2d51; + hash = (hash << 17) | (hash >> 15); + hash *= 0x1b873593; + hash ^= seed; + hash = (hash << 19) | (hash >> 13); + hash = hash * 5 + 0xe6546b64; + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= seed; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; +#endif + // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled + // with clang 7.0.1 -O2 Note that the larger tables have high variance as + // they were run fewer times + // HASH32-A // HASH32-BB // HASH32-C + // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K + // table 0.96ms // 0.95ms // 0.99ms : 20,000 inserts + // creating 20K table + // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating + // 200K table + // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M + // table + // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M + // table + // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K + // table 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes + // in 20K table 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & + // deletes in 200K table + // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M + // table 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes + // in 20M table 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & + // deletes in 200M table + // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K + // table with varying key spacing + + return (((size_t)hash << 16 << 16) | hash) ^ seed; + } else if (len == 8 && sizeof(size_t) == 8) { + size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + hash |= (size_t)(d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) + << 16 << 16; // avoid warning if size_t == 4 + hash ^= seed; + hash = (~hash) + (hash << 21); + hash ^= STBDS_ROTATE_RIGHT(hash, 24); + hash *= 265; + hash ^= STBDS_ROTATE_RIGHT(hash, 14); + hash ^= seed; + hash *= 21; + hash ^= STBDS_ROTATE_RIGHT(hash, 28); + hash += (hash << 31); + hash = (~hash) + (hash << 18); + return hash; + } else { + return stbds_siphash_bytes(p, len, seed); + } +#endif +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +static int stbds_is_key_equal(void *a, size_t elemsize, void *key, + size_t keysize, size_t keyoffset, int mode, + size_t i) { + if (mode >= STBDS_HM_STRING) + return 0 == strcmp((char *)key, + *(char **)((char *)a + elemsize * i + keyoffset)); + else + return 0 == memcmp(key, (char *)a + elemsize * i + keyoffset, keysize); +} + +#define STBDS_HASH_TO_ARR(x, elemsize) ((char *)(x) - (elemsize)) +#define STBDS_ARR_TO_HASH(x, elemsize) ((char *)(x) + (elemsize)) + +#define stbds_hash_table(a) ((stbds_hash_index *)stbds_header(a)->hash_table) + +void stbds_hmfree_func(void *a, size_t elemsize) { + if (a == NULL) + return; + if (stbds_hash_table(a) != NULL) { + if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { + size_t i; + // skip 0th element, which is default + for (i = 1; i < stbds_header(a)->length; ++i) + STBDS_FREE(NULL, *(char **)((char *)a + elemsize * i)); + } + stbds_strreset(&stbds_hash_table(a)->string); + } + STBDS_FREE(NULL, stbds_header(a)->hash_table); + STBDS_FREE(NULL, stbds_header(a)); +} + +static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, + size_t keysize, size_t keyoffset, + int mode) { + void *raw_a = STBDS_HASH_TO_ARR(a, elemsize); + stbds_hash_index *table = stbds_hash_table(raw_a); + size_t hash = mode >= STBDS_HM_STRING + ? stbds_hash_string((char *)key, table->seed) + : stbds_hash_bytes(key, keysize, table->seed); + size_t step = STBDS_BUCKET_LENGTH; + size_t limit, i; + size_t pos; + stbds_hash_bucket *bucket; + + if (hash < 2) + hash += 2; // stored hash values are forbidden from being 0, so we can + // detect empty slots + + pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); + + for (;;) { + STBDS_STATS(++stbds_hash_probes); + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + + // start searching from pos to end of bucket, this should help performance + // on small hash tables that fit in cache + for (i = pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, + bucket->index[i])) { + return (pos & ~STBDS_BUCKET_MASK) + i; + } + } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { + return -1; + } + } + + // search from beginning of bucket to pos + limit = pos & STBDS_BUCKET_MASK; + for (i = 0; i < limit; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, + bucket->index[i])) { + return (pos & ~STBDS_BUCKET_MASK) + i; + } + } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { + return -1; + } + } + + // quadratic probing + pos += step; + step += STBDS_BUCKET_LENGTH; + pos &= (table->slot_count - 1); + } + /* NOTREACHED */ +} + +void *stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, + ptrdiff_t *temp, int mode) { + size_t keyoffset = 0; + if (a == NULL) { + // make it non-empty so we can return a temp + a = stbds_arrgrowf(0, elemsize, 0, 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + *temp = STBDS_INDEX_EMPTY; + // adjust a to point after the default element + return STBDS_ARR_TO_HASH(a, elemsize); + } else { + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a, elemsize); + // adjust a to point to the default element + table = (stbds_hash_index *)stbds_header(raw_a)->hash_table; + if (table == 0) { + *temp = -1; + } else { + ptrdiff_t slot = + stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) { + *temp = STBDS_INDEX_EMPTY; + } else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + *temp = b->index[slot & STBDS_BUCKET_MASK]; + } + } + return a; + } +} + +void *stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, + int mode) { + ptrdiff_t temp; + void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode); + stbds_temp(STBDS_HASH_TO_ARR(p, elemsize)) = temp; + return p; +} + +void *stbds_hmput_default(void *a, size_t elemsize) { + // three cases: + // a is NULL <- allocate + // a has a hash table but no entries, because of shmode <- grow + // a has entries <- do nothing + if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a, elemsize))->length == 0) { + a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a, elemsize) : NULL, elemsize, 0, + 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + a = STBDS_ARR_TO_HASH(a, elemsize); + } + return a; +} + +static char *stbds_strdup(char *str); + +void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, + int mode) { + size_t keyoffset = 0; + void *raw_a; + stbds_hash_index *table; + + if (a == NULL) { + a = stbds_arrgrowf(0, elemsize, 0, 1); + memset(a, 0, elemsize); + stbds_header(a)->length += 1; + // adjust a to point AFTER the default element + a = STBDS_ARR_TO_HASH(a, elemsize); + } + + // adjust a to point to the default element + raw_a = a; + a = STBDS_HASH_TO_ARR(a, elemsize); + + table = (stbds_hash_index *)stbds_header(a)->hash_table; + + if (table == NULL || table->used_count >= table->used_count_threshold) { + stbds_hash_index *nt; + size_t slot_count; + + slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count * 2; + nt = stbds_make_hash_index(slot_count, table); + if (table) + STBDS_FREE(NULL, table); + else + nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0; + stbds_header(a)->hash_table = table = nt; + STBDS_STATS(++stbds_hash_grow); + } + + // we iterate hash table explicitly because we want to track if we saw a + // tombstone + { + size_t hash = mode >= STBDS_HM_STRING + ? stbds_hash_string((char *)key, table->seed) + : stbds_hash_bytes(key, keysize, table->seed); + size_t step = STBDS_BUCKET_LENGTH; + size_t pos; + ptrdiff_t tombstone = -1; + stbds_hash_bucket *bucket; + + // stored hash values are forbidden from being 0, so we can detect empty + // slots to early out quickly + if (hash < 2) + hash += 2; + + pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); + + for (;;) { + size_t limit, i; + STBDS_STATS(++stbds_hash_probes); + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + + // start searching from pos to end of bucket + for (i = pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, + bucket->index[i])) { + stbds_temp(a) = bucket->index[i]; + if (mode >= STBDS_HM_STRING) + stbds_temp_key(a) = + *(char **)((char *)raw_a + elemsize * bucket->index[i] + + keyoffset); + return STBDS_ARR_TO_HASH(a, elemsize); + } + } else if (bucket->hash[i] == 0) { + pos = (pos & ~STBDS_BUCKET_MASK) + i; + goto found_empty_slot; + } else if (tombstone < 0) { + if (bucket->index[i] == STBDS_INDEX_DELETED) + tombstone = (ptrdiff_t)((pos & ~STBDS_BUCKET_MASK) + i); + } + } + + // search from beginning of bucket to pos + limit = pos & STBDS_BUCKET_MASK; + for (i = 0; i < limit; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, + bucket->index[i])) { + stbds_temp(a) = bucket->index[i]; + return STBDS_ARR_TO_HASH(a, elemsize); + } + } else if (bucket->hash[i] == 0) { + pos = (pos & ~STBDS_BUCKET_MASK) + i; + goto found_empty_slot; + } else if (tombstone < 0) { + if (bucket->index[i] == STBDS_INDEX_DELETED) + tombstone = (ptrdiff_t)((pos & ~STBDS_BUCKET_MASK) + i); + } + } + + // quadratic probing + pos += step; + step += STBDS_BUCKET_LENGTH; + pos &= (table->slot_count - 1); + } + found_empty_slot: + if (tombstone >= 0) { + pos = tombstone; + --table->tombstone_count; + } + ++table->used_count; + + { + ptrdiff_t i = (ptrdiff_t)stbds_arrlen(a); + // we want to do stbds_arraddn(1), but we can't use the macros since we + // don't have something of the right type + if ((size_t)i + 1 > stbds_arrcap(a)) + *(void **)&a = stbds_arrgrowf(a, elemsize, 1, 0); + raw_a = STBDS_ARR_TO_HASH(a, elemsize); + + STBDS_ASSERT((size_t)i + 1 <= stbds_arrcap(a)); + stbds_header(a)->length = i + 1; + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + bucket->hash[pos & STBDS_BUCKET_MASK] = hash; + bucket->index[pos & STBDS_BUCKET_MASK] = i - 1; + stbds_temp(a) = i - 1; + + switch (table->string.mode) { + case STBDS_SH_STRDUP: + stbds_temp_key(a) = *(char **)((char *)a + elemsize * i) = + stbds_strdup((char *)key); + break; + case STBDS_SH_ARENA: + stbds_temp_key(a) = *(char **)((char *)a + elemsize * i) = + stbds_stralloc(&table->string, (char *)key); + break; + case STBDS_SH_DEFAULT: + stbds_temp_key(a) = *(char **)((char *)a + elemsize * i) = (char *)key; + break; + default: + memcpy((char *)a + elemsize * i, key, keysize); + break; + } + } + return STBDS_ARR_TO_HASH(a, elemsize); + } +} + +void *stbds_shmode_func(size_t elemsize, int mode) { + void *a = stbds_arrgrowf(0, elemsize, 0, 1); + stbds_hash_index *h; + memset(a, 0, elemsize); + stbds_header(a)->length = 1; + stbds_header(a)->hash_table = h = + (stbds_hash_index *)stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL); + h->string.mode = (unsigned char)mode; + return STBDS_ARR_TO_HASH(a, elemsize); +} + +void *stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, + size_t keyoffset, int mode) { + if (a == NULL) { + return 0; + } else { + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a, elemsize); + table = (stbds_hash_index *)stbds_header(raw_a)->hash_table; + stbds_temp(raw_a) = 0; + if (table == 0) { + return a; + } else { + ptrdiff_t slot; + slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) + return a; + else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + int i = slot & STBDS_BUCKET_MASK; + ptrdiff_t old_index = b->index[i]; + ptrdiff_t final_index = + (ptrdiff_t)stbds_arrlen(raw_a) - 1 - + 1; // minus one for the raw_a vs a, and minus one for 'last' + STBDS_ASSERT(slot < (ptrdiff_t)table->slot_count); + --table->used_count; + ++table->tombstone_count; + stbds_temp(raw_a) = 1; + STBDS_ASSERT(table->used_count >= 0); + // STBDS_ASSERT(table->tombstone_count < table->slot_count/4); + b->hash[i] = STBDS_HASH_DELETED; + b->index[i] = STBDS_INDEX_DELETED; + + if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP) + STBDS_FREE(NULL, *(char **)((char *)a + elemsize * old_index)); + + // if indices are the same, memcpy is a no-op, but back-pointer-fixup + // will fail, so skip + if (old_index != final_index) { + // swap delete + memmove((char *)a + elemsize * old_index, + (char *)a + elemsize * final_index, elemsize); + + // now find the slot for the last element + if (mode == STBDS_HM_STRING) + slot = stbds_hm_find_slot( + a, elemsize, + *(char **)((char *)a + elemsize * old_index + keyoffset), + keysize, keyoffset, mode); + else + slot = stbds_hm_find_slot( + a, elemsize, (char *)a + elemsize * old_index + keyoffset, + keysize, keyoffset, mode); + STBDS_ASSERT(slot >= 0); + b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + i = slot & STBDS_BUCKET_MASK; + STBDS_ASSERT(b->index[i] == final_index); + b->index[i] = old_index; + } + stbds_header(raw_a)->length -= 1; + + if (table->used_count < table->used_count_shrink_threshold && + table->slot_count > STBDS_BUCKET_LENGTH) { + stbds_header(raw_a)->hash_table = + stbds_make_hash_index(table->slot_count >> 1, table); + STBDS_FREE(NULL, table); + STBDS_STATS(++stbds_hash_shrink); + } else if (table->tombstone_count > table->tombstone_count_threshold) { + stbds_header(raw_a)->hash_table = + stbds_make_hash_index(table->slot_count, table); + STBDS_FREE(NULL, table); + STBDS_STATS(++stbds_hash_rebuild); + } + + return a; + } + } + } + /* NOTREACHED */ +} + +static char *stbds_strdup(char *str) { + // to keep replaceable allocator simple, we don't want to use strdup. + // rolling our own also avoids problem of strdup vs _strdup + size_t len = strlen(str) + 1; + char *p = (char *)STBDS_REALLOC(NULL, 0, len); + memmove(p, str, len); + return p; +} + +#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN +#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u +#endif +#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX +#define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u << 20) +#endif + +char *stbds_stralloc(stbds_string_arena *a, char *str) { + char *p; + size_t len = strlen(str) + 1; + if (len > a->remaining) { + // compute the next blocksize + size_t blocksize = a->block; + + // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that + // there are log(SIZE) allocations to free when we destroy the table + blocksize = (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize >> 1); + + // if size is under 1M, advance to next blocktype + if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX)) + ++a->block; + + if (len > blocksize) { + // if string is larger than blocksize, then just allocate the full size. + // note that we still advance string_block so block size will continue + // increasing, so e.g. if somebody only calls this with 1000-long strings, + // eventually the arena will start doubling and handling those as well + stbds_string_block *sb = + (stbds_string_block *)STBDS_REALLOC(NULL, 0, sizeof(*sb) - 8 + len); + memmove(sb->storage, str, len); + if (a->storage) { + // insert it after the first element, so that we don't waste the space + // there + sb->next = a->storage->next; + a->storage->next = sb; + } else { + sb->next = 0; + a->storage = sb; + a->remaining = 0; // this is redundant, but good for clarity + } + return sb->storage; + } else { + stbds_string_block *sb = (stbds_string_block *)STBDS_REALLOC( + NULL, 0, sizeof(*sb) - 8 + blocksize); + sb->next = a->storage; + a->storage = sb; + a->remaining = blocksize; + } + } + + STBDS_ASSERT(len <= a->remaining); + p = a->storage->storage + a->remaining - len; + a->remaining -= len; + memmove(p, str, len); + return p; +} + +void stbds_strreset(stbds_string_arena *a) { + stbds_string_block *x, *y; + x = a->storage; + while (x) { + y = x->next; + STBDS_FREE(NULL, x); + x = y; + } + memset(a, 0, sizeof(*a)); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// UNIT TESTS +// + +#ifdef STBDS_UNIT_TESTS +#include +#ifdef STBDS_ASSERT_WAS_UNDEFINED +#undef STBDS_ASSERT +#endif +#ifndef STBDS_ASSERT +#define STBDS_ASSERT assert +#include +#endif + +typedef struct { + int key, b, c, d; +} stbds_struct; +typedef struct { + int key[2], b, c, d; +} stbds_struct2; + +static char buffer[256]; +char *strkey(int n) { +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + sprintf_s(buffer, sizeof(buffer), "test_%d", n); +#else + sprintf(buffer, "test_%d", n); +#endif + return buffer; +} + +void stbds_unit_tests(void) { +#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus) + // VC6 C++ doesn't like the template<> trick on unnamed structures, so do + // nothing! + STBDS_ASSERT(0); +#else + const int testsize = 100000; + const int testsize2 = testsize / 20; + int *arr = NULL; + struct { + int key; + int value; + } *intmap = NULL; + struct { + char *key; + int value; + } *strmap = NULL, s; + struct { + stbds_struct key; + int value; + } *map = NULL; + stbds_struct *map2 = NULL; + stbds_struct2 *map3 = NULL; + stbds_string_arena sa = {0}; + int key3[2] = {1, 2}; + ptrdiff_t temp; + + int i, j; + + STBDS_ASSERT(arrlen(arr) == 0); + for (i = 0; i < 20000; i += 50) { + for (j = 0; j < i; ++j) + arrpush(arr, j); + arrfree(arr); + } + + for (i = 0; i < 4; ++i) { + arrpush(arr, 1); + arrpush(arr, 2); + arrpush(arr, 3); + arrpush(arr, 4); + arrdel(arr, i); + arrfree(arr); + arrpush(arr, 1); + arrpush(arr, 2); + arrpush(arr, 3); + arrpush(arr, 4); + arrdelswap(arr, i); + arrfree(arr); + } + + for (i = 0; i < 5; ++i) { + arrpush(arr, 1); + arrpush(arr, 2); + arrpush(arr, 3); + arrpush(arr, 4); + stbds_arrins(arr, i, 5); + STBDS_ASSERT(arr[i] == 5); + if (i < 4) + STBDS_ASSERT(arr[4] == 4); + arrfree(arr); + } + + i = 1; + STBDS_ASSERT(hmgeti(intmap, i) == -1); + hmdefault(intmap, -2); + STBDS_ASSERT(hmgeti(intmap, i) == -1); + STBDS_ASSERT(hmget(intmap, i) == -2); + for (i = 0; i < testsize; i += 2) + hmput(intmap, i, i * 5); + for (i = 0; i < testsize; i += 1) { + if (i & 1) + STBDS_ASSERT(hmget(intmap, i) == -2); + else + STBDS_ASSERT(hmget(intmap, i) == i * 5); + if (i & 1) + STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2); + else + STBDS_ASSERT(hmget_ts(intmap, i, temp) == i * 5); + } + for (i = 0; i < testsize; i += 2) + hmput(intmap, i, i * 3); + for (i = 0; i < testsize; i += 1) + if (i & 1) + STBDS_ASSERT(hmget(intmap, i) == -2); + else + STBDS_ASSERT(hmget(intmap, i) == i * 3); + for (i = 2; i < testsize; i += 4) + hmdel(intmap, i); // delete half the entries + for (i = 0; i < testsize; i += 1) + if (i & 3) + STBDS_ASSERT(hmget(intmap, i) == -2); + else + STBDS_ASSERT(hmget(intmap, i) == i * 3); + for (i = 0; i < testsize; i += 1) + hmdel(intmap, i); // delete the rest of the entries + for (i = 0; i < testsize; i += 1) + STBDS_ASSERT(hmget(intmap, i) == -2); + hmfree(intmap); + for (i = 0; i < testsize; i += 2) + hmput(intmap, i, i * 3); + hmfree(intmap); + +#if defined(__clang__) || defined(__GNUC__) +#ifndef __cplusplus + intmap = NULL; + hmput(intmap, 15, 7); + hmput(intmap, 11, 3); + hmput(intmap, 9, 5); + STBDS_ASSERT(hmget(intmap, 9) == 5); + STBDS_ASSERT(hmget(intmap, 11) == 3); + STBDS_ASSERT(hmget(intmap, 15) == 7); +#endif +#endif + + for (i = 0; i < testsize; ++i) + stralloc(&sa, strkey(i)); + strreset(&sa); + + { + s.key = "a", s.value = 1; + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key == s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + { + s.key = "a", s.value = 1; + sh_new_strdup(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + { + s.key = "a", s.value = 1; + sh_new_arena(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + for (j = 0; j < 2; ++j) { + STBDS_ASSERT(shgeti(strmap, "foo") == -1); + if (j == 0) + sh_new_strdup(strmap); + else + sh_new_arena(strmap); + STBDS_ASSERT(shgeti(strmap, "foo") == -1); + shdefault(strmap, -2); + STBDS_ASSERT(shgeti(strmap, "foo") == -1); + for (i = 0; i < testsize; i += 2) + shput(strmap, strkey(i), i * 3); + for (i = 0; i < testsize; i += 1) + if (i & 1) + STBDS_ASSERT(shget(strmap, strkey(i)) == -2); + else + STBDS_ASSERT(shget(strmap, strkey(i)) == i * 3); + for (i = 2; i < testsize; i += 4) + shdel(strmap, strkey(i)); // delete half the entries + for (i = 0; i < testsize; i += 1) + if (i & 3) + STBDS_ASSERT(shget(strmap, strkey(i)) == -2); + else + STBDS_ASSERT(shget(strmap, strkey(i)) == i * 3); + for (i = 0; i < testsize; i += 1) + shdel(strmap, strkey(i)); // delete the rest of the entries + for (i = 0; i < testsize; i += 1) + STBDS_ASSERT(shget(strmap, strkey(i)) == -2); + shfree(strmap); + } + + { + struct { + char *key; + char value; + } *hash = NULL; + char name[4] = "jen"; + shput(hash, "bob", 'h'); + shput(hash, "sally", 'e'); + shput(hash, "fred", 'l'); + shput(hash, "jen", 'x'); + shput(hash, "doug", 'o'); + + shput(hash, name, 'l'); + shfree(hash); + } + + for (i = 0; i < testsize; i += 2) { + stbds_struct s = {i, i * 2, i * 3, i * 4}; + hmput(map, s, i * 5); + } + + for (i = 0; i < testsize; i += 1) { + stbds_struct s = {i, i * 2, i * 3, i * 4}; + stbds_struct t = {i, i * 2, i * 3 + 1, i * 4}; + if (i & 1) + STBDS_ASSERT(hmget(map, s) == 0); + else + STBDS_ASSERT(hmget(map, s) == i * 5); + if (i & 1) + STBDS_ASSERT(hmget_ts(map, s, temp) == 0); + else + STBDS_ASSERT(hmget_ts(map, s, temp) == i * 5); + // STBDS_ASSERT(hmget(map, t.key) == 0); + } + + for (i = 0; i < testsize; i += 2) { + stbds_struct s = {i, i * 2, i * 3, i * 4}; + hmputs(map2, s); + } + hmfree(map); + + for (i = 0; i < testsize; i += 1) { + stbds_struct s = {i, i * 2, i * 3, i * 4}; + stbds_struct t = {i, i * 2, i * 3 + 1, i * 4}; + if (i & 1) + STBDS_ASSERT(hmgets(map2, s.key).d == 0); + else + STBDS_ASSERT(hmgets(map2, s.key).d == i * 4); + // STBDS_ASSERT(hmgetp(map2, t.key) == 0); + } + hmfree(map2); + + for (i = 0; i < testsize; i += 2) { + stbds_struct2 s = {{i, i * 2}, i * 3, i * 4, i * 5}; + hmputs(map3, s); + } + for (i = 0; i < testsize; i += 1) { + stbds_struct2 s = {{i, i * 2}, i * 3, i * 4, i * 5}; + stbds_struct2 t = {{i, i * 2}, i * 3 + 1, i * 4, i * 5}; + if (i & 1) + STBDS_ASSERT(hmgets(map3, s.key).d == 0); + else + STBDS_ASSERT(hmgets(map3, s.key).d == i * 5); + // STBDS_ASSERT(hmgetp(map3, t.key) == 0); + } +#endif +} +#endif + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2019 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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/main/mesh_if.c b/main/mesh_if.c deleted file mode 100644 index 089b18d..0000000 --- a/main/mesh_if.c +++ /dev/null @@ -1,483 +0,0 @@ -/* Mesh IP Internal Communication Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include "dhcpserver/dhcpserver.h" -#include "esp_log.h" -#include "esp_mac.h" -#include "esp_mesh.h" -#include "esp_netif.h" -#include "esp_wifi.h" -#include "esp_wifi_netif.h" -#include "lwip/lwip_napt.h" -#include "mesh_netif.h" -#include - -/******************************************************* - * Macros - *******************************************************/ -#define RX_SIZE (1560) - -#if CONFIG_MESH_USE_GLOBAL_DNS_IP -#define DNS_IP_ADDR CONFIG_MESH_GLOBAL_DNS_IP -#endif - -/******************************************************* - * Type Definitions - *******************************************************/ -typedef struct mesh_netif_driver *mesh_netif_driver_t; - -typedef struct mesh_netif_driver { - esp_netif_driver_base_t base; - uint8_t sta_mac_addr[MAC_ADDR_LEN]; -} *mesh_netif_driver_t; - -/******************************************************* - * Constants - *******************************************************/ -static const char *TAG = "mesh_netif"; -const esp_netif_ip_info_t g_mesh_netif_subnet_ip = { - // mesh subnet IP info - .ip = {.addr = ESP_IP4TOADDR(10, 0, 0, 1)}, - .gw = {.addr = ESP_IP4TOADDR(10, 0, 0, 1)}, - .netmask = {.addr = ESP_IP4TOADDR(255, 255, 0, 0)}, -}; - -/******************************************************* - * Variable Definitions - *******************************************************/ -static esp_netif_t *netif_sta = NULL; -static esp_netif_t *netif_ap = NULL; -static bool receive_task_is_running = false; -static mesh_addr_t s_route_table[CONFIG_MESH_ROUTE_TABLE_SIZE] = {0}; -static mesh_raw_recv_cb_t *s_mesh_raw_recv_cb = NULL; - -/******************************************************* - * Function Definitions - *******************************************************/ -// setup DHCP server's DNS OFFER -// -static esp_err_t set_dhcps_dns(esp_netif_t *netif, uint32_t addr) { - esp_netif_dns_info_t dns; - dns.ip.u_addr.ip4.addr = addr; - dns.ip.type = IPADDR_TYPE_V4; - dhcps_offer_t dhcps_dns_value = OFFER_DNS; - ESP_ERROR_CHECK(esp_netif_dhcps_option( - netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, - sizeof(dhcps_dns_value))); - ESP_ERROR_CHECK(esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns)); - ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(netif)); - return ESP_OK; -} - -// Receive task -// -static void receive_task(void *arg) { - esp_err_t err; - mesh_addr_t from; - int flag = 0; - mesh_data_t data; - static uint8_t rx_buf[RX_SIZE] = { - 0, - }; - - ESP_LOGD(TAG, "Receiving task started"); - while (receive_task_is_running) { - data.data = rx_buf; - data.size = RX_SIZE; - err = esp_mesh_recv(&from, &data, portMAX_DELAY, &flag, NULL, 0); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Received with err code %d %s", err, esp_err_to_name(err)); - continue; - } - if (data.proto == MESH_PROTO_BIN && s_mesh_raw_recv_cb) { - s_mesh_raw_recv_cb(&from, &data); - } - if (esp_mesh_is_root()) { - if (data.proto == MESH_PROTO_AP) { - ESP_LOGD(TAG, "Root received: from: " MACSTR " to " MACSTR " size: %d", - MAC2STR((uint8_t *)data.data), - MAC2STR((uint8_t *)(data.data + 6)), data.size); - if (netif_ap) { - // actual receive to TCP/IP stack - esp_netif_receive(netif_ap, data.data, data.size, NULL); - } - } else if (data.proto == MESH_PROTO_STA) { - ESP_LOGE(TAG, "Root station Should never receive data from mesh!"); - } - } else { - if (data.proto == MESH_PROTO_AP) { - ESP_LOGD(TAG, "Node AP should never receive data from mesh"); - } else if (data.proto == MESH_PROTO_STA) { - ESP_LOGD(TAG, "Node received: from: " MACSTR " to " MACSTR " size: %d", - MAC2STR((uint8_t *)data.data), - MAC2STR((uint8_t *)(data.data + 6)), data.size); - if (netif_sta) { - // actual receive to TCP/IP stack - esp_netif_receive(netif_sta, data.data, data.size, NULL); - } - } - } - } - vTaskDelete(NULL); -} - -// Free RX buffer (not used as the buffer is static) -// -static void mesh_free(void *h, void *buffer) { free(buffer); } - -// Transmit function variants -// -static esp_err_t mesh_netif_transmit_from_root_ap(void *h, void *buffer, - size_t len) { - // Use only to transmit data from root AP to node's AP - static const uint8_t eth_broadcast[MAC_ADDR_LEN] = {0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF}; - int route_table_size = 0; - mesh_netif_driver_t mesh_driver = h; - mesh_addr_t dest_addr; - mesh_data_t data; - ESP_LOGD(TAG, "Sending to node: " MACSTR ", size: %d", - MAC2STR((uint8_t *)buffer), len); - memcpy(dest_addr.addr, buffer, MAC_ADDR_LEN); - data.data = buffer; - data.size = len; - data.proto = MESH_PROTO_STA; // sending from root AP -> Node's STA - data.tos = MESH_TOS_P2P; - if (MAC_ADDR_EQUAL(dest_addr.addr, eth_broadcast)) { - ESP_LOGD(TAG, "Broadcasting!"); - esp_mesh_get_routing_table((mesh_addr_t *)&s_route_table, - CONFIG_MESH_ROUTE_TABLE_SIZE * 6, - &route_table_size); - for (int i = 0; i < route_table_size; i++) { - if (MAC_ADDR_EQUAL(s_route_table[i].addr, mesh_driver->sta_mac_addr)) { - ESP_LOGD(TAG, "That was me, skipping!"); - continue; - } - ESP_LOGD(TAG, "Broadcast: Sending to [%d] " MACSTR, i, - MAC2STR(s_route_table[i].addr)); - esp_err_t err = - esp_mesh_send(&s_route_table[i], &data, MESH_DATA_P2P, NULL, 0); - if (ESP_OK != err) { - ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err)); - } - } - } else { - // Standard P2P - esp_err_t err = esp_mesh_send(&dest_addr, &data, MESH_DATA_P2P, NULL, 0); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err)); - return err; - } - } - return ESP_OK; -} -static esp_err_t mesh_netif_transmit_from_root_ap_wrap(void *h, void *buffer, - size_t len, - void *netstack_buf) { - return mesh_netif_transmit_from_root_ap(h, buffer, len); -} - -static esp_err_t mesh_netif_transmit_from_node_sta(void *h, void *buffer, - size_t len) { - mesh_data_t data; - ESP_LOGD(TAG, "Sending to root, dest addr: " MACSTR ", size: %d", - MAC2STR((uint8_t *)buffer), len); - data.data = buffer; - data.size = len; - data.proto = MESH_PROTO_AP; // Node's station transmits data to root's AP - data.tos = MESH_TOS_P2P; - esp_err_t err = esp_mesh_send(NULL, &data, MESH_DATA_TODS, NULL, 0); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err)); - } - return err; -} - -static esp_err_t mesh_netif_transmit_from_node_sta_wrap(void *h, void *buffer, - size_t len, - void *netstack_buf) { - return mesh_netif_transmit_from_node_sta(h, buffer, len); -} - -// Construct and Destruct functions -// -static esp_err_t mesh_driver_start_root_ap(esp_netif_t *esp_netif, void *args) { - mesh_netif_driver_t driver = args; - driver->base.netif = esp_netif; - esp_netif_driver_ifconfig_t driver_ifconfig = { - .handle = driver, - .transmit = mesh_netif_transmit_from_root_ap, - .transmit_wrap = mesh_netif_transmit_from_root_ap_wrap, - .driver_free_rx_buffer = mesh_free}; - - return esp_netif_set_driver_config(esp_netif, &driver_ifconfig); -} - -static esp_err_t mesh_driver_start_node_sta(esp_netif_t *esp_netif, - void *args) { - mesh_netif_driver_t driver = args; - driver->base.netif = esp_netif; - esp_netif_driver_ifconfig_t driver_ifconfig = { - .handle = driver, - .transmit = mesh_netif_transmit_from_node_sta, - .transmit_wrap = mesh_netif_transmit_from_node_sta_wrap, - .driver_free_rx_buffer = mesh_free}; - - return esp_netif_set_driver_config(esp_netif, &driver_ifconfig); -} - -void mesh_delete_if_driver(mesh_netif_driver_t driver) { - // Stop the task once both drivers are removed - // receive_task_is_running = true; - free(driver); -} - -mesh_netif_driver_t mesh_create_if_driver(bool is_ap, bool is_root) { - mesh_netif_driver_t driver = calloc(1, sizeof(struct mesh_netif_driver)); - if (driver == NULL) { - ESP_LOGE(TAG, "No memory to create a wifi interface handle"); - return NULL; - } - if (is_ap && is_root) { - driver->base.post_attach = mesh_driver_start_root_ap; - } else if (!is_ap && !is_root) { - driver->base.post_attach = mesh_driver_start_node_sta; - } else { - return NULL; - } - - if (!receive_task_is_running) { - receive_task_is_running = true; - xTaskCreate(receive_task, "netif rx task", 3072, NULL, 5, NULL); - } - - // save station mac address to exclude it from routing-table on broadcast - esp_wifi_get_mac(WIFI_IF_STA, driver->sta_mac_addr); - - return driver; -} - -esp_err_t mesh_netifs_destroy(void) { - receive_task_is_running = false; - return ESP_OK; -} - -static void mesh_netif_init_station(void) { - // By default create a station that would connect to AP (expecting root to - // connect to external network) - esp_netif_config_t cfg_sta = ESP_NETIF_DEFAULT_WIFI_STA(); - netif_sta = esp_netif_new(&cfg_sta); - assert(netif_sta); - ESP_ERROR_CHECK(esp_netif_attach_wifi_station(netif_sta)); - ESP_ERROR_CHECK(esp_wifi_set_default_wifi_sta_handlers()); -} - -// Init by default for both potential root and node -// -esp_err_t mesh_netifs_init(mesh_raw_recv_cb_t *cb) { - mesh_netif_init_station(); - s_mesh_raw_recv_cb = cb; - return ESP_OK; -} - -/** - * @brief Starts AP esp-netif link over mesh (root's AP on mesh) - */ -static esp_err_t start_mesh_link_ap(void) { - uint8_t mac[MAC_ADDR_LEN]; - esp_wifi_get_mac(WIFI_IF_AP, mac); - esp_netif_set_mac(netif_ap, mac); - esp_netif_action_start(netif_ap, NULL, 0, NULL); - return ESP_OK; -} - -/** - * @brief Starts station link over wifi (root node to the router) - */ -static esp_err_t start_wifi_link_sta(void) { - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); - esp_err_t ret; - void *driver = esp_netif_get_io_driver(netif_sta); - if ((ret = esp_wifi_register_if_rxcb(driver, esp_netif_receive, netif_sta)) != - ESP_OK) { - ESP_LOGE(TAG, "esp_wifi_register_if_rxcb for if=%p failed with %d", driver, - ret); - return ESP_FAIL; - } - esp_netif_set_mac(netif_sta, mac); - esp_netif_action_start(netif_sta, NULL, 0, NULL); - return ESP_OK; -} - -/** - * @brief Starts station link over mesh (node to root over mesh) - */ -static esp_err_t start_mesh_link_sta(void) { - uint8_t mac[MAC_ADDR_LEN]; - esp_wifi_get_mac(WIFI_IF_STA, mac); - esp_netif_set_mac(netif_sta, mac); - esp_netif_action_start(netif_sta, NULL, 0, NULL); - esp_netif_action_connected(netif_sta, NULL, 0, NULL); - return ESP_OK; -} - -/** - * @brief Creates esp-netif for AP interface over mesh - * - * @return Pointer to esp-netif instance - */ -static esp_netif_t *create_mesh_link_ap(void) { - esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP(); - base_cfg.if_desc = "mesh_link_ap"; - base_cfg.ip_info = &g_mesh_netif_subnet_ip; - - esp_netif_config_t cfg = {.base = &base_cfg, - .driver = NULL, - .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP}; - esp_netif_t *netif = esp_netif_new(&cfg); - assert(netif); - return netif; -} - -/** - * @brief Destroy esp-netif for AP interface over mesh - */ -static void destory_mesh_link_ap(void) { - if (netif_ap) { - ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(netif_ap)); - esp_netif_action_disconnected(netif_ap, NULL, 0, NULL); - mesh_delete_if_driver(esp_netif_get_io_driver(netif_ap)); - esp_netif_destroy(netif_ap); - netif_ap = NULL; - } -} - -/** - * @brief Creates esp-netif for station interface over mesh - * - * @note Interface needs to be started (later) using the above APIs - * based on the actual configuration root/node, - * since root connects normally over wifi - * - * @return Pointer to esp-netif instance - */ -static esp_netif_t *create_mesh_link_sta(void) { - esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); - base_cfg.if_desc = "mesh_link_sta"; - - esp_netif_config_t cfg = {.base = &base_cfg, - .driver = NULL, - .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA}; - esp_netif_t *netif = esp_netif_new(&cfg); - assert(netif); - return netif; -} - -esp_err_t mesh_netif_start_root_ap(bool is_root, uint32_t addr) { - if (is_root) { - destory_mesh_link_ap(); - netif_ap = create_mesh_link_ap(); - mesh_netif_driver_t driver = mesh_create_if_driver(true, true); - if (driver == NULL) { - ESP_LOGE(TAG, "Failed to create wifi interface handle"); - return ESP_FAIL; - } - esp_netif_attach(netif_ap, driver); - set_dhcps_dns(netif_ap, addr); - start_mesh_link_ap(); - ip_napt_enable(g_mesh_netif_subnet_ip.ip.addr, 1); - } - return ESP_OK; -} - -esp_err_t mesh_netifs_start(bool is_root) { - if (is_root) { - // ROOT: need both sta should use standard wifi, AP mesh link netif - - // Root: Station - if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) { - ESP_LOGI(TAG, "Already wifi station, no need to do anything"); - } else if (netif_sta && - strcmp(esp_netif_get_desc(netif_sta), "mesh_link_sta") == 0) { - esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); - mesh_delete_if_driver(esp_netif_get_io_driver(netif_sta)); - esp_netif_destroy(netif_sta); - mesh_netif_init_station(); - } else if (netif_sta == NULL) { - mesh_netif_init_station(); - } - - // Root: AP is initialized only if GLOBAL DNS configured - // (otherwise have to wait until the actual DNS record received from the - // router) -#if CONFIG_MESH_USE_GLOBAL_DNS_IP - mesh_netif_start_root_ap(true, htonl(DNS_IP_ADDR)); -#endif - - } else { - // NODE: create only STA in form of mesh link - if (netif_sta && - strcmp(esp_netif_get_desc(netif_sta), "mesh_link_sta") == 0) { - ESP_LOGI(TAG, "Already mesh link station, no need to do anything"); - return ESP_OK; - } - if (netif_sta) { - esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); - // should remove the actual driver - if (strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) { - ESP_LOGI(TAG, "It was a wifi station removing stuff"); - esp_wifi_clear_default_wifi_driver_and_handlers(netif_sta); - } - esp_netif_destroy(netif_sta); - } - netif_sta = create_mesh_link_sta(); - // now we create a mesh driver and attach it to the existing netif - mesh_netif_driver_t driver = mesh_create_if_driver(false, false); - if (driver == NULL) { - ESP_LOGE(TAG, "Failed to create wifi interface handle"); - return ESP_FAIL; - } - esp_netif_attach(netif_sta, driver); - start_mesh_link_sta(); - // If we have a AP on NODE -> stop and remove it! - destory_mesh_link_ap(); - } - return ESP_OK; -} - -esp_err_t mesh_netifs_stop(void) { - if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "sta") == 0 && - netif_ap == NULL) { - return ESP_OK; - } - - if (netif_sta) { - if (strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) { - esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); - esp_netif_action_stop(netif_sta, NULL, 0, NULL); - esp_wifi_clear_default_wifi_driver_and_handlers(netif_sta); - } else { - esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); - mesh_delete_if_driver(esp_netif_get_io_driver(netif_sta)); - } - esp_netif_destroy(netif_sta); - netif_sta = NULL; - } - - destory_mesh_link_ap(); - // reserve the default (STA gets ready to become root) - mesh_netif_init_station(); - start_wifi_link_sta(); - return ESP_OK; -} - -uint8_t *mesh_netif_get_station_mac(void) { - mesh_netif_driver_t mesh = esp_netif_get_io_driver(netif_sta); - return mesh->sta_mac_addr; -} diff --git a/main/mesh_netif.h b/main/mesh_netif.h deleted file mode 100644 index 3116c71..0000000 --- a/main/mesh_netif.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Mesh IP Internal Networking Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#pragma once -#include "esp_mesh.h" - -/******************************************************* - * Macros - *******************************************************/ -#define MAC_ADDR_LEN (6u) -#define MAC_ADDR_EQUAL(a, b) (0 == memcmp(a, b, MAC_ADDR_LEN)) - -/******************************************************* - * Type Definitions - *******************************************************/ -typedef void(mesh_raw_recv_cb_t)(mesh_addr_t *from, mesh_data_t *data); - -/******************************************************* - * Function Declarations - *******************************************************/ - -/** - * @brief Initializes netifs in a default way before knowing if we are going to - * be a root - * - * @param cb callback receive function for mesh raw packets - * - * @return ESP_OK on success - */ -esp_err_t mesh_netifs_init(mesh_raw_recv_cb_t *cb); - -/** - * @brief Destroy the netifs and related structures - * - * @return ESP_OK on success - */ -esp_err_t mesh_netifs_destroy(void); - -/** - * @brief Start the mesh netifs based on the configuration (root/node) - * - * @return ESP_OK on success - */ -esp_err_t mesh_netifs_start(bool is_root); - -/** - * @brief Stop the netifs and reset to the default mode - * - * @return ESP_OK on success - */ -esp_err_t mesh_netifs_stop(void); - -/** - * @brief Start the netif for root AP - * - * Note: The AP netif needs to be started separately after root received - * an IP address from the router so the DNS address could be used for dhcps - * - * @param is_root must be true, ignored otherwise - * @param dns_addr DNS address to use in DHCP server running on roots AP - * - * @return ESP_OK on success - */ -esp_err_t mesh_netif_start_root_ap(bool is_root, uint32_t dns_addr); - -/** - * @brief Returns MAC address of the station interface - * - * Used mainly for checking node addresses of the peers in routing table - * to avoid sending data to oneself - * - * @return Pointer to MAC address - */ -uint8_t *mesh_netif_get_station_mac(void); diff --git a/main/network.c b/main/network.c index 8214f27..2855134 100644 --- a/main/network.c +++ b/main/network.c @@ -1,4 +1,9 @@ -/* Mesh Internal Communication Example +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* WiFi softAP & station Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -6,330 +11,211 @@ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ -#include "esp_err.h" #include "esp_event.h" #include "esp_log.h" #include "esp_mac.h" -#include "esp_mesh.h" +#include "esp_netif.h" +#include "esp_netif_net_stack.h" +#include "esp_netif_types.h" #include "esp_wifi.h" -#include "esp_wifi_types_generic.h" -#include "freertos/semphr.h" -#include "mesh_netif.h" +#include "lwip/inet.h" +#include "lwip/ip4_addr.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" #include "nvs_flash.h" -#include +#include #include - -/******************************************************* - * Macros - *******************************************************/ - -// commands for internal mesh communication: -// , where CMD is one character, payload is variable dep. on -// command -#define CMD_KEYPRESSED 0x55 -// CMD_KEYPRESSED: payload is always 6 bytes identifying address of node sending -// keypress event -#define CMD_ROUTE_TABLE 0x56 -// CMD_KEYPRESSED: payload is a multiple of 6 listing addresses in a routing -// table -/******************************************************* - * Constants - *******************************************************/ -static const char *MESH_TAG = "mesh_main"; -static const uint8_t MESH_ID[6] = {0x77, 0x77, 0x77, 0x77, 0x77, 0x76}; - -/******************************************************* - * Variable Definitions - *******************************************************/ -static mesh_addr_t mesh_parent_addr; -static int mesh_layer = -1; -static esp_ip4_addr_t s_current_ip; -static mesh_addr_t s_route_table[CONFIG_MESH_ROUTE_TABLE_SIZE]; -static int s_route_table_size = 0; -static SemaphoreHandle_t s_route_table_lock = NULL; -// static uint8_t s_mesh_tx_payload[CONFIG_MESH_ROUTE_TABLE_SIZE * 6 + 1]; - -/******************************************************* - * Function Definitions - *******************************************************/ - -void static recv_cb(mesh_addr_t *from, mesh_data_t *data) { - if (data->data[0] == CMD_ROUTE_TABLE) { - int size = data->size - 1; - if (s_route_table_lock == NULL || size % 6 != 0) { - ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unexpected size"); - return; - } - xSemaphoreTake(s_route_table_lock, portMAX_DELAY); - s_route_table_size = size / 6; - for (int i = 0; i < s_route_table_size; ++i) { - ESP_LOGI(MESH_TAG, "Received Routing table [%d] " MACSTR, i, - MAC2STR(data->data + 6 * i + 1)); - } - memcpy(&s_route_table, data->data + 1, size); - xSemaphoreGive(s_route_table_lock); - } else if (data->data[0] == CMD_KEYPRESSED) { - if (data->size != 7) { - ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unexpected size"); - return; - } - ESP_LOGW(MESH_TAG, "Keypressed detected on node: " MACSTR, - MAC2STR(data->data + 1)); - } else { - ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unknown command"); - } -} - -void mesh_event_handler(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) { - mesh_addr_t id = { - 0, - }; - static uint8_t last_layer = 0; - - switch (event_id) { - case MESH_EVENT_STARTED: { - esp_mesh_get_id(&id); - ESP_LOGI(MESH_TAG, "ID:" MACSTR "", - MAC2STR(id.addr)); - mesh_layer = esp_mesh_get_layer(); - } break; - case MESH_EVENT_STOPPED: { - ESP_LOGI(MESH_TAG, ""); - mesh_layer = esp_mesh_get_layer(); - } break; - case MESH_EVENT_CHILD_CONNECTED: { - mesh_event_child_connected_t *child_connected = - (mesh_event_child_connected_t *)event_data; - ESP_LOGI(MESH_TAG, "aid:%d, " MACSTR "", - child_connected->aid, MAC2STR(child_connected->mac)); - } break; - case MESH_EVENT_CHILD_DISCONNECTED: { - mesh_event_child_disconnected_t *child_disconnected = - (mesh_event_child_disconnected_t *)event_data; - ESP_LOGI(MESH_TAG, "aid:%d, " MACSTR "", - child_disconnected->aid, MAC2STR(child_disconnected->mac)); - } break; - case MESH_EVENT_ROUTING_TABLE_ADD: { - mesh_event_routing_table_change_t *routing_table = - (mesh_event_routing_table_change_t *)event_data; - ESP_LOGW(MESH_TAG, "add %d, new:%d", - routing_table->rt_size_change, routing_table->rt_size_new); - } break; - case MESH_EVENT_ROUTING_TABLE_REMOVE: { - mesh_event_routing_table_change_t *routing_table = - (mesh_event_routing_table_change_t *)event_data; - ESP_LOGW(MESH_TAG, "remove %d, new:%d", - routing_table->rt_size_change, routing_table->rt_size_new); - } break; - case MESH_EVENT_NO_PARENT_FOUND: { - mesh_event_no_parent_found_t *no_parent = - (mesh_event_no_parent_found_t *)event_data; - ESP_LOGI(MESH_TAG, "scan times:%d", - no_parent->scan_times); - } - /* TODO handler for the failure */ - break; - case MESH_EVENT_PARENT_CONNECTED: { - mesh_event_connected_t *connected = (mesh_event_connected_t *)event_data; - esp_mesh_get_id(&id); - mesh_layer = connected->self_layer; - memcpy(&mesh_parent_addr.addr, connected->connected.bssid, 6); - ESP_LOGI(MESH_TAG, - "layer:%d-->%d, parent:" MACSTR - "%s, ID:" MACSTR "", - last_layer, mesh_layer, MAC2STR(mesh_parent_addr.addr), - esp_mesh_is_root() ? "" - : (mesh_layer == 2) ? "" - : "", - MAC2STR(id.addr)); - last_layer = mesh_layer; - mesh_netifs_start(esp_mesh_is_root()); - } break; - case MESH_EVENT_PARENT_DISCONNECTED: { - mesh_event_disconnected_t *disconnected = - (mesh_event_disconnected_t *)event_data; - ESP_LOGI(MESH_TAG, "reason:%d", - disconnected->reason); - mesh_layer = esp_mesh_get_layer(); - mesh_netifs_stop(); - } break; - case MESH_EVENT_LAYER_CHANGE: { - mesh_event_layer_change_t *layer_change = - (mesh_event_layer_change_t *)event_data; - mesh_layer = layer_change->new_layer; - ESP_LOGI(MESH_TAG, "layer:%d-->%d%s", last_layer, - mesh_layer, - esp_mesh_is_root() ? "" - : (mesh_layer == 2) ? "" - : ""); - last_layer = mesh_layer; - } break; - case MESH_EVENT_ROOT_ADDRESS: { - mesh_event_root_address_t *root_addr = - (mesh_event_root_address_t *)event_data; - ESP_LOGI(MESH_TAG, "root address:" MACSTR "", - MAC2STR(root_addr->addr)); - } break; - case MESH_EVENT_VOTE_STARTED: { - mesh_event_vote_started_t *vote_started = - (mesh_event_vote_started_t *)event_data; - ESP_LOGI(MESH_TAG, - "attempts:%d, reason:%d, rc_addr:" MACSTR - "", - vote_started->attempts, vote_started->reason, - MAC2STR(vote_started->rc_addr.addr)); - } break; - case MESH_EVENT_VOTE_STOPPED: { - ESP_LOGI(MESH_TAG, ""); - break; - } - case MESH_EVENT_ROOT_SWITCH_REQ: { - mesh_event_root_switch_req_t *switch_req = - (mesh_event_root_switch_req_t *)event_data; - ESP_LOGI(MESH_TAG, - "reason:%d, rc_addr:" MACSTR "", - switch_req->reason, MAC2STR(switch_req->rc_addr.addr)); - } break; - case MESH_EVENT_ROOT_SWITCH_ACK: { - /* new root */ - mesh_layer = esp_mesh_get_layer(); - esp_mesh_get_parent_bssid(&mesh_parent_addr); - ESP_LOGI(MESH_TAG, - "layer:%d, parent:" MACSTR "", - mesh_layer, MAC2STR(mesh_parent_addr.addr)); - } break; - case MESH_EVENT_TODS_STATE: { - mesh_event_toDS_state_t *toDs_state = (mesh_event_toDS_state_t *)event_data; - ESP_LOGI(MESH_TAG, "state:%d", *toDs_state); - } break; - case MESH_EVENT_ROOT_FIXED: { - mesh_event_root_fixed_t *root_fixed = (mesh_event_root_fixed_t *)event_data; - ESP_LOGI(MESH_TAG, "%s", - root_fixed->is_fixed ? "fixed" : "not fixed"); - } break; - case MESH_EVENT_ROOT_ASKED_YIELD: { - mesh_event_root_conflict_t *root_conflict = - (mesh_event_root_conflict_t *)event_data; - ESP_LOGI(MESH_TAG, - "" MACSTR ", rssi:%d, capacity:%d", - MAC2STR(root_conflict->addr), root_conflict->rssi, - root_conflict->capacity); - } break; - case MESH_EVENT_CHANNEL_SWITCH: { - mesh_event_channel_switch_t *channel_switch = - (mesh_event_channel_switch_t *)event_data; - ESP_LOGI(MESH_TAG, "new channel:%d", - channel_switch->channel); - } break; - case MESH_EVENT_SCAN_DONE: { - mesh_event_scan_done_t *scan_done = (mesh_event_scan_done_t *)event_data; - ESP_LOGI(MESH_TAG, "number:%d", scan_done->number); - } break; - case MESH_EVENT_NETWORK_STATE: { - mesh_event_network_state_t *network_state = - (mesh_event_network_state_t *)event_data; - ESP_LOGI(MESH_TAG, "is_rootless:%d", - network_state->is_rootless); - } break; - case MESH_EVENT_STOP_RECONNECTION: { - ESP_LOGI(MESH_TAG, ""); - } break; - case MESH_EVENT_FIND_NETWORK: { - mesh_event_find_network_t *find_network = - (mesh_event_find_network_t *)event_data; - ESP_LOGI(MESH_TAG, - "new channel:%d, router BSSID:" MACSTR "", - find_network->channel, MAC2STR(find_network->router_bssid)); - } break; - case MESH_EVENT_ROUTER_SWITCH: { - mesh_event_router_switch_t *router_switch = - (mesh_event_router_switch_t *)event_data; - ESP_LOGI(MESH_TAG, - "new router:%s, channel:%d, " MACSTR "", - router_switch->ssid, router_switch->channel, - MAC2STR(router_switch->bssid)); - } break; - default: - ESP_LOGI(MESH_TAG, "unknown id:%" PRId32 "", event_id); - break; - } -} - -void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, - void *event_data) { - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - ESP_LOGI(MESH_TAG, "IP:" IPSTR, - IP2STR(&event->ip_info.ip)); - s_current_ip.addr = event->ip_info.ip.addr; -#if !CONFIG_MESH_USE_GLOBAL_DNS_IP - esp_netif_t *netif = event->esp_netif; - esp_netif_dns_info_t dns; - ESP_ERROR_CHECK(esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns)); - mesh_netif_start_root_ap(esp_mesh_is_root(), dns.ip.u_addr.ip4.addr); +#if IP_NAPT +#include "lwip/lwip_napt.h" #endif - // esp_mesh_comm_mqtt_task_start(); +#include "lwip/err.h" +#include "lwip/sys.h" + +static const char *TAG_AP = "WiFi SoftAP"; +static const char *TAG_STA = "WiFi Sta"; + +static int s_retry_num = 0; + +/* FreeRTOS event group to signal when we are connected/disconnected */ +EventGroupHandle_t s_wifi_event_group; + +static void wifi_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) { + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) { + wifi_event_ap_staconnected_t *event = + (wifi_event_ap_staconnected_t *)event_data; + ESP_LOGI(TAG_AP, "Station " MACSTR " joined, AID=%d", MAC2STR(event->mac), + event->aid); + } else if (event_base == WIFI_EVENT && + event_id == WIFI_EVENT_AP_STADISCONNECTED) { + wifi_event_ap_stadisconnected_t *event = + (wifi_event_ap_stadisconnected_t *)event_data; + ESP_LOGI(TAG_AP, "Station " MACSTR " left, AID=%d, reason:%d", + MAC2STR(event->mac), event->aid, event->reason); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + ESP_LOGI(TAG_STA, "Station started"); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG_STA, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +/* Initialize soft AP */ +esp_netif_t *wifi_init_softap(void) { + esp_netif_t *esp_netif_ap = esp_netif_create_default_wifi_ap(); + + wifi_config_t wifi_ap_config = { + .ap = + { + .ssid = EXAMPLE_ESP_WIFI_AP_SSID, + .ssid_len = strlen(EXAMPLE_ESP_WIFI_AP_SSID), + .channel = EXAMPLE_ESP_WIFI_CHANNEL, + .password = EXAMPLE_ESP_WIFI_AP_PASSWD, + .max_connection = EXAMPLE_MAX_STA_CONN, + .authmode = WIFI_AUTH_WPA2_PSK, + .pmf_cfg = + { + .required = false, + }, + }, + }; + + if (strlen(EXAMPLE_ESP_WIFI_AP_PASSWD) == 0) { + wifi_ap_config.ap.authmode = WIFI_AUTH_OPEN; + } + + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config)); + + esp_netif_dhcps_stop(esp_netif_ap); + + ESP_LOGI(TAG_AP, "wifi_init_softap finished. SSID:%s password:%s channel:%d", + EXAMPLE_ESP_WIFI_AP_SSID, EXAMPLE_ESP_WIFI_AP_PASSWD, + EXAMPLE_ESP_WIFI_CHANNEL); + + esp_netif_ip_info_t ipinfo; + IP4_ADDR(&ipinfo.ip, 172, 16, 0, 1); + IP4_ADDR(&ipinfo.gw, 172, 16, 0, 1); + IP4_ADDR(&ipinfo.netmask, 255, 255, 255, 0); + esp_netif_set_ip_info(esp_netif_ap, &ipinfo); + esp_netif_dhcps_start(esp_netif_ap); + + return esp_netif_ap; +} + +/* Initialize wifi station */ +esp_netif_t *wifi_init_sta(void) { + esp_netif_t *esp_netif_sta = esp_netif_create_default_wifi_sta(); + + wifi_config_t wifi_sta_config = { + .sta = + { + .ssid = EXAMPLE_ESP_WIFI_STA_SSID, + .password = EXAMPLE_ESP_WIFI_STA_PASSWD, + .scan_method = WIFI_ALL_CHANNEL_SCAN, + .failure_retry_cnt = EXAMPLE_ESP_MAXIMUM_RETRY, + /* Authmode threshold resets to WPA2 as default if password + * matches WPA2 standards (password len => 8). If you want to + * connect the device to deprecated WEP/WPA networks, Please set + * the threshold value to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set + * the password with length and format matching to + * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards. + */ + .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD, + .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, + }, + }; + + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config)); + + ESP_LOGI(TAG_STA, "wifi_init_sta finished."); + + return esp_netif_sta; +} + +void softap_set_dns_addr(esp_netif_t *esp_netif_ap, + esp_netif_t *esp_netif_sta) { + esp_netif_dns_info_t dns; + esp_netif_get_dns_info(esp_netif_sta, ESP_NETIF_DNS_MAIN, &dns); + uint8_t dhcps_offer_option = DHCPS_OFFER_DNS; + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(esp_netif_ap)); + ESP_ERROR_CHECK(esp_netif_dhcps_option( + esp_netif_ap, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, + &dhcps_offer_option, sizeof(dhcps_offer_option))); + ESP_ERROR_CHECK( + esp_netif_set_dns_info(esp_netif_ap, ESP_NETIF_DNS_MAIN, &dns)); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(esp_netif_ap)); } void netif_start(void) { - ESP_ERROR_CHECK(nvs_flash_init()); - /* tcpip initialization */ ESP_ERROR_CHECK(esp_netif_init()); - /* event initialization */ ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* crete network interfaces for mesh (only station instance saved for further - * manipulation, soft AP instance ignored */ - ESP_ERROR_CHECK(mesh_netifs_init(recv_cb)); - /* wifi initialization */ - wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&config)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, - &ip_event_handler, NULL)); - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH)); - ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); + // Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || + ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + /* Initialize event group */ + s_wifi_event_group = xEventGroupCreate(); + + /* Register Event handler */ + ESP_ERROR_CHECK(esp_event_handler_instance_register( + WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL)); + ESP_ERROR_CHECK(esp_event_handler_instance_register( + IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL)); + + /*Initialize WiFi */ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); + + /* Initialize AP */ + ESP_LOGI(TAG_AP, "ESP_WIFI_MODE_AP"); + esp_netif_t *esp_netif_ap = wifi_init_softap(); + + /* Initialize STA */ + ESP_LOGI(TAG_STA, "ESP_WIFI_MODE_STA"); + esp_netif_t *esp_netif_sta = wifi_init_sta(); + + /* Start WiFi */ ESP_ERROR_CHECK(esp_wifi_start()); - /* mesh initialization */ - ESP_ERROR_CHECK(esp_mesh_init()); - ESP_ERROR_CHECK(esp_event_handler_register(MESH_EVENT, ESP_EVENT_ANY_ID, - &mesh_event_handler, NULL)); - ESP_ERROR_CHECK(esp_mesh_set_max_layer(CONFIG_MESH_MAX_LAYER)); - ESP_ERROR_CHECK(esp_mesh_set_vote_percentage(1)); - ESP_ERROR_CHECK(esp_mesh_set_ap_assoc_expire(10)); - /* set blocking time of esp_mesh_send() to 30s, to prevent the esp_mesh_send() - * from permanently for some reason */ - ESP_ERROR_CHECK(esp_mesh_send_block_time(30000)); - mesh_cfg_t cfg = MESH_INIT_CONFIG_DEFAULT(); -#if !MESH_IE_ENCRYPTED - cfg.crypto_funcs = NULL; -#endif - /* mesh ID */ - memcpy((uint8_t *)&cfg.mesh_id, MESH_ID, 6); - /* router */ - cfg.channel = CONFIG_MESH_CHANNEL; - cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID); - memcpy((uint8_t *)&cfg.router.ssid, CONFIG_MESH_ROUTER_SSID, - cfg.router.ssid_len); - memcpy((uint8_t *)&cfg.router.password, CONFIG_MESH_ROUTER_PASSWD, - strlen(CONFIG_MESH_ROUTER_PASSWD)); - /* mesh softAP */ - ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE)); - cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS; - cfg.mesh_ap.nonmesh_max_connection = CONFIG_MESH_NON_MESH_AP_CONNECTIONS; - memcpy((uint8_t *)&cfg.mesh_ap.password, CONFIG_MESH_AP_PASSWD, - strlen(CONFIG_MESH_AP_PASSWD)); - ESP_ERROR_CHECK(esp_mesh_set_config(&cfg)); - // fixed root node - // wifi_config_t wifi_cfg; - // ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_AP, &wifi_cfg)); - // ESP_ERROR_CHECK( - // esp_mesh_set_parent(&wifi_cfg, &cfg.mesh_id, MESH_ROOT, - // MESH_ROOT_LAYER)); - // - /* mesh start */ - ESP_ERROR_CHECK(esp_mesh_start()); - ESP_LOGI(MESH_TAG, "mesh starts successfully, heap:%" PRId32 ", %s", - esp_get_free_heap_size(), - esp_mesh_is_root_fixed() ? "root fixed" : "root not fixed"); + /* + * Wait until either the connection is established (WIFI_CONNECTED_BIT) or + * connection failed for the maximum number of re-tries (WIFI_FAIL_BIT). + * The bits are set by event_handler() (see above) + */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, pdFALSE, portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, + * hence we can test which event actually happened. */ + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG_STA, "connected to ap SSID:%s password:%s", + EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD); + softap_set_dns_addr(esp_netif_ap, esp_netif_sta); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG_STA, "Failed to connect to SSID:%s, password:%s", + EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD); + } else { + ESP_LOGE(TAG_STA, "UNEXPECTED EVENT"); + return; + } + + /* Set sta as the default interface */ + esp_netif_set_default_netif(esp_netif_sta); + + /* Enable napt on the AP netif */ + if (esp_netif_napt_enable(esp_netif_ap) != ESP_OK) { + ESP_LOGE(TAG_STA, "NAPT not enabled on the netif: %p", esp_netif_ap); + } } diff --git a/main/network.h b/main/network.h index 003b59f..3f51460 100644 --- a/main/network.h +++ b/main/network.h @@ -1,3 +1,54 @@ #pragma once +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "freertos/task.h" + +/* The examples use WiFi configuration that you can set via project + configuration menu. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_ESP_WIFI_STA_SSID "mywifissid" +*/ + +/* STA Configuration */ +#define EXAMPLE_ESP_WIFI_STA_SSID CONFIG_ESP_WIFI_REMOTE_AP_SSID +#define EXAMPLE_ESP_WIFI_STA_PASSWD CONFIG_ESP_WIFI_REMOTE_AP_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_STA_RETRY + +#if CONFIG_ESP_WIFI_AUTH_OPEN +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN +#elif CONFIG_ESP_WIFI_AUTH_WEP +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP +#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK +#endif + +/* AP Configuration */ +#define EXAMPLE_ESP_WIFI_AP_SSID CONFIG_ESP_WIFI_AP_SSID +#define EXAMPLE_ESP_WIFI_AP_PASSWD CONFIG_ESP_WIFI_AP_PASSWORD +#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_AP_CHANNEL +#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN_AP + +/* The event group allows multiple bits for each event, but we only care about + * two events: + * - we are connected to the AP with an IP + * - we failed to connect after the maximum amount of retries */ +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +/*DHCP server option*/ +#define DHCPS_OFFER_DNS 0x02 + void netif_start(); +extern EventGroupHandle_t s_wifi_event_group;