mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80d74e9e34 | ||
|
|
43a0b500f5 | ||
|
|
3d53ed17fb | ||
|
|
4ea452307e | ||
|
|
e61f20f1ed | ||
|
|
ca11f5ab49 | ||
|
|
000d24d31a | ||
|
|
ef92f2e9c5 | ||
|
|
4e89eac8dd | ||
|
|
b015e33ea5 | ||
|
|
96e762d4a1 | ||
|
|
359060a151 | ||
|
|
af1aff7fbb | ||
|
|
517ef216c1 | ||
|
|
734c13cdf5 | ||
|
|
1bf3ad2426 | ||
|
|
884b9a4813 | ||
|
|
7153690016 | ||
|
|
f00b3a2b3a | ||
|
|
c2b5e4f3b8 | ||
|
|
bc15e9ce9a | ||
|
|
70dc6d8dc9 | ||
|
|
10b11b03a8 | ||
|
|
cb602f178c | ||
|
|
464113c5ae | ||
|
|
c34f6c54e7 | ||
|
|
8fb58ebbca | ||
|
|
dce60c6dbb | ||
|
|
f668d08804 | ||
|
|
c41a22195c | ||
|
|
900c05213b | ||
|
|
c5850e4b05 | ||
|
|
ff4649fe42 | ||
|
|
1a38e91042 | ||
|
|
b6196371bb | ||
|
|
d5beb7ba3f | ||
|
|
19acf23c49 | ||
|
|
5cd3291fec | ||
|
|
b3e0f780c3 | ||
|
|
2bdbfc27f6 | ||
|
|
3fc61a3edc | ||
|
|
12339bbf4c | ||
|
|
1fb9f922b0 | ||
|
|
b93bc02ab0 | ||
|
|
747e393a54 | ||
|
|
4e8660a21f | ||
|
|
3fe77fa9a6 | ||
|
|
b4fbaf7c13 | ||
|
|
9884241df8 | ||
|
|
63d7a4fa45 | ||
|
|
8f4b6c41a2 | ||
|
|
4493d65ec9 | ||
|
|
e5910da300 | ||
|
|
c277a1d878 | ||
|
|
7820ad3a59 | ||
|
|
23533b83a4 | ||
|
|
ab650ec5c9 | ||
|
|
8f36597048 | ||
|
|
d0213ee90a | ||
|
|
1e9fabe1ca | ||
|
|
148d3f0562 | ||
|
|
c3057df3f5 | ||
|
|
0b2e474d52 | ||
|
|
95a36b1759 | ||
|
|
86ffa3a3ea | ||
|
|
69e2b2985d | ||
|
|
c431904b56 | ||
|
|
c2a10601b9 | ||
|
|
e9a8e5468d | ||
|
|
22e602debb | ||
|
|
455cec67a5 | ||
|
|
e2dfac946d | ||
|
|
c198d2e5f1 | ||
|
|
9a70abef24 | ||
|
|
53e1c469a2 | ||
|
|
139c2061a8 | ||
|
|
15b361fbac | ||
|
|
3130e59244 | ||
|
|
93286836f9 | ||
|
|
91b57aa9a8 | ||
|
|
220231e647 | ||
|
|
c438bb0bc5 |
No files matched your search
@@ -17,7 +17,7 @@ Check out [Mongoose OS](https://mongoose-os.com) - open source embedded operatin
|
|||||||
# Support
|
# Support
|
||||||
- [Study mongoose example code](https://github.com/cesanta/mongoose/tree/master/examples)
|
- [Study mongoose example code](https://github.com/cesanta/mongoose/tree/master/examples)
|
||||||
- [Read User Guide and API reference](https://cesanta.com/docs/overview/intro.html)
|
- [Read User Guide and API reference](https://cesanta.com/docs/overview/intro.html)
|
||||||
- [Support Forum - ask your technical questions here](https://forum.mongoose-os.com/categories/mongoose)
|
- [Support Forum - ask your technical questions here](https://community.mongoose-os.com/)
|
||||||
- [Commercial licensing and support available](https://www.cesanta.com/licensing.html)
|
- [Commercial licensing and support available](https://www.cesanta.com/licensing.html)
|
||||||
- [Check our latest releases](https://github.com/cesanta/mongoose/releases)
|
- [Check our latest releases](https://github.com/cesanta/mongoose/releases)
|
||||||
|
|
||||||
@@ -61,8 +61,11 @@ the functionality:
|
|||||||
|
|
||||||
# Contributions
|
# Contributions
|
||||||
|
|
||||||
To submit contributions, sign [Cesanta CLA](https://cesanta.com/cla.html)
|
Contributions are welcome! Please follow the guidelines below:
|
||||||
and send GitHub pull request.
|
|
||||||
|
- Sign [Cesanta CLA](https://cesanta.com/cla.html) and send GitHub pull request
|
||||||
|
- When making pull requests, please make sure that it has only one commit,
|
||||||
|
and imlements/fixes only one piece of functionality
|
||||||
|
|
||||||
# Looking for a pre-compiled Mongoose web server Windows or Mac binary?
|
# Looking for a pre-compiled Mongoose web server Windows or Mac binary?
|
||||||
- [Download pre-compiled Mongoose web server binary.](https://www.cesanta.com/binary.html)
|
- [Download pre-compiled Mongoose web server binary.](https://www.cesanta.com/binary.html)
|
||||||
@@ -4,9 +4,12 @@ symbol_kind: "intro"
|
|||||||
decl_name: "mbuf.h"
|
decl_name: "mbuf.h"
|
||||||
items:
|
items:
|
||||||
- { name: mbuf_append.md }
|
- { name: mbuf_append.md }
|
||||||
|
- { name: mbuf_append_and_free.md }
|
||||||
|
- { name: mbuf_clear.md }
|
||||||
- { name: mbuf_free.md }
|
- { name: mbuf_free.md }
|
||||||
- { name: mbuf_init.md }
|
- { name: mbuf_init.md }
|
||||||
- { name: mbuf_insert.md }
|
- { name: mbuf_insert.md }
|
||||||
|
- { name: mbuf_move.md }
|
||||||
- { name: mbuf_remove.md }
|
- { name: mbuf_remove.md }
|
||||||
- { name: mbuf_resize.md }
|
- { name: mbuf_resize.md }
|
||||||
- { name: mbuf_trim.md }
|
- { name: mbuf_trim.md }
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: "mbuf_append_and_free()"
|
||||||
|
decl_name: "mbuf_append_and_free"
|
||||||
|
symbol_kind: "func"
|
||||||
|
signature: |
|
||||||
|
size_t mbuf_append_and_free(struct mbuf *, void *data, size_t data_size);
|
||||||
|
---
|
||||||
|
|
||||||
|
Appends data to the Mbuf and frees it (data must be heap-allocated).
|
||||||
|
|
||||||
|
Returns the number of bytes appended or 0 if out of memory.
|
||||||
|
data is freed irrespective of return value.
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: "mbuf_clear()"
|
||||||
|
decl_name: "mbuf_clear"
|
||||||
|
symbol_kind: "func"
|
||||||
|
signature: |
|
||||||
|
void mbuf_clear(struct mbuf *);
|
||||||
|
---
|
||||||
|
|
||||||
|
Removes all the data from mbuf (if any).
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: "mbuf_move()"
|
||||||
|
decl_name: "mbuf_move"
|
||||||
|
symbol_kind: "func"
|
||||||
|
signature: |
|
||||||
|
void mbuf_move(struct mbuf *from, struct mbuf *to);
|
||||||
|
---
|
||||||
|
|
||||||
|
Moves the state from one mbuf to the other.
|
||||||
|
|
||||||
@@ -30,7 +30,9 @@ The user-defined event handler will receive following extra events:
|
|||||||
- MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
|
- MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
|
||||||
handshake request. `ev_data` contains parsed HTTP request.
|
handshake request. `ev_data` contains parsed HTTP request.
|
||||||
- MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
|
- MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
|
||||||
handshake. `ev_data` is `NULL`.
|
handshake. `ev_data` is a `struct http_message` containing the
|
||||||
|
client's request (server mode) or server's response (client).
|
||||||
|
In client mode handler can examine `resp_code`, which should be 101.
|
||||||
- MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
|
- MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
|
||||||
`struct websocket_message *`
|
`struct websocket_message *`
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,14 @@ signature: |
|
|||||||
struct mg_str data;
|
struct mg_str data;
|
||||||
int status; /* <0 on error */
|
int status; /* <0 on error */
|
||||||
void *user_data;
|
void *user_data;
|
||||||
|
/*
|
||||||
|
* User handler can indicate how much of the data was consumed
|
||||||
|
* by setting this variable. By default, it is assumed that all
|
||||||
|
* data has been consumed by the handler.
|
||||||
|
* If not all data was consumed, user's handler will be invoked again later
|
||||||
|
* with the remainder.
|
||||||
|
*/
|
||||||
|
size_t num_data_consumed;
|
||||||
};
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ that can be, and must be, called from a different (non-IO) thread.
|
|||||||
`func` callback function will be called by the IO thread for each
|
`func` callback function will be called by the IO thread for each
|
||||||
connection. When called, the event will be `MG_EV_POLL`, and a message will
|
connection. When called, the event will be `MG_EV_POLL`, and a message will
|
||||||
be passed as the `ev_data` pointer. Maximum message size is capped
|
be passed as the `ev_data` pointer. Maximum message size is capped
|
||||||
by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
|
by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes by default.
|
||||||
|
|
||||||
@@ -16,9 +16,6 @@ signature: |
|
|||||||
struct mbuf send_mbuf; /* Data scheduled for sending */
|
struct mbuf send_mbuf; /* Data scheduled for sending */
|
||||||
time_t last_io_time; /* Timestamp of the last socket IO */
|
time_t last_io_time; /* Timestamp of the last socket IO */
|
||||||
double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */
|
double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */
|
||||||
#if MG_ENABLE_SSL
|
|
||||||
void *ssl_if_data; /* SSL library data. */
|
|
||||||
#endif
|
|
||||||
mg_event_handler_t proto_handler; /* Protocol-specific event handler */
|
mg_event_handler_t proto_handler; /* Protocol-specific event handler */
|
||||||
void *proto_data; /* Protocol-specific data */
|
void *proto_data; /* Protocol-specific data */
|
||||||
void (*proto_data_destructor)(void *proto_data);
|
void (*proto_data_destructor)(void *proto_data);
|
||||||
@@ -46,20 +43,30 @@ signature: |
|
|||||||
#define MG_F_WANT_READ (1 << 6) /* SSL specific */
|
#define MG_F_WANT_READ (1 << 6) /* SSL specific */
|
||||||
#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */
|
#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */
|
||||||
#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
|
#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
|
||||||
|
#define MG_F_RECV_AND_CLOSE (1 << 9) /* Drain rx and close the connection. */
|
||||||
|
|
||||||
/* Flags that are settable by user */
|
/* Flags that are settable by user */
|
||||||
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
||||||
#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
|
#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
|
||||||
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
|
|
||||||
#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */
|
/* Flags for protocol handlers */
|
||||||
|
#define MG_F_PROTO_1 (1 << 12)
|
||||||
|
#define MG_F_PROTO_2 (1 << 13)
|
||||||
#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
|
#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
|
||||||
|
|
||||||
#define MG_F_USER_1 (1 << 20) /* Flags left for application */
|
/* Flags left for application */
|
||||||
|
#define MG_F_USER_1 (1 << 20)
|
||||||
#define MG_F_USER_2 (1 << 21)
|
#define MG_F_USER_2 (1 << 21)
|
||||||
#define MG_F_USER_3 (1 << 22)
|
#define MG_F_USER_3 (1 << 22)
|
||||||
#define MG_F_USER_4 (1 << 23)
|
#define MG_F_USER_4 (1 << 23)
|
||||||
#define MG_F_USER_5 (1 << 24)
|
#define MG_F_USER_5 (1 << 24)
|
||||||
#define MG_F_USER_6 (1 << 25)
|
#define MG_F_USER_6 (1 << 25)
|
||||||
|
|
||||||
|
#if MG_ENABLE_SSL
|
||||||
|
void *ssl_if_data; /* SSL library data. */
|
||||||
|
#else
|
||||||
|
void *unused_ssl_if_data; /* To keep the size of the structure the same. */
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ bool bm222_get_data(struct bm222_ctx *ctx) {
|
|||||||
s->y = v[3];
|
s->y = v[3];
|
||||||
s->z = v[5];
|
s->z = v[5];
|
||||||
if (d > max_d) max_d = d;
|
if (d > max_d) max_d = d;
|
||||||
LOG(LL_VERBOSE_DEBUG, ("dx %d dy %d dz %d d %d", dx, dy, dz, d));
|
LOG(LL_VERBOSE_DEBUG,
|
||||||
|
("dx %d dy %d dz %d d %d", (int) dx, (int) dy, (int) dz, (int) d));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (overflow ? bm222_fifo_init(ctx) : true); /* Clear the ovf flag. */
|
return (overflow ? bm222_fifo_init(ctx) : true); /* Clear the ovf flag. */
|
||||||
|
|||||||
+84
-14
@@ -1,11 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
* Copyright (c) 2014-2018 Cesanta Software Limited
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CS_COMMON_CS_DBG_H_
|
#ifndef CS_COMMON_CS_DBG_H_
|
||||||
#define CS_COMMON_CS_DBG_H_
|
#define CS_COMMON_CS_DBG_H_
|
||||||
|
|
||||||
|
#include "common/platform.h"
|
||||||
|
|
||||||
#if CS_ENABLE_STDIO
|
#if CS_ENABLE_STDIO
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -14,6 +28,10 @@
|
|||||||
#define CS_ENABLE_DEBUG 0
|
#define CS_ENABLE_DEBUG 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CS_LOG_PREFIX_LEN
|
||||||
|
#define CS_LOG_PREFIX_LEN 24
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CS_LOG_ENABLE_TS_DIFF
|
#ifndef CS_LOG_ENABLE_TS_DIFF
|
||||||
#define CS_LOG_ENABLE_TS_DIFF 0
|
#define CS_LOG_ENABLE_TS_DIFF 0
|
||||||
#endif
|
#endif
|
||||||
@@ -22,6 +40,9 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it.
|
||||||
|
*/
|
||||||
enum cs_log_level {
|
enum cs_log_level {
|
||||||
LL_NONE = -1,
|
LL_NONE = -1,
|
||||||
LL_ERROR = 0,
|
LL_ERROR = 0,
|
||||||
@@ -34,29 +55,78 @@ enum cs_log_level {
|
|||||||
_LL_MAX = 5,
|
_LL_MAX = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set max log level to print; messages with the level above the given one will
|
||||||
|
* not be printed.
|
||||||
|
*/
|
||||||
void cs_log_set_level(enum cs_log_level level);
|
void cs_log_set_level(enum cs_log_level level);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A comma-separated set of prefix=level.
|
||||||
|
* prefix is matched against the log prefix exactly as printed, including line
|
||||||
|
* number, but partial match is ok. Check stops on first matching entry.
|
||||||
|
* If nothing matches, default level is used.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* main.c:=4 - everything from main C at verbose debug level.
|
||||||
|
* mongoose.c=1,mjs.c=1,=4 - everything at verbose debug except mg_* and mjs_*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void cs_log_set_file_level(const char *file_level);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper function which prints message prefix with the given `level`.
|
||||||
|
* If message should be printed (according to the current log level
|
||||||
|
* and filter), prints the prefix and returns 1, otherwise returns 0.
|
||||||
|
*
|
||||||
|
* Clients should typically just use `LOG()` macro.
|
||||||
|
*/
|
||||||
|
int cs_log_print_prefix(enum cs_log_level level, const char *fname, int line);
|
||||||
|
|
||||||
|
extern enum cs_log_level cs_log_level;
|
||||||
|
|
||||||
#if CS_ENABLE_STDIO
|
#if CS_ENABLE_STDIO
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set file to write logs into. If `NULL`, logs go to `stderr`.
|
||||||
|
*/
|
||||||
void cs_log_set_file(FILE *file);
|
void cs_log_set_file(FILE *file);
|
||||||
|
|
||||||
extern enum cs_log_level cs_log_threshold;
|
/*
|
||||||
void cs_log_print_prefix(const char *func);
|
* Prints log to the current log file, appends "\n" in the end and flushes the
|
||||||
void cs_log_printf(const char *fmt, ...);
|
* stream.
|
||||||
|
*/
|
||||||
|
void cs_log_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
||||||
|
|
||||||
#define LOG(l, x) \
|
#if CS_ENABLE_STDIO
|
||||||
if (cs_log_threshold >= l) { \
|
|
||||||
cs_log_print_prefix(__func__); \
|
/*
|
||||||
cs_log_printf x; \
|
* Format and print message `x` with the given level `l`. Example:
|
||||||
}
|
*
|
||||||
|
* ```c
|
||||||
|
* LOG(LL_INFO, ("my info message: %d", 123));
|
||||||
|
* LOG(LL_DEBUG, ("my debug message: %d", 123));
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
#define LOG(l, x) \
|
||||||
|
do { \
|
||||||
|
if (cs_log_print_prefix(l, __FILE__, __LINE__)) { \
|
||||||
|
cs_log_printf x; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define LOG(l, x) ((void) l)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CS_NDEBUG
|
#ifndef CS_NDEBUG
|
||||||
|
|
||||||
#define DBG(x) \
|
/*
|
||||||
if (cs_log_threshold >= LL_VERBOSE_DEBUG) { \
|
* Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))`
|
||||||
cs_log_print_prefix(__func__); \
|
*/
|
||||||
cs_log_printf x; \
|
#define DBG(x) LOG(LL_VERBOSE_DEBUG, x)
|
||||||
}
|
|
||||||
|
|
||||||
#else /* NDEBUG */
|
#else /* NDEBUG */
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
#define BM222_ADDR 0x18
|
#define BM222_ADDR 0x18
|
||||||
#define TMP006_ADDR 0x41
|
#define TMP006_ADDR 0x41
|
||||||
|
|
||||||
void fs_slfs_set_new_file_size(const char *name, size_t size);
|
void fs_slfs_set_file_size(const char *name, size_t size);
|
||||||
|
|
||||||
static const char *upload_form =
|
static const char *upload_form =
|
||||||
"\
|
"\
|
||||||
@@ -70,7 +70,7 @@ static struct mg_str upload_fname(struct mg_connection *nc,
|
|||||||
if (nc->user_data != NULL) {
|
if (nc->user_data != NULL) {
|
||||||
intptr_t cl = (intptr_t) nc->user_data;
|
intptr_t cl = (intptr_t) nc->user_data;
|
||||||
if (cl >= 0) {
|
if (cl >= 0) {
|
||||||
fs_slfs_set_new_file_size(fn + 3, cl);
|
fs_slfs_set_file_size(fn + 3, cl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lfn.len = fname.len + 4;
|
lfn.len = fname.len + 4;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void SimpleLinkWlanEventHandler(SlWlanEvent_t *e) {
|
|||||||
LOG(LL_INFO, ("WiFi: station disconnected"));
|
LOG(LL_INFO, ("WiFi: station disconnected"));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(LL_INFO, ("WiFi: event %d", e->Event));
|
LOG(LL_INFO, ("WiFi: event %d", (int) e->Event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *e) {
|
|||||||
} else if (e->Event == SL_NETAPP_IP_LEASED_EVENT) {
|
} else if (e->Event == SL_NETAPP_IP_LEASED_EVENT) {
|
||||||
LOG(LL_INFO, ("IP leased"));
|
LOG(LL_INFO, ("IP leased"));
|
||||||
} else {
|
} else {
|
||||||
LOG(LL_INFO, ("NetApp event %d", e->Event));
|
LOG(LL_INFO, ("NetApp event %d", (int) e->Event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ static int check_pass(const char *user, const char *pass) {
|
|||||||
* or NULL if not found.
|
* or NULL if not found.
|
||||||
*/
|
*/
|
||||||
static struct session *get_session(struct http_message *hm) {
|
static struct session *get_session(struct http_message *hm) {
|
||||||
struct mg_str *cookie_header = mg_get_http_header(hm, "cookie");
|
|
||||||
if (cookie_header == NULL) goto clean;
|
|
||||||
char ssid_buf[21];
|
char ssid_buf[21];
|
||||||
char *ssid = ssid_buf;
|
char *ssid = ssid_buf;
|
||||||
struct session *ret = NULL;
|
struct session *ret = NULL;
|
||||||
|
struct mg_str *cookie_header = mg_get_http_header(hm, "cookie");
|
||||||
|
if (cookie_header == NULL) goto clean;
|
||||||
if (!mg_http_parse_header2(cookie_header, SESSION_COOKIE_NAME, &ssid,
|
if (!mg_http_parse_header2(cookie_header, SESSION_COOKIE_NAME, &ssid,
|
||||||
sizeof(ssid_buf))) {
|
sizeof(ssid_buf))) {
|
||||||
goto clean;
|
goto clean;
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
PROG = http_proxy_client
|
||||||
|
#SSL_LIB=openssl
|
||||||
|
CFLAGS_EXTRA = -DMG_ENABLE_CALLBACK_USERDATA=1
|
||||||
|
include ../examples.mk
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* This program fetches HTTP URLs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MG_ENABLE_CALLBACK_USERDATA 1
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_exit_flag = 0;
|
||||||
|
static int s_show_headers = 0;
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *c, int ev, void *ev_data,
|
||||||
|
void *userdata) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_CONNECT:
|
||||||
|
if (*(int *) ev_data != 0) {
|
||||||
|
fprintf(stderr, "connect() failed: %s\n", strerror(*(int *) ev_data));
|
||||||
|
s_exit_flag = 1;
|
||||||
|
} else {
|
||||||
|
// Stage 2. Connection to the HTTP proxy is established.
|
||||||
|
// Write CONNECT request, and turn this connection to HTTP.
|
||||||
|
// NOTE: target URL is passed to us as userdata.
|
||||||
|
unsigned port = 80;
|
||||||
|
struct mg_str scheme, host;
|
||||||
|
mg_parse_uri(mg_mk_str((char *) userdata), &scheme, NULL, &host, &port,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
if (port == 0) port = (scheme.len == 5) ? 443 : 80;
|
||||||
|
mg_printf(c, "CONNECT %.*s:%u HTTP/1.1\r\n\r\n", (int) host.len, host.p,
|
||||||
|
port);
|
||||||
|
// Now set the flag and wait for the connection establishment
|
||||||
|
c->flags |= MG_F_USER_1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_RECV:
|
||||||
|
// Stage 3. Check if proxy replied. Here, we don't parse the reply
|
||||||
|
// for simplicity. Assume success, and write HTTP request.
|
||||||
|
if (c->flags & MG_F_USER_1) {
|
||||||
|
struct mg_str host, path;
|
||||||
|
c->flags &= ~MG_F_USER_1;
|
||||||
|
mg_parse_uri(mg_mk_str((char *) userdata), NULL, NULL, &host, NULL,
|
||||||
|
&path, NULL, NULL);
|
||||||
|
if (path.len == 0) path = mg_mk_str("/");
|
||||||
|
mg_printf(c, "GET %.*s HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) path.len,
|
||||||
|
path.p, (int) host.len, host.p);
|
||||||
|
mg_set_protocol_http_websocket(c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_HTTP_REPLY:
|
||||||
|
c->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
if (s_show_headers) {
|
||||||
|
fwrite(hm->message.p, 1, hm->message.len, stdout);
|
||||||
|
} else {
|
||||||
|
fwrite(hm->body.p, 1, hm->body.len, stdout);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
s_exit_flag = 1;
|
||||||
|
break;
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
if (s_exit_flag == 0) {
|
||||||
|
printf("Server closed connection\n");
|
||||||
|
s_exit_flag = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
/* Process command line arguments */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--show-headers") == 0) {
|
||||||
|
s_show_headers = 1;
|
||||||
|
} else if (strcmp(argv[i], "--hexdump") == 0 && i + 1 < argc) {
|
||||||
|
mgr.hexdump_file = argv[++i];
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 2 != argc) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s [--hexdump <file>] "
|
||||||
|
"[--show-headers] PROXY_HOST:PROXY_PORT URL\n",
|
||||||
|
argv[0]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage 1. Connect to the HTTP proxy as to a plain TCP server.
|
||||||
|
// Pass URL as a callback argument
|
||||||
|
mg_connect(&mgr, argv[i], ev_handler, argv[i + 1]);
|
||||||
|
|
||||||
|
while (s_exit_flag == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -14,7 +14,6 @@ static int s_done = 0;
|
|||||||
static int s_is_connected = 0;
|
static int s_is_connected = 0;
|
||||||
|
|
||||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
|
||||||
(void) nc;
|
(void) nc;
|
||||||
|
|
||||||
switch (ev) {
|
switch (ev) {
|
||||||
@@ -26,8 +25,14 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
||||||
printf("-- Connected\n");
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
s_is_connected = 1;
|
if (hm->resp_code == 101) {
|
||||||
|
printf("-- Connected\n");
|
||||||
|
s_is_connected = 1;
|
||||||
|
} else {
|
||||||
|
printf("-- Connection failed! HTTP code %d\n", hm->resp_code);
|
||||||
|
/* Connection will be closed after this. */
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MG_EV_POLL: {
|
case MG_EV_POLL: {
|
||||||
@@ -66,6 +71,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MG_EV_WEBSOCKET_FRAME: {
|
case MG_EV_WEBSOCKET_FRAME: {
|
||||||
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
printf("%.*s\n", (int) wm->size, wm->data);
|
printf("%.*s\n", (int) wm->size, wm->data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+677
-912
File diff suppressed because it is too large.
Load diff
+127
-496
File diff suppressed because it is too large.
Load diff
+53
-26
@@ -24,7 +24,7 @@
|
|||||||
#include "common/cs_time.h"
|
#include "common/cs_time.h"
|
||||||
#include "common/str_util.h"
|
#include "common/str_util.h"
|
||||||
|
|
||||||
enum cs_log_level cs_log_threshold WEAK =
|
enum cs_log_level cs_log_level WEAK =
|
||||||
#if CS_ENABLE_DEBUG
|
#if CS_ENABLE_DEBUG
|
||||||
LL_VERBOSE_DEBUG;
|
LL_VERBOSE_DEBUG;
|
||||||
#else
|
#else
|
||||||
@@ -32,10 +32,9 @@ enum cs_log_level cs_log_threshold WEAK =
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CS_ENABLE_STDIO
|
#if CS_ENABLE_STDIO
|
||||||
static char *s_filter_pattern = NULL;
|
static char *s_file_level = NULL;
|
||||||
static size_t s_filter_pattern_len;
|
|
||||||
|
|
||||||
void cs_log_set_filter(const char *pattern) WEAK;
|
void cs_log_set_file_level(const char *file_level) WEAK;
|
||||||
|
|
||||||
FILE *cs_log_file WEAK = NULL;
|
FILE *cs_log_file WEAK = NULL;
|
||||||
|
|
||||||
@@ -45,34 +44,62 @@ double cs_log_ts WEAK;
|
|||||||
|
|
||||||
enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE;
|
enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE;
|
||||||
|
|
||||||
void cs_log_set_filter(const char *pattern) {
|
void cs_log_set_file_level(const char *file_level) {
|
||||||
free(s_filter_pattern);
|
char *fl = s_file_level;
|
||||||
if (pattern != NULL) {
|
if (file_level != NULL) {
|
||||||
s_filter_pattern = strdup(pattern);
|
s_file_level = strdup(file_level);
|
||||||
s_filter_pattern_len = strlen(pattern);
|
|
||||||
} else {
|
} else {
|
||||||
s_filter_pattern = NULL;
|
s_file_level = NULL;
|
||||||
s_filter_pattern_len = 0;
|
|
||||||
}
|
}
|
||||||
|
free(fl);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK;
|
int cs_log_print_prefix(enum cs_log_level level, const char *file, int ln) WEAK;
|
||||||
int cs_log_print_prefix(enum cs_log_level level, const char *func,
|
int cs_log_print_prefix(enum cs_log_level level, const char *file, int ln) {
|
||||||
const char *filename) {
|
char prefix[CS_LOG_PREFIX_LEN], *q;
|
||||||
char prefix[21];
|
const char *p;
|
||||||
|
size_t fl = 0, ll = 0, pl = 0;
|
||||||
|
|
||||||
if (level > cs_log_threshold) return 0;
|
if (level > cs_log_level && s_file_level == NULL) return 0;
|
||||||
if (s_filter_pattern != NULL &&
|
|
||||||
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 &&
|
p = file + strlen(file);
|
||||||
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) {
|
|
||||||
return 0;
|
while (p != file) {
|
||||||
|
const char c = *(p - 1);
|
||||||
|
if (c == '/' || c == '\\') break;
|
||||||
|
p--;
|
||||||
|
fl++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ll = (ln < 10000 ? (ln < 1000 ? (ln < 100 ? (ln < 10 ? 1 : 2) : 3) : 4) : 5);
|
||||||
|
if (fl > (sizeof(prefix) - ll - 2)) fl = (sizeof(prefix) - ll - 2);
|
||||||
|
|
||||||
|
pl = fl + 1 + ll;
|
||||||
|
memcpy(prefix, p, fl);
|
||||||
|
q = prefix + pl;
|
||||||
|
memset(q, ' ', sizeof(prefix) - pl);
|
||||||
|
do {
|
||||||
|
*(--q) = '0' + (ln % 10);
|
||||||
|
ln /= 10;
|
||||||
|
} while (ln > 0);
|
||||||
|
*(--q) = ':';
|
||||||
|
|
||||||
|
if (s_file_level != NULL) {
|
||||||
|
enum cs_log_level pll = cs_log_level;
|
||||||
|
struct mg_str fl = mg_mk_str(s_file_level), ps = MG_MK_STR_N(prefix, pl);
|
||||||
|
struct mg_str k, v;
|
||||||
|
while ((fl = mg_next_comma_list_entry_n(fl, &k, &v)).p != NULL) {
|
||||||
|
bool yes = !(!mg_str_starts_with(ps, k) || v.len == 0);
|
||||||
|
if (!yes) continue;
|
||||||
|
pll = (enum cs_log_level)(*v.p - '0');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (level > pll) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(prefix, func, 20);
|
|
||||||
prefix[20] = '\0';
|
|
||||||
if (cs_log_file == NULL) cs_log_file = stderr;
|
if (cs_log_file == NULL) cs_log_file = stderr;
|
||||||
cs_log_cur_msg_level = level;
|
cs_log_cur_msg_level = level;
|
||||||
fprintf(cs_log_file, "%-20s ", prefix);
|
fwrite(prefix, 1, sizeof(prefix), cs_log_file);
|
||||||
#if CS_LOG_ENABLE_TS_DIFF
|
#if CS_LOG_ENABLE_TS_DIFF
|
||||||
{
|
{
|
||||||
double now = cs_time();
|
double now = cs_time();
|
||||||
@@ -101,15 +128,15 @@ void cs_log_set_file(FILE *file) {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void cs_log_set_filter(const char *pattern) {
|
void cs_log_set_file_level(const char *file_level) {
|
||||||
(void) pattern;
|
(void) file_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CS_ENABLE_STDIO */
|
#endif /* CS_ENABLE_STDIO */
|
||||||
|
|
||||||
void cs_log_set_level(enum cs_log_level level) WEAK;
|
void cs_log_set_level(enum cs_log_level level) WEAK;
|
||||||
void cs_log_set_level(enum cs_log_level level) {
|
void cs_log_set_level(enum cs_log_level level) {
|
||||||
cs_log_threshold = level;
|
cs_log_level = level;
|
||||||
#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO
|
#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO
|
||||||
cs_log_ts = cs_time();
|
cs_log_ts = cs_time();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+22
-39
@@ -28,6 +28,10 @@
|
|||||||
#define CS_ENABLE_DEBUG 0
|
#define CS_ENABLE_DEBUG 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CS_LOG_PREFIX_LEN
|
||||||
|
#define CS_LOG_PREFIX_LEN 24
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CS_LOG_ENABLE_TS_DIFF
|
#ifndef CS_LOG_ENABLE_TS_DIFF
|
||||||
#define CS_LOG_ENABLE_TS_DIFF 0
|
#define CS_LOG_ENABLE_TS_DIFF 0
|
||||||
#endif
|
#endif
|
||||||
@@ -58,51 +62,28 @@ enum cs_log_level {
|
|||||||
void cs_log_set_level(enum cs_log_level level);
|
void cs_log_set_level(enum cs_log_level level);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set log filter. NULL (a default) logs everything.
|
* A comma-separated set of prefix=level.
|
||||||
* Otherwise, function name and file name will be tested against the given
|
* prefix is matched against the log prefix exactly as printed, including line
|
||||||
* pattern, and only matching messages will be printed.
|
* number, but partial match is ok. Check stops on first matching entry.
|
||||||
|
* If nothing matches, default level is used.
|
||||||
*
|
*
|
||||||
* For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`.
|
* Examples:
|
||||||
|
* main.c:=4 - everything from main C at verbose debug level.
|
||||||
|
* mongoose.c=1,mjs.c=1,=4 - everything at verbose debug except mg_* and mjs_*
|
||||||
*
|
*
|
||||||
* Example:
|
|
||||||
* ```c
|
|
||||||
* void foo(void) {
|
|
||||||
* LOG(LL_INFO, ("hello from foo"));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* void bar(void) {
|
|
||||||
* LOG(LL_INFO, ("hello from bar"));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* void test(void) {
|
|
||||||
* cs_log_set_filter(NULL);
|
|
||||||
* foo();
|
|
||||||
* bar();
|
|
||||||
*
|
|
||||||
* cs_log_set_filter("f*");
|
|
||||||
* foo();
|
|
||||||
* bar(); // Will NOT print anything
|
|
||||||
*
|
|
||||||
* cs_log_set_filter("bar");
|
|
||||||
* foo(); // Will NOT print anything
|
|
||||||
* bar();
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
void cs_log_set_filter(const char *pattern);
|
void cs_log_set_file_level(const char *file_level);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function which prints message prefix with the given `level`, function
|
* Helper function which prints message prefix with the given `level`.
|
||||||
* name `func` and `filename`. If message should be printed (accordingly to the
|
* If message should be printed (according to the current log level
|
||||||
* current log level and filter), prints the prefix and returns 1, otherwise
|
* and filter), prints the prefix and returns 1, otherwise returns 0.
|
||||||
* returns 0.
|
|
||||||
*
|
*
|
||||||
* Clients should typically just use `LOG()` macro.
|
* Clients should typically just use `LOG()` macro.
|
||||||
*/
|
*/
|
||||||
int cs_log_print_prefix(enum cs_log_level level, const char *func,
|
int cs_log_print_prefix(enum cs_log_level level, const char *fname, int line);
|
||||||
const char *filename);
|
|
||||||
|
|
||||||
extern enum cs_log_level cs_log_threshold;
|
extern enum cs_log_level cs_log_level;
|
||||||
|
|
||||||
#if CS_ENABLE_STDIO
|
#if CS_ENABLE_STDIO
|
||||||
|
|
||||||
@@ -127,9 +108,11 @@ void cs_log_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
|||||||
* LOG(LL_DEBUG, ("my debug message: %d", 123));
|
* LOG(LL_DEBUG, ("my debug message: %d", 123));
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
#define LOG(l, x) \
|
#define LOG(l, x) \
|
||||||
do { \
|
do { \
|
||||||
if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \
|
if (cs_log_print_prefix(l, __FILE__, __LINE__)) { \
|
||||||
|
cs_log_printf x; \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -22,8 +22,9 @@
|
|||||||
#define CS_P_NRF51 12
|
#define CS_P_NRF51 12
|
||||||
#define CS_P_NRF52 10
|
#define CS_P_NRF52 10
|
||||||
#define CS_P_PIC32 11
|
#define CS_P_PIC32 11
|
||||||
|
#define CS_P_RS14100 18
|
||||||
#define CS_P_STM32 16
|
#define CS_P_STM32 16
|
||||||
/* Next id: 18 */
|
/* Next id: 19 */
|
||||||
|
|
||||||
/* If not specified explicitly, we guess platform by defines. */
|
/* If not specified explicitly, we guess platform by defines. */
|
||||||
#ifndef CS_PLATFORM
|
#ifndef CS_PLATFORM
|
||||||
@@ -55,6 +56,8 @@
|
|||||||
#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
|
#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
|
||||||
defined(TARGET_IS_TM4C129_RA2)
|
defined(TARGET_IS_TM4C129_RA2)
|
||||||
#define CS_PLATFORM CS_P_TM4C129
|
#define CS_PLATFORM CS_P_TM4C129
|
||||||
|
#elif defined(RS14100)
|
||||||
|
#define CS_PLATFORM CS_P_RS14100
|
||||||
#elif defined(STM32)
|
#elif defined(STM32)
|
||||||
#define CS_PLATFORM CS_P_STM32
|
#define CS_PLATFORM CS_P_STM32
|
||||||
#endif
|
#endif
|
||||||
@@ -89,7 +92,11 @@
|
|||||||
#include "common/platforms/platform_nxp_lpc.h"
|
#include "common/platforms/platform_nxp_lpc.h"
|
||||||
#include "common/platforms/platform_nxp_kinetis.h"
|
#include "common/platforms/platform_nxp_kinetis.h"
|
||||||
#include "common/platforms/platform_pic32.h"
|
#include "common/platforms/platform_pic32.h"
|
||||||
|
#include "common/platforms/platform_rs14100.h"
|
||||||
#include "common/platforms/platform_stm32.h"
|
#include "common/platforms/platform_stm32.h"
|
||||||
|
#if CS_PLATFORM == CS_P_CUSTOM
|
||||||
|
#include <platform_custom.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Common stuff */
|
/* Common stuff */
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "arm_exc.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -31,33 +33,7 @@
|
|||||||
#define MGOS_ENABLE_CORE_DUMP 1
|
#define MGOS_ENABLE_CORE_DUMP 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct arm_exc_frame {
|
#if __FPU_PRESENT && !defined(MGOS_BOOT_BUILD)
|
||||||
uint32_t r0;
|
|
||||||
uint32_t r1;
|
|
||||||
uint32_t r2;
|
|
||||||
uint32_t r3;
|
|
||||||
uint32_t r12;
|
|
||||||
uint32_t lr;
|
|
||||||
uint32_t pc;
|
|
||||||
uint32_t xpsr;
|
|
||||||
#ifdef ARM_HAVE_FPU
|
|
||||||
uint32_t s[16];
|
|
||||||
uint32_t fpscr;
|
|
||||||
uint32_t reserved;
|
|
||||||
#endif
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct arm_gdb_reg_file {
|
|
||||||
uint32_t r[13];
|
|
||||||
uint32_t sp;
|
|
||||||
uint32_t lr;
|
|
||||||
uint32_t pc;
|
|
||||||
uint32_t cpsr;
|
|
||||||
uint64_t d[16];
|
|
||||||
uint32_t fpscr;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
#if ARM_HAVE_FPU && !defined(MGOS_BOOT_BUILD)
|
|
||||||
static void save_s16_s31(uint32_t *dst) {
|
static void save_s16_s31(uint32_t *dst) {
|
||||||
__asm volatile(
|
__asm volatile(
|
||||||
"\
|
"\
|
||||||
@@ -91,17 +67,25 @@ static void print_fpu_regs(const uint32_t *regs, int off, int n) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static struct arm_gdb_reg_file *s_rf = NULL;
|
||||||
|
|
||||||
|
void arm_exc_dump_regs(void) {
|
||||||
|
mgos_cd_write_section(MGOS_CORE_DUMP_SECTION_REGS, s_rf, sizeof(*s_rf));
|
||||||
|
}
|
||||||
|
|
||||||
void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
|
void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
|
||||||
struct arm_gdb_reg_file *rf) {
|
struct arm_gdb_reg_file *rf) {
|
||||||
char buf[8];
|
char buf[8];
|
||||||
const char *name;
|
const char *name;
|
||||||
(void) ef;
|
#if __MPU_PRESENT
|
||||||
|
MPU->CTRL = 0; // Disable MPU.
|
||||||
|
#endif
|
||||||
portDISABLE_INTERRUPTS();
|
portDISABLE_INTERRUPTS();
|
||||||
|
s_rf = rf;
|
||||||
switch (isr_no) {
|
switch (isr_no) {
|
||||||
case 0:
|
case 0:
|
||||||
name = "ThreadMode";
|
name = "ThreadMode";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
case 7:
|
case 7:
|
||||||
case 8:
|
case 8:
|
||||||
@@ -110,7 +94,6 @@ void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
|
|||||||
case 13:
|
case 13:
|
||||||
name = "Reserved";
|
name = "Reserved";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
name = "NMI";
|
name = "NMI";
|
||||||
break;
|
break;
|
||||||
@@ -155,12 +138,14 @@ void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
|
|||||||
rf->r[4], 5, rf->r[5], 6, rf->r[6], 7, rf->r[7]);
|
rf->r[4], 5, rf->r[5], 6, rf->r[6], 7, rf->r[7]);
|
||||||
mgos_cd_printf(" R8: 0x%08lx R9: 0x%08lx R10: 0x%08lx R11: 0x%08lx\n",
|
mgos_cd_printf(" R8: 0x%08lx R9: 0x%08lx R10: 0x%08lx R11: 0x%08lx\n",
|
||||||
rf->r[8], rf->r[9], rf->r[10], rf->r[11]);
|
rf->r[8], rf->r[9], rf->r[10], rf->r[11]);
|
||||||
mgos_cd_printf(" R12: 0x%08lx SP: 0x%08lx LR: 0x%08lx PC: 0x%08lx\n",
|
mgos_cd_printf(" R12: 0x%08lx SP: 0x%08lx LR: 0x%08lx PC: 0x%08lx\n",
|
||||||
rf->r[12], rf->sp, rf->lr, rf->pc);
|
rf->r[12], rf->sp, rf->lr, rf->pc);
|
||||||
mgos_cd_printf(" PSR: 0x%08lx\n", rf->cpsr);
|
mgos_cd_printf(" PSR: 0x%08lx MSP: 0x%08lx PSP: 0x%08lx\n", rf->xpsr,
|
||||||
|
rf->msp, rf->psp);
|
||||||
}
|
}
|
||||||
|
#if __FPU_PRESENT
|
||||||
memset(rf->d, 0, sizeof(rf->d));
|
memset(rf->d, 0, sizeof(rf->d));
|
||||||
#if ARM_HAVE_FPU && !defined(MGOS_BOOT_BUILD)
|
#if !defined(MGOS_BOOT_BUILD)
|
||||||
rf->fpscr = ef->fpscr;
|
rf->fpscr = ef->fpscr;
|
||||||
memcpy((uint8_t *) rf->d, ef->s, sizeof(ef->s));
|
memcpy((uint8_t *) rf->d, ef->s, sizeof(ef->s));
|
||||||
print_fpu_regs((uint32_t *) rf->d, 0, ARRAY_SIZE(ef->s));
|
print_fpu_regs((uint32_t *) rf->d, 0, ARRAY_SIZE(ef->s));
|
||||||
@@ -173,11 +158,9 @@ void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
|
|||||||
#else
|
#else
|
||||||
rf->fpscr = 0;
|
rf->fpscr = 0;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#if MGOS_ENABLE_CORE_DUMP
|
#if MGOS_ENABLE_CORE_DUMP
|
||||||
mgos_cd_emit_header();
|
mgos_cd_write();
|
||||||
mgos_cd_emit_section(MGOS_CORE_DUMP_SECTION_REGS, rf, sizeof(*rf));
|
|
||||||
mgos_cd_emit_section("SRAM", (void *) SRAM_BASE_ADDR, SRAM_SIZE);
|
|
||||||
mgos_cd_emit_footer();
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef MGOS_HALT_ON_EXCEPTION
|
#ifdef MGOS_HALT_ON_EXCEPTION
|
||||||
mgos_cd_printf("Halting\n");
|
mgos_cd_printf("Halting\n");
|
||||||
@@ -188,4 +171,5 @@ void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
|
|||||||
mgos_cd_printf("Rebooting\n");
|
mgos_cd_printf("Rebooting\n");
|
||||||
mgos_dev_system_restart();
|
mgos_dev_system_restart();
|
||||||
#endif
|
#endif
|
||||||
|
(void) ef;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2019 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct arm_exc_frame {
|
||||||
|
uint32_t r0;
|
||||||
|
uint32_t r1;
|
||||||
|
uint32_t r2;
|
||||||
|
uint32_t r3;
|
||||||
|
uint32_t r12;
|
||||||
|
uint32_t lr;
|
||||||
|
uint32_t pc;
|
||||||
|
uint32_t xpsr;
|
||||||
|
#if __FPU_PRESENT
|
||||||
|
uint32_t s[16];
|
||||||
|
uint32_t fpscr;
|
||||||
|
uint32_t reserved;
|
||||||
|
#endif
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct arm_gdb_reg_file {
|
||||||
|
uint32_t r[13];
|
||||||
|
uint32_t sp;
|
||||||
|
uint32_t lr;
|
||||||
|
uint32_t pc;
|
||||||
|
uint32_t xpsr;
|
||||||
|
#if __FPU_PRESENT
|
||||||
|
uint64_t d[16];
|
||||||
|
uint32_t fpscr;
|
||||||
|
#endif
|
||||||
|
// MSP and PSP are our extension.
|
||||||
|
uint32_t msp;
|
||||||
|
uint32_t psp;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
|
||||||
|
struct arm_gdb_reg_file *rf);
|
||||||
|
void arm_exc_dump_regs(void);
|
||||||
@@ -28,12 +28,12 @@ arm_exc_handler_top:
|
|||||||
mrseq r1, msp
|
mrseq r1, msp
|
||||||
mrsne r1, psp
|
mrsne r1, psp
|
||||||
// r1 -> arm_exc_frame prepared for us by the CPU
|
// r1 -> arm_exc_frame prepared for us by the CPU
|
||||||
#if ARM_HAVE_FPU
|
#if __FPU_PRESENT
|
||||||
add r0, r1, #104 // sizeof(arm_exc_frame)
|
add r0, r1, #104 // sizeof(arm_exc_frame)
|
||||||
sub sp, #328 // sizeof(arm_gdb_reg_file)
|
sub sp, #208 // sizeof(arm_gdb_reg_file)
|
||||||
#else
|
#else
|
||||||
add r0, r1, #32 // sizeof(arm_exc_frame)
|
add r0, r1, #32 // sizeof(arm_exc_frame)
|
||||||
sub sp, #328 // sizeof(arm_gdb_reg_file)
|
sub sp, #76 // sizeof(arm_gdb_reg_file)
|
||||||
#endif
|
#endif
|
||||||
mov r2, sp
|
mov r2, sp
|
||||||
// r0 -> original sp, r2 -> arm_gdb_reg_file to fill
|
// r0 -> original sp, r2 -> arm_gdb_reg_file to fill
|
||||||
@@ -63,6 +63,17 @@ arm_exc_handler_top:
|
|||||||
str r3, [r2, #60]
|
str r3, [r2, #60]
|
||||||
ldr r3, [r1, #28] // xpsr
|
ldr r3, [r1, #28] // xpsr
|
||||||
str r3, [r2, #64]
|
str r3, [r2, #64]
|
||||||
|
#if __FPU_PRESENT
|
||||||
|
mrs r3, msp
|
||||||
|
str r3, [r2, #200] // msp
|
||||||
|
mrs r3, psp
|
||||||
|
str r3, [r2, #204] // psp
|
||||||
|
#else
|
||||||
|
mrs r3, msp
|
||||||
|
str r3, [r2, #68] // msp
|
||||||
|
mrs r3, psp
|
||||||
|
str r3, [r2, #72] // psp
|
||||||
|
#endif
|
||||||
|
|
||||||
mrs r0, ipsr
|
mrs r0, ipsr
|
||||||
b arm_exc_handler_bottom
|
b arm_exc_handler_bottom
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2019 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.arch armv7e-m
|
||||||
|
.syntax unified
|
||||||
|
.thumb
|
||||||
|
|
||||||
|
/* These are required to satisfy TI linker. */
|
||||||
|
.eabi_attribute Tag_ABI_align_needed, 1
|
||||||
|
.eabi_attribute Tag_ABI_align_preserved, 1
|
||||||
|
|
||||||
|
.global mgos_nsleep100_impl
|
||||||
|
.global mgos_nsleep100_loop_count
|
||||||
|
|
||||||
|
.section .text.IRAM.mgos_nsleep100_impl
|
||||||
|
.type mgos_nsleep100_impl, %function
|
||||||
|
.align 4
|
||||||
|
|
||||||
|
mgos_nsleep100_impl:
|
||||||
|
ldr r3, =mgos_nsleep100_loop_count
|
||||||
|
ldr r3, [r3]
|
||||||
|
mul r0, r3
|
||||||
|
mov r1, #6
|
||||||
|
udiv r0, r0, r1
|
||||||
|
cbz r0, xxx
|
||||||
|
lxx:
|
||||||
|
subs r0, #1
|
||||||
|
bne lxx
|
||||||
|
xxx:
|
||||||
|
bx lr
|
||||||
|
.align 4
|
||||||
|
.size mgos_nsleep100_impl, . - mgos_nsleep100_impl
|
||||||
@@ -26,7 +26,11 @@
|
|||||||
.global mgos_nsleep100_impl
|
.global mgos_nsleep100_impl
|
||||||
.global mgos_nsleep100_loop_count
|
.global mgos_nsleep100_loop_count
|
||||||
|
|
||||||
|
#if defined(TARGET_IS_CC3200) || defined(TARGET_IS_CC3220)
|
||||||
.section .iram.mgos_nsleep100_impl
|
.section .iram.mgos_nsleep100_impl
|
||||||
|
#else
|
||||||
|
.section .text.IRAM.mgos_nsleep100_impl
|
||||||
|
#endif
|
||||||
.type mgos_nsleep100_impl, %function
|
.type mgos_nsleep100_impl, %function
|
||||||
.align 4
|
.align 4
|
||||||
|
|
||||||
@@ -34,13 +38,17 @@ mgos_nsleep100_impl:
|
|||||||
ldr r3, =mgos_nsleep100_loop_count
|
ldr r3, =mgos_nsleep100_loop_count
|
||||||
ldr r3, [r3]
|
ldr r3, [r3]
|
||||||
mul r0, r3
|
mul r0, r3
|
||||||
|
#if defined(STM32L4)
|
||||||
|
mov r1, #3
|
||||||
|
#else
|
||||||
mov r1, #6
|
mov r1, #6
|
||||||
|
#endif
|
||||||
udiv r0, r0, r1
|
udiv r0, r0, r1
|
||||||
cbz r0, xxx
|
cbz r0, mgos_nsleep100_impl_out
|
||||||
lxx:
|
mgos_nsleep100_impl_loop:
|
||||||
subs r0, #1
|
subs r0, #1
|
||||||
bne lxx
|
bne mgos_nsleep100_impl_loop
|
||||||
xxx:
|
mgos_nsleep100_impl_out:
|
||||||
bx lr
|
bx lr
|
||||||
.align 4
|
.align 4
|
||||||
.size mgos_nsleep100_impl, . - mgos_nsleep100_impl
|
.size mgos_nsleep100_impl, . - mgos_nsleep100_impl
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
.global mgos_nsleep100_impl
|
.global mgos_nsleep100_impl
|
||||||
.global mgos_nsleep100_loop_count
|
.global mgos_nsleep100_loop_count
|
||||||
|
|
||||||
.section .iram.mgos_nsleep100_impl
|
.section .text.IRAM.mgos_nsleep100_impl
|
||||||
.type mgos_nsleep100_impl, %function
|
.type mgos_nsleep100_impl, %function
|
||||||
.align 4
|
.align 4
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
#include "eagle_soc.h"
|
#include "eagle_soc.h"
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
#include "../../../miniz.c"
|
#include "miniz.c"
|
||||||
#elif defined(ESP32)
|
#elif defined(ESP32)
|
||||||
#include "rom/efuse.h"
|
#include "rom/efuse.h"
|
||||||
#include "rom/miniz.h"
|
#include "rom/miniz.h"
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
|
|
||||||
/* Param: baud rate. */
|
/* Param: baud rate. */
|
||||||
uint32_t params[1] __attribute__((section(".params")));
|
uint32_t params[2] __attribute__((section(".params")));
|
||||||
|
|
||||||
#define FLASH_BLOCK_SIZE 65536
|
#define FLASH_BLOCK_SIZE 65536
|
||||||
#define FLASH_SECTOR_SIZE 4096
|
#define FLASH_SECTOR_SIZE 4096
|
||||||
@@ -470,6 +470,27 @@ uint8_t cmd_loop(void) {
|
|||||||
resp = 0;
|
resp = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CMD_READ_REG: {
|
||||||
|
len = SLIP_recv(args, sizeof(args));
|
||||||
|
if (len == 4) {
|
||||||
|
uint32_t value = READ_PERI_REG((uint32_t *) args[0]);
|
||||||
|
SLIP_send(&value, sizeof(value));
|
||||||
|
resp = 0;
|
||||||
|
} else {
|
||||||
|
resp = 0x91;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_WRITE_REG: {
|
||||||
|
len = SLIP_recv(args, sizeof(args));
|
||||||
|
if (len == 8) {
|
||||||
|
WRITE_PERI_REG((uint32_t *) args[0], args[1]);
|
||||||
|
resp = 0;
|
||||||
|
} else {
|
||||||
|
resp = 0xa1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SLIP_send(&resp, 1);
|
SLIP_send(&resp, 1);
|
||||||
} while (cmd != CMD_BOOT_FW && cmd != CMD_REBOOT);
|
} while (cmd != CMD_BOOT_FW && cmd != CMD_REBOOT);
|
||||||
@@ -477,8 +498,7 @@ uint8_t cmd_loop(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void stub_main1(void) {
|
void stub_main1(void) {
|
||||||
uint32_t baud_rate = params[0];
|
uint32_t old_baud_rate = params[0], new_baud_rate = params[1];
|
||||||
uint32_t greeting = 0x4941484f; /* OHAI */
|
|
||||||
uint8_t last_cmd;
|
uint8_t last_cmd;
|
||||||
|
|
||||||
/* This points at us right now, reset for next boot. */
|
/* This points at us right now, reset for next boot. */
|
||||||
@@ -487,6 +507,7 @@ void stub_main1(void) {
|
|||||||
/* Selects SPI functions for flash pins. */
|
/* Selects SPI functions for flash pins. */
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
SelectSpiFunction();
|
SelectSpiFunction();
|
||||||
|
spi_flash_attach();
|
||||||
SET_PERI_REG_MASK(0x3FF00014, 1); /* Switch to 160 MHz */
|
SET_PERI_REG_MASK(0x3FF00014, 1); /* Switch to 160 MHz */
|
||||||
#elif defined(ESP32)
|
#elif defined(ESP32)
|
||||||
esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), 0 /* legacy */);
|
esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), 0 /* legacy */);
|
||||||
@@ -496,9 +517,10 @@ void stub_main1(void) {
|
|||||||
0 /* deviceId */, 16 * 1024 * 1024 /* chip_size */, FLASH_BLOCK_SIZE,
|
0 /* deviceId */, 16 * 1024 * 1024 /* chip_size */, FLASH_BLOCK_SIZE,
|
||||||
FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, 0xffff /* status_mask */);
|
FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, 0xffff /* status_mask */);
|
||||||
|
|
||||||
if (baud_rate > 0) {
|
uint32_t old_div = 0;
|
||||||
|
if (new_baud_rate > 0) {
|
||||||
ets_delay_us(10000);
|
ets_delay_us(10000);
|
||||||
set_baud_rate(0, baud_rate);
|
old_div = set_baud_rate(0, old_baud_rate, new_baud_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Give host time to get ready too. */
|
/* Give host time to get ready too. */
|
||||||
@@ -509,7 +531,7 @@ void stub_main1(void) {
|
|||||||
WRITE_PERI_REG(UART_FIFO_REG(0), 0x55);
|
WRITE_PERI_REG(UART_FIFO_REG(0), 0x55);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
SLIP_send(&greeting, 4);
|
SLIP_send(&old_div, 4);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
last_cmd = cmd_loop();
|
last_cmd = cmd_loop();
|
||||||
@@ -525,7 +547,7 @@ void stub_main1(void) {
|
|||||||
* then jumps to 0x4000108a, then checks strapping bits again (which will
|
* then jumps to 0x4000108a, then checks strapping bits again (which will
|
||||||
* not have changed), and then proceeds to 0x400010a8.
|
* not have changed), and then proceeds to 0x400010a8.
|
||||||
*/
|
*/
|
||||||
volatile uint32_t *sp = &baud_rate;
|
volatile uint32_t *sp = &old_baud_rate;
|
||||||
while (*sp != (uint32_t) 0x40001100) sp++;
|
while (*sp != (uint32_t) 0x40001100) sp++;
|
||||||
*sp = 0x400010a8;
|
*sp = 0x400010a8;
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -91,6 +91,24 @@ enum stub_cmd {
|
|||||||
* Output: arguments.
|
* Output: arguments.
|
||||||
*/
|
*/
|
||||||
CMD_ECHO = 8,
|
CMD_ECHO = 8,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read register value.
|
||||||
|
*
|
||||||
|
* Args: register address.
|
||||||
|
* Input: None.
|
||||||
|
* Output: register value.
|
||||||
|
*/
|
||||||
|
CMD_READ_REG = 9,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write register value.
|
||||||
|
*
|
||||||
|
* Args: register address, value.
|
||||||
|
* Input: None.
|
||||||
|
* Output: None.
|
||||||
|
*/
|
||||||
|
CMD_WRITE_REG = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ */
|
#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ */
|
||||||
@@ -42,5 +42,10 @@ SECTIONS {
|
|||||||
INCLUDE "components/esp32/ld/esp32.rom.ld"
|
INCLUDE "components/esp32/ld/esp32.rom.ld"
|
||||||
INCLUDE "components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld"
|
INCLUDE "components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld"
|
||||||
|
|
||||||
|
PROVIDE(ets_isr_mask = 0x400067fc);
|
||||||
|
PROVIDE(ets_isr_unmask = 0x40006808);
|
||||||
|
PROVIDE(MD5Init = 0x4005da7c);
|
||||||
|
PROVIDE(MD5Update = 0x4005da9c);
|
||||||
|
PROVIDE(MD5Final = 0x4005db1c);
|
||||||
PROVIDE(esp_rom_spiflash_attach = 0x40062a6c);
|
PROVIDE(esp_rom_spiflash_attach = 0x40062a6c);
|
||||||
PROVIDE(esp_rom_spiflash_config_clk = 0x40062bc8);
|
PROVIDE(esp_rom_spiflash_config_clk = 0x40062bc8);
|
||||||
@@ -19,9 +19,13 @@
|
|||||||
|
|
||||||
#include "rom_functions.h"
|
#include "rom_functions.h"
|
||||||
|
|
||||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) {
|
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||||
uint32_t master_freq = ets_get_detected_xtal_freq() << 4;
|
uint32_t new_baud_rate) {
|
||||||
master_freq += (baud_rate / 2);
|
uint32_t uart_reg = REG_READ(UART_CLKDIV_REG(uart_no));
|
||||||
uint32_t div = master_freq / baud_rate;
|
uint32_t uart_div = uart_reg & UART_CLKDIV_M;
|
||||||
uart_div_modify(uart_no, div);
|
uint32_t fraction = (uart_reg >> UART_CLKDIV_FRAG_S) & UART_CLKDIV_FRAG_V;
|
||||||
|
uart_div = (uart_div << 4) + fraction;
|
||||||
|
uint32_t master_freq = uart_div * old_baud_rate;
|
||||||
|
uart_div_modify(uart_no, master_freq / new_baud_rate);
|
||||||
|
return uart_div;
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate);
|
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||||
|
uint32_t new_baud_rate);
|
||||||
|
|
||||||
#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ */
|
#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ */
|
||||||
@@ -23,13 +23,13 @@ all: $(STUB_ELF)
|
|||||||
$(STUB_ELF): $(STUB) $(LIBS)
|
$(STUB_ELF): $(STUB) $(LIBS)
|
||||||
@echo " CC $^ -> $@"
|
@echo " CC $^ -> $@"
|
||||||
@[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR)
|
@[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR)
|
||||||
@docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \
|
docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \
|
||||||
"cd /src/common/platforms/esp8266/stubs && \
|
"cd /src/common/platforms/esp8266/stubs && \
|
||||||
$(XT_CC) -std=c99 -Wall -Werror -Os -DESP8266 \
|
$(XT_CC) -std=c99 -Wall -Werror -Os -DESP8266 \
|
||||||
-mtext-section-literals -mlongcalls -nostdlib -fno-builtin \
|
-mtext-section-literals -mlongcalls -nostdlib -fno-builtin \
|
||||||
-I. -I/src/common/platforms/esp \
|
-I. -I/src/common/platforms/esp -I/src/common/platforms/esp8266 \
|
||||||
-I/opt/Espressif/ESP8266_SDK \
|
-I/opt/Espressif/ESP8266_SDK \
|
||||||
-Wl,-static -ffunction-sections -Wl,--gc-sections \
|
-Wl,-static -ffunction-sections -lgcc -Wl,--gc-sections \
|
||||||
-Tstub.ld $(CFLAGS) -o $@ $^"
|
-Tstub.ld $(CFLAGS) -o $@ $^"
|
||||||
|
|
||||||
wrap: $(STUB_JSON)
|
wrap: $(STUB_JSON)
|
||||||
|
|||||||
@@ -41,3 +41,4 @@ INCLUDE "eagle.rom.addr.v6.ld"
|
|||||||
PROVIDE(SPIFlashModeConfig = 0x40004568);
|
PROVIDE(SPIFlashModeConfig = 0x40004568);
|
||||||
PROVIDE(SPI_erase_sector = 0x400040c0);
|
PROVIDE(SPI_erase_sector = 0x400040c0);
|
||||||
PROVIDE(SPI_erase_block = 0x40004120);
|
PROVIDE(SPI_erase_block = 0x40004120);
|
||||||
|
PROVIDE(uart_div_modify = 0x400039d8);
|
||||||
@@ -18,9 +18,15 @@
|
|||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
|
|
||||||
#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B
|
#define UART_CLKDIV_M (UART_CLKDIV_CNT << UART_CLKDIV_S)
|
||||||
|
|
||||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) {
|
void uart_div_modify(uint8_t uart_no, uint32_t div);
|
||||||
uint32_t div = UART_CLKDIV_26MHZ(baud_rate);
|
|
||||||
WRITE_PERI_REG(UART_CLKDIV_REG(uart_no), div & 0xfffff);
|
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||||
|
uint32_t new_baud_rate) {
|
||||||
|
uint32_t uart_reg = READ_PERI_REG(UART_CLKDIV_REG(uart_no));
|
||||||
|
uint32_t uart_div = uart_reg & UART_CLKDIV_M;
|
||||||
|
uint32_t master_freq = uart_div * old_baud_rate;
|
||||||
|
uart_div_modify(uart_no, master_freq / new_baud_rate);
|
||||||
|
return uart_div;
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate);
|
#include "uart_register.h"
|
||||||
|
|
||||||
|
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||||
|
uint32_t new_baud_rate);
|
||||||
|
|
||||||
#define REG_UART_BASE(i) (0x60000000 + (i) *0xf00)
|
|
||||||
#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0)
|
#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0)
|
||||||
#define UART_CONF1_REG(i) (REG_UART_BASE(i) + 0x24)
|
#define UART_CONF1_REG(i) (REG_UART_BASE(i) + 0x24)
|
||||||
#define UART_RX_TOUT_EN (BIT(31))
|
#define UART_RX_TOUT_EN (BIT(31))
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# Analyzers output of firmware compiler with -DESP_ENABLE_MALLOC_TRACES
|
|
||||||
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
allocs = {}
|
|
||||||
|
|
||||||
for l in sys.stdin:
|
|
||||||
m = re.match(r'(ca|ma|re|fr) (\S+)\s*(\S*)\s*(\S*)', l)
|
|
||||||
if not m: continue
|
|
||||||
op = m.group(1)
|
|
||||||
if op in ('ca', 'ma'):
|
|
||||||
allocs[m.group(2)] = long(m.group(3))
|
|
||||||
else:
|
|
||||||
if m.group(2) in allocs:
|
|
||||||
del allocs[m.group(2)]
|
|
||||||
if op == 're':
|
|
||||||
allocs[m.group(3)] = long(m.group(4))
|
|
||||||
|
|
||||||
for k, v in sorted(allocs.iteritems()):
|
|
||||||
print k, v
|
|
||||||
@@ -67,7 +67,6 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MG_SIG_CLOSE_CONN: {
|
case MG_SIG_CLOSE_CONN: {
|
||||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
|
||||||
mg_close_conn(nc);
|
mg_close_conn(nc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -92,6 +91,10 @@ void mg_lwip_if_init(struct mg_iface *iface) {
|
|||||||
LOG(LL_INFO, ("Mongoose %s, LwIP %u.%u.%u", MG_VERSION, LWIP_VERSION_MAJOR,
|
LOG(LL_INFO, ("Mongoose %s, LwIP %u.%u.%u", MG_VERSION, LWIP_VERSION_MAJOR,
|
||||||
LWIP_VERSION_MINOR, LWIP_VERSION_REVISION));
|
LWIP_VERSION_MINOR, LWIP_VERSION_REVISION));
|
||||||
iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data));
|
iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data));
|
||||||
|
#if !NO_SYS && !LWIP_TCPIP_CORE_LOCKING
|
||||||
|
sys_sem_new(&s_tcpip_call_lock_sem, 1);
|
||||||
|
sys_sem_new(&s_tcpip_call_sync_sem, 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_lwip_if_free(struct mg_iface *iface) {
|
void mg_lwip_if_free(struct mg_iface *iface) {
|
||||||
@@ -133,7 +136,7 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
|
|||||||
if (nc->sock != INVALID_SOCKET &&
|
if (nc->sock != INVALID_SOCKET &&
|
||||||
!(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL &&
|
!(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL &&
|
||||||
cs->pcb.tcp->unsent != NULL) {
|
cs->pcb.tcp->unsent != NULL) {
|
||||||
tcpip_callback(tcp_output_tcpip, cs->pcb.tcp);
|
mg_lwip_netif_run_on_tcpip(tcp_output_tcpip, cs->pcb.tcp);
|
||||||
}
|
}
|
||||||
if (nc->ev_timer_time > 0) {
|
if (nc->ev_timer_time > 0) {
|
||||||
if (num_timers == 0 || nc->ev_timer_time < min_timer) {
|
if (num_timers == 0 || nc->ev_timer_time < min_timer) {
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
#include <lwip/tcp.h>
|
#include <lwip/tcp.h>
|
||||||
#include <lwip/tcpip.h>
|
#include <lwip/tcpip.h>
|
||||||
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
|
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
|
||||||
#include <lwip/priv/tcp_priv.h> /* For tcp_seg */
|
#include <lwip/priv/tcp_priv.h> /* For tcp_seg */
|
||||||
|
#include <lwip/priv/tcpip_priv.h> /* For tcpip_api_call */
|
||||||
#else
|
#else
|
||||||
#include <lwip/tcp_impl.h>
|
#include <lwip/tcp_impl.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -64,9 +65,35 @@
|
|||||||
#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
|
#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if NO_SYS
|
#if !NO_SYS
|
||||||
#define tcpip_callback(fn, arg) (fn)(arg)
|
#if LWIP_TCPIP_CORE_LOCKING
|
||||||
typedef void (*tcpip_callback_fn)(void *arg);
|
/* With locking tcpip_api_call is just a function call wrapped in lock/unlock,
|
||||||
|
* so we can get away with just casting. */
|
||||||
|
void mg_lwip_netif_run_on_tcpip(void (*fn)(void *), void *arg) {
|
||||||
|
tcpip_api_call((tcpip_api_call_fn) fn, (struct tcpip_api_call_data *) arg);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static sys_sem_t s_tcpip_call_lock_sem = NULL;
|
||||||
|
static sys_sem_t s_tcpip_call_sync_sem = NULL;
|
||||||
|
struct mg_lwip_netif_tcpip_call_ctx {
|
||||||
|
void (*fn)(void *);
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
static void xxx_tcpip(void *arg) {
|
||||||
|
struct mg_lwip_netif_tcpip_call_ctx *ctx =
|
||||||
|
(struct mg_lwip_netif_tcpip_call_ctx *) arg;
|
||||||
|
ctx->fn(ctx->arg);
|
||||||
|
sys_sem_signal(&s_tcpip_call_sync_sem);
|
||||||
|
}
|
||||||
|
void mg_lwip_netif_run_on_tcpip(void (*fn)(void *), void *arg) {
|
||||||
|
struct mg_lwip_netif_tcpip_call_ctx ctx = {.fn = fn, .arg = arg};
|
||||||
|
sys_arch_sem_wait(&s_tcpip_call_lock_sem, 0);
|
||||||
|
tcpip_send_msg_wait_sem(xxx_tcpip, &ctx, &s_tcpip_call_sync_sem);
|
||||||
|
sys_sem_signal(&s_tcpip_call_lock_sem);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define mg_lwip_netif_run_on_tcpip(fn, arg) (fn)(arg)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void mg_lwip_if_init(struct mg_iface *iface);
|
void mg_lwip_if_init(struct mg_iface *iface);
|
||||||
@@ -75,7 +102,8 @@ void mg_lwip_if_add_conn(struct mg_connection *nc);
|
|||||||
void mg_lwip_if_remove_conn(struct mg_connection *nc);
|
void mg_lwip_if_remove_conn(struct mg_connection *nc);
|
||||||
time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms);
|
time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms);
|
||||||
|
|
||||||
#if defined(RTOS_SDK) || defined(ESP_PLATFORM)
|
// If compiling for Mongoose OS.
|
||||||
|
#ifdef MGOS
|
||||||
extern void mgos_lock();
|
extern void mgos_lock();
|
||||||
extern void mgos_unlock();
|
extern void mgos_unlock();
|
||||||
#else
|
#else
|
||||||
@@ -244,7 +272,7 @@ static void mg_lwip_if_connect_tcp_tcpip(void *arg) {
|
|||||||
void mg_lwip_if_connect_tcp(struct mg_connection *nc,
|
void mg_lwip_if_connect_tcp(struct mg_connection *nc,
|
||||||
const union socket_address *sa) {
|
const union socket_address *sa) {
|
||||||
struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa};
|
struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa};
|
||||||
tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx);
|
mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_tcp_tcpip, &ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -337,7 +365,7 @@ static void mg_lwip_if_connect_udp_tcpip(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_lwip_if_connect_udp(struct mg_connection *nc) {
|
void mg_lwip_if_connect_udp(struct mg_connection *nc) {
|
||||||
tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc);
|
mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_udp_tcpip, nc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcp_close_tcpip(void *arg) {
|
static void tcp_close_tcpip(void *arg) {
|
||||||
@@ -423,7 +451,7 @@ static void mg_lwip_if_listen_tcp_tcpip(void *arg) {
|
|||||||
|
|
||||||
int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
|
int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
|
||||||
struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
|
struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
|
||||||
tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx);
|
mg_lwip_netif_run_on_tcpip(mg_lwip_if_listen_tcp_tcpip, &ctx);
|
||||||
return ctx.ret;
|
return ctx.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,7 +477,7 @@ static void mg_lwip_if_listen_udp_tcpip(void *arg) {
|
|||||||
|
|
||||||
int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
|
int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
|
||||||
struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
|
struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
|
||||||
tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx);
|
mg_lwip_netif_run_on_tcpip(mg_lwip_if_listen_udp_tcpip, &ctx);
|
||||||
return ctx.ret;
|
return ctx.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,7 +502,7 @@ static void mg_lwip_tcp_write_tcpip(void *arg) {
|
|||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen,
|
DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen,
|
||||||
tpcb->unsent, tpcb->unacked));
|
tpcb->unsent, tpcb->unacked));
|
||||||
tcpip_callback(tcp_output_tcpip, tpcb);
|
mg_lwip_netif_run_on_tcpip(tcp_output_tcpip, tpcb);
|
||||||
ctx->ret = 0;
|
ctx->ret = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -517,7 +545,7 @@ int mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) {
|
|||||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||||
if (tpcb == NULL) return -1;
|
if (tpcb == NULL) return -1;
|
||||||
if (tpcb->snd_buf <= 0) return 0;
|
if (tpcb->snd_buf <= 0) return 0;
|
||||||
tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx);
|
mg_lwip_netif_run_on_tcpip(mg_lwip_tcp_write_tcpip, &ctx);
|
||||||
return ctx.ret;
|
return ctx.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,7 +577,7 @@ static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data,
|
|||||||
if (p == NULL) return 0;
|
if (p == NULL) return 0;
|
||||||
memcpy(p->payload, data, len);
|
memcpy(p->payload, data, len);
|
||||||
struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
|
struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
|
||||||
tcpip_callback(udp_sendto_tcpip, &ctx);
|
mg_lwip_netif_run_on_tcpip(udp_sendto_tcpip, &ctx);
|
||||||
cs->err = ctx.ret;
|
cs->err = ctx.ret;
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
return (cs->err == ERR_OK ? (int) len : -2);
|
return (cs->err == ERR_OK ? (int) len : -2);
|
||||||
@@ -581,7 +609,7 @@ struct tcp_recved_ctx {
|
|||||||
|
|
||||||
void tcp_recved_tcpip(void *arg) {
|
void tcp_recved_tcpip(void *arg) {
|
||||||
struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
|
struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
|
||||||
tcp_recved(ctx->tpcb, ctx->len);
|
if (ctx->tpcb != NULL) tcp_recved(ctx->tpcb, ctx->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
|
static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
|
||||||
@@ -610,7 +638,7 @@ static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
|
|||||||
mgos_unlock();
|
mgos_unlock();
|
||||||
if (res > 0) {
|
if (res > 0) {
|
||||||
struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = res};
|
struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = res};
|
||||||
tcpip_callback(tcp_recved_tcpip, &ctx);
|
mg_lwip_netif_run_on_tcpip(tcp_recved_tcpip, &ctx);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -637,7 +665,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
|
|||||||
tcp_arg(tpcb, NULL);
|
tcp_arg(tpcb, NULL);
|
||||||
DBG(("%p tcp_close %p", nc, tpcb));
|
DBG(("%p tcp_close %p", nc, tpcb));
|
||||||
tcp_arg(tpcb, NULL);
|
tcp_arg(tpcb, NULL);
|
||||||
tcpip_callback(tcp_close_tcpip, tpcb);
|
mg_lwip_netif_run_on_tcpip(tcp_close_tcpip, tpcb);
|
||||||
}
|
}
|
||||||
while (cs->rx_chain != NULL) {
|
while (cs->rx_chain != NULL) {
|
||||||
struct pbuf *seg = cs->rx_chain;
|
struct pbuf *seg = cs->rx_chain;
|
||||||
@@ -651,7 +679,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
|
|||||||
struct udp_pcb *upcb = cs->pcb.udp;
|
struct udp_pcb *upcb = cs->pcb.udp;
|
||||||
if (upcb != NULL) {
|
if (upcb != NULL) {
|
||||||
DBG(("%p udp_remove %p", nc, upcb));
|
DBG(("%p udp_remove %p", nc, upcb));
|
||||||
tcpip_callback(udp_remove_tcpip, upcb);
|
mg_lwip_netif_run_on_tcpip(udp_remove_tcpip, upcb);
|
||||||
}
|
}
|
||||||
memset(cs, 0, sizeof(*cs));
|
memset(cs, 0, sizeof(*cs));
|
||||||
MG_FREE(cs);
|
MG_FREE(cs);
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2019 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CS_COMMON_PLATFORMS_PLATFORM_RS14100_H_
|
||||||
|
#define CS_COMMON_PLATFORMS_PLATFORM_RS14100_H_
|
||||||
|
#if CS_PLATFORM == CS_P_RS14100
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef MGOS_HAVE_VFS_COMMON
|
||||||
|
#include <mgos_vfs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define to64(x) strtoll(x, NULL, 10)
|
||||||
|
#define INT64_FMT "lld"
|
||||||
|
#define SIZE_T_FMT "u"
|
||||||
|
typedef struct stat cs_stat_t;
|
||||||
|
#define DIRSEP '/'
|
||||||
|
|
||||||
|
#ifndef CS_ENABLE_STDIO
|
||||||
|
#define CS_ENABLE_STDIO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_FILESYSTEM
|
||||||
|
#define MG_ENABLE_FILESYSTEM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CS_PLATFORM == CS_P_RS14100 */
|
||||||
|
#endif /* CS_COMMON_PLATFORMS_PLATFORM_RS14100_H_ */
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
|
|
||||||
#if _MSC_VER < 1700
|
#if defined(_MSC_VER) && (_MSC_VER < 1700)
|
||||||
typedef int bool;
|
typedef int bool;
|
||||||
#else
|
#else
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ int sl_set_ssl_opts(int sock, struct mg_connection *nc);
|
|||||||
|
|
||||||
#endif /* SL_MAJOR_VERSION_NUM < 2 */
|
#endif /* SL_MAJOR_VERSION_NUM < 2 */
|
||||||
|
|
||||||
int slfs_open(const unsigned char *fname, uint32_t flags);
|
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token);
|
||||||
|
|
||||||
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
|
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
|
||||||
|
|
||||||
|
|||||||
@@ -31,14 +31,14 @@
|
|||||||
#include "common/mg_mem.h"
|
#include "common/mg_mem.h"
|
||||||
|
|
||||||
#if SL_MAJOR_VERSION_NUM < 2
|
#if SL_MAJOR_VERSION_NUM < 2
|
||||||
int slfs_open(const unsigned char *fname, uint32_t flags) {
|
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token) {
|
||||||
_i32 fh;
|
_i32 fh;
|
||||||
_i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh);
|
_i32 r = sl_FsOpen(fname, flags, (unsigned long *) token, &fh);
|
||||||
return (r < 0 ? r : fh);
|
return (r < 0 ? r : fh);
|
||||||
}
|
}
|
||||||
#else /* SL_MAJOR_VERSION_NUM >= 2 */
|
#else /* SL_MAJOR_VERSION_NUM >= 2 */
|
||||||
int slfs_open(const unsigned char *fname, uint32_t flags) {
|
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token) {
|
||||||
return sl_FsOpen(fname, flags, NULL /* token */);
|
return sl_FsOpen(fname, flags, (unsigned long *) token);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -54,9 +54,11 @@ const char *drop_dir(const char *fname, bool *is_slfs);
|
|||||||
#define FS_SLFS_MAX_FILE_SIZE (64 * 1024)
|
#define FS_SLFS_MAX_FILE_SIZE (64 * 1024)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct sl_file_size_hint {
|
struct sl_file_open_info {
|
||||||
char *name;
|
char *name;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t *token;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sl_fd_info {
|
struct sl_fd_info {
|
||||||
@@ -66,7 +68,10 @@ struct sl_fd_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES];
|
static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES];
|
||||||
static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES];
|
static struct sl_file_open_info s_sl_file_open_infos[MAX_OPEN_SLFS_FILES];
|
||||||
|
|
||||||
|
static struct sl_file_open_info *fs_slfs_find_foi(const char *name,
|
||||||
|
bool create);
|
||||||
|
|
||||||
static int sl_fs_to_errno(_i32 r) {
|
static int sl_fs_to_errno(_i32 r) {
|
||||||
DBG(("SL error: %d", (int) r));
|
DBG(("SL error: %d", (int) r));
|
||||||
@@ -107,7 +112,13 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) {
|
|||||||
_u32 am = 0;
|
_u32 am = 0;
|
||||||
fi->size = (size_t) -1;
|
fi->size = (size_t) -1;
|
||||||
int rw = (flags & 3);
|
int rw = (flags & 3);
|
||||||
size_t new_size = FS_SLFS_MAX_FILE_SIZE;
|
size_t new_size = 0;
|
||||||
|
struct sl_file_open_info *foi =
|
||||||
|
fs_slfs_find_foi(pathname, false /* create */);
|
||||||
|
if (foi != NULL) {
|
||||||
|
LOG(LL_DEBUG, ("FOI for %s: %d 0x%x %p", pathname, (int) foi->size,
|
||||||
|
(unsigned int) foi->flags, foi->token));
|
||||||
|
}
|
||||||
if (rw == O_RDONLY) {
|
if (rw == O_RDONLY) {
|
||||||
SlFsFileInfo_t sl_fi;
|
SlFsFileInfo_t sl_fi;
|
||||||
_i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi);
|
_i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi);
|
||||||
@@ -122,24 +133,27 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) {
|
|||||||
return set_errno(ENOTSUP);
|
return set_errno(ENOTSUP);
|
||||||
}
|
}
|
||||||
if (flags & O_CREAT) {
|
if (flags & O_CREAT) {
|
||||||
size_t i;
|
if (foi->size > 0) {
|
||||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
new_size = foi->size;
|
||||||
if (s_sl_file_size_hints[i].name != NULL &&
|
} else {
|
||||||
strcmp(s_sl_file_size_hints[i].name, pathname) == 0) {
|
new_size = FS_SLFS_MAX_FILE_SIZE;
|
||||||
new_size = s_sl_file_size_hints[i].size;
|
|
||||||
MG_FREE(s_sl_file_size_hints[i].name);
|
|
||||||
s_sl_file_size_hints[i].name = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
am = FS_MODE_OPEN_CREATE(new_size, 0);
|
am = FS_MODE_OPEN_CREATE(new_size, 0);
|
||||||
} else {
|
} else {
|
||||||
am = SL_FS_WRITE;
|
am = SL_FS_WRITE;
|
||||||
}
|
}
|
||||||
|
#if SL_MAJOR_VERSION_NUM >= 2
|
||||||
|
am |= SL_FS_OVERWRITE;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
fi->fh = slfs_open((_u8 *) pathname, am);
|
uint32_t *token = NULL;
|
||||||
LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am,
|
if (foi != NULL) {
|
||||||
(unsigned int) new_size, (int) fi->fh));
|
am |= foi->flags;
|
||||||
|
token = foi->token;
|
||||||
|
}
|
||||||
|
fi->fh = slfs_open((_u8 *) pathname, am, token);
|
||||||
|
LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x, %p) sz %u = %d", pathname, (int) am,
|
||||||
|
token, (unsigned int) new_size, (int) fi->fh));
|
||||||
int r;
|
int r;
|
||||||
if (fi->fh >= 0) {
|
if (fi->fh >= 0) {
|
||||||
fi->pos = 0;
|
fi->pos = 0;
|
||||||
@@ -243,16 +257,46 @@ int fs_slfs_rename(const char *from, const char *to) {
|
|||||||
return set_errno(ENOTSUP);
|
return set_errno(ENOTSUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fs_slfs_set_new_file_size(const char *name, size_t size) {
|
static struct sl_file_open_info *fs_slfs_find_foi(const char *name,
|
||||||
int i;
|
bool create) {
|
||||||
|
int i = 0;
|
||||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||||
if (s_sl_file_size_hints[i].name == NULL) {
|
if (s_sl_file_open_infos[i].name != NULL &&
|
||||||
DBG(("File size hint: %s %d", name, (int) size));
|
strcmp(drop_dir(s_sl_file_open_infos[i].name, NULL), name) == 0) {
|
||||||
s_sl_file_size_hints[i].name = strdup(name);
|
|
||||||
s_sl_file_size_hints[i].size = size;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (i != MAX_OPEN_SLFS_FILES) return &s_sl_file_open_infos[i];
|
||||||
|
if (!create) return NULL;
|
||||||
|
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||||
|
if (s_sl_file_open_infos[i].name == NULL) break;
|
||||||
|
}
|
||||||
|
if (i == MAX_OPEN_SLFS_FILES) {
|
||||||
|
i = 0; /* Evict a random slot. */
|
||||||
|
}
|
||||||
|
if (s_sl_file_open_infos[i].name != NULL) {
|
||||||
|
free(s_sl_file_open_infos[i].name);
|
||||||
|
}
|
||||||
|
s_sl_file_open_infos[i].name = strdup(name);
|
||||||
|
return &s_sl_file_open_infos[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs_slfs_set_file_size(const char *name, size_t size) {
|
||||||
|
struct sl_file_open_info *foi = fs_slfs_find_foi(name, true /* create */);
|
||||||
|
foi->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs_slfs_set_file_flags(const char *name, uint32_t flags, uint32_t *token) {
|
||||||
|
struct sl_file_open_info *foi = fs_slfs_find_foi(name, true /* create */);
|
||||||
|
foi->flags = flags;
|
||||||
|
foi->token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs_slfs_unset_file_flags(const char *name) {
|
||||||
|
struct sl_file_open_info *foi = fs_slfs_find_foi(name, false /* create */);
|
||||||
|
if (foi == NULL) return;
|
||||||
|
free(foi->name);
|
||||||
|
memset(foi, 0, sizeof(*foi));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */
|
#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */
|
||||||
@@ -39,7 +39,9 @@ off_t fs_slfs_lseek(int fd, off_t offset, int whence);
|
|||||||
int fs_slfs_unlink(const char *filename);
|
int fs_slfs_unlink(const char *filename);
|
||||||
int fs_slfs_rename(const char *from, const char *to);
|
int fs_slfs_rename(const char *from, const char *to);
|
||||||
|
|
||||||
void fs_slfs_set_new_file_size(const char *name, size_t size);
|
void fs_slfs_set_file_size(const char *name, size_t size);
|
||||||
|
void fs_slfs_set_file_flags(const char *name, uint32_t flags, uint32_t *token);
|
||||||
|
void fs_slfs_unset_file_flags(const char *name);
|
||||||
|
|
||||||
#endif /* defined(MG_FS_SLFS) */
|
#endif /* defined(MG_FS_SLFS) */
|
||||||
|
|
||||||
|
|||||||
@@ -115,9 +115,9 @@ bool pem_to_der(const char *pem_file, const char *der_file) {
|
|||||||
pf = fopen(pem_file, "r");
|
pf = fopen(pem_file, "r");
|
||||||
if (pf == NULL) goto clean;
|
if (pf == NULL) goto clean;
|
||||||
remove(der_file);
|
remove(der_file);
|
||||||
fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
|
fs_slfs_set_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, 2048);
|
||||||
2048);
|
|
||||||
df = fopen(der_file, "w");
|
df = fopen(der_file, "w");
|
||||||
|
fs_slfs_unset_file_flags(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN);
|
||||||
if (df == NULL) goto clean;
|
if (df == NULL) goto clean;
|
||||||
while (1) {
|
while (1) {
|
||||||
char pem_buf[70];
|
char pem_buf[70];
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
Copyright (c) 2011 The stlink project (github.com/texane/stlink) contributors.
|
|
||||||
All rights reserved.
|
|
||||||
Copyright (c) 2011 The "Capt'ns Missing Link" Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
* Neither the name of Martin Capitanio nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,869 +0,0 @@
|
|||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "stlink.h"
|
|
||||||
#include "usb.h"
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#define O_BINARY 0 //! @todo get rid of this OH MY (@xor-gate)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* todo: stm32l15xxx flash memory, pm0062 manual */
|
|
||||||
|
|
||||||
/* stm32f FPEC flash controller interface, pm0063 manual */
|
|
||||||
// TODO - all of this needs to be abstracted out....
|
|
||||||
// STM32F05x is identical, based on RM0091 (DM00031936, Doc ID 018940 Rev 2,
|
|
||||||
// August 2012)
|
|
||||||
#define FLASH_REGS_ADDR 0x40022000
|
|
||||||
#define FLASH_REGS_SIZE 0x28
|
|
||||||
|
|
||||||
#define FLASH_ACR (FLASH_REGS_ADDR + 0x00)
|
|
||||||
#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04)
|
|
||||||
#define FLASH_SR (FLASH_REGS_ADDR + 0x0c)
|
|
||||||
#define FLASH_CR (FLASH_REGS_ADDR + 0x10)
|
|
||||||
#define FLASH_AR (FLASH_REGS_ADDR + 0x14)
|
|
||||||
#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c)
|
|
||||||
#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20)
|
|
||||||
|
|
||||||
// For STM32F05x, the RDPTR_KEY may be wrong, but as it is not used anywhere...
|
|
||||||
#define FLASH_RDPTR_KEY 0x00a5
|
|
||||||
#define FLASH_KEY1 0x45670123
|
|
||||||
#define FLASH_KEY2 0xcdef89ab
|
|
||||||
|
|
||||||
#define FLASH_SR_BSY 0
|
|
||||||
#define FLASH_SR_EOP 5
|
|
||||||
|
|
||||||
#define FLASH_CR_PG 0
|
|
||||||
#define FLASH_CR_PER 1
|
|
||||||
#define FLASH_CR_MER 2
|
|
||||||
#define FLASH_CR_STRT 6
|
|
||||||
#define FLASH_CR_LOCK 7
|
|
||||||
|
|
||||||
// 32L = 32F1 same CoreID as 32F4!
|
|
||||||
#define STM32L_FLASH_REGS_ADDR ((uint32_t) 0x40023c00)
|
|
||||||
#define STM32L_FLASH_ACR (STM32L_FLASH_REGS_ADDR + 0x00)
|
|
||||||
#define STM32L_FLASH_PECR (STM32L_FLASH_REGS_ADDR + 0x04)
|
|
||||||
#define STM32L_FLASH_PDKEYR (STM32L_FLASH_REGS_ADDR + 0x08)
|
|
||||||
#define STM32L_FLASH_PEKEYR (STM32L_FLASH_REGS_ADDR + 0x0c)
|
|
||||||
#define STM32L_FLASH_PRGKEYR (STM32L_FLASH_REGS_ADDR + 0x10)
|
|
||||||
#define STM32L_FLASH_OPTKEYR (STM32L_FLASH_REGS_ADDR + 0x14)
|
|
||||||
#define STM32L_FLASH_SR (STM32L_FLASH_REGS_ADDR + 0x18)
|
|
||||||
#define STM32L_FLASH_OBR (STM32L_FLASH_REGS_ADDR + 0x1c)
|
|
||||||
#define STM32L_FLASH_WRPR (STM32L_FLASH_REGS_ADDR + 0x20)
|
|
||||||
#define FLASH_L1_FPRG 10
|
|
||||||
#define FLASH_L1_PROG 3
|
|
||||||
|
|
||||||
// 32L4 register base is at FLASH_REGS_ADDR (0x40022000)
|
|
||||||
#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08)
|
|
||||||
#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10)
|
|
||||||
#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14)
|
|
||||||
#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20)
|
|
||||||
|
|
||||||
#define STM32L4_FLASH_SR_BSY 16
|
|
||||||
#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */
|
|
||||||
|
|
||||||
#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */
|
|
||||||
#define STM32L4_FLASH_CR_PG 0 /* Program */
|
|
||||||
#define STM32L4_FLASH_CR_PER 1 /* Page erase */
|
|
||||||
#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */
|
|
||||||
#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */
|
|
||||||
#define STM32L4_FLASH_CR_STRT 16 /* Start command */
|
|
||||||
#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */
|
|
||||||
#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */
|
|
||||||
// Bits requesting flash operations (useful when we want to clear them)
|
|
||||||
#define STM32L4_FLASH_CR_OPBITS \
|
|
||||||
((1lu << STM32L4_FLASH_CR_PG) | (1lu << STM32L4_FLASH_CR_PER) | \
|
|
||||||
(1lu << STM32L4_FLASH_CR_MER1) | (1lu << STM32L4_FLASH_CR_MER1))
|
|
||||||
// Page is fully specified by BKER and PNB
|
|
||||||
#define STM32L4_FLASH_CR_PAGEMASK (0x1fflu << STM32L4_FLASH_CR_PNB)
|
|
||||||
|
|
||||||
#define STM32L4_FLASH_OPTR_DUALBANK 21
|
|
||||||
|
|
||||||
// STM32L0x flash register base and offsets
|
|
||||||
// same as 32L1 above
|
|
||||||
// RM0090 - DM00031020.pdf
|
|
||||||
#define STM32L0_FLASH_REGS_ADDR ((uint32_t) 0x40022000)
|
|
||||||
#define FLASH_ACR_OFF ((uint32_t) 0x00)
|
|
||||||
#define FLASH_PECR_OFF ((uint32_t) 0x04)
|
|
||||||
#define FLASH_PDKEYR_OFF ((uint32_t) 0x08)
|
|
||||||
#define FLASH_PEKEYR_OFF ((uint32_t) 0x0c)
|
|
||||||
#define FLASH_PRGKEYR_OFF ((uint32_t) 0x10)
|
|
||||||
#define FLASH_OPTKEYR_OFF ((uint32_t) 0x14)
|
|
||||||
#define FLASH_SR_OFF ((uint32_t) 0x18)
|
|
||||||
#define FLASH_OBR_OFF ((uint32_t) 0x1c)
|
|
||||||
#define FLASH_WRPR_OFF ((uint32_t) 0x20)
|
|
||||||
|
|
||||||
// STM32F4
|
|
||||||
#define FLASH_F4_REGS_ADDR ((uint32_t) 0x40023c00)
|
|
||||||
#define FLASH_F4_KEYR (FLASH_F4_REGS_ADDR + 0x04)
|
|
||||||
#define FLASH_F4_OPT_KEYR (FLASH_F4_REGS_ADDR + 0x08)
|
|
||||||
#define FLASH_F4_SR (FLASH_F4_REGS_ADDR + 0x0c)
|
|
||||||
#define FLASH_F4_CR (FLASH_F4_REGS_ADDR + 0x10)
|
|
||||||
#define FLASH_F4_OPT_CR (FLASH_F4_REGS_ADDR + 0x14)
|
|
||||||
#define FLASH_F4_CR_STRT 16
|
|
||||||
#define FLASH_F4_CR_LOCK 31
|
|
||||||
#define FLASH_F4_CR_SER 1
|
|
||||||
#define FLASH_F4_CR_SNB 3
|
|
||||||
#define FLASH_F4_CR_SNB_MASK 0xf8
|
|
||||||
#define FLASH_F4_SR_BSY 16
|
|
||||||
|
|
||||||
#define L1_WRITE_BLOCK_SIZE 0x80
|
|
||||||
#define L0_WRITE_BLOCK_SIZE 0x40
|
|
||||||
|
|
||||||
#define STLINK_REG_CM3_CPUID 0xE000ED00
|
|
||||||
#define STLINK_REG_CM3_FP_CTRL 0xE0002000
|
|
||||||
#define STLINK_REG_CM3_FP_COMP0 0xE0002008
|
|
||||||
|
|
||||||
/* Cortex™-M3 Technical Reference Manual */
|
|
||||||
/* Debug Halting Control and Status Register */
|
|
||||||
#define STLINK_REG_DHCSR 0xe000edf0
|
|
||||||
#define STLINK_REG_DHCSR_DBGKEY 0xa05f0000
|
|
||||||
#define STLINK_REG_DCRSR 0xe000edf4
|
|
||||||
#define STLINK_REG_DCRDR 0xe000edf8
|
|
||||||
|
|
||||||
struct stlink_chipid_params {
|
|
||||||
uint32_t chip_id;
|
|
||||||
char *description;
|
|
||||||
uint32_t flash_size_reg;
|
|
||||||
uint32_t flash_pagesize;
|
|
||||||
uint32_t sram_size;
|
|
||||||
uint32_t bootrom_base;
|
|
||||||
uint32_t bootrom_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
void write_uint32(unsigned char *buf, uint32_t ui) {
|
|
||||||
if (!is_bigendian()) { // le -> le (don't swap)
|
|
||||||
buf[0] = ((unsigned char *) &ui)[0];
|
|
||||||
buf[1] = ((unsigned char *) &ui)[1];
|
|
||||||
buf[2] = ((unsigned char *) &ui)[2];
|
|
||||||
buf[3] = ((unsigned char *) &ui)[3];
|
|
||||||
} else {
|
|
||||||
buf[0] = ((unsigned char *) &ui)[3];
|
|
||||||
buf[1] = ((unsigned char *) &ui)[2];
|
|
||||||
buf[2] = ((unsigned char *) &ui)[1];
|
|
||||||
buf[3] = ((unsigned char *) &ui)[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_uint16(unsigned char *buf, uint16_t ui) {
|
|
||||||
if (!is_bigendian()) { // le -> le (don't swap)
|
|
||||||
buf[0] = ((unsigned char *) &ui)[0];
|
|
||||||
buf[1] = ((unsigned char *) &ui)[1];
|
|
||||||
} else {
|
|
||||||
buf[0] = ((unsigned char *) &ui)[1];
|
|
||||||
buf[1] = ((unsigned char *) &ui)[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t read_uint32(const unsigned char *c, const int pt) {
|
|
||||||
uint32_t ui;
|
|
||||||
char *p = (char *) &ui;
|
|
||||||
|
|
||||||
if (!is_bigendian()) { // le -> le (don't swap)
|
|
||||||
p[0] = c[pt + 0];
|
|
||||||
p[1] = c[pt + 1];
|
|
||||||
p[2] = c[pt + 2];
|
|
||||||
p[3] = c[pt + 3];
|
|
||||||
} else {
|
|
||||||
p[0] = c[pt + 3];
|
|
||||||
p[1] = c[pt + 2];
|
|
||||||
p[2] = c[pt + 1];
|
|
||||||
p[3] = c[pt + 0];
|
|
||||||
}
|
|
||||||
return ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t __attribute__((unused)) read_flash_rdp(stlink_t *sl) {
|
|
||||||
uint32_t rdp;
|
|
||||||
stlink_read_debug32(sl, FLASH_WRPR, &rdp);
|
|
||||||
return rdp & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t read_flash_cr(stlink_t *sl) {
|
|
||||||
uint32_t res;
|
|
||||||
stlink_read_debug32(sl, FLASH_F4_CR, &res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned int is_flash_locked(stlink_t *sl) {
|
|
||||||
/* return non zero for true */
|
|
||||||
uint32_t cr_lock_shift = FLASH_F4_CR_LOCK, cr = read_flash_cr(sl);
|
|
||||||
return cr & (1 << cr_lock_shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unlock_flash(stlink_t *sl) {
|
|
||||||
uint32_t key_reg = FLASH_F4_KEYR;
|
|
||||||
stlink_write_debug32(sl, key_reg, FLASH_KEY1);
|
|
||||||
stlink_write_debug32(sl, key_reg, FLASH_KEY2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int unlock_flash_if(stlink_t *sl) {
|
|
||||||
/* unlock flash if already locked */
|
|
||||||
|
|
||||||
if (is_flash_locked(sl)) {
|
|
||||||
unlock_flash(sl);
|
|
||||||
if (is_flash_locked(sl)) {
|
|
||||||
printf("Failed to unlock flash!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lock_flash(stlink_t *sl) {
|
|
||||||
uint32_t n;
|
|
||||||
uint32_t cr_reg = FLASH_F4_CR;
|
|
||||||
uint32_t cr_lock_shift = FLASH_F4_CR_LOCK;
|
|
||||||
|
|
||||||
n = read_flash_cr(sl) | (1 << cr_lock_shift);
|
|
||||||
stlink_write_debug32(sl, cr_reg, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_flash_cr_pg(stlink_t *sl) {
|
|
||||||
uint32_t cr_reg, x;
|
|
||||||
|
|
||||||
x = read_flash_cr(sl);
|
|
||||||
|
|
||||||
cr_reg = FLASH_F4_CR;
|
|
||||||
x |= 1 << FLASH_CR_PG;
|
|
||||||
|
|
||||||
stlink_write_debug32(sl, cr_reg, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((unused)) clear_flash_cr_pg(stlink_t *sl) {
|
|
||||||
uint32_t cr_reg, n;
|
|
||||||
|
|
||||||
cr_reg = FLASH_F4_CR;
|
|
||||||
|
|
||||||
n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG);
|
|
||||||
stlink_write_debug32(sl, cr_reg, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_flash_cr_per(stlink_t *sl) {
|
|
||||||
const uint32_t n = 1 << FLASH_CR_PER;
|
|
||||||
stlink_write_debug32(sl, FLASH_CR, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((unused)) clear_flash_cr_per(stlink_t *sl) {
|
|
||||||
const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER);
|
|
||||||
stlink_write_debug32(sl, FLASH_CR, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_flash_cr_mer(stlink_t *sl, bool v) {
|
|
||||||
uint32_t val, cr_reg, cr_mer, cr_pg;
|
|
||||||
|
|
||||||
cr_reg = FLASH_F4_CR;
|
|
||||||
cr_mer = 1 << FLASH_CR_MER;
|
|
||||||
cr_pg = 1 << FLASH_CR_PG;
|
|
||||||
|
|
||||||
stlink_read_debug32(sl, cr_reg, &val);
|
|
||||||
if (val & cr_pg) {
|
|
||||||
/* STM32F030 will drop MER bit if PG was set */
|
|
||||||
val &= ~cr_pg;
|
|
||||||
stlink_write_debug32(sl, cr_reg, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v)
|
|
||||||
val |= cr_mer;
|
|
||||||
else
|
|
||||||
val &= ~cr_mer;
|
|
||||||
stlink_write_debug32(sl, cr_reg, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *sl) {
|
|
||||||
uint32_t val, cr_reg, cr_mer;
|
|
||||||
|
|
||||||
cr_reg = FLASH_F4_CR;
|
|
||||||
cr_mer = 1 << FLASH_CR_MER;
|
|
||||||
|
|
||||||
stlink_read_debug32(sl, cr_reg, &val);
|
|
||||||
val &= ~cr_mer;
|
|
||||||
stlink_write_debug32(sl, cr_reg, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_flash_cr_strt(stlink_t *sl) {
|
|
||||||
uint32_t val, cr_reg, cr_strt;
|
|
||||||
|
|
||||||
cr_reg = FLASH_F4_CR;
|
|
||||||
cr_strt = 1 << FLASH_F4_CR_STRT;
|
|
||||||
|
|
||||||
stlink_read_debug32(sl, cr_reg, &val);
|
|
||||||
val |= cr_strt;
|
|
||||||
stlink_write_debug32(sl, cr_reg, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t read_flash_sr(stlink_t *sl) {
|
|
||||||
uint32_t res, sr_reg;
|
|
||||||
sr_reg = FLASH_F4_SR;
|
|
||||||
stlink_read_debug32(sl, sr_reg, &res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned int is_flash_busy(stlink_t *sl) {
|
|
||||||
uint32_t sr_busy_shift;
|
|
||||||
sr_busy_shift = FLASH_F4_SR_BSY;
|
|
||||||
return read_flash_sr(sl) & (1 << sr_busy_shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_flash_busy(stlink_t *sl) {
|
|
||||||
/* todo: add some delays here */
|
|
||||||
while (is_flash_busy(sl))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_flash_busy_progress(stlink_t *sl) {
|
|
||||||
while (is_flash_busy(sl)) {
|
|
||||||
usleep(10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned int is_flash_eop(stlink_t *sl) {
|
|
||||||
return read_flash_sr(sl) & (1 << FLASH_SR_EOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((unused)) clear_flash_sr_eop(stlink_t *sl) {
|
|
||||||
const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP);
|
|
||||||
stlink_write_debug32(sl, FLASH_SR, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((unused)) wait_flash_eop(stlink_t *sl) {
|
|
||||||
/* todo: add some delays here */
|
|
||||||
while (is_flash_eop(sl) == 0)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void write_flash_ar(stlink_t *sl, uint32_t n) {
|
|
||||||
stlink_write_debug32(sl, FLASH_AR, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void write_flash_cr_psiz(stlink_t *sl, uint32_t n) {
|
|
||||||
uint32_t x = read_flash_cr(sl);
|
|
||||||
x &= ~(0x03 << 8);
|
|
||||||
x |= (n << 8);
|
|
||||||
stlink_write_debug32(sl, FLASH_F4_CR, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void write_flash_cr_snb(stlink_t *sl, uint32_t n) {
|
|
||||||
uint32_t x = read_flash_cr(sl);
|
|
||||||
x &= ~FLASH_F4_CR_SNB_MASK;
|
|
||||||
x |= (n << FLASH_F4_CR_SNB);
|
|
||||||
x |= (1 << FLASH_F4_CR_SER);
|
|
||||||
stlink_write_debug32(sl, FLASH_F4_CR, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void write_flash_cr_bker_pnb(stlink_t *sl, uint32_t n) {
|
|
||||||
stlink_write_debug32(sl, STM32L4_FLASH_SR,
|
|
||||||
0xFFFFFFFF & ~(1 << STM32L4_FLASH_SR_BSY));
|
|
||||||
uint32_t x = read_flash_cr(sl);
|
|
||||||
x &= ~STM32L4_FLASH_CR_OPBITS;
|
|
||||||
x &= ~STM32L4_FLASH_CR_PAGEMASK;
|
|
||||||
x &= ~(1 << STM32L4_FLASH_CR_MER1);
|
|
||||||
x &= ~(1 << STM32L4_FLASH_CR_MER2);
|
|
||||||
x |= (n << STM32L4_FLASH_CR_PNB);
|
|
||||||
x |= (1lu << STM32L4_FLASH_CR_PER);
|
|
||||||
stlink_write_debug32(sl, STM32L4_FLASH_CR, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stlink_close(stlink_t *sl) {
|
|
||||||
if (!sl) return;
|
|
||||||
_stlink_usb_close(sl);
|
|
||||||
free(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_exit_debug_mode(stlink_t *sl) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = stlink_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY);
|
|
||||||
if (ret == -1) return ret;
|
|
||||||
|
|
||||||
return _stlink_usb_exit_debug_mode(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_enter_swd_mode(stlink_t *sl) {
|
|
||||||
return _stlink_usb_enter_swd_mode(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force the core into the debug mode -> halted state.
|
|
||||||
int stlink_force_debug(stlink_t *sl) {
|
|
||||||
return _stlink_usb_force_debug(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_exit_dfu_mode(stlink_t *sl) {
|
|
||||||
return _stlink_usb_exit_dfu_mode(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_core_id(stlink_t *sl) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = _stlink_usb_core_id(sl);
|
|
||||||
if (ret == -1) {
|
|
||||||
printf("Failed to read core_id\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_chip_id(stlink_t *sl, uint32_t *chip_id) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = stlink_read_debug32(sl, 0xE0042000, chip_id);
|
|
||||||
if (ret == -1) return ret;
|
|
||||||
|
|
||||||
if (*chip_id == 0)
|
|
||||||
ret = stlink_read_debug32(
|
|
||||||
sl, 0x40015800,
|
|
||||||
chip_id); // Try Corex M0 DBGMCU_IDCODE register address
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reads and decodes the flash parameters, as dynamically as possible
|
|
||||||
* @param sl
|
|
||||||
* @return 0 for success, or -1 for unsupported core type.
|
|
||||||
*/
|
|
||||||
static const struct stlink_chipid_params F7_params = {
|
|
||||||
// RM0385 and DS10916 document was used to find these paramaters
|
|
||||||
.chip_id = STLINK_CHIPID_STM32_F7,
|
|
||||||
.description = "F7 device",
|
|
||||||
.flash_size_reg = 0x1ff0f442, // section 41.2
|
|
||||||
.flash_pagesize = 0x800, // No flash pages
|
|
||||||
.sram_size = 0x50000, // "SRAM" byte size in hex from DS Fig 18
|
|
||||||
.bootrom_base =
|
|
||||||
0x00100000, // "System memory" starting address from DS Fig 18
|
|
||||||
.bootrom_size = 0xEDC0 // "System memory" byte size in hex from DS Fig 18
|
|
||||||
};
|
|
||||||
|
|
||||||
int stlink_load_device_params(stlink_t *sl) {
|
|
||||||
const struct stlink_chipid_params *params = &F7_params;
|
|
||||||
stlink_core_id(sl);
|
|
||||||
uint32_t chip_id;
|
|
||||||
uint32_t flash_size;
|
|
||||||
|
|
||||||
stlink_chip_id(sl, &chip_id);
|
|
||||||
sl->chip_id = chip_id & 0xfff;
|
|
||||||
/* Fix chip_id for F4 rev A errata , Read CPU ID, as CoreID is the same for
|
|
||||||
* F2/F4*/
|
|
||||||
if (sl->chip_id == 0x411) {
|
|
||||||
uint32_t cpuid;
|
|
||||||
stlink_read_debug32(sl, 0xE000ED00, &cpuid);
|
|
||||||
if ((cpuid & 0xfff0) == 0xc240) sl->chip_id = 0x413;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sl->chip_id != STLINK_CHIPID_STM32_F7) {
|
|
||||||
printf("unsupported chip id! %d\n", chip_id);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are fixed...
|
|
||||||
sl->flash_base = STM32_FLASH_BASE;
|
|
||||||
sl->sram_base = STM32_SRAM_BASE;
|
|
||||||
stlink_read_debug32(sl, (params->flash_size_reg) & ~3, &flash_size);
|
|
||||||
if (params->flash_size_reg & 2) flash_size = flash_size >> 16;
|
|
||||||
flash_size = flash_size & 0xffff;
|
|
||||||
|
|
||||||
sl->flash_size = flash_size * 1024;
|
|
||||||
sl->flash_pgsz = params->flash_pagesize;
|
|
||||||
sl->sram_size = params->sram_size;
|
|
||||||
sl->sys_base = params->bootrom_base;
|
|
||||||
sl->sys_size = params->bootrom_size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_reset(stlink_t *sl) {
|
|
||||||
return _stlink_usb_reset(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_jtag_reset(stlink_t *sl, int value) {
|
|
||||||
return _stlink_usb_jtag_reset(sl, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_run(stlink_t *sl) {
|
|
||||||
return _stlink_usb_run(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_set_swdclk(stlink_t *sl, uint16_t divisor) {
|
|
||||||
return _stlink_usb_set_swdclk(sl, divisor);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_status(stlink_t *sl) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = _stlink_usb_status(sl);
|
|
||||||
stlink_core_stat(sl);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_target_voltage(stlink_t *sl) {
|
|
||||||
return _stlink_usb_target_voltage(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) {
|
|
||||||
return _stlink_usb_read_debug32(sl, addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
|
|
||||||
return _stlink_usb_write_debug32(sl, addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
|
|
||||||
if (len % 4 != 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Error: Data length doesn't have a 32 bit alignment: +%d byte.\n",
|
|
||||||
len % 4);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return _stlink_usb_write_mem32(sl, addr, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
|
|
||||||
if (len % 4 != 0) { // !!! never ever: fw gives just wrong values
|
|
||||||
fprintf(stderr,
|
|
||||||
"Error: Data length doesn't have a 32 bit alignment: +%d byte.\n",
|
|
||||||
len % 4);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return _stlink_usb_read_mem32(sl, addr, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) {
|
|
||||||
if (len > 0x40) { // !!! never ever: Writing more then 0x40 bytes gives
|
|
||||||
// unexpected behaviour
|
|
||||||
fprintf(stderr, "Error: Data length > 64: +%d byte.\n", len);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return _stlink_usb_write_mem8(sl, addr, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx) {
|
|
||||||
return _stlink_usb_write_reg(sl, reg, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) {
|
|
||||||
if (r_idx > 20 || r_idx < 0) {
|
|
||||||
fprintf(stderr, "Error: register index must be in [0..20]\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _stlink_usb_read_reg(sl, r_idx, regp);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool stlink_is_core_halted(stlink_t *sl) {
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
stlink_status(sl);
|
|
||||||
if (sl->q_buf[0] == STLINK_CORE_HALTED) ret = true;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_step(stlink_t *sl) {
|
|
||||||
return _stlink_usb_step(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_current_mode(stlink_t *sl) {
|
|
||||||
return _stlink_usb_current_mode(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of delegates.... Common code below here...
|
|
||||||
|
|
||||||
// Endianness
|
|
||||||
// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html
|
|
||||||
// const int i = 1;
|
|
||||||
// #define is_bigendian() ( (*(char*)&i) == 0 )
|
|
||||||
|
|
||||||
unsigned int is_bigendian(void) {
|
|
||||||
static volatile const unsigned int i = 1;
|
|
||||||
return *(volatile const char *) &i == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t read_uint16(const unsigned char *c, const int pt) {
|
|
||||||
uint32_t ui;
|
|
||||||
char *p = (char *) &ui;
|
|
||||||
|
|
||||||
if (!is_bigendian()) { // le -> le (don't swap)
|
|
||||||
p[0] = c[pt + 0];
|
|
||||||
p[1] = c[pt + 1];
|
|
||||||
} else {
|
|
||||||
p[0] = c[pt + 1];
|
|
||||||
p[1] = c[pt + 0];
|
|
||||||
}
|
|
||||||
return ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
// same as above with entrypoint.
|
|
||||||
|
|
||||||
void stlink_run_at(stlink_t *sl, stm32_addr_t addr) {
|
|
||||||
stlink_write_reg(sl, addr, 15); /* pc register */
|
|
||||||
|
|
||||||
stlink_run(sl);
|
|
||||||
|
|
||||||
while (stlink_is_core_halted(sl)) usleep(3000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stlink_core_stat(stlink_t *sl) {
|
|
||||||
if (sl->q_len <= 0) return;
|
|
||||||
|
|
||||||
switch (sl->q_buf[0]) {
|
|
||||||
case STLINK_CORE_RUNNING:
|
|
||||||
sl->core_stat = STLINK_CORE_RUNNING;
|
|
||||||
return;
|
|
||||||
case STLINK_CORE_HALTED:
|
|
||||||
sl->core_stat = STLINK_CORE_HALTED;
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
sl->core_stat = STLINK_CORE_STAT_UNKNOWN;
|
|
||||||
fprintf(stderr, " core status: unknown\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr) {
|
|
||||||
unsigned int val;
|
|
||||||
/* set stack*/
|
|
||||||
stlink_read_debug32(sl, addr, &val);
|
|
||||||
stlink_write_reg(sl, val, 13);
|
|
||||||
/* Set PC to the reset routine*/
|
|
||||||
stlink_read_debug32(sl, addr + 4, &val);
|
|
||||||
stlink_write_reg(sl, val, 15);
|
|
||||||
stlink_run(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef bool (*save_block_fn)(void *arg, uint8_t *block, ssize_t len);
|
|
||||||
|
|
||||||
static int stlink_read(stlink_t *sl, stm32_addr_t addr, size_t size,
|
|
||||||
save_block_fn fn, void *fn_arg) {
|
|
||||||
int error = -1;
|
|
||||||
|
|
||||||
if (size < 1) size = sl->flash_size;
|
|
||||||
|
|
||||||
if (size > sl->flash_size) size = sl->flash_size;
|
|
||||||
|
|
||||||
size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz;
|
|
||||||
for (size_t off = 0; off < size; off += cmp_size) {
|
|
||||||
size_t aligned_size;
|
|
||||||
|
|
||||||
/* adjust last page size */
|
|
||||||
if ((off + cmp_size) > size) cmp_size = size - off;
|
|
||||||
|
|
||||||
aligned_size = cmp_size;
|
|
||||||
if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1);
|
|
||||||
|
|
||||||
stlink_read_mem32(sl, addr + (uint32_t) off, aligned_size);
|
|
||||||
|
|
||||||
if (!fn(fn_arg, sl->q_buf, aligned_size)) {
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
error = 0;
|
|
||||||
|
|
||||||
on_error:
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf,
|
|
||||||
size_t size) {
|
|
||||||
/* write the buffer right after the loader */
|
|
||||||
size_t chunk = size & ~0x3;
|
|
||||||
size_t rem = size & 0x3;
|
|
||||||
if (chunk) {
|
|
||||||
memcpy(sl->q_buf, buf, chunk);
|
|
||||||
stlink_write_mem32(sl, fl->buf_addr, chunk);
|
|
||||||
}
|
|
||||||
if (rem) {
|
|
||||||
memcpy(sl->q_buf, buf + chunk, rem);
|
|
||||||
stlink_write_mem8(sl, (fl->buf_addr) + (uint32_t) chunk, rem);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t calculate_F7_sectornum(uint32_t flashaddr) {
|
|
||||||
flashaddr &= ~STM32_FLASH_BASE; // Page now holding the actual flash address
|
|
||||||
if (flashaddr < 0x20000)
|
|
||||||
return (flashaddr / 0x8000);
|
|
||||||
else if (flashaddr < 0x40000)
|
|
||||||
return (4);
|
|
||||||
else
|
|
||||||
return (flashaddr / 0x40000) + 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr) {
|
|
||||||
if (sl->chip_id == STLINK_CHIPID_STM32_F7) {
|
|
||||||
uint32_t sector = calculate_F7_sectornum(flashaddr);
|
|
||||||
if (sector < 4)
|
|
||||||
sl->flash_pgsz = 0x8000;
|
|
||||||
else if (sector < 5)
|
|
||||||
sl->flash_pgsz = 0x20000;
|
|
||||||
else
|
|
||||||
sl->flash_pgsz = 0x40000;
|
|
||||||
}
|
|
||||||
return (uint32_t) sl->flash_pgsz;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Erase a page of flash, assumes sl is fully populated with things like
|
|
||||||
* chip/core ids
|
|
||||||
* @param sl stlink context
|
|
||||||
* @param flashaddr an address in the flash page to erase
|
|
||||||
* @return 0 on success -ve on failure
|
|
||||||
*/
|
|
||||||
int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) {
|
|
||||||
/* wait for ongoing op to finish */
|
|
||||||
wait_flash_busy(sl);
|
|
||||||
|
|
||||||
/* unlock if locked */
|
|
||||||
unlock_flash_if(sl);
|
|
||||||
|
|
||||||
/* select the page to erase */
|
|
||||||
// calculate the actual page from the address
|
|
||||||
uint32_t sector = calculate_F7_sectornum(flashaddr);
|
|
||||||
|
|
||||||
write_flash_cr_snb(sl, sector);
|
|
||||||
|
|
||||||
/* start erase operation */
|
|
||||||
set_flash_cr_strt(sl);
|
|
||||||
|
|
||||||
/* wait for completion */
|
|
||||||
wait_flash_busy(sl);
|
|
||||||
|
|
||||||
/* relock the flash */
|
|
||||||
// todo: fails to program if this is in
|
|
||||||
lock_flash(sl);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify addr..addr+len is binary identical to base...base+len
|
|
||||||
* @param sl stlink context
|
|
||||||
* @param address stm device address
|
|
||||||
* @param data host side buffer to check against
|
|
||||||
* @param length how much
|
|
||||||
* @return 0 for success, -ve for failure
|
|
||||||
*/
|
|
||||||
int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data,
|
|
||||||
unsigned length) {
|
|
||||||
size_t off;
|
|
||||||
size_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz;
|
|
||||||
printf("Verifying flash...\n");
|
|
||||||
for (off = 0; off < length; off += cmp_size) {
|
|
||||||
size_t aligned_size;
|
|
||||||
|
|
||||||
/* adjust last page size */
|
|
||||||
if ((off + cmp_size) > length) cmp_size = length - off;
|
|
||||||
|
|
||||||
aligned_size = cmp_size;
|
|
||||||
if (aligned_size & (4 - 1)) aligned_size = (cmp_size + 4) & ~(4 - 1);
|
|
||||||
|
|
||||||
stlink_read_mem32(sl, address + (uint32_t) off, aligned_size);
|
|
||||||
|
|
||||||
if (memcmp(sl->q_buf, data + off, cmp_size)) {
|
|
||||||
printf("Verification of flash failed at offset: %u\n",
|
|
||||||
(unsigned int) off);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t *base,
|
|
||||||
uint32_t len, uint8_t eraseonly) {
|
|
||||||
size_t off;
|
|
||||||
flash_loader_t fl;
|
|
||||||
printf("Writing %d (%#x) bytes to stm32 address: %u (%#x)\n", len, len, addr,
|
|
||||||
addr);
|
|
||||||
/* check addr range is inside the flash */
|
|
||||||
stlink_calculate_pagesize(sl, addr);
|
|
||||||
if (addr < sl->flash_base) {
|
|
||||||
printf("addr too low %#x < %#x\n", addr, sl->flash_base);
|
|
||||||
return -1;
|
|
||||||
} else if ((addr + len) < addr) {
|
|
||||||
printf("addr overruns\n");
|
|
||||||
return -1;
|
|
||||||
} else if ((addr + len) > (sl->flash_base + sl->flash_size)) {
|
|
||||||
printf("addr too high\n");
|
|
||||||
return -1;
|
|
||||||
} else if (addr & 1) {
|
|
||||||
printf("unaligned addr 0x%x\n", addr);
|
|
||||||
return -1;
|
|
||||||
} else if (len & 1) {
|
|
||||||
printf("unaligned len 0x%x -- padding with zero\n", len);
|
|
||||||
len += 1;
|
|
||||||
} else if (addr & (sl->flash_pgsz - 1)) {
|
|
||||||
printf("addr not a multiple of pagesize, not supported\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we've loaded the context with the chip details
|
|
||||||
stlink_core_id(sl);
|
|
||||||
/* erase each page */
|
|
||||||
int page_count = 0;
|
|
||||||
for (off = 0; off < len;
|
|
||||||
off += stlink_calculate_pagesize(sl, addr + (uint32_t) off)) {
|
|
||||||
fprintf(stdout, "Erasing flash page at addr 0x%08X\n", (int) (addr + off));
|
|
||||||
/* addr must be an addr inside the page */
|
|
||||||
if (stlink_erase_flash_page(sl, addr + (uint32_t) off) == -1) {
|
|
||||||
printf("Failed to erase_flash_page(%#zx) == -1\n", addr + off);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
page_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eraseonly) return 0;
|
|
||||||
|
|
||||||
/* flash loader initialization */
|
|
||||||
if (stlink_flash_loader_init(sl, &fl) == -1) {
|
|
||||||
printf("stlink_flash_loader_init() == -1\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First unlock the cr */
|
|
||||||
unlock_flash_if(sl);
|
|
||||||
|
|
||||||
/* TODO: Check that Voltage range is 2.7 - 3.6 V */
|
|
||||||
/* set parallelisim to 32 bit*/
|
|
||||||
int voltage = stlink_target_voltage(sl);
|
|
||||||
if (voltage == -1) {
|
|
||||||
printf("Failed to read Target voltage\n");
|
|
||||||
return voltage;
|
|
||||||
} else if (voltage > 2700) {
|
|
||||||
write_flash_cr_psiz(sl, 2);
|
|
||||||
} else {
|
|
||||||
printf(
|
|
||||||
"Target voltage (%d mV) too low for 32-bit flash, using 8-bit "
|
|
||||||
"flash writes\n",
|
|
||||||
voltage);
|
|
||||||
write_flash_cr_psiz(sl, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set programming mode */
|
|
||||||
set_flash_cr_pg(sl);
|
|
||||||
|
|
||||||
for (off = 0; off < len;) {
|
|
||||||
size_t size = len - off > 0x8000 ? 0x8000 : len - off;
|
|
||||||
printf("Writing %d bytes at 0x%08X\n", (int) size,
|
|
||||||
(int) (addr + (uint32_t) off));
|
|
||||||
if (stlink_flash_loader_run(sl, &fl, addr + (uint32_t) off, base + off,
|
|
||||||
size) == -1) {
|
|
||||||
printf("stlink_flash_loader_run(%#zx) failed! == -1\n", addr + off);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
off += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Relock flash */
|
|
||||||
lock_flash(sl);
|
|
||||||
|
|
||||||
return stlink_verify_write_flash(sl, addr, base, len);
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
/* simple wrapper around the stlink_flash_write function */
|
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "stlink.h"
|
|
||||||
#include "usb.h"
|
|
||||||
|
|
||||||
#define FLASH_ADDRESS 0x8000000
|
|
||||||
|
|
||||||
int stm32_flash(const char *device_name, void *data, int len) {
|
|
||||||
stlink_t *sl = NULL;
|
|
||||||
int err = -1;
|
|
||||||
uint8_t serial[16];
|
|
||||||
|
|
||||||
int j = (int) strlen(device_name);
|
|
||||||
int length = j / 2; // the length of the destination-array
|
|
||||||
if (j % 2 != 0) return -1;
|
|
||||||
|
|
||||||
for (size_t k = 0; j >= 0 && k < sizeof(serial); ++k, j -= 2) {
|
|
||||||
char buffer[3] = {0};
|
|
||||||
memcpy(buffer, device_name + j, 2);
|
|
||||||
serial[length - k] = (uint8_t) strtol(buffer, NULL, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
sl = stlink_open_usb(1, (char *) serial);
|
|
||||||
|
|
||||||
if (sl == NULL) return -1;
|
|
||||||
|
|
||||||
if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) {
|
|
||||||
if (stlink_exit_dfu_mode(sl)) {
|
|
||||||
printf("Failed to exit DFU mode\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) {
|
|
||||||
if (stlink_enter_swd_mode(sl)) {
|
|
||||||
printf("Failed to enter SWD mode\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stlink_jtag_reset(sl, 2)) {
|
|
||||||
printf("Failed to reset JTAG\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stlink_reset(sl)) {
|
|
||||||
printf("Failed to reset device\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Core must be halted to use RAM based flashloaders
|
|
||||||
if (stlink_force_debug(sl)) {
|
|
||||||
printf("Failed to halt the core\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stlink_status(sl)) {
|
|
||||||
printf("Failed to get Core's status\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size = 0;
|
|
||||||
|
|
||||||
if ((FLASH_ADDRESS > sl->flash_base + sl->flash_size)) {
|
|
||||||
printf("Unknown memory region\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = stlink_write_flash(sl, FLASH_ADDRESS, (uint8_t *) data, len, 0);
|
|
||||||
stlink_fwrite_finalize(sl, FLASH_ADDRESS);
|
|
||||||
|
|
||||||
if (err == -1) {
|
|
||||||
printf("stlink_fwrite_flash() == -1\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
stlink_jtag_reset(sl, 2);
|
|
||||||
stlink_reset(sl);
|
|
||||||
|
|
||||||
err = 0;
|
|
||||||
|
|
||||||
on_error:
|
|
||||||
stlink_exit_debug_mode(sl);
|
|
||||||
stlink_close(sl);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#include "stlink.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
static const uint8_t loader_code_stm32f7[] = {
|
|
||||||
0x08, 0x4b, 0x72, 0xb1, 0x04, 0x68, 0x0c, 0x60,
|
|
||||||
0xbf, 0xf3, 0x4f, 0x8f, // DSB Memory barrier for in order flash write
|
|
||||||
0xdc, 0x89, 0x14, 0xf0, 0x01, 0x0f, 0xfb, 0xd1,
|
|
||||||
0x00, 0xf1, 0x04, 0x00, 0x01, 0xf1, 0x04, 0x01,
|
|
||||||
0xa2, 0xf1, 0x01, 0x02, 0xef, 0xe7, 0x00, 0xbe, // bkpt #0x00
|
|
||||||
0x00, 0x3c, 0x02, 0x40,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define WAIT_ROUNDS 10000
|
|
||||||
|
|
||||||
int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl) {
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
/* allocate the loader in sram */
|
|
||||||
if (stlink_flash_loader_write_to_sram(sl, &fl->loader_addr, &size) == -1) {
|
|
||||||
printf("Failed to write flash loader to sram!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate a one page buffer in sram right after loader */
|
|
||||||
fl->buf_addr = fl->loader_addr + (uint32_t) size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr,
|
|
||||||
size_t *size) {
|
|
||||||
memcpy(sl->q_buf, loader_code_stm32f7, sizeof(loader_code_stm32f7));
|
|
||||||
stlink_write_mem32(sl, sl->sram_base, sizeof(loader_code_stm32f7));
|
|
||||||
|
|
||||||
*addr = sl->sram_base;
|
|
||||||
*size = sizeof(loader_code_stm32f7);
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl,
|
|
||||||
stm32_addr_t target, const uint8_t *buf,
|
|
||||||
size_t size) {
|
|
||||||
struct stlink_reg rr;
|
|
||||||
int i = 0;
|
|
||||||
size_t count = 0;
|
|
||||||
|
|
||||||
// FIXME This can never return -1
|
|
||||||
if (write_buffer_to_sram(sl, fl, buf, size) == -1) {
|
|
||||||
// IMPOSSIBLE!
|
|
||||||
printf("write_buffer_to_sram() == -1\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
count = size / sizeof(uint32_t);
|
|
||||||
if (size % sizeof(uint32_t)) ++count;
|
|
||||||
|
|
||||||
/* setup core */
|
|
||||||
stlink_write_reg(sl, fl->buf_addr, 0); /* source */
|
|
||||||
stlink_write_reg(sl, target, 1); /* target */
|
|
||||||
stlink_write_reg(sl, (uint32_t) count, 2); /* count */
|
|
||||||
stlink_write_reg(
|
|
||||||
sl, 0,
|
|
||||||
3); /* flash bank 0 (input), only used on F0, but armless fopr others */
|
|
||||||
stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */
|
|
||||||
|
|
||||||
/* run loader */
|
|
||||||
stlink_run(sl);
|
|
||||||
|
|
||||||
/* wait until done (reaches breakpoint) */
|
|
||||||
for (i = 0; i < WAIT_ROUNDS; i++) {
|
|
||||||
usleep(10);
|
|
||||||
if (stlink_is_core_halted(sl)) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= WAIT_ROUNDS) {
|
|
||||||
printf("flash loader run error\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check written byte count */
|
|
||||||
stlink_read_reg(sl, 2, &rr);
|
|
||||||
if (rr.r[2] != 0) {
|
|
||||||
printf("write error, count == %u\n", rr.r[2]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* File: stlink.h
|
|
||||||
*
|
|
||||||
* This should contain all the common top level stlink interfaces, regardless
|
|
||||||
* of how the backend does the work....
|
|
||||||
*/
|
|
||||||
#ifndef STLINK_FLASH_LOADER_H_
|
|
||||||
#define STLINK_FLASH_LOADER_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "stlink.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int stlink_flash_loader_init(stlink_t *sl, flash_loader_t *fl);
|
|
||||||
int stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t *addr,
|
|
||||||
size_t *size);
|
|
||||||
int stlink_flash_loader_run(stlink_t *sl, flash_loader_t *fl,
|
|
||||||
stm32_addr_t target, const uint8_t *buf,
|
|
||||||
size_t size);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* STLINK_FLASH_LOADER_H_ */
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package stm32
|
|
||||||
|
|
||||||
// #cgo CFLAGS: -I/usr/include/libusb-1.0/
|
|
||||||
// #cgo LDFLAGS: -lusb-1.0
|
|
||||||
// #include "stm32_flash.h"
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"cesanta.com/mos/flash/common"
|
|
||||||
"github.com/cesanta/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FlashOpts struct {
|
|
||||||
ShareName string
|
|
||||||
Timeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func Flash(fw *common.FirmwareBundle, opts *FlashOpts) error {
|
|
||||||
data, err := fw.GetPartData("boot")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Annotatef(err, "invalid manifest")
|
|
||||||
}
|
|
||||||
|
|
||||||
flash_res := C.stm32_flash(C.CString(opts.ShareName),
|
|
||||||
unsafe.Pointer(&data[0]),
|
|
||||||
C.int(len(data)))
|
|
||||||
|
|
||||||
if flash_res != 0 {
|
|
||||||
return errors.Errorf("flash failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
/*
|
|
||||||
* File: stlink.h
|
|
||||||
*
|
|
||||||
* This should contain all the common top level stlink interfaces, regardless
|
|
||||||
* of how the backend does the work....
|
|
||||||
*/
|
|
||||||
#ifndef STLINK_H
|
|
||||||
#define STLINK_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STLINK_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
|
||||||
|
|
||||||
// Max data transfer size.
|
|
||||||
// 6kB = max mem32_read block, 8kB sram
|
|
||||||
//#define Q_BUF_LEN 96
|
|
||||||
#define Q_BUF_LEN (1024 * 100)
|
|
||||||
|
|
||||||
// STLINK_DEBUG_RESETSYS, etc:
|
|
||||||
#define STLINK_CORE_RUNNING 0x80
|
|
||||||
#define STLINK_CORE_HALTED 0x81
|
|
||||||
#define STLINK_CORE_STAT_UNKNOWN -1
|
|
||||||
|
|
||||||
#define STLINK_GET_VERSION 0xf1
|
|
||||||
#define STLINK_GET_CURRENT_MODE 0xf5
|
|
||||||
#define STLINK_GET_TARGET_VOLTAGE 0xF7
|
|
||||||
|
|
||||||
#define STLINK_DEBUG_COMMAND 0xF2
|
|
||||||
#define STLINK_DFU_COMMAND 0xF3
|
|
||||||
#define STLINK_DFU_EXIT 0x07
|
|
||||||
|
|
||||||
// STLINK_GET_CURRENT_MODE
|
|
||||||
#define STLINK_DEV_DFU_MODE 0x00
|
|
||||||
#define STLINK_DEV_MASS_MODE 0x01
|
|
||||||
#define STLINK_DEV_DEBUG_MODE 0x02
|
|
||||||
#define STLINK_DEV_UNKNOWN_MODE -1
|
|
||||||
|
|
||||||
// TODO - possible poor names...
|
|
||||||
#define STLINK_SWD_ENTER 0x30
|
|
||||||
#define STLINK_SWD_READCOREID 0x32 // TBD
|
|
||||||
#define STLINK_JTAG_WRITEDEBUG_32BIT 0x35
|
|
||||||
#define STLINK_JTAG_READDEBUG_32BIT 0x36
|
|
||||||
#define STLINK_JTAG_DRIVE_NRST 0x3c
|
|
||||||
|
|
||||||
#define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43
|
|
||||||
|
|
||||||
/* cortex core ids */
|
|
||||||
// TODO clean this up...
|
|
||||||
#define STM32VL_CORE_ID 0x1ba01477
|
|
||||||
#define STM32F7_CORE_ID 0x5ba02477
|
|
||||||
|
|
||||||
// Constant STM32 memory map figures
|
|
||||||
#define STM32_FLASH_BASE 0x08000000
|
|
||||||
#define STM32_SRAM_BASE 0x20000000
|
|
||||||
|
|
||||||
// Baud rate divisors for SWDCLK
|
|
||||||
#define STLINK_SWDCLK_4MHZ_DIVISOR 0
|
|
||||||
#define STLINK_SWDCLK_1P8MHZ_DIVISOR 1
|
|
||||||
#define STLINK_SWDCLK_1P2MHZ_DIVISOR 2
|
|
||||||
#define STLINK_SWDCLK_950KHZ_DIVISOR 3
|
|
||||||
#define STLINK_SWDCLK_480KHZ_DIVISOR 7
|
|
||||||
#define STLINK_SWDCLK_240KHZ_DIVISOR 15
|
|
||||||
#define STLINK_SWDCLK_125KHZ_DIVISOR 31
|
|
||||||
#define STLINK_SWDCLK_100KHZ_DIVISOR 40
|
|
||||||
#define STLINK_SWDCLK_50KHZ_DIVISOR 79
|
|
||||||
#define STLINK_SWDCLK_25KHZ_DIVISOR 158
|
|
||||||
#define STLINK_SWDCLK_15KHZ_DIVISOR 265
|
|
||||||
#define STLINK_SWDCLK_5KHZ_DIVISOR 798
|
|
||||||
|
|
||||||
#define STLINK_CHIPID_STM32_F7 0x449
|
|
||||||
|
|
||||||
/* Enough space to hold both a V2 command or a V1 command packaged as generic
|
|
||||||
* scsi*/
|
|
||||||
#define C_BUF_LEN 32
|
|
||||||
|
|
||||||
struct stlink_reg {
|
|
||||||
uint32_t r[16];
|
|
||||||
uint32_t s[32];
|
|
||||||
uint32_t xpsr;
|
|
||||||
uint32_t main_sp;
|
|
||||||
uint32_t process_sp;
|
|
||||||
uint32_t rw;
|
|
||||||
uint32_t rw2;
|
|
||||||
uint8_t control;
|
|
||||||
uint8_t faultmask;
|
|
||||||
uint8_t basepri;
|
|
||||||
uint8_t primask;
|
|
||||||
uint32_t fpscr;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef uint32_t stm32_addr_t;
|
|
||||||
|
|
||||||
typedef struct flash_loader {
|
|
||||||
stm32_addr_t loader_addr; /* loader sram adddr */
|
|
||||||
stm32_addr_t buf_addr; /* buffer sram address */
|
|
||||||
} flash_loader_t;
|
|
||||||
|
|
||||||
typedef struct stlink_version_ {
|
|
||||||
uint32_t stlink_v;
|
|
||||||
uint32_t jtag_v;
|
|
||||||
uint32_t swim_v;
|
|
||||||
uint32_t st_vid;
|
|
||||||
uint32_t stlink_pid;
|
|
||||||
} stlink_version_t;
|
|
||||||
|
|
||||||
typedef struct _stlink stlink_t;
|
|
||||||
|
|
||||||
struct _stlink {
|
|
||||||
void *backend_data;
|
|
||||||
|
|
||||||
// Room for the command header
|
|
||||||
unsigned char c_buf[C_BUF_LEN];
|
|
||||||
// Data transferred from or to device
|
|
||||||
unsigned char q_buf[Q_BUF_LEN];
|
|
||||||
int q_len;
|
|
||||||
|
|
||||||
// transport layer verboseness: 0 for no debug info, 10 for lots
|
|
||||||
uint32_t core_id;
|
|
||||||
uint32_t chip_id;
|
|
||||||
int core_stat;
|
|
||||||
|
|
||||||
char serial[16];
|
|
||||||
int serial_size;
|
|
||||||
|
|
||||||
stm32_addr_t flash_base;
|
|
||||||
size_t flash_size;
|
|
||||||
size_t flash_pgsz;
|
|
||||||
|
|
||||||
/* sram settings */
|
|
||||||
stm32_addr_t sram_base;
|
|
||||||
size_t sram_size;
|
|
||||||
|
|
||||||
// bootloader
|
|
||||||
stm32_addr_t sys_base;
|
|
||||||
size_t sys_size;
|
|
||||||
|
|
||||||
struct stlink_version_ version;
|
|
||||||
};
|
|
||||||
|
|
||||||
int stlink_enter_swd_mode(stlink_t *sl);
|
|
||||||
int stlink_enter_jtag_mode(stlink_t *sl);
|
|
||||||
int stlink_exit_debug_mode(stlink_t *sl);
|
|
||||||
int stlink_exit_dfu_mode(stlink_t *sl);
|
|
||||||
void stlink_close(stlink_t *sl);
|
|
||||||
int stlink_core_id(stlink_t *sl);
|
|
||||||
int stlink_reset(stlink_t *sl);
|
|
||||||
int stlink_jtag_reset(stlink_t *sl, int value);
|
|
||||||
int stlink_run(stlink_t *sl);
|
|
||||||
int stlink_status(stlink_t *sl);
|
|
||||||
int stlink_version(stlink_t *sl);
|
|
||||||
int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data);
|
|
||||||
int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len);
|
|
||||||
int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data);
|
|
||||||
int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len);
|
|
||||||
int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len);
|
|
||||||
int stlink_read_all_regs(stlink_t *sl, struct stlink_reg *regp);
|
|
||||||
int stlink_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp);
|
|
||||||
int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx);
|
|
||||||
int stlink_step(stlink_t *sl);
|
|
||||||
int stlink_current_mode(stlink_t *sl);
|
|
||||||
int stlink_force_debug(stlink_t *sl);
|
|
||||||
int stlink_target_voltage(stlink_t *sl);
|
|
||||||
int stlink_set_swdclk(stlink_t *sl, uint16_t divisor);
|
|
||||||
int stlink_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data,
|
|
||||||
uint32_t length, uint8_t eraseonly);
|
|
||||||
int stlink_fwrite_flash(stlink_t *sl, const char *path, stm32_addr_t addr);
|
|
||||||
int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data,
|
|
||||||
uint32_t length);
|
|
||||||
int stlink_chip_id(stlink_t *sl, uint32_t *chip_id);
|
|
||||||
int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr);
|
|
||||||
uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr);
|
|
||||||
uint16_t read_uint16(const unsigned char *c, const int pt);
|
|
||||||
void stlink_core_stat(stlink_t *sl);
|
|
||||||
unsigned int is_bigendian(void);
|
|
||||||
uint32_t read_uint32(const unsigned char *c, const int pt);
|
|
||||||
void write_uint32(unsigned char *buf, uint32_t ui);
|
|
||||||
void write_uint16(unsigned char *buf, uint16_t ui);
|
|
||||||
bool stlink_is_core_halted(stlink_t *sl);
|
|
||||||
int write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf,
|
|
||||||
size_t size);
|
|
||||||
int write_loader_to_sram(stlink_t *sl, stm32_addr_t *addr, size_t *size);
|
|
||||||
int stlink_load_device_params(stlink_t *sl);
|
|
||||||
void stlink_fwrite_finalize(stlink_t *sl, stm32_addr_t addr);
|
|
||||||
|
|
||||||
#include "usb.h"
|
|
||||||
#include "flash_loader.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* STLINK_H */
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef STM32_FLASH_H_
|
|
||||||
#define STM32_FLASH_H_
|
|
||||||
|
|
||||||
int stm32_flash(const char *device_name, void *data, int len);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,773 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <libusb.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "stlink.h"
|
|
||||||
|
|
||||||
enum SCSI_Generic_Direction { SG_DXFER_TO_DEV = 0, SG_DXFER_FROM_DEV = 0x80 };
|
|
||||||
|
|
||||||
enum stlink_debug_commands {
|
|
||||||
STLINK_DEBUG_ENTER_JTAG = 0x00,
|
|
||||||
STLINK_DEBUG_GETSTATUS = 0x01,
|
|
||||||
STLINK_DEBUG_FORCEDEBUG = 0x02,
|
|
||||||
STLINK_DEBUG_RESETSYS = 0x03,
|
|
||||||
STLINK_DEBUG_READALLREGS = 0x04,
|
|
||||||
STLINK_DEBUG_READREG = 0x05,
|
|
||||||
STLINK_DEBUG_WRITEREG = 0x06,
|
|
||||||
STLINK_DEBUG_READMEM_32BIT = 0x07,
|
|
||||||
STLINK_DEBUG_WRITEMEM_32BIT = 0x08,
|
|
||||||
STLINK_DEBUG_RUNCORE = 0x09,
|
|
||||||
STLINK_DEBUG_STEPCORE = 0x0a,
|
|
||||||
STLINK_DEBUG_SETFP = 0x0b,
|
|
||||||
STLINK_DEBUG_WRITEMEM_8BIT = 0x0d,
|
|
||||||
STLINK_DEBUG_CLEARFP = 0x0e,
|
|
||||||
STLINK_DEBUG_WRITEDEBUGREG = 0x0f,
|
|
||||||
STLINK_DEBUG_ENTER = 0x20,
|
|
||||||
STLINK_DEBUG_EXIT = 0x21,
|
|
||||||
STLINK_DEBUG_READCOREID = 0x22,
|
|
||||||
STLINK_DEBUG_ENTER_SWD = 0xa3
|
|
||||||
};
|
|
||||||
|
|
||||||
void _stlink_usb_close(stlink_t *sl) {
|
|
||||||
if (!sl) return;
|
|
||||||
|
|
||||||
struct stlink_libusb *const handle = sl->backend_data;
|
|
||||||
// maybe we couldn't even get the usb device?
|
|
||||||
if (handle != NULL) {
|
|
||||||
if (handle->usb_handle != NULL) {
|
|
||||||
libusb_close(handle->usb_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_exit(handle->libusb_ctx);
|
|
||||||
free(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t send_recv(struct stlink_libusb *handle, int terminate,
|
|
||||||
unsigned char *txbuf, size_t txsize, unsigned char *rxbuf,
|
|
||||||
size_t rxsize) {
|
|
||||||
/* note: txbuf and rxbuf can point to the same area */
|
|
||||||
int res = 0;
|
|
||||||
int t;
|
|
||||||
|
|
||||||
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_req, txbuf,
|
|
||||||
(int) txsize, &res, 3000);
|
|
||||||
if (t) {
|
|
||||||
printf("[!] send_recv send request failed: %s\n", libusb_error_name(t));
|
|
||||||
return -1;
|
|
||||||
} else if ((size_t) res != txsize) {
|
|
||||||
printf("[!] send_recv send request wrote %u bytes (instead of %u).\n",
|
|
||||||
(unsigned int) res, (unsigned int) txsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rxsize != 0) {
|
|
||||||
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, rxbuf,
|
|
||||||
(int) rxsize, &res, 3000);
|
|
||||||
if (t) {
|
|
||||||
printf("[!] send_recv read reply failed: %s\n", libusb_error_name(t));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((handle->protocoll == 1) && terminate) {
|
|
||||||
/* Read the SG reply */
|
|
||||||
unsigned char sg_buf[13];
|
|
||||||
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, sg_buf, 13,
|
|
||||||
&res, 3000);
|
|
||||||
if (t) {
|
|
||||||
printf("[!] send_recv read storage failed: %s\n", libusb_error_name(t));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* The STLink doesn't seem to evaluate the sequence number */
|
|
||||||
handle->sg_transfer_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int send_only(struct stlink_libusb *handle, int terminate,
|
|
||||||
unsigned char *txbuf, size_t txsize) {
|
|
||||||
return (int) send_recv(handle, terminate, txbuf, txsize, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fill_command(stlink_t *sl, enum SCSI_Generic_Direction dir,
|
|
||||||
uint32_t len) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
int i = 0;
|
|
||||||
memset(cmd, 0, sizeof(sl->c_buf));
|
|
||||||
if (slu->protocoll == 1) {
|
|
||||||
cmd[i++] = 'U';
|
|
||||||
cmd[i++] = 'S';
|
|
||||||
cmd[i++] = 'B';
|
|
||||||
cmd[i++] = 'C';
|
|
||||||
write_uint32(&cmd[i], slu->sg_transfer_idx);
|
|
||||||
write_uint32(&cmd[i + 4], len);
|
|
||||||
i += 8;
|
|
||||||
cmd[i++] = (dir == SG_DXFER_FROM_DEV) ? 0x80 : 0;
|
|
||||||
cmd[i++] = 0; /* Logical unit */
|
|
||||||
cmd[i++] = 0xa; /* Command length */
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_version(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
uint32_t rep_len = 6;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_GET_VERSION;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_GET_VERSION\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t _stlink_usb_target_voltage(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const rdata = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
uint32_t rep_len = 8;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
uint32_t factor, reading;
|
|
||||||
int voltage;
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_GET_TARGET_VOLTAGE;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_GET_TARGET_VOLTAGE\n");
|
|
||||||
return -1;
|
|
||||||
} else if (size != 8) {
|
|
||||||
printf("[!] wrong length STLINK_GET_TARGET_VOLTAGE\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
factor =
|
|
||||||
(rdata[3] << 24) | (rdata[2] << 16) | (rdata[1] << 8) | (rdata[0] << 0);
|
|
||||||
reading =
|
|
||||||
(rdata[7] << 24) | (rdata[6] << 16) | (rdata[5] << 8) | (rdata[4] << 0);
|
|
||||||
voltage = 2400 * reading / factor;
|
|
||||||
|
|
||||||
return voltage;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const rdata = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
const int rep_len = 8;
|
|
||||||
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_JTAG_READDEBUG_32BIT;
|
|
||||||
write_uint32(&cmd[i], addr);
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_JTAG_READDEBUG_32BIT\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
*data = read_uint32(rdata, 4);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const rdata = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
const int rep_len = 2;
|
|
||||||
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_JTAG_WRITEDEBUG_32BIT;
|
|
||||||
write_uint32(&cmd[i], addr);
|
|
||||||
write_uint32(&cmd[i + 4], data);
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_JTAG_WRITEDEBUG_32BIT\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
int i, ret;
|
|
||||||
|
|
||||||
i = fill_command(sl, SG_DXFER_TO_DEV, len);
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT;
|
|
||||||
write_uint32(&cmd[i], addr);
|
|
||||||
write_uint16(&cmd[i + 4], len);
|
|
||||||
ret = send_only(slu, 0, cmd, slu->cmd_len);
|
|
||||||
if (ret == -1) return ret;
|
|
||||||
|
|
||||||
ret = send_only(slu, 1, data, len);
|
|
||||||
if (ret == -1) return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
int i, ret;
|
|
||||||
|
|
||||||
i = fill_command(sl, SG_DXFER_TO_DEV, 0);
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT;
|
|
||||||
write_uint32(&cmd[i], addr);
|
|
||||||
write_uint16(&cmd[i + 4], len);
|
|
||||||
ret = send_only(slu, 0, cmd, slu->cmd_len);
|
|
||||||
if (ret == -1) return ret;
|
|
||||||
|
|
||||||
ret = send_only(slu, 1, data, len);
|
|
||||||
if (ret == -1) return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_current_mode(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_GET_CURRENT_MODE;
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_GET_CURRENT_MODE\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return sl->q_buf[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_core_id(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 4;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_READCOREID;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_READCOREID\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sl->core_id = read_uint32(data, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_status(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_GETSTATUS;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_GETSTATUS\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
sl->q_len = (int) size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_force_debug(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_FORCEDEBUG;
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_FORCEDEBUG\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_enter_swd_mode(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
const int rep_len = 0;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_ENTER;
|
|
||||||
cmd[i++] = STLINK_DEBUG_ENTER_SWD;
|
|
||||||
|
|
||||||
size = send_only(slu, 1, cmd, slu->cmd_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_ENTER\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_exit_dfu_mode(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, 0);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DFU_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DFU_EXIT;
|
|
||||||
|
|
||||||
size = send_only(slu, 1, cmd, slu->cmd_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DFU_EXIT\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO - not convinced this does anything...
|
|
||||||
* @param sl
|
|
||||||
*/
|
|
||||||
int _stlink_usb_reset(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_RESETSYS;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_RESETSYS\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_jtag_reset(stlink_t *sl, int value) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_JTAG_DRIVE_NRST;
|
|
||||||
cmd[i++] = value;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_JTAG_DRIVE_NRST\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_step(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_STEPCORE;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_STEPCORE\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This seems to do a good job of restarting things from the beginning?
|
|
||||||
* @param sl
|
|
||||||
*/
|
|
||||||
int _stlink_usb_run(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_RUNCORE;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_RUNCORE\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int rep_len = 2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// clock speed only supported by stlink/v2 and for firmware >= 22
|
|
||||||
if (sl->version.stlink_v >= 2 && sl->version.jtag_v >= 22) {
|
|
||||||
i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_APIV2_SWD_SET_FREQ;
|
|
||||||
cmd[i++] = clk_divisor & 0xFF;
|
|
||||||
cmd[i++] = (clk_divisor >> 8) & 0xFF;
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_APIV2_SWD_SET_FREQ\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_exit_debug_mode(stlink_t *sl) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, 0);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_EXIT;
|
|
||||||
|
|
||||||
size = send_only(slu, 1, cmd, slu->cmd_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_only STLINK_DEBUG_EXIT\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_READMEM_32BIT;
|
|
||||||
write_uint32(&cmd[i], addr);
|
|
||||||
write_uint16(&cmd[i + 4], len);
|
|
||||||
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_READMEM_32BIT\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
|
|
||||||
sl->q_len = (int) size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
uint32_t r;
|
|
||||||
uint32_t rep_len = 4;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_READREG;
|
|
||||||
cmd[i++] = (uint8_t) r_idx;
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_READREG\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
sl->q_len = (int) size;
|
|
||||||
r = read_uint32(sl->q_buf, 0);
|
|
||||||
|
|
||||||
switch (r_idx) {
|
|
||||||
case 16:
|
|
||||||
regp->xpsr = r;
|
|
||||||
break;
|
|
||||||
case 17:
|
|
||||||
regp->main_sp = r;
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
regp->process_sp = r;
|
|
||||||
break;
|
|
||||||
case 19:
|
|
||||||
regp->rw = r; /* XXX ?(primask, basemask etc.) */
|
|
||||||
break;
|
|
||||||
case 20:
|
|
||||||
regp->rw2 = r; /* XXX ?(primask, basemask etc.) */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
regp->r[r_idx] = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) {
|
|
||||||
struct stlink_libusb *const slu = sl->backend_data;
|
|
||||||
unsigned char *const data = sl->q_buf;
|
|
||||||
unsigned char *const cmd = sl->c_buf;
|
|
||||||
ssize_t size;
|
|
||||||
uint32_t rep_len = 2;
|
|
||||||
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
|
||||||
|
|
||||||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
|
||||||
cmd[i++] = STLINK_DEBUG_WRITEREG;
|
|
||||||
cmd[i++] = idx;
|
|
||||||
write_uint32(&cmd[i], reg);
|
|
||||||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
|
|
||||||
if (size == -1) {
|
|
||||||
printf("[!] send_recv STLINK_DEBUG_WRITEREG\n");
|
|
||||||
return (int) size;
|
|
||||||
}
|
|
||||||
sl->q_len = (int) size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
stlink_t *stlink_open_usb(bool reset, char serial[16]) {
|
|
||||||
stlink_t *sl = NULL;
|
|
||||||
struct stlink_libusb *slu = NULL;
|
|
||||||
int ret = -1;
|
|
||||||
int config;
|
|
||||||
|
|
||||||
sl = calloc(1, sizeof(stlink_t));
|
|
||||||
slu = calloc(1, sizeof(struct stlink_libusb));
|
|
||||||
if (sl == NULL) goto on_malloc_error;
|
|
||||||
if (slu == NULL) goto on_malloc_error;
|
|
||||||
|
|
||||||
sl->backend_data = slu;
|
|
||||||
|
|
||||||
sl->core_stat = STLINK_CORE_STAT_UNKNOWN;
|
|
||||||
if (libusb_init(&(slu->libusb_ctx))) {
|
|
||||||
printf("failed to init libusb context, wrong version of libraries?\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_device **list;
|
|
||||||
/** @todo We should use ssize_t and use it as a counter if > 0. As per libusb
|
|
||||||
* API: ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device
|
|
||||||
* ***list) */
|
|
||||||
int cnt = (int) libusb_get_device_list(slu->libusb_ctx, &list);
|
|
||||||
struct libusb_device_descriptor desc;
|
|
||||||
int devBus = 0;
|
|
||||||
int devAddr = 0;
|
|
||||||
|
|
||||||
/* @TODO: Reading a environment variable in a usb open function is not very
|
|
||||||
nice, this
|
|
||||||
should be refactored and moved into the CLI tools, and instead of giving
|
|
||||||
USB_BUS:USB_ADDR a real stlink
|
|
||||||
serial string should be passed to this function. Probably people are using
|
|
||||||
this but this is very odd because
|
|
||||||
as programmer can change to multiple busses and it is better to detect them
|
|
||||||
based on serial. */
|
|
||||||
char *device = getenv("STLINK_DEVICE");
|
|
||||||
if (device) {
|
|
||||||
char *c = strchr(device, ':');
|
|
||||||
if (c == NULL) {
|
|
||||||
printf("STLINK_DEVICE must be <USB_BUS>:<USB_ADDR> format\n");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
devBus = atoi(device);
|
|
||||||
*c++ = 0;
|
|
||||||
devAddr = atoi(c);
|
|
||||||
printf("bus %03d dev %03d\n", devBus, devAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (cnt--) {
|
|
||||||
libusb_get_device_descriptor(list[cnt], &desc);
|
|
||||||
if (desc.idVendor != STLINK_USB_VID_ST) continue;
|
|
||||||
|
|
||||||
if (devBus && devAddr) {
|
|
||||||
if ((libusb_get_bus_number(list[cnt]) != devBus) ||
|
|
||||||
(libusb_get_device_address(list[cnt]) != devAddr)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((desc.idProduct == STLINK_USB_PID_STLINK_32L) ||
|
|
||||||
(desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO)) {
|
|
||||||
struct libusb_device_handle *handle;
|
|
||||||
|
|
||||||
ret = libusb_open(list[cnt], &handle);
|
|
||||||
if (ret) continue;
|
|
||||||
|
|
||||||
sl->serial_size = libusb_get_string_descriptor_ascii(
|
|
||||||
handle, desc.iSerialNumber, (unsigned char *) sl->serial,
|
|
||||||
sizeof(sl->serial));
|
|
||||||
libusb_close(handle);
|
|
||||||
|
|
||||||
if ((serial == NULL) || (*serial == 0)) break;
|
|
||||||
|
|
||||||
if (sl->serial_size < 0) continue;
|
|
||||||
|
|
||||||
if (memcmp(serial, &sl->serial, sl->serial_size) == 0) break;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (desc.idProduct == STLINK_USB_PID_STLINK) {
|
|
||||||
slu->protocoll = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cnt < 0) {
|
|
||||||
printf("Couldn't find %s ST-Link/V2 devices\n",
|
|
||||||
(devBus && devAddr) ? "matched" : "any");
|
|
||||||
goto on_error;
|
|
||||||
} else {
|
|
||||||
ret = libusb_open(list[cnt], &slu->usb_handle);
|
|
||||||
if (ret != 0) {
|
|
||||||
printf("Error %d (%s) opening ST-Link/V2 device %03d:%03d\n", ret,
|
|
||||||
strerror(errno), libusb_get_bus_number(list[cnt]),
|
|
||||||
libusb_get_device_address(list[cnt]));
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_free_device_list(list, 1);
|
|
||||||
|
|
||||||
if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) {
|
|
||||||
ret = libusb_detach_kernel_driver(slu->usb_handle, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
printf("libusb_detach_kernel_driver(() error %s\n", strerror(-ret));
|
|
||||||
goto on_libusb_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (libusb_get_configuration(slu->usb_handle, &config)) {
|
|
||||||
/* this may fail for a previous configured device */
|
|
||||||
printf("libusb_get_configuration()\n");
|
|
||||||
goto on_libusb_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config != 1) {
|
|
||||||
printf("setting new configuration (%d -> 1)\n", config);
|
|
||||||
if (libusb_set_configuration(slu->usb_handle, 1)) {
|
|
||||||
/* this may fail for a previous configured device */
|
|
||||||
printf("libusb_set_configuration() failed\n");
|
|
||||||
goto on_libusb_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (libusb_claim_interface(slu->usb_handle, 0)) {
|
|
||||||
printf(
|
|
||||||
"Stlink usb device found, but unable to claim (probably already in "
|
|
||||||
"use?)\n");
|
|
||||||
goto on_libusb_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO - could use the scanning techniq from stm8 code here...
|
|
||||||
slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN;
|
|
||||||
if (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO) {
|
|
||||||
slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT;
|
|
||||||
} else {
|
|
||||||
slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
slu->sg_transfer_idx = 0;
|
|
||||||
// TODO - never used at the moment, always CMD_SIZE
|
|
||||||
slu->cmd_len = (slu->protocoll == 1) ? STLINK_SG_SIZE : STLINK_CMD_SIZE;
|
|
||||||
|
|
||||||
if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) {
|
|
||||||
printf("-- exit_dfu_mode\n");
|
|
||||||
stlink_exit_dfu_mode(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) {
|
|
||||||
stlink_enter_swd_mode(sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
sl->version.stlink_v = 2;
|
|
||||||
|
|
||||||
if (reset) {
|
|
||||||
if (sl->version.stlink_v > 1) stlink_jtag_reset(sl, 2);
|
|
||||||
stlink_reset(sl);
|
|
||||||
usleep(10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = stlink_load_device_params(sl);
|
|
||||||
|
|
||||||
// Set the stlink clock speed (default is 1800kHz)
|
|
||||||
stlink_set_swdclk(sl, STLINK_SWDCLK_1P8MHZ_DIVISOR);
|
|
||||||
|
|
||||||
on_libusb_error:
|
|
||||||
if (ret == -1) {
|
|
||||||
stlink_close(sl);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sl;
|
|
||||||
|
|
||||||
on_error:
|
|
||||||
if (slu->libusb_ctx) libusb_exit(slu->libusb_ctx);
|
|
||||||
|
|
||||||
on_malloc_error:
|
|
||||||
if (sl != NULL) free(sl);
|
|
||||||
if (slu != NULL) free(slu);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* File: stlink/usb.h
|
|
||||||
* Author: karl
|
|
||||||
*
|
|
||||||
* Created on October 1, 2011, 11:29 PM
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef STLINK_USB_H
|
|
||||||
#define STLINK_USB_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <libusb.h>
|
|
||||||
|
|
||||||
#include "stlink.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STLINK_USB_VID_ST 0x0483
|
|
||||||
#define STLINK_USB_PID_STLINK 0x3744
|
|
||||||
#define STLINK_USB_PID_STLINK_32L 0x3748
|
|
||||||
#define STLINK_USB_PID_STLINK_NUCLEO 0x374b
|
|
||||||
|
|
||||||
#define STLINK_SG_SIZE 31
|
|
||||||
#define STLINK_CMD_SIZE 16
|
|
||||||
|
|
||||||
struct stlink_libusb {
|
|
||||||
libusb_context *libusb_ctx;
|
|
||||||
libusb_device_handle *usb_handle;
|
|
||||||
unsigned int ep_req;
|
|
||||||
unsigned int ep_rep;
|
|
||||||
int protocoll;
|
|
||||||
unsigned int sg_transfer_idx;
|
|
||||||
unsigned int cmd_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a stlink
|
|
||||||
* @param verbose Verbosity loglevel
|
|
||||||
* @param reset Reset stlink programmer
|
|
||||||
* @param serial Serial number to search for, when NULL the first stlink found
|
|
||||||
* is opened (binary format)
|
|
||||||
* @retval NULL Error while opening the stlink
|
|
||||||
* @retval !NULL Stlink found and ready to use
|
|
||||||
*/
|
|
||||||
stlink_t *stlink_open_usb(bool reset, char serial[16]);
|
|
||||||
|
|
||||||
void _stlink_usb_close(stlink_t *sl);
|
|
||||||
int _stlink_usb_version(stlink_t *sl);
|
|
||||||
int32_t _stlink_usb_target_voltage(stlink_t *sl);
|
|
||||||
int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data);
|
|
||||||
int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data);
|
|
||||||
int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len);
|
|
||||||
int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len);
|
|
||||||
int _stlink_usb_current_mode(stlink_t *sl);
|
|
||||||
int _stlink_usb_core_id(stlink_t *sl);
|
|
||||||
int _stlink_usb_status(stlink_t *sl);
|
|
||||||
int _stlink_usb_force_debug(stlink_t *sl);
|
|
||||||
int _stlink_usb_enter_swd_mode(stlink_t *sl);
|
|
||||||
int _stlink_usb_exit_dfu_mode(stlink_t *sl);
|
|
||||||
int _stlink_usb_reset(stlink_t *sl);
|
|
||||||
int _stlink_usb_jtag_reset(stlink_t *sl, int value);
|
|
||||||
int _stlink_usb_step(stlink_t *sl);
|
|
||||||
int _stlink_usb_run(stlink_t *sl);
|
|
||||||
int _stlink_usb_set_swdclk(stlink_t *sl, uint16_t clk_divisor);
|
|
||||||
int _stlink_usb_exit_debug_mode(stlink_t *sl);
|
|
||||||
int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len);
|
|
||||||
int _stlink_usb_read_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp);
|
|
||||||
int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* STLINK_USB_H */
|
|
||||||
+1
-20
@@ -1,26 +1,7 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004-2013 Sergey Lyubka
|
|
||||||
* Copyright (c) 2013-2015 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.cesanta.com/license>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
||||||
#define CS_MONGOOSE_SRC_COMMON_H_
|
#define CS_MONGOOSE_SRC_COMMON_H_
|
||||||
|
|
||||||
#define MG_VERSION "6.13"
|
#define MG_VERSION "6.17"
|
||||||
|
|
||||||
/* Local tweaks, applied before any of Mongoose's own headers. */
|
/* Local tweaks, applied before any of Mongoose's own headers. */
|
||||||
#ifdef MG_LOCALS
|
#ifdef MG_LOCALS
|
||||||
|
|||||||
+14
-10
@@ -85,35 +85,39 @@ int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) {
|
|||||||
return mbuf_append(io, begin, end - begin);
|
return mbuf_append(io, begin, end - begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) {
|
int mg_dns_encode_name_s(struct mbuf *io, struct mg_str name) {
|
||||||
const char *s;
|
const char *s;
|
||||||
unsigned char n;
|
unsigned char n;
|
||||||
size_t pos = io->len;
|
size_t pos = io->len;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if ((s = strchr(name, '.')) == NULL) {
|
if ((s = mg_strchr(name, '.')) == NULL) {
|
||||||
s = name + len;
|
s = name.p + name.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s - name > 127) {
|
if (s - name.p > 127) {
|
||||||
return -1; /* TODO(mkm) cover */
|
return -1; /* TODO(mkm) cover */
|
||||||
}
|
}
|
||||||
n = s - name; /* chunk length */
|
n = s - name.p; /* chunk length */
|
||||||
mbuf_append(io, &n, 1); /* send length */
|
mbuf_append(io, &n, 1); /* send length */
|
||||||
mbuf_append(io, name, n);
|
mbuf_append(io, name.p, n);
|
||||||
|
|
||||||
if (*s == '.') {
|
if (n < name.len && *s == '.') {
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
name += n;
|
name.p += n;
|
||||||
len -= n;
|
name.len -= n;
|
||||||
} while (*s != '\0');
|
} while (name.len > 0);
|
||||||
mbuf_append(io, "\0", 1); /* Mark end of host name */
|
mbuf_append(io, "\0", 1); /* Mark end of host name */
|
||||||
|
|
||||||
return io->len - pos;
|
return io->len - pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) {
|
||||||
|
return mg_dns_encode_name_s(io, mg_mk_str_n(name, len));
|
||||||
|
}
|
||||||
|
|
||||||
int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
|
int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
|
||||||
const char *name, size_t nlen, const void *rdata,
|
const char *name, size_t nlen, const void *rdata,
|
||||||
size_t rlen) {
|
size_t rlen) {
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
|
|||||||
* Encodes a DNS name.
|
* Encodes a DNS name.
|
||||||
*/
|
*/
|
||||||
int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len);
|
int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len);
|
||||||
|
int mg_dns_encode_name_s(struct mbuf *io, struct mg_str name);
|
||||||
|
|
||||||
/* Low-level: parses a DNS response. */
|
/* Low-level: parses a DNS response. */
|
||||||
int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg);
|
int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg);
|
||||||
|
|||||||
+199
-131
@@ -140,6 +140,7 @@ struct mg_http_multipart_stream {
|
|||||||
void *user_data;
|
void *user_data;
|
||||||
enum mg_http_multipart_stream_state state;
|
enum mg_http_multipart_stream_state state;
|
||||||
int processing_part;
|
int processing_part;
|
||||||
|
int data_avail;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mg_reverse_proxy_data {
|
struct mg_reverse_proxy_data {
|
||||||
@@ -176,20 +177,29 @@ struct mg_http_proto_data {
|
|||||||
size_t rcvd; /* How many bytes we have received. */
|
size_t rcvd; /* How many bytes we have received. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mg_http_conn_destructor(void *proto_data);
|
static void mg_http_proto_data_destructor(void *proto_data);
|
||||||
|
|
||||||
struct mg_connection *mg_connect_http_base(
|
struct mg_connection *mg_connect_http_base(
|
||||||
struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
|
struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
|
||||||
struct mg_connect_opts opts, const char *scheme1, const char *scheme2,
|
struct mg_connect_opts opts, const char *scheme1, const char *scheme2,
|
||||||
const char *scheme_ssl1, const char *scheme_ssl2, const char *url,
|
const char *scheme_ssl1, const char *scheme_ssl2, const char *url,
|
||||||
struct mg_str *path, struct mg_str *user_info, struct mg_str *host);
|
struct mg_str *path, struct mg_str *user_info, struct mg_str *host);
|
||||||
|
|
||||||
|
MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data(
|
||||||
|
struct mg_connection *c) {
|
||||||
|
/* If we have proto data from previous connection, flush it. */
|
||||||
|
if (c->proto_data != NULL) {
|
||||||
|
void *pd = c->proto_data;
|
||||||
|
c->proto_data = NULL;
|
||||||
|
mg_http_proto_data_destructor(pd);
|
||||||
|
}
|
||||||
|
c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data));
|
||||||
|
c->proto_data_destructor = mg_http_proto_data_destructor;
|
||||||
|
return (struct mg_http_proto_data *) c->proto_data;
|
||||||
|
}
|
||||||
|
|
||||||
static struct mg_http_proto_data *mg_http_get_proto_data(
|
static struct mg_http_proto_data *mg_http_get_proto_data(
|
||||||
struct mg_connection *c) {
|
struct mg_connection *c) {
|
||||||
if (c->proto_data == NULL) {
|
|
||||||
c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data));
|
|
||||||
c->proto_data_destructor = mg_http_conn_destructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (struct mg_http_proto_data *) c->proto_data;
|
return (struct mg_http_proto_data *) c->proto_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +236,7 @@ static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) {
|
|||||||
current = tmp;
|
current = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
ep = NULL;
|
*ep = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) {
|
static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) {
|
||||||
@@ -245,7 +255,7 @@ static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_http_conn_destructor(void *proto_data) {
|
static void mg_http_proto_data_destructor(void *proto_data) {
|
||||||
struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data;
|
struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data;
|
||||||
#if MG_ENABLE_FILESYSTEM
|
#if MG_ENABLE_FILESYSTEM
|
||||||
mg_http_free_proto_data_file(&pd->file);
|
mg_http_free_proto_data_file(&pd->file);
|
||||||
@@ -318,37 +328,53 @@ static const struct {
|
|||||||
MIME_ENTRY("asf", "video/x-ms-asf"),
|
MIME_ENTRY("asf", "video/x-ms-asf"),
|
||||||
MIME_ENTRY("avi", "video/x-msvideo"),
|
MIME_ENTRY("avi", "video/x-msvideo"),
|
||||||
MIME_ENTRY("bmp", "image/bmp"),
|
MIME_ENTRY("bmp", "image/bmp"),
|
||||||
{NULL, 0, NULL}};
|
{NULL, 0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
static struct mg_str mg_get_mime_type(const char *path, const char *dflt,
|
static struct mg_str mg_get_mime_types_entry(struct mg_str path) {
|
||||||
const struct mg_serve_http_opts *opts) {
|
size_t i;
|
||||||
|
for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) {
|
||||||
|
if (path.len < mg_static_builtin_mime_types[i].ext_len + 1) continue;
|
||||||
|
struct mg_str ext = MG_MK_STR_N(mg_static_builtin_mime_types[i].extension,
|
||||||
|
mg_static_builtin_mime_types[i].ext_len);
|
||||||
|
struct mg_str pext = MG_MK_STR_N(path.p + (path.len - ext.len), ext.len);
|
||||||
|
if (pext.p[-1] == '.' && mg_strcasecmp(ext, pext) == 0) {
|
||||||
|
return mg_mk_str(mg_static_builtin_mime_types[i].mime_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mg_mk_str(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
MG_INTERNAL int mg_get_mime_type_encoding(
|
||||||
|
struct mg_str path, struct mg_str *type, struct mg_str *encoding,
|
||||||
|
const struct mg_serve_http_opts *opts) {
|
||||||
const char *ext, *overrides;
|
const char *ext, *overrides;
|
||||||
size_t i, path_len;
|
struct mg_str k, v;
|
||||||
struct mg_str r, k, v;
|
|
||||||
|
|
||||||
path_len = strlen(path);
|
|
||||||
|
|
||||||
overrides = opts->custom_mime_types;
|
overrides = opts->custom_mime_types;
|
||||||
while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) {
|
while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) {
|
||||||
ext = path + (path_len - k.len);
|
ext = path.p + (path.len - k.len);
|
||||||
if (path_len > k.len && mg_vcasecmp(&k, ext) == 0) {
|
if (path.len > k.len && mg_vcasecmp(&k, ext) == 0) {
|
||||||
return v;
|
*type = v;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) {
|
*type = mg_get_mime_types_entry(path);
|
||||||
ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len);
|
|
||||||
if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' &&
|
/* Check for .html.gz, .js.gz, etc. */
|
||||||
mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) {
|
if (mg_vcmp(type, "application/x-gunzip") == 0) {
|
||||||
r.p = mg_static_builtin_mime_types[i].mime_type;
|
struct mg_str path2 = mg_mk_str_n(path.p, path.len - 3);
|
||||||
r.len = strlen(r.p);
|
struct mg_str type2 = mg_get_mime_types_entry(path2);
|
||||||
return r;
|
LOG(LL_ERROR, ("'%.*s' '%.*s' '%.*s'", (int) path.len, path.p,
|
||||||
|
(int) path2.len, path2.p, (int) type2.len, type2.p));
|
||||||
|
if (type2.len > 0) {
|
||||||
|
*type = type2;
|
||||||
|
*encoding = mg_mk_str("gzip");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.p = dflt;
|
return (type->len > 0);
|
||||||
r.len = strlen(r.p);
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -445,7 +471,7 @@ int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s = mg_skip(s, end, " ", &hm->proto);
|
s = mg_skip(s, end, " ", &hm->proto);
|
||||||
if (end - s < 4 || s[3] != ' ') return -1;
|
if (end - s < 4 || s[0] < '0' || s[0] > '9' || s[3] != ' ') return -1;
|
||||||
hm->resp_code = atoi(s);
|
hm->resp_code = atoi(s);
|
||||||
if (hm->resp_code < 100 || hm->resp_code >= 600) return -1;
|
if (hm->resp_code < 100 || hm->resp_code >= 600) return -1;
|
||||||
s += 4;
|
s += 4;
|
||||||
@@ -518,7 +544,8 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
|||||||
/* Rate-limited */
|
/* Rate-limited */
|
||||||
}
|
}
|
||||||
if (pd->file.sent >= pd->file.cl) {
|
if (pd->file.sent >= pd->file.cl) {
|
||||||
LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent));
|
LOG(LL_DEBUG, ("%p done, %d bytes, ka %d", nc, (int) pd->file.sent,
|
||||||
|
pd->file.keepalive));
|
||||||
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
mg_http_free_proto_data_file(&pd->file);
|
mg_http_free_proto_data_file(&pd->file);
|
||||||
}
|
}
|
||||||
@@ -654,12 +681,12 @@ struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc,
|
|||||||
int matched, matched_max = 0;
|
int matched, matched_max = 0;
|
||||||
struct mg_http_endpoint *ep;
|
struct mg_http_endpoint *ep;
|
||||||
|
|
||||||
if (nc == NULL) {
|
if (nc == NULL) return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pd = mg_http_get_proto_data(nc);
|
pd = mg_http_get_proto_data(nc);
|
||||||
|
|
||||||
|
if (pd == NULL) return NULL;
|
||||||
|
|
||||||
ep = pd->endpoints;
|
ep = pd->endpoints;
|
||||||
while (ep != NULL) {
|
while (ep != NULL) {
|
||||||
if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) {
|
if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) {
|
||||||
@@ -731,13 +758,13 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
if (ev == MG_EV_CLOSE) {
|
if (ev == MG_EV_CLOSE) {
|
||||||
#if MG_ENABLE_HTTP_CGI
|
#if MG_ENABLE_HTTP_CGI
|
||||||
/* Close associated CGI forwarder connection */
|
/* Close associated CGI forwarder connection */
|
||||||
if (pd->cgi.cgi_nc != NULL) {
|
if (pd != NULL && pd->cgi.cgi_nc != NULL) {
|
||||||
pd->cgi.cgi_nc->user_data = NULL;
|
pd->cgi.cgi_nc->user_data = NULL;
|
||||||
pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||||
if (pd->mp_stream.boundary != NULL) {
|
if (pd != NULL && pd->mp_stream.boundary != NULL) {
|
||||||
/*
|
/*
|
||||||
* Multipart message is in progress, but connection is closed.
|
* Multipart message is in progress, but connection is closed.
|
||||||
* Finish part and request with an error flag.
|
* Finish part and request with an error flag.
|
||||||
@@ -745,6 +772,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
struct mg_http_multipart_part mp;
|
struct mg_http_multipart_part mp;
|
||||||
memset(&mp, 0, sizeof(mp));
|
memset(&mp, 0, sizeof(mp));
|
||||||
mp.status = -1;
|
mp.status = -1;
|
||||||
|
mp.user_data = pd->mp_stream.user_data;
|
||||||
mp.var_name = pd->mp_stream.var_name;
|
mp.var_name = pd->mp_stream.var_name;
|
||||||
mp.file_name = pd->mp_stream.file_name;
|
mp.file_name = pd->mp_stream.file_name;
|
||||||
mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler),
|
mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler),
|
||||||
@@ -758,40 +786,55 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
if (io->len > 0 &&
|
if (io->len > 0 &&
|
||||||
(req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) {
|
(req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) {
|
||||||
/*
|
/*
|
||||||
* For HTTP messages without Content-Length, always send HTTP message
|
* For HTTP messages without Content-Length, always send HTTP message
|
||||||
* before MG_EV_CLOSE message.
|
* before MG_EV_CLOSE message.
|
||||||
*/
|
*/
|
||||||
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
||||||
hm->message.len = io->len;
|
hm->message.len = io->len;
|
||||||
hm->body.len = io->buf + io->len - hm->body.p;
|
hm->body.len = io->buf + io->len - hm->body.p;
|
||||||
deliver_chunk(nc, hm, req_len);
|
deliver_chunk(nc, hm, req_len);
|
||||||
mg_http_call_endpoint_handler(nc, ev2, hm);
|
mg_http_call_endpoint_handler(nc, ev2, hm);
|
||||||
}
|
}
|
||||||
pd->rcvd = 0;
|
if (pd != NULL && pd->endpoint_handler != NULL &&
|
||||||
|
pd->endpoint_handler != nc->handler) {
|
||||||
|
mg_call(nc, pd->endpoint_handler, nc->user_data, ev, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_FILESYSTEM
|
#if MG_ENABLE_FILESYSTEM
|
||||||
if (pd->file.fp != NULL) {
|
if (pd != NULL && pd->file.fp != NULL) {
|
||||||
mg_http_transfer_file_data(nc);
|
mg_http_transfer_file_data(nc);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
|
mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
|
||||||
|
|
||||||
|
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||||
|
if (pd != NULL && pd->mp_stream.boundary != NULL &&
|
||||||
|
(ev == MG_EV_RECV || ev == MG_EV_POLL)) {
|
||||||
|
if (ev == MG_EV_RECV) {
|
||||||
|
pd->rcvd += *(int *) ev_data;
|
||||||
|
mg_http_multipart_continue(nc);
|
||||||
|
} else if (pd->mp_stream.data_avail) {
|
||||||
|
/* Try re-delivering the data. */
|
||||||
|
mg_http_multipart_continue(nc);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
|
||||||
|
|
||||||
if (ev == MG_EV_RECV) {
|
if (ev == MG_EV_RECV) {
|
||||||
struct mg_str *s;
|
struct mg_str *s;
|
||||||
pd->rcvd += *(int *) ev_data;
|
|
||||||
|
|
||||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
|
||||||
if (pd->mp_stream.boundary != NULL) {
|
|
||||||
mg_http_multipart_continue(nc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
|
|
||||||
|
|
||||||
again:
|
again:
|
||||||
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
||||||
|
|
||||||
|
if (req_len > 0) {
|
||||||
|
/* New request - new proto data */
|
||||||
|
pd = mg_http_create_proto_data(nc);
|
||||||
|
pd->rcvd = io->len;
|
||||||
|
}
|
||||||
|
|
||||||
if (req_len > 0 &&
|
if (req_len > 0 &&
|
||||||
(s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL &&
|
(s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL &&
|
||||||
mg_vcasecmp(s, "chunked") == 0) {
|
mg_vcasecmp(s, "chunked") == 0) {
|
||||||
@@ -816,16 +859,23 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
/* Do nothing, request is not yet fully buffered */
|
/* Do nothing, request is not yet fully buffered */
|
||||||
}
|
}
|
||||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||||
else if (nc->listener == NULL &&
|
else if (nc->listener == NULL && (nc->flags & MG_F_IS_WEBSOCKET)) {
|
||||||
mg_get_http_header(hm, "Sec-WebSocket-Accept")) {
|
|
||||||
/* We're websocket client, got handshake response from server. */
|
/* We're websocket client, got handshake response from server. */
|
||||||
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
|
DBG(("%p WebSocket upgrade code %d", nc, hm->resp_code));
|
||||||
mbuf_remove(io, req_len);
|
if (hm->resp_code == 101 &&
|
||||||
nc->proto_handler = mg_ws_handler;
|
mg_get_http_header(hm, "Sec-WebSocket-Accept")) {
|
||||||
nc->flags |= MG_F_IS_WEBSOCKET;
|
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
|
||||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||||
NULL);
|
hm);
|
||||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
mbuf_remove(io, req_len);
|
||||||
|
nc->proto_handler = mg_ws_handler;
|
||||||
|
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||||
|
} else {
|
||||||
|
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||||
|
hm);
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
mbuf_remove(io, req_len);
|
||||||
|
}
|
||||||
} else if (nc->listener != NULL &&
|
} else if (nc->listener != NULL &&
|
||||||
(vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) {
|
(vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) {
|
||||||
struct mg_http_endpoint *ep;
|
struct mg_http_endpoint *ep;
|
||||||
@@ -856,7 +906,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
mg_ws_handshake(nc, vec, hm);
|
mg_ws_handshake(nc, vec, hm);
|
||||||
}
|
}
|
||||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||||
NULL);
|
hm);
|
||||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -896,18 +946,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
/* If this is a CGI request, we are not done either. */
|
/* If this is a CGI request, we are not done either. */
|
||||||
if (pd->cgi.cgi_nc != NULL) request_done = 0;
|
if (pd->cgi.cgi_nc != NULL) request_done = 0;
|
||||||
#endif
|
#endif
|
||||||
if (request_done) {
|
if (request_done && io->len > 0) goto again;
|
||||||
/* This request is done but we may receive another on this connection.
|
|
||||||
*/
|
|
||||||
mg_http_conn_destructor(pd);
|
|
||||||
nc->proto_data = NULL;
|
|
||||||
if (io->len > 0) {
|
|
||||||
/* We already have data for the next one, restart parsing. */
|
|
||||||
pd = mg_http_get_proto_data(nc);
|
|
||||||
pd->rcvd = io->len;
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -983,8 +1022,9 @@ exit_mp:
|
|||||||
|
|
||||||
#define CONTENT_DISPOSITION "Content-Disposition: "
|
#define CONTENT_DISPOSITION "Content-Disposition: "
|
||||||
|
|
||||||
static void mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
static size_t mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
||||||
const char *data, size_t data_len) {
|
const char *data,
|
||||||
|
size_t data_len) {
|
||||||
struct mg_http_multipart_part mp;
|
struct mg_http_multipart_part mp;
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
||||||
memset(&mp, 0, sizeof(mp));
|
memset(&mp, 0, sizeof(mp));
|
||||||
@@ -994,8 +1034,11 @@ static void mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
|||||||
mp.user_data = pd->mp_stream.user_data;
|
mp.user_data = pd->mp_stream.user_data;
|
||||||
mp.data.p = data;
|
mp.data.p = data;
|
||||||
mp.data.len = data_len;
|
mp.data.len = data_len;
|
||||||
|
mp.num_data_consumed = data_len;
|
||||||
mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp);
|
mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp);
|
||||||
pd->mp_stream.user_data = mp.user_data;
|
pd->mp_stream.user_data = mp.user_data;
|
||||||
|
pd->mp_stream.data_avail = (mp.num_data_consumed != data_len);
|
||||||
|
return mp.num_data_consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mg_http_multipart_finalize(struct mg_connection *c) {
|
static int mg_http_multipart_finalize(struct mg_connection *c) {
|
||||||
@@ -1133,19 +1176,25 @@ static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) {
|
|||||||
|
|
||||||
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
||||||
if (boundary == NULL) {
|
if (boundary == NULL) {
|
||||||
int data_size = (io->len - (pd->mp_stream.boundary_len + 6));
|
int data_len = (io->len - (pd->mp_stream.boundary_len + 6));
|
||||||
if (data_size > 0) {
|
if (data_len > 0) {
|
||||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf,
|
size_t consumed = mg_http_multipart_call_handler(
|
||||||
data_size);
|
c, MG_EV_HTTP_PART_DATA, io->buf, (size_t) data_len);
|
||||||
mbuf_remove(io, data_size);
|
mbuf_remove(io, consumed);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} else if (boundary != NULL) {
|
} else if (boundary != NULL) {
|
||||||
int data_size = (boundary - io->buf - 4);
|
size_t data_len = ((size_t)(boundary - io->buf) - 4);
|
||||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, data_size);
|
size_t consumed = mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA,
|
||||||
mbuf_remove(io, (boundary - io->buf));
|
io->buf, data_len);
|
||||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
mbuf_remove(io, consumed);
|
||||||
return 1;
|
if (consumed == data_len) {
|
||||||
|
mbuf_remove(io, 4);
|
||||||
|
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1425,12 +1474,15 @@ static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
|
void mg_http_serve_file_internal(struct mg_connection *nc,
|
||||||
const char *path, const struct mg_str mime_type,
|
struct http_message *hm, const char *path,
|
||||||
const struct mg_str extra_headers) {
|
struct mg_str mime_type,
|
||||||
|
struct mg_str encoding,
|
||||||
|
struct mg_str extra_headers) {
|
||||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
|
||||||
cs_stat_t st;
|
cs_stat_t st;
|
||||||
LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p));
|
LOG(LL_DEBUG, ("%p [%s] %.*s %.*s", nc, path, (int) mime_type.len,
|
||||||
|
mime_type.p, (int) encoding.len, encoding.p));
|
||||||
if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) {
|
if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) {
|
||||||
int code, err = mg_get_errno();
|
int code, err = mg_get_errno();
|
||||||
switch (err) {
|
switch (err) {
|
||||||
@@ -1469,8 +1521,9 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
|
|||||||
} else {
|
} else {
|
||||||
status_code = 206;
|
status_code = 206;
|
||||||
cl = r2 - r1 + 1;
|
cl = r2 - r1 + 1;
|
||||||
snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT
|
snprintf(range, sizeof(range),
|
||||||
"-%" INT64_FMT "/%" INT64_FMT "\r\n",
|
"Content-Range: bytes %" INT64_FMT "-%" INT64_FMT
|
||||||
|
"/%" INT64_FMT "\r\n",
|
||||||
r1, r1 + cl - 1, (int64_t) st.st_size);
|
r1, r1 + cl - 1, (int64_t) st.st_size);
|
||||||
#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \
|
#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \
|
||||||
_XOPEN_SOURCE >= 600
|
_XOPEN_SOURCE >= 600
|
||||||
@@ -1495,13 +1548,6 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
|
|||||||
mg_http_construct_etag(etag, sizeof(etag), &st);
|
mg_http_construct_etag(etag, sizeof(etag), &st);
|
||||||
mg_gmt_time_string(current_time, sizeof(current_time), &t);
|
mg_gmt_time_string(current_time, sizeof(current_time), &t);
|
||||||
mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime);
|
mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime);
|
||||||
/*
|
|
||||||
* Content length casted to size_t because:
|
|
||||||
* 1) that's the maximum buffer size anyway
|
|
||||||
* 2) ESP8266 RTOS SDK newlib vprintf cannot contain a 64bit arg at non-last
|
|
||||||
* position
|
|
||||||
* TODO(mkm): fix ESP8266 RTOS SDK
|
|
||||||
*/
|
|
||||||
mg_send_response_line_s(nc, status_code, extra_headers);
|
mg_send_response_line_s(nc, status_code, extra_headers);
|
||||||
mg_printf(nc,
|
mg_printf(nc,
|
||||||
"Date: %s\r\n"
|
"Date: %s\r\n"
|
||||||
@@ -1511,17 +1557,29 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
|
|||||||
"Connection: %s\r\n"
|
"Connection: %s\r\n"
|
||||||
"Content-Length: %" SIZE_T_FMT
|
"Content-Length: %" SIZE_T_FMT
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"%sEtag: %s\r\n\r\n",
|
"%s"
|
||||||
|
"Etag: %s\r\n",
|
||||||
current_time, last_modified, (int) mime_type.len, mime_type.p,
|
current_time, last_modified, (int) mime_type.len, mime_type.p,
|
||||||
(pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range,
|
(pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range,
|
||||||
etag);
|
etag);
|
||||||
|
if (encoding.len > 0) {
|
||||||
|
mg_printf(nc, "Content-Encoding: %.*s\r\n", (int) encoding.len,
|
||||||
|
encoding.p);
|
||||||
|
}
|
||||||
|
mg_send(nc, "\r\n", 2);
|
||||||
pd->file.cl = cl;
|
pd->file.cl = cl;
|
||||||
pd->file.type = DATA_FILE;
|
pd->file.type = DATA_FILE;
|
||||||
mg_http_transfer_file_data(nc);
|
mg_http_transfer_file_data(nc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
|
||||||
|
const char *path, const struct mg_str mime_type,
|
||||||
|
const struct mg_str extra_headers) {
|
||||||
|
mg_http_serve_file_internal(nc, hm, path, mime_type, mg_mk_str(NULL),
|
||||||
|
extra_headers);
|
||||||
|
}
|
||||||
|
|
||||||
static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
|
static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
|
||||||
struct http_message *hm,
|
struct http_message *hm,
|
||||||
struct mg_serve_http_opts *opts) {
|
struct mg_serve_http_opts *opts) {
|
||||||
@@ -1531,8 +1589,12 @@ static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
mg_http_serve_file(nc, hm, path, mg_get_mime_type(path, "text/plain", opts),
|
struct mg_str type = MG_NULL_STR, encoding = MG_NULL_STR;
|
||||||
mg_mk_str(opts->extra_headers));
|
if (!mg_get_mime_type_encoding(mg_mk_str(path), &type, &encoding, opts)) {
|
||||||
|
type = mg_mk_str("text/plain");
|
||||||
|
}
|
||||||
|
mg_http_serve_file_internal(nc, hm, path, type, encoding,
|
||||||
|
mg_mk_str(opts->extra_headers));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1808,7 +1870,7 @@ void cs_md5(char buf[33], ...) {
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, buf);
|
va_start(ap, buf);
|
||||||
while ((p = va_arg(ap, const unsigned char *) ) != NULL) {
|
while ((p = va_arg(ap, const unsigned char *)) != NULL) {
|
||||||
msgs[num_msgs] = p;
|
msgs[num_msgs] = p;
|
||||||
msg_lens[num_msgs] = va_arg(ap, size_t);
|
msg_lens[num_msgs] = va_arg(ap, size_t);
|
||||||
num_msgs++;
|
num_msgs++;
|
||||||
@@ -1923,7 +1985,7 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
|||||||
struct mg_str nc, struct mg_str nonce,
|
struct mg_str nc, struct mg_str nonce,
|
||||||
struct mg_str auth_domain, FILE *fp) {
|
struct mg_str auth_domain, FILE *fp) {
|
||||||
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
|
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
|
||||||
char expected_response[33];
|
char exp_resp[33];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read passwords file line by line. If should have htdigest format,
|
* Read passwords file line by line. If should have htdigest format,
|
||||||
@@ -1937,11 +1999,10 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
|||||||
/* Username and domain matched, check the password */
|
/* Username and domain matched, check the password */
|
||||||
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
||||||
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||||
qop.p, qop.len, expected_response);
|
qop.p, qop.len, exp_resp);
|
||||||
LOG(LL_DEBUG,
|
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
||||||
("%.*s %s %.*s %s", (int) username.len, username.p, f_domain,
|
f_domain, (int) response.len, response.p, exp_resp));
|
||||||
(int) response.len, response.p, expected_response));
|
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
|
||||||
return mg_ncasecmp(response.p, expected_response, response.len) == 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2041,7 +2102,7 @@ static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name,
|
|||||||
href = mg_url_encode(mg_mk_str(file_name));
|
href = mg_url_encode(mg_mk_str(file_name));
|
||||||
mg_printf_http_chunk(nc,
|
mg_printf_http_chunk(nc,
|
||||||
"<tr><td><a href=\"%s%s\">%s%s</a></td>"
|
"<tr><td><a href=\"%s%s\">%s%s</a></td>"
|
||||||
"<td>%s</td><td name=%" INT64_FMT ">%s</td></tr>\n",
|
"<td>%s</td><td name=\"%" INT64_FMT "\">%s</td></tr>",
|
||||||
href.p, slash, path, slash, mod, is_dir ? -1 : fsize,
|
href.p, slash, path, slash, mod, is_dir ? -1 : fsize,
|
||||||
size);
|
size);
|
||||||
free((void *) href.p);
|
free((void *) href.p);
|
||||||
@@ -2107,23 +2168,24 @@ static void mg_send_directory_listing(struct mg_connection *nc, const char *dir,
|
|||||||
|
|
||||||
mg_printf_http_chunk(
|
mg_printf_http_chunk(
|
||||||
nc,
|
nc,
|
||||||
"<html><head><title>Index of %.*s</title>%s%s"
|
"<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
|
||||||
"<style>th,td {text-align: left; padding-right: 1em; "
|
"<style>th,td {text-align: left; padding-right: 1em; "
|
||||||
"font-family: monospace; }</style></head>\n"
|
"font-family: monospace; }</style></head>"
|
||||||
"<body><h1>Index of %.*s</h1>\n<table cellpadding=0><thead>"
|
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
|
||||||
"<tr><th><a href=# rel=0>Name</a></th><th>"
|
"<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
|
||||||
"<a href=# rel=1>Modified</a</th>"
|
"<a href=\"#\" rel=\"1\">Modified</a></th>"
|
||||||
"<th><a href=# rel=2>Size</a></th></tr>"
|
"<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
|
||||||
"<tr><td colspan=3><hr></td></tr>\n"
|
"<tr><td colspan=\"3\"><hr></td></tr>"
|
||||||
"</thead>\n"
|
"</thead>"
|
||||||
"<tbody id=tb>",
|
"<tbody id=\"tb\">",
|
||||||
(int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2,
|
(int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2,
|
||||||
(int) hm->uri.len, hm->uri.p);
|
(int) hm->uri.len, hm->uri.p);
|
||||||
mg_scan_directory(nc, dir, opts, mg_print_dir_entry);
|
mg_scan_directory(nc, dir, opts, mg_print_dir_entry);
|
||||||
mg_printf_http_chunk(nc,
|
mg_printf_http_chunk(nc,
|
||||||
"</tbody><tr><td colspan=3><hr></td></tr>\n"
|
"</tbody>"
|
||||||
"</table>\n"
|
"<tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
|
||||||
"<address>%s</address>\n"
|
"</table>"
|
||||||
|
"<address>%s</address>"
|
||||||
"</body></html>",
|
"</body></html>",
|
||||||
mg_version_header);
|
mg_version_header);
|
||||||
mg_send_http_chunk(nc, "", 0);
|
mg_send_http_chunk(nc, "", 0);
|
||||||
@@ -2440,13 +2502,13 @@ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
|
|||||||
*p++ = DIRSEP;
|
*p++ = DIRSEP;
|
||||||
/* No NULs and DIRSEPs in the component (percent-encoded). */
|
/* No NULs and DIRSEPs in the component (percent-encoded). */
|
||||||
for (i = 0; i < component.len; i++, p++) {
|
for (i = 0; i < component.len; i++, p++) {
|
||||||
if (*p == '\0' || *p == DIRSEP
|
if (*p == '\0' ||
|
||||||
|
*p == DIRSEP
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
/* On Windows, "/" is also accepted, so check for that too. */
|
/* On Windows, "/" is also accepted, so check for that too. */
|
||||||
||
|
|| *p == '/'
|
||||||
*p == '/'
|
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
ok = 0;
|
ok = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2536,9 +2598,11 @@ void mg_http_send_digest_auth_request(struct mg_connection *c,
|
|||||||
domain, (unsigned long) mg_time());
|
domain, (unsigned long) mg_time());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_http_send_options(struct mg_connection *nc) {
|
static void mg_http_send_options(struct mg_connection *nc,
|
||||||
|
struct mg_serve_http_opts *opts) {
|
||||||
|
mg_send_response_line(nc, 200, opts->extra_headers);
|
||||||
mg_printf(nc, "%s",
|
mg_printf(nc, "%s",
|
||||||
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS"
|
"Allow: GET, POST, HEAD, CONNECT, OPTIONS"
|
||||||
#if MG_ENABLE_HTTP_WEBDAV
|
#if MG_ENABLE_HTTP_WEBDAV
|
||||||
", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2"
|
", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2"
|
||||||
#endif
|
#endif
|
||||||
@@ -2645,7 +2709,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
|||||||
#endif
|
#endif
|
||||||
#endif /* MG_ENABLE_HTTP_WEBDAV */
|
#endif /* MG_ENABLE_HTTP_WEBDAV */
|
||||||
} else if (!mg_vcmp(&hm->method, "OPTIONS")) {
|
} else if (!mg_vcmp(&hm->method, "OPTIONS")) {
|
||||||
mg_http_send_options(nc);
|
mg_http_send_options(nc, opts);
|
||||||
} else if (is_directory && index_file == NULL) {
|
} else if (is_directory && index_file == NULL) {
|
||||||
#if MG_ENABLE_DIRECTORY_LISTING
|
#if MG_ENABLE_DIRECTORY_LISTING
|
||||||
if (strcmp(opts->enable_directory_listing, "yes") == 0) {
|
if (strcmp(opts->enable_directory_listing, "yes") == 0) {
|
||||||
@@ -2657,7 +2721,9 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
|||||||
mg_http_send_error(nc, 501, NULL);
|
mg_http_send_error(nc, 501, NULL);
|
||||||
#endif
|
#endif
|
||||||
} else if (mg_is_not_modified(hm, &st)) {
|
} else if (mg_is_not_modified(hm, &st)) {
|
||||||
mg_http_send_error(nc, 304, "Not Modified");
|
/* Note: not using mg_http_send_error in order to keep connection alive */
|
||||||
|
/* Note: passing extra headers allow users to control session cookies */
|
||||||
|
mg_send_head(nc, 304, 0, opts->extra_headers);
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts);
|
mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts);
|
||||||
}
|
}
|
||||||
@@ -2790,7 +2856,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
|
|||||||
#ifdef SPIFFS_ERR_FULL
|
#ifdef SPIFFS_ERR_FULL
|
||||||
|| mg_get_errno() == SPIFFS_ERR_FULL
|
|| mg_get_errno() == SPIFFS_ERR_FULL
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
mg_printf(nc,
|
mg_printf(nc,
|
||||||
"HTTP/1.1 413 Payload Too Large\r\n"
|
"HTTP/1.1 413 Payload Too Large\r\n"
|
||||||
"Content-Type: text/plain\r\n"
|
"Content-Type: text/plain\r\n"
|
||||||
@@ -2949,8 +3015,9 @@ struct mg_connection *mg_connect_http_opt(
|
|||||||
if (path.len == 0) path = mg_mk_str("/");
|
if (path.len == 0) path = mg_mk_str("/");
|
||||||
if (host.len == 0) host = mg_mk_str("");
|
if (host.len == 0) host = mg_mk_str("");
|
||||||
|
|
||||||
mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT
|
mg_printf(nc,
|
||||||
"\r\n%.*s%s\r\n%s",
|
"%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT
|
||||||
|
"\r\n%.*s%s\r\n%s",
|
||||||
(post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p,
|
(post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p,
|
||||||
(int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len,
|
(int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len,
|
||||||
(auth.buf == NULL ? "" : auth.buf), extra_headers, post_data);
|
(auth.buf == NULL ? "" : auth.buf), extra_headers, post_data);
|
||||||
@@ -3036,6 +3103,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
|||||||
if (new_ep == NULL) return;
|
if (new_ep == NULL) return;
|
||||||
|
|
||||||
pd = mg_http_get_proto_data(nc);
|
pd = mg_http_get_proto_data(nc);
|
||||||
|
if (pd == NULL) pd = mg_http_create_proto_data(nc);
|
||||||
new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path));
|
new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path));
|
||||||
if (opts.auth_domain != NULL && opts.auth_file != NULL) {
|
if (opts.auth_domain != NULL && opts.auth_file != NULL) {
|
||||||
new_ep->auth_domain = strdup(opts.auth_domain);
|
new_ep->auth_domain = strdup(opts.auth_domain);
|
||||||
@@ -3058,7 +3126,7 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
|
|||||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||||
|| ev == MG_EV_HTTP_MULTIPART_REQUEST
|
|| ev == MG_EV_HTTP_MULTIPART_REQUEST
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
struct mg_http_endpoint *ep =
|
struct mg_http_endpoint *ep =
|
||||||
mg_http_get_endpoint_handler(nc->listener, &hm->uri);
|
mg_http_get_endpoint_handler(nc->listener, &hm->uri);
|
||||||
if (ep != NULL) {
|
if (ep != NULL) {
|
||||||
|
|||||||
+16
-3
@@ -12,8 +12,8 @@
|
|||||||
|
|
||||||
#if MG_ENABLE_HTTP
|
#if MG_ENABLE_HTTP
|
||||||
|
|
||||||
#include "mg_net.h"
|
|
||||||
#include "common/mg_str.h"
|
#include "common/mg_str.h"
|
||||||
|
#include "mg_net.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -80,6 +80,14 @@ struct mg_http_multipart_part {
|
|||||||
struct mg_str data;
|
struct mg_str data;
|
||||||
int status; /* <0 on error */
|
int status; /* <0 on error */
|
||||||
void *user_data;
|
void *user_data;
|
||||||
|
/*
|
||||||
|
* User handler can indicate how much of the data was consumed
|
||||||
|
* by setting this variable. By default, it is assumed that all
|
||||||
|
* data has been consumed by the handler.
|
||||||
|
* If not all data was consumed, user's handler will be invoked again later
|
||||||
|
* with the remainder.
|
||||||
|
*/
|
||||||
|
size_t num_data_consumed;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* SSI call context */
|
/* SSI call context */
|
||||||
@@ -98,7 +106,7 @@ struct mg_ssi_call_ctx {
|
|||||||
|
|
||||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||||
#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */
|
#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */
|
||||||
#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */
|
#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* struct http_message * */
|
||||||
#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */
|
#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */
|
||||||
#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */
|
#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */
|
||||||
#endif
|
#endif
|
||||||
@@ -112,6 +120,9 @@ struct mg_ssi_call_ctx {
|
|||||||
#define MG_EV_HTTP_MULTIPART_REQUEST_END 125
|
#define MG_EV_HTTP_MULTIPART_REQUEST_END 125
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MG_F_WEBSOCKET_NO_DEFRAG MG_F_PROTO_1
|
||||||
|
#define MG_F_DELETE_CHUNK MG_F_PROTO_2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attaches a built-in HTTP event handler to the given connection.
|
* Attaches a built-in HTTP event handler to the given connection.
|
||||||
* The user-defined event handler will receive following extra events:
|
* The user-defined event handler will receive following extra events:
|
||||||
@@ -137,7 +148,9 @@ struct mg_ssi_call_ctx {
|
|||||||
* - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
|
* - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
|
||||||
* handshake request. `ev_data` contains parsed HTTP request.
|
* handshake request. `ev_data` contains parsed HTTP request.
|
||||||
* - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
|
* - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
|
||||||
* handshake. `ev_data` is `NULL`.
|
* handshake. `ev_data` is a `struct http_message` containing the
|
||||||
|
* client's request (server mode) or server's response (client).
|
||||||
|
* In client mode handler can examine `resp_code`, which should be 101.
|
||||||
* - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
|
* - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
|
||||||
* `struct websocket_message *`
|
* `struct websocket_message *`
|
||||||
*
|
*
|
||||||
|
|||||||
+11
-3
@@ -170,7 +170,7 @@ MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
|
|||||||
const char *path,
|
const char *path,
|
||||||
const struct mg_serve_http_opts *opts) {
|
const struct mg_serve_http_opts *opts) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
struct mg_str mime_type;
|
struct mg_str mime_type = MG_NULL_STR, encoding = MG_NULL_STR;
|
||||||
DBG(("%p %s", nc, path));
|
DBG(("%p %s", nc, path));
|
||||||
|
|
||||||
if ((fp = mg_fopen(path, "rb")) == NULL) {
|
if ((fp = mg_fopen(path, "rb")) == NULL) {
|
||||||
@@ -178,12 +178,20 @@ MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
|
|||||||
} else {
|
} else {
|
||||||
mg_set_close_on_exec((sock_t) fileno(fp));
|
mg_set_close_on_exec((sock_t) fileno(fp));
|
||||||
|
|
||||||
mime_type = mg_get_mime_type(path, "text/plain", opts);
|
if (!mg_get_mime_type_encoding(mg_mk_str(path), &mime_type, &encoding,
|
||||||
|
opts)) {
|
||||||
|
mime_type = mg_mk_str("text/plain");
|
||||||
|
}
|
||||||
mg_send_response_line(nc, 200, opts->extra_headers);
|
mg_send_response_line(nc, 200, opts->extra_headers);
|
||||||
mg_printf(nc,
|
mg_printf(nc,
|
||||||
"Content-Type: %.*s\r\n"
|
"Content-Type: %.*s\r\n"
|
||||||
"Connection: close\r\n\r\n",
|
"Connection: close\r\n",
|
||||||
(int) mime_type.len, mime_type.p);
|
(int) mime_type.len, mime_type.p);
|
||||||
|
if (encoding.len > 0) {
|
||||||
|
mg_printf(nc, "Content-Encoding: %.*s\r\n", (int) encoding.len,
|
||||||
|
encoding.p);
|
||||||
|
}
|
||||||
|
mg_send(nc, "\r\n", 2);
|
||||||
mg_send_ssi_file(nc, hm, path, fp, 0, opts);
|
mg_send_ssi_file(nc, hm, path, fp, 0, opts);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
|||||||
@@ -478,6 +478,8 @@ void mg_send_websocket_handshake3v(struct mg_connection *nc,
|
|||||||
}
|
}
|
||||||
mg_printf(nc, "\r\n");
|
mg_printf(nc, "\r\n");
|
||||||
|
|
||||||
|
nc->flags |= MG_F_IS_WEBSOCKET;
|
||||||
|
|
||||||
mbuf_free(&auth);
|
mbuf_free(&auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,9 @@
|
|||||||
#include "mg_http.h"
|
#include "mg_http.h"
|
||||||
#include "mg_net.h"
|
#include "mg_net.h"
|
||||||
|
|
||||||
|
#ifndef MG_CTL_MSG_MESSAGE_SIZE
|
||||||
#define MG_CTL_MSG_MESSAGE_SIZE 8192
|
#define MG_CTL_MSG_MESSAGE_SIZE 8192
|
||||||
|
#endif
|
||||||
|
|
||||||
/* internals that need to be accessible in unit tests */
|
/* internals that need to be accessible in unit tests */
|
||||||
MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc,
|
MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc,
|
||||||
@@ -82,6 +84,9 @@ extern void *(*test_calloc)(size_t count, size_t size);
|
|||||||
#if MG_ENABLE_HTTP
|
#if MG_ENABLE_HTTP
|
||||||
struct mg_serve_http_opts;
|
struct mg_serve_http_opts;
|
||||||
|
|
||||||
|
MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data(
|
||||||
|
struct mg_connection *c);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reassemble the content of the buffer (buf, blen) which should be
|
* Reassemble the content of the buffer (buf, blen) which should be
|
||||||
* in the HTTP chunked encoding, by collapsing data chunks to the
|
* in the HTTP chunked encoding, by collapsing data chunks to the
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2004-2013 Sergey Lyubka
|
||||||
|
* Copyright (c) 2013-2020 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.cesanta.com/license>.
|
||||||
|
*/
|
||||||
@@ -19,6 +19,7 @@ HEADERS = mg_common.h \
|
|||||||
$(COMMON)/platforms/platform_nxp_lpc.h \
|
$(COMMON)/platforms/platform_nxp_lpc.h \
|
||||||
$(COMMON)/platforms/platform_nxp_kinetis.h \
|
$(COMMON)/platforms/platform_nxp_kinetis.h \
|
||||||
$(COMMON)/platforms/platform_pic32.h \
|
$(COMMON)/platforms/platform_pic32.h \
|
||||||
|
$(COMMON)/platforms/platform_rs14100.h \
|
||||||
$(COMMON)/platforms/platform_stm32.h \
|
$(COMMON)/platforms/platform_stm32.h \
|
||||||
$(COMMON)/platforms/lwip/mg_lwip.h \
|
$(COMMON)/platforms/lwip/mg_lwip.h \
|
||||||
$(COMMON)/cs_md5.h \
|
$(COMMON)/cs_md5.h \
|
||||||
|
|||||||
+67
-27
@@ -10,6 +10,8 @@
|
|||||||
#include "mg_internal.h"
|
#include "mg_internal.h"
|
||||||
#include "mg_mqtt.h"
|
#include "mg_mqtt.h"
|
||||||
|
|
||||||
|
#define MG_F_MQTT_PING_PENDING MG_F_PROTO_1
|
||||||
|
|
||||||
static uint16_t getu16(const char *p) {
|
static uint16_t getu16(const char *p) {
|
||||||
const uint8_t *up = (const uint8_t *) p;
|
const uint8_t *up = (const uint8_t *) p;
|
||||||
return (up[0] << 8) + up[1];
|
return (up[0] << 8) + up[1];
|
||||||
@@ -23,8 +25,8 @@ static const char *scanto(const char *p, struct mg_str *s) {
|
|||||||
|
|
||||||
MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
|
MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
|
||||||
uint8_t header;
|
uint8_t header;
|
||||||
size_t len = 0, len_len = 0;
|
uint32_t len, len_len; /* must be 32-bit, see #1055 */
|
||||||
const char *p, *end;
|
const char *p, *end, *eop = &io->buf[io->len];
|
||||||
unsigned char lc = 0;
|
unsigned char lc = 0;
|
||||||
int cmd;
|
int cmd;
|
||||||
|
|
||||||
@@ -35,18 +37,16 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
|
|||||||
/* decode mqtt variable length */
|
/* decode mqtt variable length */
|
||||||
len = len_len = 0;
|
len = len_len = 0;
|
||||||
p = io->buf + 1;
|
p = io->buf + 1;
|
||||||
while ((size_t)(p - io->buf) < io->len) {
|
while (p < eop) {
|
||||||
lc = *((const unsigned char *) p++);
|
lc = *((const unsigned char *) p++);
|
||||||
len += (lc & 0x7f) << 7 * len_len;
|
len += (lc & 0x7f) << 7 * len_len;
|
||||||
len_len++;
|
len_len++;
|
||||||
if (!(lc & 0x80)) break;
|
if (!(lc & 0x80)) break;
|
||||||
if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG;
|
if (len_len > sizeof(len)) return MG_MQTT_ERROR_MALFORMED_MSG;
|
||||||
}
|
}
|
||||||
|
|
||||||
end = p + len;
|
end = p + len;
|
||||||
if (lc & 0x80 || len > (io->len - (p - io->buf))) {
|
if (lc & 0x80 || end > eop) return MG_MQTT_ERROR_INCOMPLETE_MSG;
|
||||||
return MG_MQTT_ERROR_INCOMPLETE_MSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
mm->cmd = cmd;
|
mm->cmd = cmd;
|
||||||
mm->qos = MG_MQTT_GET_QOS(header);
|
mm->qos = MG_MQTT_GET_QOS(header);
|
||||||
@@ -100,7 +100,9 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
|
|||||||
case MG_MQTT_CMD_PUBREL:
|
case MG_MQTT_CMD_PUBREL:
|
||||||
case MG_MQTT_CMD_PUBCOMP:
|
case MG_MQTT_CMD_PUBCOMP:
|
||||||
case MG_MQTT_CMD_SUBACK:
|
case MG_MQTT_CMD_SUBACK:
|
||||||
|
if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
|
||||||
mm->message_id = getu16(p);
|
mm->message_id = getu16(p);
|
||||||
|
p += 2;
|
||||||
break;
|
break;
|
||||||
case MG_MQTT_CMD_PUBLISH: {
|
case MG_MQTT_CMD_PUBLISH: {
|
||||||
p = scanto(p, &mm->topic);
|
p = scanto(p, &mm->topic);
|
||||||
@@ -172,6 +174,10 @@ static void mqtt_handler(struct mg_connection *nc, int ev,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (mm.cmd == MG_MQTT_CMD_PINGRESP) {
|
||||||
|
LOG(LL_DEBUG, ("Recv PINGRESP"));
|
||||||
|
nc->flags &= ~MG_F_MQTT_PING_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data));
|
nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data));
|
||||||
mbuf_remove(io, len);
|
mbuf_remove(io, len);
|
||||||
@@ -182,10 +188,26 @@ static void mqtt_handler(struct mg_connection *nc, int ev,
|
|||||||
struct mg_mqtt_proto_data *pd =
|
struct mg_mqtt_proto_data *pd =
|
||||||
(struct mg_mqtt_proto_data *) nc->proto_data;
|
(struct mg_mqtt_proto_data *) nc->proto_data;
|
||||||
double now = mg_time();
|
double now = mg_time();
|
||||||
if (pd->keep_alive > 0 && pd->last_control_time > 0 &&
|
if (pd->keep_alive > 0 && pd->last_control_time > 0) {
|
||||||
(now - pd->last_control_time) > pd->keep_alive) {
|
double diff = (now - pd->last_control_time);
|
||||||
LOG(LL_DEBUG, ("Send PINGREQ"));
|
if (diff > pd->keep_alive) {
|
||||||
mg_mqtt_ping(nc);
|
if (diff < 1500000000) {
|
||||||
|
if (!(nc->flags & MG_F_MQTT_PING_PENDING)) {
|
||||||
|
LOG(LL_DEBUG, ("Send PINGREQ"));
|
||||||
|
nc->flags |= MG_F_MQTT_PING_PENDING;
|
||||||
|
mg_mqtt_ping(nc);
|
||||||
|
} else {
|
||||||
|
LOG(LL_DEBUG, ("Ping timeout"));
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Wall time has just been set. Avoid immediate ping,
|
||||||
|
* more likely than not it is not needed. The standard allows for
|
||||||
|
* 1.5X interval for ping requests, so even if were just about to
|
||||||
|
* send one, we should be ok waiting 0.4X more. */
|
||||||
|
pd->last_control_time = now - pd->keep_alive * 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -196,26 +218,44 @@ static void mg_mqtt_proto_data_destructor(void *proto_data) {
|
|||||||
MG_FREE(proto_data);
|
MG_FREE(proto_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct mg_str mg_mqtt_next_topic_component(struct mg_str *topic) {
|
||||||
|
struct mg_str res = *topic;
|
||||||
|
const char *c = mg_strchr(*topic, '/');
|
||||||
|
if (c != NULL) {
|
||||||
|
res.len = (c - topic->p);
|
||||||
|
topic->len -= (res.len + 1);
|
||||||
|
topic->p += (res.len + 1);
|
||||||
|
} else {
|
||||||
|
topic->len = 0;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Refernce: https://mosquitto.org/man/mqtt-7.html */
|
||||||
int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) {
|
int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) {
|
||||||
/* TODO(mkm): implement real matching */
|
struct mg_str ec, tc;
|
||||||
if (memchr(exp.p, '#', exp.len)) {
|
if (exp.len == 0) return 0;
|
||||||
/* exp `foo/#` will become `foo/` */
|
while (1) {
|
||||||
exp.len -= 1;
|
ec = mg_mqtt_next_topic_component(&exp);
|
||||||
/*
|
tc = mg_mqtt_next_topic_component(&topic);
|
||||||
* topic should be longer than the expression: e.g. topic `foo/bar` does
|
if (ec.len == 0) {
|
||||||
* match `foo/#`, but neither `foo` nor `foo/` do.
|
if (tc.len != 0) return 0;
|
||||||
*/
|
if (exp.len == 0) break;
|
||||||
if (topic.len <= exp.len) {
|
continue;
|
||||||
|
}
|
||||||
|
if (mg_vcmp(&ec, "+") == 0) {
|
||||||
|
if (tc.len == 0 && topic.len == 0) return 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mg_vcmp(&ec, "#") == 0) {
|
||||||
|
/* Must be the last component in the expression or it's invalid. */
|
||||||
|
return (exp.len == 0);
|
||||||
|
}
|
||||||
|
if (mg_strcmp(ec, tc) != 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Truncate topic so that it'll pass the next length check */
|
|
||||||
topic.len = exp.len;
|
|
||||||
}
|
}
|
||||||
if (topic.len != exp.len) {
|
return (tc.len == 0 && topic.len == 0);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return strncmp(topic.p, exp.p, exp.len) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) {
|
int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) {
|
||||||
|
|||||||
+1
-1
@@ -101,10 +101,10 @@ struct mg_mqtt_proto_data {
|
|||||||
|
|
||||||
/* Message flags */
|
/* Message flags */
|
||||||
#define MG_MQTT_RETAIN 0x1
|
#define MG_MQTT_RETAIN 0x1
|
||||||
#define MG_MQTT_DUP 0x4
|
|
||||||
#define MG_MQTT_QOS(qos) ((qos) << 1)
|
#define MG_MQTT_QOS(qos) ((qos) << 1)
|
||||||
#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
|
#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
|
||||||
#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
|
#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
|
||||||
|
#define MG_MQTT_DUP 0x8
|
||||||
|
|
||||||
/* Connection flags */
|
/* Connection flags */
|
||||||
#define MG_MQTT_CLEAN_SESSION 0x02
|
#define MG_MQTT_CLEAN_SESSION 0x02
|
||||||
|
|||||||
+54
-22
@@ -128,8 +128,16 @@ MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) {
|
|||||||
static int mg_do_recv(struct mg_connection *nc);
|
static int mg_do_recv(struct mg_connection *nc);
|
||||||
|
|
||||||
int mg_if_poll(struct mg_connection *nc, double now) {
|
int mg_if_poll(struct mg_connection *nc, double now) {
|
||||||
if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
|
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) {
|
||||||
(nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) {
|
mg_close_conn(nc);
|
||||||
|
return 0;
|
||||||
|
} else if (nc->flags & MG_F_SEND_AND_CLOSE) {
|
||||||
|
if (nc->send_mbuf.len == 0) {
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
mg_close_conn(nc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else if (nc->flags & MG_F_RECV_AND_CLOSE) {
|
||||||
mg_close_conn(nc);
|
mg_close_conn(nc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -172,6 +180,13 @@ void mg_destroy_conn(struct mg_connection *conn, int destroy_if) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_close_conn(struct mg_connection *conn) {
|
void mg_close_conn(struct mg_connection *conn) {
|
||||||
|
/* See if there's any remaining data to deliver. Skip if user completely
|
||||||
|
* throttled the connection there will be no progress anyway. */
|
||||||
|
if (conn->sock != INVALID_SOCKET && mg_do_recv(conn) == -2) {
|
||||||
|
/* Receive is throttled, wait. */
|
||||||
|
conn->flags |= MG_F_RECV_AND_CLOSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
#if MG_ENABLE_SSL
|
#if MG_ENABLE_SSL
|
||||||
if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) {
|
if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) {
|
||||||
mg_ssl_if_conn_close_notify(conn);
|
mg_ssl_if_conn_close_notify(conn);
|
||||||
@@ -262,6 +277,7 @@ void mg_mgr_free(struct mg_mgr *m) {
|
|||||||
|
|
||||||
for (conn = m->active_connections; conn != NULL; conn = tmp_conn) {
|
for (conn = m->active_connections; conn != NULL; conn = tmp_conn) {
|
||||||
tmp_conn = conn->next;
|
tmp_conn = conn->next;
|
||||||
|
conn->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
mg_close_conn(conn);
|
mg_close_conn(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,8 +341,8 @@ static int mg_resolve2(const char *host, struct in_addr *ina) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||||
memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *));
|
memcpy(&h, &p->ai_addr, sizeof(h));
|
||||||
memcpy(ina, &h->sin_addr, sizeof(ina));
|
memcpy(ina, &h->sin_addr, sizeof(*ina));
|
||||||
}
|
}
|
||||||
freeaddrinfo(servinfo);
|
freeaddrinfo(servinfo);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -524,7 +540,7 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) {
|
|||||||
nc->iface = lc->iface;
|
nc->iface = lc->iface;
|
||||||
if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
|
if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
|
||||||
mg_add_conn(nc->mgr, nc);
|
mg_add_conn(nc->mgr, nc);
|
||||||
LOG(LL_DEBUG, ("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags));
|
LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
|
||||||
return nc;
|
return nc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,18 +581,23 @@ static int mg_do_recv(struct mg_connection *nc) {
|
|||||||
((nc->flags & MG_F_LISTENING) && !(nc->flags & MG_F_UDP))) {
|
((nc->flags & MG_F_LISTENING) && !(nc->flags & MG_F_UDP))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
len = recv_avail_size(nc, len);
|
do {
|
||||||
if (len == 0) return -2;
|
len = recv_avail_size(nc, len);
|
||||||
if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) {
|
if (len == 0) {
|
||||||
mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len);
|
res = -2;
|
||||||
}
|
break;
|
||||||
buf = nc->recv_mbuf.buf + nc->recv_mbuf.len;
|
}
|
||||||
len = nc->recv_mbuf.size - nc->recv_mbuf.len;
|
if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) {
|
||||||
if (nc->flags & MG_F_UDP) {
|
mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len);
|
||||||
res = mg_recv_udp(nc, buf, len);
|
}
|
||||||
} else {
|
buf = nc->recv_mbuf.buf + nc->recv_mbuf.len;
|
||||||
res = mg_recv_tcp(nc, buf, len);
|
len = nc->recv_mbuf.size - nc->recv_mbuf.len;
|
||||||
}
|
if (nc->flags & MG_F_UDP) {
|
||||||
|
res = mg_recv_udp(nc, buf, len);
|
||||||
|
} else {
|
||||||
|
res = mg_recv_tcp(nc, buf, len);
|
||||||
|
}
|
||||||
|
} while (res > 0 && !(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_UDP)));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,7 +714,9 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) {
|
|||||||
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
if (n != 0) {
|
||||||
|
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@@ -854,6 +877,16 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
|
|||||||
return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts);
|
return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mg_ev_handler_empty(struct mg_connection *c, int ev,
|
||||||
|
void *ev_data MG_UD_ARG(void *user_data)) {
|
||||||
|
(void) c;
|
||||||
|
(void) ev;
|
||||||
|
(void) ev_data;
|
||||||
|
#if MG_ENABLE_CALLBACK_USERDATA
|
||||||
|
(void) user_data;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
|
struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
|
||||||
MG_CB(mg_event_handler_t callback,
|
MG_CB(mg_event_handler_t callback,
|
||||||
void *user_data),
|
void *user_data),
|
||||||
@@ -865,6 +898,8 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
|
|||||||
|
|
||||||
MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
|
MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
|
||||||
|
|
||||||
|
if (callback == NULL) callback = mg_ev_handler_empty;
|
||||||
|
|
||||||
if ((nc = mg_create_connection(mgr, callback, add_sock_opts)) == NULL) {
|
if ((nc = mg_create_connection(mgr, callback, add_sock_opts)) == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -978,10 +1013,7 @@ struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
|
|||||||
opts.user_data = user_data;
|
opts.user_data = user_data;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (callback == NULL) {
|
if (callback == NULL) callback = mg_ev_handler_empty;
|
||||||
MG_SET_PTRPTR(opts.error_string, "handler is required");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
|
MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
|
||||||
|
|
||||||
|
|||||||
+14
-7
@@ -110,9 +110,6 @@ struct mg_connection {
|
|||||||
struct mbuf send_mbuf; /* Data scheduled for sending */
|
struct mbuf send_mbuf; /* Data scheduled for sending */
|
||||||
time_t last_io_time; /* Timestamp of the last socket IO */
|
time_t last_io_time; /* Timestamp of the last socket IO */
|
||||||
double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */
|
double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */
|
||||||
#if MG_ENABLE_SSL
|
|
||||||
void *ssl_if_data; /* SSL library data. */
|
|
||||||
#endif
|
|
||||||
mg_event_handler_t proto_handler; /* Protocol-specific event handler */
|
mg_event_handler_t proto_handler; /* Protocol-specific event handler */
|
||||||
void *proto_data; /* Protocol-specific data */
|
void *proto_data; /* Protocol-specific data */
|
||||||
void (*proto_data_destructor)(void *proto_data);
|
void (*proto_data_destructor)(void *proto_data);
|
||||||
@@ -140,20 +137,30 @@ struct mg_connection {
|
|||||||
#define MG_F_WANT_READ (1 << 6) /* SSL specific */
|
#define MG_F_WANT_READ (1 << 6) /* SSL specific */
|
||||||
#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */
|
#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */
|
||||||
#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
|
#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
|
||||||
|
#define MG_F_RECV_AND_CLOSE (1 << 9) /* Drain rx and close the connection. */
|
||||||
|
|
||||||
/* Flags that are settable by user */
|
/* Flags that are settable by user */
|
||||||
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
||||||
#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
|
#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
|
||||||
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
|
|
||||||
#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */
|
/* Flags for protocol handlers */
|
||||||
|
#define MG_F_PROTO_1 (1 << 12)
|
||||||
|
#define MG_F_PROTO_2 (1 << 13)
|
||||||
#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
|
#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
|
||||||
|
|
||||||
#define MG_F_USER_1 (1 << 20) /* Flags left for application */
|
/* Flags left for application */
|
||||||
|
#define MG_F_USER_1 (1 << 20)
|
||||||
#define MG_F_USER_2 (1 << 21)
|
#define MG_F_USER_2 (1 << 21)
|
||||||
#define MG_F_USER_3 (1 << 22)
|
#define MG_F_USER_3 (1 << 22)
|
||||||
#define MG_F_USER_4 (1 << 23)
|
#define MG_F_USER_4 (1 << 23)
|
||||||
#define MG_F_USER_5 (1 << 24)
|
#define MG_F_USER_5 (1 << 24)
|
||||||
#define MG_F_USER_6 (1 << 25)
|
#define MG_F_USER_6 (1 << 25)
|
||||||
|
|
||||||
|
#if MG_ENABLE_SSL
|
||||||
|
void *ssl_if_data; /* SSL library data. */
|
||||||
|
#else
|
||||||
|
void *unused_ssl_if_data; /* To keep the size of the structure the same. */
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -225,7 +232,7 @@ int mg_mgr_poll(struct mg_mgr *mgr, int milli);
|
|||||||
* `func` callback function will be called by the IO thread for each
|
* `func` callback function will be called by the IO thread for each
|
||||||
* connection. When called, the event will be `MG_EV_POLL`, and a message will
|
* connection. When called, the event will be `MG_EV_POLL`, and a message will
|
||||||
* be passed as the `ev_data` pointer. Maximum message size is capped
|
* be passed as the `ev_data` pointer. Maximum message size is capped
|
||||||
* by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
|
* by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes by default.
|
||||||
*/
|
*/
|
||||||
void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
|
void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
|
||||||
size_t len);
|
size_t len);
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ void mg_socket_if_connect_tcp(struct mg_connection *nc,
|
|||||||
#endif
|
#endif
|
||||||
rc = connect(nc->sock, &sa->sa, sizeof(sa->sin));
|
rc = connect(nc->sock, &sa->sa, sizeof(sa->sin));
|
||||||
nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0;
|
nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0;
|
||||||
DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(),
|
DBG(("%p sock %d rc %d errno %d err %d", nc, (int) nc->sock, rc,
|
||||||
nc->err));
|
mg_get_errno(), nc->err));
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_socket_if_connect_udp(struct mg_connection *nc) {
|
void mg_socket_if_connect_udp(struct mg_connection *nc) {
|
||||||
@@ -218,8 +218,8 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
|
|||||||
int worth_logging =
|
int worth_logging =
|
||||||
fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE));
|
fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE));
|
||||||
if (worth_logging) {
|
if (worth_logging) {
|
||||||
DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock,
|
DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc,
|
||||||
fd_flags, nc->flags, (int) nc->recv_mbuf.len,
|
(int) nc->sock, fd_flags, nc->flags, (int) nc->recv_mbuf.len,
|
||||||
(int) nc->send_mbuf.len));
|
(int) nc->send_mbuf.len));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
|
|||||||
if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc);
|
if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc);
|
||||||
|
|
||||||
if (worth_logging) {
|
if (worth_logging) {
|
||||||
DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock,
|
DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, (int) nc->sock,
|
||||||
nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
|
nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,8 +279,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
|
|||||||
#if MG_ENABLE_BROADCAST
|
#if MG_ENABLE_BROADCAST
|
||||||
static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) {
|
static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) {
|
||||||
struct ctl_msg ctl_msg;
|
struct ctl_msg ctl_msg;
|
||||||
int len =
|
int len = (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
|
||||||
(int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
|
|
||||||
size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0);
|
size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0);
|
||||||
DBG(("read %d from ctl socket", len));
|
DBG(("read %d from ctl socket", len));
|
||||||
(void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */
|
(void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */
|
||||||
@@ -299,7 +298,7 @@ void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) {
|
|||||||
mg_set_non_blocking_mode(sock);
|
mg_set_non_blocking_mode(sock);
|
||||||
mg_set_close_on_exec(sock);
|
mg_set_close_on_exec(sock);
|
||||||
nc->sock = sock;
|
nc->sock = sock;
|
||||||
DBG(("%p %d", nc, sock));
|
DBG(("%p %d", nc, (int) sock));
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_socket_if_init(struct mg_iface *iface) {
|
void mg_socket_if_init(struct mg_iface *iface) {
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ extern "C" {
|
|||||||
struct mg_ssl_if_ctx;
|
struct mg_ssl_if_ctx;
|
||||||
struct mg_connection;
|
struct mg_connection;
|
||||||
|
|
||||||
void mg_ssl_if_init();
|
void mg_ssl_if_init(void);
|
||||||
|
|
||||||
enum mg_ssl_if_result {
|
enum mg_ssl_if_result {
|
||||||
MG_SSL_OK = 0,
|
MG_SSL_OK = 0,
|
||||||
|
|||||||
+18
-12
@@ -44,7 +44,11 @@ struct mg_ssl_if_ctx {
|
|||||||
mbedtls_ssl_context *ssl;
|
mbedtls_ssl_context *ssl;
|
||||||
mbedtls_x509_crt *cert;
|
mbedtls_x509_crt *cert;
|
||||||
mbedtls_pk_context *key;
|
mbedtls_pk_context *key;
|
||||||
|
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
|
||||||
|
char *ca_chain_file;
|
||||||
|
#else
|
||||||
mbedtls_x509_crt *ca_cert;
|
mbedtls_x509_crt *ca_cert;
|
||||||
|
#endif
|
||||||
struct mbuf cipher_suites;
|
struct mbuf cipher_suites;
|
||||||
size_t saved_len;
|
size_t saved_len;
|
||||||
};
|
};
|
||||||
@@ -52,7 +56,7 @@ struct mg_ssl_if_ctx {
|
|||||||
/* Must be provided by the platform. ctx is struct mg_connection. */
|
/* Must be provided by the platform. ctx is struct mg_connection. */
|
||||||
extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len);
|
extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len);
|
||||||
|
|
||||||
void mg_ssl_if_init() {
|
void mg_ssl_if_init(void) {
|
||||||
LOG(LL_INFO, ("%s", MBEDTLS_VERSION_STRING_FULL));
|
LOG(LL_INFO, ("%s", MBEDTLS_VERSION_STRING_FULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,18 +224,17 @@ static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) {
|
|||||||
MG_FREE(ctx->key);
|
MG_FREE(ctx->key);
|
||||||
ctx->key = NULL;
|
ctx->key = NULL;
|
||||||
}
|
}
|
||||||
|
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
|
||||||
|
MG_FREE(ctx->ca_chain_file);
|
||||||
|
ctx->ca_chain_file = NULL;
|
||||||
|
#else
|
||||||
if (ctx->ca_cert != NULL) {
|
if (ctx->ca_cert != NULL) {
|
||||||
mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL);
|
mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL);
|
||||||
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
|
|
||||||
if (ctx->conf->ca_chain_file != NULL) {
|
|
||||||
MG_FREE((void *) ctx->conf->ca_chain_file);
|
|
||||||
ctx->conf->ca_chain_file = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
mbedtls_x509_crt_free(ctx->ca_cert);
|
mbedtls_x509_crt_free(ctx->ca_cert);
|
||||||
MG_FREE(ctx->ca_cert);
|
MG_FREE(ctx->ca_cert);
|
||||||
ctx->ca_cert = NULL;
|
ctx->ca_cert = NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) {
|
enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) {
|
||||||
@@ -312,11 +315,11 @@ void mg_ssl_if_conn_free(struct mg_connection *nc) {
|
|||||||
mbedtls_ssl_free(ctx->ssl);
|
mbedtls_ssl_free(ctx->ssl);
|
||||||
MG_FREE(ctx->ssl);
|
MG_FREE(ctx->ssl);
|
||||||
}
|
}
|
||||||
mg_ssl_if_mbed_free_certs_and_keys(ctx);
|
|
||||||
if (ctx->conf != NULL) {
|
if (ctx->conf != NULL) {
|
||||||
mbedtls_ssl_config_free(ctx->conf);
|
mbedtls_ssl_config_free(ctx->conf);
|
||||||
MG_FREE(ctx->conf);
|
MG_FREE(ctx->conf);
|
||||||
}
|
}
|
||||||
|
mg_ssl_if_mbed_free_certs_and_keys(ctx);
|
||||||
mbuf_free(&ctx->cipher_suites);
|
mbuf_free(&ctx->cipher_suites);
|
||||||
memset(ctx, 0, sizeof(*ctx));
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
MG_FREE(ctx);
|
MG_FREE(ctx);
|
||||||
@@ -328,12 +331,15 @@ static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx,
|
|||||||
mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
|
mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
|
||||||
return MG_SSL_OK;
|
return MG_SSL_OK;
|
||||||
}
|
}
|
||||||
|
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
|
||||||
|
ctx->ca_chain_file = strdup(ca_cert);
|
||||||
|
if (ctx->ca_chain_file == NULL) return MG_SSL_ERROR;
|
||||||
|
if (mbedtls_ssl_conf_ca_chain_file(ctx->conf, ctx->ca_chain_file, NULL) != 0) {
|
||||||
|
return MG_SSL_ERROR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert));
|
ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert));
|
||||||
mbedtls_x509_crt_init(ctx->ca_cert);
|
mbedtls_x509_crt_init(ctx->ca_cert);
|
||||||
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
|
|
||||||
ca_cert = strdup(ca_cert);
|
|
||||||
mbedtls_ssl_conf_ca_chain_file(ctx->conf, ca_cert, NULL);
|
|
||||||
#else
|
|
||||||
if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) {
|
if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) {
|
||||||
return MG_SSL_ERROR;
|
return MG_SSL_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-4
@@ -10,10 +10,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
#ifndef KR_VERSION
|
#ifndef KR_VERSION
|
||||||
#include <openssl/tls1.h>
|
#include <openssl/tls1.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const char *mg_default_session_id_context = "mongoose";
|
||||||
|
|
||||||
struct mg_ssl_if_ctx {
|
struct mg_ssl_if_ctx {
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
@@ -21,7 +24,7 @@ struct mg_ssl_if_ctx {
|
|||||||
size_t identity_len;
|
size_t identity_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mg_ssl_if_init() {
|
void mg_ssl_if_init(void) {
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +78,9 @@ enum mg_ssl_if_result mg_ssl_if_conn_init(
|
|||||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
|
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
|
||||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3);
|
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3);
|
||||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
|
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
|
||||||
|
SSL_CTX_set_session_id_context(ctx->ssl_ctx,
|
||||||
|
(void *) mg_default_session_id_context,
|
||||||
|
strlen(mg_default_session_id_context));
|
||||||
#ifdef MG_SSL_OPENSSL_NO_COMPRESSION
|
#ifdef MG_SSL_OPENSSL_NO_COMPRESSION
|
||||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
||||||
#endif
|
#endif
|
||||||
@@ -132,6 +138,17 @@ static enum mg_ssl_if_result mg_ssl_if_ssl_err(struct mg_connection *nc,
|
|||||||
int res) {
|
int res) {
|
||||||
struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
|
struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
|
||||||
int err = SSL_get_error(ctx->ssl, res);
|
int err = SSL_get_error(ctx->ssl, res);
|
||||||
|
/*
|
||||||
|
* We've just fetched the last error from the queue.
|
||||||
|
* Now we need to clear the error queue. If we do not, then the following
|
||||||
|
* can happen (actually reported):
|
||||||
|
* - A new connection is accept()-ed with cert error (e.g. self-signed cert)
|
||||||
|
* - Since all accept()-ed connections share listener's context,
|
||||||
|
* - *ALL* SSL accepted connection report read error on the next poll cycle.
|
||||||
|
* Thus a single errored connection can close all the rest, unrelated ones.
|
||||||
|
* Clearing the error keeps the shared SSL_CTX in an OK state.
|
||||||
|
*/
|
||||||
|
ERR_clear_error();
|
||||||
if (err == SSL_ERROR_WANT_READ) return MG_SSL_WANT_READ;
|
if (err == SSL_ERROR_WANT_READ) return MG_SSL_WANT_READ;
|
||||||
if (err == SSL_ERROR_WANT_WRITE) return MG_SSL_WANT_WRITE;
|
if (err == SSL_ERROR_WANT_WRITE) return MG_SSL_WANT_WRITE;
|
||||||
DBG(("%p %p SSL error: %d %d", nc, ctx->ssl_ctx, res, err));
|
DBG(("%p %p SSL error: %d %d", nc, ctx->ssl_ctx, res, err));
|
||||||
@@ -310,7 +327,7 @@ static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) {
|
|||||||
: MG_SSL_ERROR);
|
: MG_SSL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef KR_VERSION
|
#if !defined(KR_VERSION) && !defined(LIBRESSL_VERSION_NUMBER)
|
||||||
static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint,
|
static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint,
|
||||||
char *identity,
|
char *identity,
|
||||||
unsigned int max_identity_len,
|
unsigned int max_identity_len,
|
||||||
@@ -376,10 +393,10 @@ static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx,
|
|||||||
(void) ctx;
|
(void) ctx;
|
||||||
(void) identity;
|
(void) identity;
|
||||||
(void) key_str;
|
(void) key_str;
|
||||||
/* Krypton does not support PSK. */
|
/* Krypton / LibreSSL does not support PSK. */
|
||||||
return MG_SSL_ERROR;
|
return MG_SSL_ERROR;
|
||||||
}
|
}
|
||||||
#endif /* defined(KR_VERSION) */
|
#endif /* !defined(KR_VERSION) && !defined(LIBRESSL_VERSION_NUMBER) */
|
||||||
|
|
||||||
const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
|
const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
|
||||||
const char *ca_cert) {
|
const char *ca_cert) {
|
||||||
|
|||||||
+8
-4
@@ -9,18 +9,18 @@ SRC_DIR = ../src
|
|||||||
COMMON_DIR_PUB = ../src/common
|
COMMON_DIR_PUB = ../src/common
|
||||||
COMMON_DIR_DEV = ../../common
|
COMMON_DIR_DEV = ../../common
|
||||||
|
|
||||||
ifneq ("$(wildcard ../../fw/defs)","")
|
# Our dev repo and public mongoose repo have different layouts, so here
|
||||||
|
# we make it work on both
|
||||||
|
ifneq ("$(wildcard ../../common.mk)","")
|
||||||
COMMON_PARENT = ../..
|
COMMON_PARENT = ../..
|
||||||
else
|
else
|
||||||
COMMON_PARENT = $(SRC_DIR)
|
COMMON_PARENT = $(SRC_DIR)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
TEST_SOURCES = unit_test.c test_util.c test_main.c
|
TEST_SOURCES = unit_test.c test_util.c test_main.c fuzz.c
|
||||||
AMALGAMATED_SOURCES = ../mongoose.c
|
AMALGAMATED_SOURCES = ../mongoose.c
|
||||||
KRYPTON_PATH = $(REPO_ROOT)/krypton
|
KRYPTON_PATH = $(REPO_ROOT)/krypton
|
||||||
|
|
||||||
# Our dev repo and public mongoose repo have different layouts, so here
|
|
||||||
# we make it work on both
|
|
||||||
VPATH = $(MONGOOSE_DIR) $(SRC_DIR) $(COMMON_DIR_PUB) $(COMMON_DIR_DEV) $(COMMON_PARENT)
|
VPATH = $(MONGOOSE_DIR) $(SRC_DIR) $(COMMON_DIR_PUB) $(COMMON_DIR_DEV) $(COMMON_PARENT)
|
||||||
|
|
||||||
# or Krypton, or mbedTLS
|
# or Krypton, or mbedTLS
|
||||||
@@ -118,6 +118,10 @@ clean_index_cgi:
|
|||||||
@echo -e "CLEAN\tindex.cgi"
|
@echo -e "CLEAN\tindex.cgi"
|
||||||
@rm -f data/cgi/index.cgi*
|
@rm -f data/cgi/index.cgi*
|
||||||
|
|
||||||
|
fuzz:
|
||||||
|
$(CC) fuzz.c ../mongoose.c -o /tmp/$@ -fsanitize=fuzzer,address -g -I..
|
||||||
|
/tmp/$@
|
||||||
|
|
||||||
# Interactive:
|
# Interactive:
|
||||||
# docker run -v $(CURDIR)/../..:/cesanta -t -i --entrypoint=/bin/bash cesanta/mongoose_test
|
# docker run -v $(CURDIR)/../..:/cesanta -t -i --entrypoint=/bin/bash cesanta/mongoose_test
|
||||||
docker:
|
docker:
|
||||||
|
|||||||
+41
-47
@@ -1,49 +1,43 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
Certificate:
|
||||||
MIIEpAIBAAKCAQEAv3/TSSi5hZDwMKG43eqe+GzR1lRMXVYt9I1Mr987v1DT99xR
|
Data:
|
||||||
Dcpfo/3aj6B/V/G67oPz+zbVZN/ZPvvA1Z82T7ixcBFsGIXgEWzxUm1UCUf51ftl
|
Version: 3 (0x2)
|
||||||
MlOaf24cdyegi0y8hRdkWLoC7w0vuMfrgR6cmpbI2LSDSMaXXX2qDoofQsFUYaJN
|
Serial Number:
|
||||||
Nn3uqRK0ixs/jzbzbAT9q2BWYwySUX4VEgADpmi0FyANDjEhmdktxQW9l6IGGzF8
|
11:2a:0e:3c:6a:8c:85:ff:6e:6a:bc:db:95:51:70:ce:b4:30:78:c7
|
||||||
M9mA053hIgZwo+9qf9X3nfNUTWMvisMQtxm0mRYgvD53Oix08VLs6bodNTVOLQoc
|
Signature Algorithm: ecdsa-with-SHA256
|
||||||
H0uH3CTs+H3Z0CkcZaAJe/kwCLFhls9ee3M0nQIDAQABAoIBAQCsADPWUi3QOg6C
|
Issuer: C = IE, L = Dublin, O = Cesanta, CN = Test Root
|
||||||
n79cE5AVsigHSlAMxYshTIjErs0LWZ4J0mk66bpdoXTd7Fp0szojYYGS8f1ZTXXj
|
Validity
|
||||||
jFv3g7lUgZ9d+UgN/rDy9dcLIgeJDozoFZUfTthF/LC0lXMtqw7ou8n1p51a+Y0T
|
Not Before: May 9 21:51:44 2020 GMT
|
||||||
ev2cS9J9R1G+0uPYSgdKgcRsqsLJQS4fu5CAk9d0aeTTl009uxcn9yfTUjwOaR5J
|
Not After : May 9 21:51:44 2050 GMT
|
||||||
PuNmunAEvhE/DGSkt5oNXo7t8Q2L3mYSM0MwKdDFqoQdZAV6TMTv22Mjb6SxOOnJ
|
Subject: C = IE, L = Dublin, O = Cesanta, CN = Test Root
|
||||||
r5gNK2BmM6oNPWvzY0PoI0LcLgFNDWIMqIq4mg73MdzszakaNRDlOYtLAuKbTF3Q
|
Subject Public Key Info:
|
||||||
SDq8OkZBAoGBAOn6B5jBxSa+5GcIIeGqtiRhDMExyblt3Gk2gaw6UIZpvVDXPWWm
|
Public Key Algorithm: id-ecPublicKey
|
||||||
r0tkGJrYecmqesN7DGmmdkyx8KONF+yzYLxSsIEGNesvVYe6PXTDZYYI57As4Z4W
|
Public-Key: (256 bit)
|
||||||
DFlCDt2FaKuMXxyOlUCiXg94z8IJBJ2ldCmmG34gBSvuFe6V5x4XE3crAoGBANGG
|
pub:
|
||||||
P7AWw6uygfjog6B2dFT6n+9UhpyJlqwfPi5eD9V5JXtWlH6xWi3dRfuYAIafg95I
|
04:2c:ab:d1:02:66:24:96:d7:12:3e:09:50:4f:f1:
|
||||||
W8/OZGHrj44gNCgYjvZHud+H3NPJPZ7lftoay5KeShBAa/pCd67OMxp1SvvONYcp
|
50:ee:51:e8:55:03:5e:ba:b1:1d:98:b2:72:79:27:
|
||||||
7TSwm5s+hOJvQOpw2wg0cXnfrxGKpGLOFaRddp9XAoGAFdeXefUs2G8dl1i1AQIU
|
a8:1b:31:0d:5d:50:21:ff:42:f2:da:74:17:5e:53:
|
||||||
utSsgiSJtlvBJblG5bMT7VhVqgRN4P1sg9c2TM5EoETf7PvBruMxS/uYgUwcnaYp
|
b2:65:41:c1:fc:84:de:4a:11:b9:8c:f4:19:d9:c4:
|
||||||
M6tser7/rZLfoyoJrqrHAXo3VsT50u4v/O0jwh5AJTOXdW0CFeSSb1NR4cVBvw3B
|
ca:2b:ea:eb:2c
|
||||||
CFpPWrjWgsFZHsqzpqV01b0CgYEAkDft4pDowmgumlvBLlQaotuX9q6hsWHrOjKP
|
ASN1 OID: prime256v1
|
||||||
JG9OSswGhq0DrWj5/5PNNe5cfk2SARChUZpo8hWoTFXSUL8GuHKKeFgWIhjkt1iU
|
NIST CURVE: P-256
|
||||||
RiAne5ZEuIb/S9UweDwqZM3TfRtlMNIlGh1uHh+cbBfUAQsJWM5wRUk4QcTCfdgI
|
X509v3 extensions:
|
||||||
gYhrvCUCgYBB6u8Q49RjrTBxWK8bcZOjVhYNrd3xrCunFVMt2QAXGGrRaXpqUMnp
|
X509v3 Basic Constraints:
|
||||||
xNUmGe9vGux+s0TRguZcLEX3vX+wFyBfFKwZY9hSU7PFY/da8echpu37JasKvAov
|
CA:TRUE
|
||||||
5+5XWI5RgF+SFVk+Q7St2BlSJa/vBAH8vtrX9Dt/hN/VSo2mAPAyMQ==
|
X509v3 Key Usage:
|
||||||
-----END RSA PRIVATE KEY-----
|
Digital Signature, Key Encipherment, Key Agreement, Certificate Sign, CRL Sign
|
||||||
|
Signature Algorithm: ecdsa-with-SHA256
|
||||||
|
30:46:02:21:00:9c:71:6c:00:8c:06:41:0c:91:2f:cd:41:d3:
|
||||||
|
87:47:e9:df:3a:22:ad:25:7c:bf:0e:2b:39:dd:7a:0c:4e:68:
|
||||||
|
1d:02:21:00:8f:c1:22:30:10:61:5d:51:10:ea:08:2d:02:63:
|
||||||
|
67:67:32:b5:06:63:96:57:bb:78:47:0a:88:d9:19:2e:f3:be
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDjjCCAnagAwIBAgIJAIOEuwkahzkOMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNV
|
MIIBqjCCAU+gAwIBAgIUESoOPGqMhf9uarzblVFwzrQweMcwCgYIKoZIzj0EAwIw
|
||||||
BAMTAm5zMQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1Ymxp
|
RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50
|
||||||
bjAeFw0xNDA4MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5z
|
YTESMBAGA1UEAwwJVGVzdCBSb290MCAXDTIwMDUwOTIxNTE0NFoYDzIwNTAwNTA5
|
||||||
MQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjCCASIw
|
MjE1MTQ0WjBEMQswCQYDVQQGEwJJRTEPMA0GA1UEBwwGRHVibGluMRAwDgYDVQQK
|
||||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9/00kouYWQ8DChuN3qnvhs0dZU
|
DAdDZXNhbnRhMRIwEAYDVQQDDAlUZXN0IFJvb3QwWTATBgcqhkjOPQIBBggqhkjO
|
||||||
TF1WLfSNTK/fO79Q0/fcUQ3KX6P92o+gf1fxuu6D8/s21WTf2T77wNWfNk+4sXAR
|
PQMBBwNCAAQsq9ECZiSW1xI+CVBP8VDuUehVA166sR2YsnJ5J6gbMQ1dUCH/QvLa
|
||||||
bBiF4BFs8VJtVAlH+dX7ZTJTmn9uHHcnoItMvIUXZFi6Au8NL7jH64EenJqWyNi0
|
dBdeU7JlQcH8hN5KEbmM9BnZxMor6ussox0wGzAMBgNVHRMEBTADAQH/MAsGA1Ud
|
||||||
g0jGl119qg6KH0LBVGGiTTZ97qkStIsbP48282wE/atgVmMMklF+FRIAA6ZotBcg
|
DwQEAwIBrjAKBggqhkjOPQQDAgNJADBGAiEAnHFsAIwGQQyRL81B04dH6d86Iq0l
|
||||||
DQ4xIZnZLcUFvZeiBhsxfDPZgNOd4SIGcKPvan/V953zVE1jL4rDELcZtJkWILw+
|
fL8OKzndegxOaB0CIQCPwSIwEGFdURDqCC0CY2dnMrUGY5ZXu3hHCojZGS7zvg==
|
||||||
dzosdPFS7Om6HTU1Ti0KHB9Lh9wk7Ph92dApHGWgCXv5MAixYZbPXntzNJ0CAwEA
|
|
||||||
AaOBmjCBlzAdBgNVHQ4EFgQUsz/nOHpjMkV8pk9dFpy41batoTcwaAYDVR0jBGEw
|
|
||||||
X4AUsz/nOHpjMkV8pk9dFpy41batoTehPKQ6MDgxCzAJBgNVBAMTAm5zMQswCQYD
|
|
||||||
VQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAIOEuwkahzkO
|
|
||||||
MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEDOtAl7bgAXgcL3HRlV
|
|
||||||
H71tkUaok589PIqsTE4d8s8tFBZ92CyWD8ZPU46HbbyJXMFoFxiN7PvCzOBlgoZM
|
|
||||||
r80HJWZc9tSlqK0NIbIyk1aeM06+F8qB+8/vw/spIkdYzDv3avwyOrc6fFnEzbwz
|
|
||||||
5BFFrF2G9JajRKAP5snAV9iM8I2TD4l+w75MXXl7/DBEohdMBsTeDrrXj4q4sgoB
|
|
||||||
L/yLeCoK6inkMTU5DwcGbiqvNnZA+9T654qlAlKjPMObGGPphK5/QKcOnV7Qtdju
|
|
||||||
DHzDsDimdVbz9G1cxXs/AI/35GD7IDTdNTtmBhkf4/tsQ7Ua80xpIowb1fFUHmo1
|
|
||||||
UAo=
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
+48
-43
@@ -1,45 +1,50 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
Certificate:
|
||||||
MIIEpAIBAAKCAQEAwV5xaK7ez2/TX7vSgJ0a3YbZj2l1VQ2rMzqO1Id01xlWbF/U
|
Data:
|
||||||
rebwhAdVtWcT9R6RaBTPDGaILkV38u77M2BxIHX4MSnR6WezoA2bGMgvt3+tq2N6
|
Version: 3 (0x2)
|
||||||
q+xkj57vwBEqedBjscVtFkoWtsX8pKwtNlMB1NvTa8p5+BNsvpvzaDX+51+FotId
|
Serial Number:
|
||||||
wvieQfQYgFg36HpOtOyyIV31rZ/5+qtoce8gU6wApHxmovTnQPoduNM6fOUJCHDd
|
03:d8:95:71:ba:5f:70:c8:4d:6a:e8:a6:0f:aa:40:d5:fc:d9:bc:6e
|
||||||
Lz90EeBREtoTVgoWcKvQoCEwJQSBmeDZgkA8Q1OYmbYoS12tIyi8rTkseRj5BvPH
|
Signature Algorithm: ecdsa-with-SHA256
|
||||||
iXfNmHFKliAjvlsml5qI44I9DoagPubTf6qR5wIDAQABAoIBACZ6VZTgH0Ql22jU
|
Issuer: C = IE, L = Dublin, O = Cesanta, CN = Test Root
|
||||||
ZhnjqUHloIsyEAABvUxvXZaa8bwPtavREfAc4UVUdFCpl0YSdBrC8URlbrnOZwT3
|
Validity
|
||||||
WxMpILm139JgoP2R/iNeMbunsh8QkA1nuTRW0NfnZ4vPnqUou33XbFKgIY7zLMfT
|
Not Before: May 9 21:51:52 2020 GMT
|
||||||
3xdNQzMJHzP20Xh03RG81J2rCPMfLScTRo2XxcSxmhhS/p2WLk6pnmMHiNgYGGwX
|
Not After : May 9 21:51:52 2030 GMT
|
||||||
gcdK5lIVjMMNxgcltC30x90v0o0GDRM8/+wua+/vfn8rr3iudv9IHzL8xIzpi6NY
|
Subject: CN = client
|
||||||
CXJ8Kxd6Jtgsr3Boj5i6Mqi3Q/Trxt+rIA4bKAFXxwcp4+GmRIJtQFFiTWXpLCPC
|
Subject Public Key Info:
|
||||||
tLT4CHECgYEA7iCbrGjWHJ4QsUWUGrGlw1/sQ0SIv9BdZm8RydHzpRVtQOi+YOuU
|
Public Key Algorithm: id-ecPublicKey
|
||||||
i6raVaXWzUBKgKcs/htVjAMTiePs/yhlU/MGXivz6uTX/nrD7ISJImmK2K50hgUe
|
Public-Key: (256 bit)
|
||||||
+UBnFKmBMVaNxD9RFWPJkfmNXfW7nBkqSa9CxlBcYPuOcPtZDqRl+gkCgYEAz+HX
|
pub:
|
||||||
8wh3SHKb1cAI+o4caclpUTpGa9/zW4k+7gOh72WCKaqxTNvBvNyZGdXc9t5ToDSf
|
04:e2:2e:72:7b:b6:2d:a3:d3:3b:0e:b1:4e:8a:09:
|
||||||
xxsDXWG10lcHBIGLj4QBEoSWp9I43lid5swY3mCo7CjTl+1l03IfDNaC6CYQFp5p
|
19:66:ff:d6:0e:d4:3f:47:8c:20:ab:06:db:25:77:
|
||||||
ZnKlsQUwR38t/uiyZpnnicCAZjqIfJbeQ5jD6G8CgYB8ufmwQa08ihJmN/KOVNRl
|
8b:2a:ac:fa:a4:e2:f8:97:ba:10:c5:fa:5d:0b:ee:
|
||||||
VF31EfWquqHhYHXpxx2eL23tXLszGtHQoioASIANPAqJ/oaTho+1aXsXc5oUP/1r
|
28:16:56:78:0f:30:17:2b:6b:04:6c:dc:c8:f8:12:
|
||||||
DlUciFsXgswb0APFY9pMewmt2xrPg+koVvJnIS25QQO6cguvb3gKDLNeLrMY3RmI
|
23:d3:2f:01:58
|
||||||
RNNt+nOYnMqMJSsNf1CmuQKBgQCiCZxWaCbyZcNqncFh7BvhqYlaM15o/6ulkhln
|
ASN1 OID: prime256v1
|
||||||
VZWIEUugRtjk2/bry9fa94TBORNeMSbKABhjVaJwTj2+GWw7dd2QHaGBNq/1QIX0
|
NIST CURVE: P-256
|
||||||
POq1jAqf6kLkjbttUes6CosHgYPQ3bGylXLpxO2ZDV1A8Qj+SMDd8xsilEWHN+IQ
|
X509v3 extensions:
|
||||||
NqeeKQKBgQDe4c7VVG+WvRRKshTh8+tjzc9nXKE2AWgwnw729SMFZO/WqX2FPp2C
|
X509v3 Basic Constraints:
|
||||||
7C99XJTVBsCBy8VzuyaojeTKkag0YL3v6UTZYUeyu0YTHGQ33WVPaqdCAo840nmG
|
CA:FALSE
|
||||||
ttwHVqshB9c67HHiYOOFt1VmT3xW6x6yympUyRqR0L+BZ1wOS3h2vQ==
|
X509v3 Key Usage:
|
||||||
-----END RSA PRIVATE KEY-----
|
Digital Signature, Key Encipherment, Key Agreement
|
||||||
|
X509v3 Extended Key Usage:
|
||||||
|
TLS Web Client Authentication
|
||||||
|
Signature Algorithm: ecdsa-with-SHA256
|
||||||
|
30:46:02:21:00:de:e5:30:ae:50:e9:a7:14:a0:c3:79:29:df:
|
||||||
|
bf:d3:a3:f8:19:b0:19:b5:ab:3e:6e:c9:29:18:86:ff:fe:a7:
|
||||||
|
b0:02:21:00:f5:ba:90:d6:1c:fe:ff:05:44:9a:b1:20:2c:ee:
|
||||||
|
00:68:20:85:f7:0f:86:a2:13:1e:86:9a:03:6d:74:aa:72:c2
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIC6DCCAdACBRQJQlZlMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw
|
MIIBhzCCASygAwIBAgIUA9iVcbpfcMhNauimD6pA1fzZvG4wCgYIKoZIzj0EAwIw
|
||||||
CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4
|
RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50
|
||||||
MzAxOTA3NDRaFw0yNDA4MjcxOTA3NDRaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK
|
YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE1MloXDTMwMDUwOTIx
|
||||||
EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN
|
NTE1MlowETEPMA0GA1UEAwwGY2xpZW50MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
|
||||||
AQEBBQADggEPADCCAQoCggEBAMFecWiu3s9v01+70oCdGt2G2Y9pdVUNqzM6jtSH
|
QgAE4i5ye7Yto9M7DrFOigkZZv/WDtQ/R4wgqwbbJXeLKqz6pOL4l7oQxfpdC+4o
|
||||||
dNcZVmxf1K3m8IQHVbVnE/UekWgUzwxmiC5Fd/Lu+zNgcSB1+DEp0elns6ANmxjI
|
FlZ4DzAXK2sEbNzI+BIj0y8BWKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw
|
||||||
L7d/ratjeqvsZI+e78ARKnnQY7HFbRZKFrbF/KSsLTZTAdTb02vKefgTbL6b82g1
|
EwYDVR0lBAwwCgYIKwYBBQUHAwIwCgYIKoZIzj0EAwIDSQAwRgIhAN7lMK5Q6acU
|
||||||
/udfhaLSHcL4nkH0GIBYN+h6TrTssiFd9a2f+fqraHHvIFOsAKR8ZqL050D6HbjT
|
oMN5Kd+/06P4GbAZtas+bskpGIb//qewAiEA9bqQ1hz+/wVEmrEgLO4AaCCF9w+G
|
||||||
OnzlCQhw3S8/dBHgURLaE1YKFnCr0KAhMCUEgZng2YJAPENTmJm2KEtdrSMovK05
|
ohMehpoDbXSqcsI=
|
||||||
LHkY+Qbzx4l3zZhxSpYgI75bJpeaiOOCPQ6GoD7m03+qkecCAwEAATANBgkqhkiG
|
|
||||||
9w0BAQUFAAOCAQEAJ+wZ/IgAF5LIu0yOfJlaFRJLunKHZENigiVjYvkTdM7NI3O2
|
|
||||||
1AZGY4O8H5Fs3YT5ZY3vas/n6IwWTk3o/JSPXojMFo82XkbI1k2cm3oLtwgEGN3p
|
|
||||||
s5yFsjZE3H7fQJ9wHIzESBPHFY6dwwgMsNENuAM2zkwFpbAkisKhjK+EyUCXauok
|
|
||||||
7zJY6RVPMaNojsje4iE/SBtSOnK/9WDBAgpCznHrSChJmKs4FsU7ZTO+Dg+0vQln
|
|
||||||
l8/yBcEGAFe0GA2D9NvZKH5IoNmitvtU9zdNDK4dzC3Q+C28IjW5jE8peDFtdGs1
|
|
||||||
P0u4kRxmb4UH1DchgoWlZjL2lSFScJ7L4xY2aQ==
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgk5sv3YwDGTm29Czy
|
||||||
|
UWuKlbfbvZqZv1fFRBOn2S0D7SuhRANCAATiLnJ7ti2j0zsOsU6KCRlm/9YO1D9H
|
||||||
|
jCCrBtsld4sqrPqk4viXuhDF+l0L7igWVngPMBcrawRs3Mj4EiPTLwFY
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
|
struct http_message req;
|
||||||
|
mg_parse_http((const char *) data, size, &req, 0);
|
||||||
|
mg_parse_http((const char *) data, size, &req, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+48
-43
@@ -1,45 +1,50 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
Certificate:
|
||||||
MIIEpAIBAAKCAQEA1mONQ0hAXOL9lb15Pz4fqXXNHREsF3a7/NoMJdQDclx0+a32
|
Data:
|
||||||
MhuHcO6R7Fhsc0mZMuzbmAFLMmIIgXPMKQBZLoA12yCBlZPyKFoWUhFrLa3gUjO6
|
Version: 3 (0x2)
|
||||||
CZlBKqkUVEACpVrQ41ihapeeUHa0uryt3tXwMn2/853yzi1uciGYi4ULTy3yTE/n
|
Serial Number:
|
||||||
qRIVJLiBDSC9WNFLg26f/W4YRW7tANOk2b/W/Ws9B/n7vNDgHG7Lpd38YTpFhhXT
|
6e:73:28:55:df:13:b5:61:f5:4f:4f:5d:00:d9:0a:d8:b5:3a:21:4b
|
||||||
n3xlt/VcczkQhW79Moh6/lY6sLg6H15EjHKHeTn8t9BRm+qYi/CvC258YF/Qz/qK
|
Signature Algorithm: ecdsa-with-SHA256
|
||||||
agSsLT/3FrQ6+aQgg/Eyao0IWAql49PQNxuwPQIDAQABAoIBAQC5y3S1BnyhAyb5
|
Issuer: C = IE, L = Dublin, O = Cesanta, CN = Test Root
|
||||||
Ckd1g4U0+x5TPnqTqxanvuAgOGj0RyQo7ZYbPrhWKqrTxJ3YG8Rk2dhFF3nvo/3z
|
Validity
|
||||||
EkOwlNi07++8g6NJ2flW9xu469eSsslg8+saPnK3Yeh4SzD/1ICLRlg9ZECTQwzF
|
Not Before: May 9 21:51:49 2020 GMT
|
||||||
eJbGM2oCl/AuVIgEHmNFDdCBuT9f0b7j3/Z3aK3lKzqzBYQgZ5fd8UxT+Kn4oAuS
|
Not After : May 9 21:51:49 2030 GMT
|
||||||
cLr3lQT1s6xZOAYn7O2GvXEC+yMMbvm0a97MdwSpQez1WcE9YxtCgAWwn5EmSXlh
|
Subject: CN = server
|
||||||
296iLtbaM1wgYOykJUOUoSgijf8pUfotk4Zj/y1KPHXePgAlyGCtE1zasiYb5K+5
|
Subject Public Key Info:
|
||||||
LuajD++BAoGBAPpKWLNzQBwQLiFaJgt6bLOxlEUR+EnjdFePDPJtyCCCiKJiKO5c
|
Public Key Algorithm: id-ecPublicKey
|
||||||
Z5s/FT1JDQawouhjQwYqT48hbGBPjWRHkSkzB7+cg6FVSKkQRYX2TsSFvN+KCu32
|
Public-Key: (256 bit)
|
||||||
oSgDV9cFo68v1csoZIQ41TtHC82db4OTv9MPUe3Glujnep1TOTwspAM1AoGBANtH
|
pub:
|
||||||
i+HWKOxOm7f/R2VX1ys9UjkAK+msac512XWSLAzBs7NFnB7iJ7m3Bh3ydb1ZiTgW
|
04:92:e0:46:9c:89:c3:37:a9:74:eb:35:55:43:55:
|
||||||
l6bIdoT8TLPYNIXJ6uohhxPU5h3v81PHqIuJMBtmHCQjq3nxeH9mOsfjOFvS1cQa
|
5c:ac:eb:c7:e4:50:ee:f4:c0:ba:17:02:5c:d9:ed:
|
||||||
At45F9pK/5sQpOkkaBGSv8jXUFIKBEDBErourVHpAoGAK0gSAK4sZu3xXDkfnRqF
|
b4:d4:ff:21:12:9a:b4:43:f4:89:4b:69:e4:6d:2b:
|
||||||
k6lgr3UFD5nys3V8UqvjUKPiBtqco2N9Ux5ciOWKCB8hfLg1jephKaoo+JqpI68w
|
96:1f:fc:01:4d:30:5a:79:73:76:ba:19:41:cc:c5:
|
||||||
jgRSEbN6G7sIvpueuiS2yEssNyfC7hWZFrdFSFykSpYmDWSlxSuizAZkJyFTeFhj
|
16:2b:bf:74:28
|
||||||
cpcSnuCZlhr5XB1ZJ2u8zQUCgYEAke5QgpCDFZjO+ynR+vj1gppBwRuDHfUXSUaW
|
ASN1 OID: prime256v1
|
||||||
3S7VT/wNOq6F0uvRYkASuxVkFAqlToWCkYVxktlRtpKZibwyMXT0r1cNejj5Z/VF
|
NIST CURVE: P-256
|
||||||
Du/S6zkOW2K9uN7hwW9oiSSHmlx61RI2fGvkmus0pp7yERKgi6ltJx1cH+z4nZug
|
X509v3 extensions:
|
||||||
efWcdRkCgYBy+XdmsxgNZOunlSC6VZiD0Ve/VFrCtKPWUivKDAZZPKl0T/1tbTwb
|
X509v3 Basic Constraints:
|
||||||
I/N4zTF82jx88rDz+6jN5nOy9qbSR5TeCy6WlBesTvXm49awr5jSK3WkcLgmO+JI
|
CA:FALSE
|
||||||
Zr2ozCBhUG6RvVsUPp2kXEsmwZMV/e9faFAlIXeJhKum6hZmfOgodg==
|
X509v3 Key Usage:
|
||||||
-----END RSA PRIVATE KEY-----
|
Digital Signature, Key Encipherment, Key Agreement
|
||||||
|
X509v3 Extended Key Usage:
|
||||||
|
TLS Web Server Authentication
|
||||||
|
Signature Algorithm: ecdsa-with-SHA256
|
||||||
|
30:46:02:21:00:fa:3a:c7:1e:cb:8c:27:59:41:8d:77:dd:7b:
|
||||||
|
cb:8c:08:15:16:b9:6e:70:e6:47:38:d1:55:42:e0:d7:66:c8:
|
||||||
|
f0:02:21:00:cc:70:4d:96:28:00:d3:c7:39:53:74:b2:49:87:
|
||||||
|
27:92:1b:ab:1a:0e:74:06:59:42:23:47:98:43:d8:20:a7:fa
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIC6DCCAdACBRQJQlZkMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw
|
MIIBhzCCASygAwIBAgIUbnMoVd8TtWH1T09dANkK2LU6IUswCgYIKoZIzj0EAwIw
|
||||||
CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4
|
RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50
|
||||||
MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK
|
YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE0OVoXDTMwMDUwOTIx
|
||||||
EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN
|
NTE0OVowETEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
|
||||||
AQEBBQADggEPADCCAQoCggEBANZjjUNIQFzi/ZW9eT8+H6l1zR0RLBd2u/zaDCXU
|
QgAEkuBGnInDN6l06zVVQ1VcrOvH5FDu9MC6FwJc2e201P8hEpq0Q/SJS2nkbSuW
|
||||||
A3JcdPmt9jIbh3DukexYbHNJmTLs25gBSzJiCIFzzCkAWS6ANdsggZWT8ihaFlIR
|
H/wBTTBaeXN2uhlBzMUWK790KKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw
|
||||||
ay2t4FIzugmZQSqpFFRAAqVa0ONYoWqXnlB2tLq8rd7V8DJ9v/Od8s4tbnIhmIuF
|
EwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSQAwRgIhAPo6xx7LjCdZ
|
||||||
C08t8kxP56kSFSS4gQ0gvVjRS4Nun/1uGEVu7QDTpNm/1v1rPQf5+7zQ4Bxuy6Xd
|
QY133XvLjAgVFrlucOZHONFVQuDXZsjwAiEAzHBNligA08c5U3SySYcnkhurGg50
|
||||||
/GE6RYYV0598Zbf1XHM5EIVu/TKIev5WOrC4Oh9eRIxyh3k5/LfQUZvqmIvwrwtu
|
BllCI0eYQ9ggp/o=
|
||||||
fGBf0M/6imoErC0/9xa0OvmkIIPxMmqNCFgKpePT0DcbsD0CAwEAATANBgkqhkiG
|
|
||||||
9w0BAQUFAAOCAQEAoVXK97WA24tp3JyPBJKr28gFSUtOBNDPdY8atWaqw7PwUIIM
|
|
||||||
qhs3BTag96tgSoaISRwRphz2LM1Cl+QlItYXySAnxPKrUsA0S6DlxnA6Hq3s2wTR
|
|
||||||
6yIT7oDUDKcWkVQcQmuNGdfxCvZXkCih9lnQn++xHcuVn9mZmjXW2xk42ljDTZCp
|
|
||||||
CM29betpcmuho6sFXsBhY7WjQWg7UpRZat0bOwleS4fsePebMKrnr/6cq4bVw59U
|
|
||||||
XvhSFBlLoGMYteJ82fOYH6pUO1hiPr6ww5d819LPcJEcRpcxCdQZqIq680Kp7+GY
|
|
||||||
0wkyOYr0gkNwWVP7IUZ0FExaQ/s54g71Kd0OgA==
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglNni0t9Dg9icgG8w
|
||||||
|
kbfxWSS+TuNgbtNybIQXcm3NHpmhRANCAASS4EacicM3qXTrNVVDVVys68fkUO70
|
||||||
|
wLoXAlzZ7bTU/yESmrRD9IlLaeRtK5Yf/AFNMFp5c3a6GUHMxRYrv3Qo
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
+119
-78
@@ -15,12 +15,12 @@
|
|||||||
* license, as set out in <https://www.cesanta.com/license>.
|
* license, as set out in <https://www.cesanta.com/license>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mongoose.h"
|
|
||||||
#include "src/mg_internal.h"
|
|
||||||
#include "unit_test.h"
|
#include "unit_test.h"
|
||||||
|
#include "common/cs_md5.h"
|
||||||
#include "common/test_main.h"
|
#include "common/test_main.h"
|
||||||
#include "common/test_util.h"
|
#include "common/test_util.h"
|
||||||
#include "common/cs_md5.h"
|
#include "mongoose.h"
|
||||||
|
#include "src/mg_internal.h"
|
||||||
|
|
||||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L && !defined(WIN32)
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L && !defined(WIN32)
|
||||||
#define __func__ ""
|
#define __func__ ""
|
||||||
@@ -975,12 +975,6 @@ static void connect_fail_cb(struct mg_connection *nc, int ev, void *p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ev_handler_empty(struct mg_connection *nc, int ev, void *p) {
|
|
||||||
(void) nc;
|
|
||||||
(void) ev;
|
|
||||||
(void) p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *test_connection_errors(void) {
|
static const char *test_connection_errors(void) {
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
struct mg_bind_opts bopts;
|
struct mg_bind_opts bopts;
|
||||||
@@ -995,25 +989,21 @@ static const char *test_connection_errors(void) {
|
|||||||
bopts.error_string = &error_string;
|
bopts.error_string = &error_string;
|
||||||
|
|
||||||
ASSERT(mg_bind_opt(&mgr, "blah://12", NULL, bopts) == NULL);
|
ASSERT(mg_bind_opt(&mgr, "blah://12", NULL, bopts) == NULL);
|
||||||
ASSERT_STREQ(error_string, "handler is required");
|
|
||||||
|
|
||||||
ASSERT(mg_bind_opt(&mgr, "blah://12", ev_handler_empty, bopts) == NULL);
|
|
||||||
ASSERT_STREQ(error_string, "cannot parse address");
|
ASSERT_STREQ(error_string, "cannot parse address");
|
||||||
|
|
||||||
ASSERT(mg_bind_opt(&mgr, "tcp://8.8.8.8:88", ev_handler_empty, bopts) ==
|
ASSERT(mg_bind_opt(&mgr, "tcp://8.8.8.8:88", NULL, bopts) == NULL);
|
||||||
NULL);
|
|
||||||
ASSERT_STREQ(error_string, "failed to open listener");
|
ASSERT_STREQ(error_string, "failed to open listener");
|
||||||
|
|
||||||
#if MG_ENABLE_SSL
|
#if MG_ENABLE_SSL
|
||||||
bopts.ssl_cert = S_PEM;
|
bopts.ssl_cert = S_PEM;
|
||||||
ASSERT(mg_bind_opt(&mgr, "udp://:0", ev_handler_empty, bopts) == NULL);
|
ASSERT(mg_bind_opt(&mgr, "udp://:0", NULL, bopts) == NULL);
|
||||||
ASSERT_STREQ(error_string, "SSL for UDP is not supported");
|
ASSERT_STREQ(error_string, "SSL for UDP is not supported");
|
||||||
bopts.ssl_cert = "no_such_file";
|
bopts.ssl_cert = "no_such_file";
|
||||||
ASSERT(mg_bind_opt(&mgr, "tcp://:0", ev_handler_empty, bopts) == NULL);
|
ASSERT(mg_bind_opt(&mgr, "tcp://:0", NULL, bopts) == NULL);
|
||||||
ASSERT_STREQ(error_string, "Invalid SSL cert");
|
ASSERT_STREQ(error_string, "Invalid SSL cert");
|
||||||
bopts.ssl_cert = NULL;
|
bopts.ssl_cert = NULL;
|
||||||
bopts.ssl_ca_cert = "no_such_file";
|
bopts.ssl_ca_cert = "no_such_file";
|
||||||
ASSERT(mg_bind_opt(&mgr, "tcp://:0", ev_handler_empty, bopts) == NULL);
|
ASSERT(mg_bind_opt(&mgr, "tcp://:0", NULL, bopts) == NULL);
|
||||||
ASSERT_STREQ(error_string, "Invalid SSL CA cert");
|
ASSERT_STREQ(error_string, "Invalid SSL CA cert");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1710,7 +1700,7 @@ static void cb10(struct mg_connection *nc, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void endpoint_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
static void default_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
struct http_message *hm = (struct http_message *) ev_data;
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
|
|
||||||
@@ -1724,7 +1714,8 @@ static void endpoint_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
} else if (ev == MG_EV_CLOSE) {
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
if (nc->listener != NULL) {
|
if (nc->listener != NULL) {
|
||||||
(*(int *) nc->listener->user_data) += 100;
|
(*(int *) nc->listener->user_data) += 1;
|
||||||
|
DBG(("%p == default close", nc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1737,7 +1728,8 @@ static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) {
|
|||||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
break;
|
break;
|
||||||
case MG_EV_CLOSE:
|
case MG_EV_CLOSE:
|
||||||
(*(int *) nc->listener->user_data)++;
|
DBG(("%p == hello1 close", nc));
|
||||||
|
(*(int *) nc->listener->user_data) += 10;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1750,7 +1742,8 @@ static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) {
|
|||||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
break;
|
break;
|
||||||
case MG_EV_CLOSE:
|
case MG_EV_CLOSE:
|
||||||
(*(int *) nc->listener->user_data)++;
|
DBG(("%p == hello2 close", nc));
|
||||||
|
(*(int *) nc->listener->user_data) += 100;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1763,7 +1756,8 @@ static void handle_hello5(struct mg_connection *nc, int ev, void *ev_data) {
|
|||||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
break;
|
break;
|
||||||
case MG_EV_CLOSE:
|
case MG_EV_CLOSE:
|
||||||
(*(int *) nc->listener->user_data)++;
|
DBG(("%p == hello5 close", nc));
|
||||||
|
(*(int *) nc->listener->user_data) += 1000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1823,7 +1817,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
|
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
/* mgr.hexdump_file = "-"; */
|
/* mgr.hexdump_file = "-"; */
|
||||||
ASSERT((nc = mg_bind(&mgr, local_addr, endpoint_handler)) != NULL);
|
ASSERT((nc = mg_bind(&mgr, local_addr, default_handler)) != NULL);
|
||||||
mg_register_http_endpoint(nc, "/hello1", handle_hello1 MG_UD_ARG(NULL));
|
mg_register_http_endpoint(nc, "/hello1", handle_hello1 MG_UD_ARG(NULL));
|
||||||
mg_register_http_endpoint(nc, "/hello1/hello2",
|
mg_register_http_endpoint(nc, "/hello1/hello2",
|
||||||
handle_hello2 MG_UD_ARG(NULL));
|
handle_hello2 MG_UD_ARG(NULL));
|
||||||
@@ -1888,7 +1882,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[I am Hello again] 37");
|
ASSERT_STREQ(buf, "[I am Hello again] 37");
|
||||||
|
|
||||||
ASSERT_EQ(close_count, 700);
|
ASSERT_EQ(close_count, 1117);
|
||||||
|
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
@@ -1997,7 +1991,7 @@ static const char *test_http_serve_file_streaming(void) {
|
|||||||
ASSERT((nc = mg_connect(&mgr, local_addr, srv2)) != NULL);
|
ASSERT((nc = mg_connect(&mgr, local_addr, srv2)) != NULL);
|
||||||
mg_set_protocol_http_websocket(nc);
|
mg_set_protocol_http_websocket(nc);
|
||||||
nc->user_data = &status;
|
nc->user_data = &status;
|
||||||
mg_printf(nc, "GET / HTTP/1.1\r\n\r\n");
|
mg_printf(nc, "GET / HTTP/1.0\r\n\r\n");
|
||||||
poll_until(&mgr, 30, c_int_ne, &status, (void *) 0);
|
poll_until(&mgr, 30, c_int_ne, &status, (void *) 0);
|
||||||
ASSERT_EQ(status, 1);
|
ASSERT_EQ(status, 1);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
@@ -2011,7 +2005,8 @@ static const char *test_http(void) {
|
|||||||
char buf[50] = "", status[100] = "", mime1[20] = "", mime2[100] = "";
|
char buf[50] = "", status[100] = "", mime1[20] = "", mime2[100] = "";
|
||||||
char opt_buf[1024] = "";
|
char opt_buf[1024] = "";
|
||||||
const char *opt_answer =
|
const char *opt_answer =
|
||||||
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS, MKCOL, "
|
"HTTP/1.1 200 OK\r\nServer: Mongoose/" MG_VERSION
|
||||||
|
"\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS, MKCOL, "
|
||||||
"PUT, DELETE, PROPFIND, MOVE";
|
"PUT, DELETE, PROPFIND, MOVE";
|
||||||
char url[1000];
|
char url[1000];
|
||||||
|
|
||||||
@@ -2539,32 +2534,6 @@ static const char *test_http_range(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cb3(struct mg_connection *nc, int ev, void *ev_data) {
|
|
||||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
|
||||||
|
|
||||||
if (ev == MG_EV_WEBSOCKET_FRAME) {
|
|
||||||
const char *reply = wm->size == 2 && !memcmp(wm->data, "hi", 2) ? "A" : "B";
|
|
||||||
mg_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", reply);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cb4(struct mg_connection *nc, int ev, void *ev_data) {
|
|
||||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
|
||||||
|
|
||||||
if (ev == MG_EV_WEBSOCKET_FRAME) {
|
|
||||||
memcpy(nc->user_data, wm->data, wm->size);
|
|
||||||
mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, NULL, 0);
|
|
||||||
} else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE) {
|
|
||||||
/* Send "hi" to server. server must reply "A". */
|
|
||||||
struct mg_str h[2];
|
|
||||||
h[0].p = "h";
|
|
||||||
h[0].len = 1;
|
|
||||||
h[1].p = "i";
|
|
||||||
h[1].len = 1;
|
|
||||||
mg_send_websocket_framev(nc, WEBSOCKET_OP_TEXT, h, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cb_ws_server(struct mg_connection *nc, int ev, void *ev_data) {
|
static void cb_ws_server(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
|
|
||||||
@@ -2779,6 +2748,38 @@ static const char *test_websocket(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cb3(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
|
if (ev != MG_EV_WEBSOCKET_FRAME) return;
|
||||||
|
DBG(("server data '%.*s'", (int) wm->size, wm->data));
|
||||||
|
const char *reply = wm->size == 2 && !memcmp(wm->data, "hi", 2) ? "A" : "B";
|
||||||
|
mg_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb4(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
char *buf = (char *) nc->user_data;
|
||||||
|
if (ev == MG_EV_WEBSOCKET_FRAME) {
|
||||||
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
|
DBG(("client data '%.*s'", (int) wm->size, wm->data));
|
||||||
|
memcpy(buf, wm->data, wm->size);
|
||||||
|
mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, NULL, 0);
|
||||||
|
} else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
DBG(("code %d", hm->resp_code));
|
||||||
|
if (hm->resp_code == 101) {
|
||||||
|
/* Send "hi" to server. server must reply "A". */
|
||||||
|
struct mg_str h[2];
|
||||||
|
h[0].p = "h";
|
||||||
|
h[0].len = 1;
|
||||||
|
h[1].p = "i";
|
||||||
|
h[1].len = 1;
|
||||||
|
mg_send_websocket_framev(nc, WEBSOCKET_OP_TEXT, h, 2);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, 20, "code %d", hm->resp_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void cbwep(struct mg_connection *c, int ev, void *ev_data) {
|
static void cbwep(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
char *buf = (char *) c->user_data;
|
char *buf = (char *) c->user_data;
|
||||||
@@ -2786,6 +2787,9 @@ static void cbwep(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
switch (ev) {
|
switch (ev) {
|
||||||
case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST:
|
case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST:
|
||||||
strcat(buf, "R");
|
strcat(buf, "R");
|
||||||
|
if (buf[0] != '0') {
|
||||||
|
mg_http_send_error(c, 403, "I don't like you");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
|
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
|
||||||
strcat(buf, "D");
|
strcat(buf, "D");
|
||||||
@@ -2802,7 +2806,7 @@ static const char *test_websocket_endpoint(void) {
|
|||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
struct mg_connection *nc;
|
struct mg_connection *nc;
|
||||||
const char *local_addr = "127.0.0.1:7798";
|
const char *local_addr = "127.0.0.1:7798";
|
||||||
char buf[20] = "", buf2[20] = "";
|
char buf[20] = "", buf2[20] = "0";
|
||||||
|
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
/* mgr.hexdump_file = "-"; */
|
/* mgr.hexdump_file = "-"; */
|
||||||
@@ -2817,10 +2821,21 @@ static const char *test_websocket_endpoint(void) {
|
|||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
mg_send_websocket_handshake(nc, "/boo", NULL);
|
mg_send_websocket_handshake(nc, "/boo", NULL);
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
mg_mgr_free(&mgr);
|
|
||||||
|
|
||||||
/* Check that test buffer has been filled by the callback properly. */
|
/* Check that test buffer has been filled by the callback properly. */
|
||||||
ASSERT_STREQ(buf, "RDF|hi");
|
ASSERT_STREQ(buf, "0RDF|hi");
|
||||||
|
|
||||||
|
/* Test handshake failure */
|
||||||
|
ASSERT((nc = mg_connect(&mgr, local_addr, cb4)) != NULL);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
buf[0] = '\0';
|
||||||
|
buf2[0] = '1';
|
||||||
|
buf2[1] = '\0';
|
||||||
|
nc->user_data = buf;
|
||||||
|
mg_send_websocket_handshake(nc, "/boo", NULL);
|
||||||
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
|
ASSERT_STREQ(buf, "code 403");
|
||||||
|
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -3379,15 +3394,36 @@ static const char *test_mqtt_parse_mqtt_qos1(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char *test_mqtt_match_topic_expression(void) {
|
static const char *test_mqtt_match_topic_expression(void) {
|
||||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo")), 1);
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo")));
|
||||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/")), 0);
|
ASSERT(mg_mqtt_vmatch_topic_expression("/foo", mg_mk_str("/foo")));
|
||||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/bar")), 0);
|
ASSERT(mg_mqtt_vmatch_topic_expression("+/foo", mg_mk_str("/foo")));
|
||||||
|
ASSERT(!mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foobar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo//")));
|
||||||
|
ASSERT(!mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(!mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/+")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/bar", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/+", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("+/bar", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("+/+", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/+/bar", mg_mk_str("foo//bar")));
|
||||||
|
ASSERT(!mg_mqtt_vmatch_topic_expression("foo/+/+", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/+/#", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("+/foo/bar", mg_mk_str("/foo/bar")));
|
||||||
|
|
||||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo")), 0);
|
ASSERT(!mg_mqtt_vmatch_topic_expression("", mg_mk_str("")));
|
||||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/")), 0);
|
ASSERT(mg_mqtt_vmatch_topic_expression("/", mg_mk_str("")));
|
||||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar")), 1);
|
ASSERT(mg_mqtt_vmatch_topic_expression("/", mg_mk_str("/")));
|
||||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar/baz")),
|
|
||||||
1);
|
ASSERT(mg_mqtt_vmatch_topic_expression("#", mg_mk_str("")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("#", mg_mk_str("foo")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("#", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar")));
|
||||||
|
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar/baz")));
|
||||||
|
ASSERT(!mg_mqtt_vmatch_topic_expression("#/foo", mg_mk_str("foo")));
|
||||||
|
ASSERT(!mg_mqtt_vmatch_topic_expression("#/foo", mg_mk_str("bar/foo")));
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -3961,6 +3997,7 @@ static const char *test_http_chunk2(void) {
|
|||||||
nc.mgr = &mgr;
|
nc.mgr = &mgr;
|
||||||
nc.sock = INVALID_SOCKET;
|
nc.sock = INVALID_SOCKET;
|
||||||
nc.handler = eh_chunk2;
|
nc.handler = eh_chunk2;
|
||||||
|
mg_http_create_proto_data(&nc);
|
||||||
hm.message.len = hm.body.len = ~0;
|
hm.message.len = hm.body.len = ~0;
|
||||||
|
|
||||||
s_handle_chunk_event = 0;
|
s_handle_chunk_event = 0;
|
||||||
@@ -4093,12 +4130,6 @@ static void cb_mp_send_one_byte(struct mg_connection *nc, int ev, void *p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cb_mp_empty(struct mg_connection *nc, int ev, void *p) {
|
|
||||||
(void) nc;
|
|
||||||
(void) ev;
|
|
||||||
(void) p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char b1[] =
|
static const char b1[] =
|
||||||
"111111111111111111111111111111111111111111111111111111111111111\r\n"
|
"111111111111111111111111111111111111111111111111111111111111111\r\n"
|
||||||
"111111111111111111111111111111111111111111111111111111111111111\r\n"
|
"111111111111111111111111111111111111111111111111111111111111111\r\n"
|
||||||
@@ -4222,13 +4253,13 @@ static const char *test_http_multipart2(void) {
|
|||||||
"\r\n--Asrf456BGe4h\r\n", b1, b2, b4);
|
"\r\n--Asrf456BGe4h\r\n", b1, b2, b4);
|
||||||
|
|
||||||
/* Testing delivering to endpoint handler*/
|
/* Testing delivering to endpoint handler*/
|
||||||
nc_listen = mg_bind(&mgr, "8766", cb_mp_empty);
|
nc_listen = mg_bind(&mgr, "8766", NULL);
|
||||||
nc_listen->user_data = &mpd;
|
nc_listen->user_data = &mpd;
|
||||||
|
|
||||||
mg_set_protocol_http_websocket(nc_listen);
|
mg_set_protocol_http_websocket(nc_listen);
|
||||||
mg_register_http_endpoint(nc_listen, "/test", cb_mp_srv MG_UD_ARG(NULL));
|
mg_register_http_endpoint(nc_listen, "/test", cb_mp_srv MG_UD_ARG(NULL));
|
||||||
|
|
||||||
ASSERT((c = mg_connect_http(&mgr, cb_mp_empty, "http://127.0.0.1:8766/test",
|
ASSERT((c = mg_connect_http(&mgr, NULL, "http://127.0.0.1:8766/test",
|
||||||
"Connection: keep-alive\r\nContent-Type: "
|
"Connection: keep-alive\r\nContent-Type: "
|
||||||
"multipart/form-data; boundary=Asrf456BGe4h",
|
"multipart/form-data; boundary=Asrf456BGe4h",
|
||||||
multi_part_req)) != NULL);
|
multi_part_req)) != NULL);
|
||||||
@@ -4265,7 +4296,7 @@ static const char *test_http_multipart2(void) {
|
|||||||
/* Test interrupted request */
|
/* Test interrupted request */
|
||||||
multi_part_req[1800] = '\0';
|
multi_part_req[1800] = '\0';
|
||||||
c = mg_connect_http(
|
c = mg_connect_http(
|
||||||
&mgr, cb_mp_empty, "http://127.0.0.1:8765",
|
&mgr, NULL, "http://127.0.0.1:8765",
|
||||||
"Content-Type: multipart/form-data; boundary=Asrf456BGe4h",
|
"Content-Type: multipart/form-data; boundary=Asrf456BGe4h",
|
||||||
multi_part_req);
|
multi_part_req);
|
||||||
|
|
||||||
@@ -4286,7 +4317,7 @@ static const char *test_http_multipart2(void) {
|
|||||||
|
|
||||||
ASSERT(
|
ASSERT(
|
||||||
mg_connect_http(
|
mg_connect_http(
|
||||||
&mgr, cb_mp_empty, "http://127.0.0.1:8766/test",
|
&mgr, NULL, "http://127.0.0.1:8766/test",
|
||||||
"Content-Type: multipart/form-data; boundary=Asrf456BGe4h",
|
"Content-Type: multipart/form-data; boundary=Asrf456BGe4h",
|
||||||
"\r\n--Asrf456BGe4h\r\n"
|
"\r\n--Asrf456BGe4h\r\n"
|
||||||
"Content-Disposition: form-data; name=\"d\"; filename=\"small\"\r\n"
|
"Content-Disposition: form-data; name=\"d\"; filename=\"small\"\r\n"
|
||||||
@@ -4306,7 +4337,7 @@ static const char *test_http_multipart2(void) {
|
|||||||
* See https://github.com/cesanta/dev/issues/6974
|
* See https://github.com/cesanta/dev/issues/6974
|
||||||
* This request should not lead to crash
|
* This request should not lead to crash
|
||||||
*/
|
*/
|
||||||
c = mg_connect(&mgr, "127.0.0.1:8766", cb_mp_empty);
|
c = mg_connect(&mgr, "127.0.0.1:8766", NULL);
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"POST /test HTTP/1.1\r\n"
|
"POST /test HTTP/1.1\r\n"
|
||||||
"Connection: keep-alive\r\n"
|
"Connection: keep-alive\r\n"
|
||||||
@@ -4359,7 +4390,7 @@ static const char *test_http_multipart_pipeline(void) {
|
|||||||
snprintf(multi_part_req, sizeof(multi_part_req), multi_part_req_fmt,
|
snprintf(multi_part_req, sizeof(multi_part_req), multi_part_req_fmt,
|
||||||
"\r\n--Asrf456BGe4h\r\n", b1, b2, b4);
|
"\r\n--Asrf456BGe4h\r\n", b1, b2, b4);
|
||||||
|
|
||||||
ASSERT((c = mg_connect_http(&mgr, cb_mp_empty, "http://127.0.0.1:8765/test",
|
ASSERT((c = mg_connect_http(&mgr, NULL, "http://127.0.0.1:8765/test",
|
||||||
"Content-Type: "
|
"Content-Type: "
|
||||||
"multipart/form-data;boundary=Asrf456BGe4h\r\n"
|
"multipart/form-data;boundary=Asrf456BGe4h\r\n"
|
||||||
"Connection: keep-alive",
|
"Connection: keep-alive",
|
||||||
@@ -4559,6 +4590,15 @@ static const char *test_dns_encode(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *test_dns_encode_name(void) {
|
||||||
|
struct mbuf mb;
|
||||||
|
mbuf_init(&mb, 0);
|
||||||
|
ASSERT_EQ(mg_dns_encode_name(&mb, "www.cesanta.com.net.org", 15), 17);
|
||||||
|
ASSERT_STREQ_NZ(mb.buf, "\x03" "www" "\x07" "cesanta" "\x03" "com");
|
||||||
|
mbuf_free(&mb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *test_dns_uncompress(void) {
|
static const char *test_dns_uncompress(void) {
|
||||||
/*
|
/*
|
||||||
* Order or string constants is important. Names being uncompressed
|
* Order or string constants is important. Names being uncompressed
|
||||||
@@ -5643,7 +5683,7 @@ static const char *test_socks(void) {
|
|||||||
mg_set_protocol_http_websocket(c);
|
mg_set_protocol_http_websocket(c);
|
||||||
|
|
||||||
/* Start socks proxy */
|
/* Start socks proxy */
|
||||||
ASSERT((c = mg_bind(&mgr, proxy_addr, ev_handler_empty)) != NULL);
|
ASSERT((c = mg_bind(&mgr, proxy_addr, NULL)) != NULL);
|
||||||
mg_set_protocol_socks(c);
|
mg_set_protocol_socks(c);
|
||||||
|
|
||||||
/* Create HTTP client that uses socks proxy */
|
/* Create HTTP client that uses socks proxy */
|
||||||
@@ -5665,7 +5705,7 @@ static const char *test_socks(void) {
|
|||||||
mbuf_resize(&c->recv_mbuf, 10000000);
|
mbuf_resize(&c->recv_mbuf, 10000000);
|
||||||
|
|
||||||
/* Run event loop. Use more cycles to let file download complete. */
|
/* Run event loop. Use more cycles to let file download complete. */
|
||||||
poll_until(&mgr, 10, c_str_ne, status, (void *) "");
|
poll_until(&mgr, 15, c_str_ne, status, (void *) "");
|
||||||
ASSERT_STREQ(status, "success");
|
ASSERT_STREQ(status, "success");
|
||||||
|
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
@@ -5750,6 +5790,7 @@ const char *tests_run(const char *filter) {
|
|||||||
RUN_TEST(test_mqtt_broker);
|
RUN_TEST(test_mqtt_broker);
|
||||||
#endif
|
#endif
|
||||||
RUN_TEST(test_dns_encode);
|
RUN_TEST(test_dns_encode);
|
||||||
|
RUN_TEST(test_dns_encode_name);
|
||||||
RUN_TEST(test_dns_uncompress);
|
RUN_TEST(test_dns_uncompress);
|
||||||
RUN_TEST(test_dns_decode);
|
RUN_TEST(test_dns_decode);
|
||||||
RUN_TEST(test_dns_decode_truncated);
|
RUN_TEST(test_dns_decode_truncated);
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Mongoose is distributed as two files, `mongoose.c` and `mongoose.h` for ease of integration.
|
Mongoose is distributed as two files, `mongoose.c` and `mongoose.h` for ease of integration.
|
||||||
However, when developing Mongoose itself, it can be quite difficult to work with them.
|
However, when developing Mongoose itself, it can be quite difficult to work with them.
|
||||||
Internally, these files are an _amalgamation_ of source an header modules.
|
Internally, these files are an _amalgamation_ of source and header modules.
|
||||||
This directory contains utilities to split and re-constitute amalgamated files.
|
This directory contains utilities to split and re-constitute amalgamated files.
|
||||||
|
|
||||||
Here's how `mongoose.c` can be split into its consituent parts:
|
Here's how `mongoose.c` can be split into its consituent parts:
|
||||||
|
|||||||
+33
-13
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# Generate a reversible amalgamation of several C source files
|
# Generate a reversible amalgamation of several C source files
|
||||||
# along with their required internal headers.
|
# along with their required internal headers.
|
||||||
@@ -32,10 +32,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import io
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Produce an amalgamated source')
|
parser = argparse.ArgumentParser(description='Produce an amalgamated source')
|
||||||
parser.add_argument('--prefix', default="NS",
|
parser.add_argument('--prefix', default="NS",
|
||||||
@@ -59,16 +59,17 @@ parser.add_argument('--exportable-headers', dest="export", action='store_true',
|
|||||||
help='allow exporting internal headers')
|
help='allow exporting internal headers')
|
||||||
parser.add_argument('-I', default=['.'], dest='include_path', help='include path', action='append')
|
parser.add_argument('-I', default=['.'], dest='include_path', help='include path', action='append')
|
||||||
parser.add_argument('sources', nargs='*', help='sources')
|
parser.add_argument('sources', nargs='*', help='sources')
|
||||||
|
parser.add_argument('--license', dest="license", help='License file')
|
||||||
|
|
||||||
class File(object):
|
class File(object):
|
||||||
def __init__(self, name, parent_name):
|
def __init__(self, name, parent_name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.parent_name = parent_name
|
self.parent_name = parent_name
|
||||||
self.buf = StringIO()
|
self.buf = io.StringIO()
|
||||||
emit_file(self.buf, self.name, self.parent_name)
|
emit_file(self.buf, self.name, self.parent_name)
|
||||||
|
|
||||||
def emit(self):
|
def emit(self):
|
||||||
print self.buf.getvalue(),
|
print('%s' % self.buf.getvalue(), end='')
|
||||||
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -103,25 +104,39 @@ def resolve(path, parent_name):
|
|||||||
break
|
break
|
||||||
if os.path.exists(p) and not args.norel:
|
if os.path.exists(p) and not args.norel:
|
||||||
p = os.path.realpath(p).replace('%s%s' % (os.getcwd(), os.sep), '')
|
p = os.path.realpath(p).replace('%s%s' % (os.getcwd(), os.sep), '')
|
||||||
# print >>sys.stderr, '%s %s -> %s (cwd %s)' % (path, parent_name, p, os.getcwd())
|
|
||||||
return p.replace(os.sep, '/')
|
return p.replace(os.sep, '/')
|
||||||
|
|
||||||
def emit_line_directive(out, name, parent_name):
|
def emit_line_directive(out, name, parent_name):
|
||||||
print >>out, '''#ifdef %(prefix)s_MODULE_LINES
|
print ('''#ifdef %(prefix)s_MODULE_LINES
|
||||||
#line 1 "%(name)s"
|
#line 1 "%(name)s"
|
||||||
#endif''' % dict(
|
#endif''' % dict(
|
||||||
prefix = args.prefix,
|
prefix = args.prefix,
|
||||||
name = resolve(name, parent_name),
|
name = resolve(name, parent_name),
|
||||||
)
|
), file=out)
|
||||||
|
|
||||||
def emit_body(out, name, parent_name):
|
def emit_body(out, name, parent_name):
|
||||||
resolved_name = resolve(name, parent_name)
|
resolved_name = resolve(name, parent_name)
|
||||||
if not args.strict and not os.path.exists(resolved_name):
|
if not args.strict and not os.path.exists(resolved_name):
|
||||||
print >>out, '#include "%s"' % (name,)
|
print('#include "%s"' % name, file=out)
|
||||||
return
|
return
|
||||||
|
|
||||||
with open(resolved_name) as f:
|
with open(resolved_name) as f:
|
||||||
|
in_comment = False
|
||||||
|
comment = ''
|
||||||
for l in f:
|
for l in f:
|
||||||
|
if in_comment:
|
||||||
|
comment += l
|
||||||
|
if re.match('\s*\*/$', l):
|
||||||
|
in_comment = False
|
||||||
|
if not re.match('.*Copyright.*Cesanta', comment, re.M | re.S):
|
||||||
|
out.write(comment)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if re.match('/\*$', l):
|
||||||
|
in_comment = True
|
||||||
|
comment = l
|
||||||
|
continue
|
||||||
|
|
||||||
match = re.match('( *#include "(.*)")', l)
|
match = re.match('( *#include "(.*)")', l)
|
||||||
if match:
|
if match:
|
||||||
all, path_to_include = match.groups()
|
all, path_to_include = match.groups()
|
||||||
@@ -129,9 +144,9 @@ def emit_body(out, name, parent_name):
|
|||||||
if not should_ignore(path_to_include, parent_name):
|
if not should_ignore(path_to_include, parent_name):
|
||||||
already_included.add(path_to_include)
|
already_included.add(path_to_include)
|
||||||
includes.append(File(path_to_include, resolved_name))
|
includes.append(File(path_to_include, resolved_name))
|
||||||
print >>out, '/* Amalgamated: %s */' % (all,)
|
print('/* Amalgamated: %s */' % all, file=out)
|
||||||
else:
|
else:
|
||||||
print >>out, l,
|
out.write(l)
|
||||||
|
|
||||||
|
|
||||||
def emit_file(out, name, parent_name):
|
def emit_file(out, name, parent_name):
|
||||||
@@ -155,15 +170,20 @@ if sys.platform == "win32":
|
|||||||
import os, msvcrt
|
import os, msvcrt
|
||||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||||
|
|
||||||
|
|
||||||
|
if args.license:
|
||||||
|
with open(args.license) as f:
|
||||||
|
print(f.read())
|
||||||
|
|
||||||
if args.public:
|
if args.public:
|
||||||
print '#include "%s"' % (args.public)
|
print('#include "%s"' % args.public)
|
||||||
|
|
||||||
for i in includes:
|
for i in includes:
|
||||||
i.emit()
|
i.emit()
|
||||||
|
|
||||||
if args.export:
|
if args.export:
|
||||||
print '#ifndef %s_EXPORT_INTERNAL_HEADERS' % (args.prefix,)
|
print('#ifndef %s_EXPORT_INTERNAL_HEADERS' % (args.prefix,))
|
||||||
for i in sources:
|
for i in sources:
|
||||||
i.emit()
|
i.emit()
|
||||||
if args.export:
|
if args.export:
|
||||||
print '#endif /* %s_EXPORT_INTERNAL_HEADERS */' % (args.prefix,)
|
print('#endif /* %s_EXPORT_INTERNAL_HEADERS */' % (args.prefix,))
|
||||||
Reference in new issue
Block a user