diff --git a/README.md b/README.md
index 17bbfe45aac44b56742f655534982507fb0824d4..5235006919355f2abac20f9e43956fedf50401d3 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Check out [Mongoose OS](https://mongoose-os.com) - open source embedded operatin
 # Support
 - [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)
-- [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)
 - [Check our latest releases](https://github.com/cesanta/mongoose/releases)
 
diff --git a/mongoose.c b/mongoose.c
index 9026762e34ae0c32fc1cd2ab63c09f17b0692e07..9571d57493d09265b90187e6a3f30b149809df19 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -88,6 +88,9 @@ extern void *(*test_calloc)(size_t count, size_t size);
 #if MG_ENABLE_HTTP
 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
  * in the HTTP chunked encoding, by collapsing data chunks to the
@@ -2705,8 +2708,8 @@ static int mg_resolve2(const char *host, struct in_addr *ina) {
     return 0;
   }
   for (p = servinfo; p != NULL; p = p->ai_next) {
-    memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *));
-    memcpy(ina, &h->sin_addr, sizeof(ina));
+    memcpy(&h, &p->ai_addr, sizeof(h));
+    memcpy(ina, &h->sin_addr, sizeof(*ina));
   }
   freeaddrinfo(servinfo);
   return 1;
@@ -3241,6 +3244,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);
 }
 
+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,
                                      MG_CB(mg_event_handler_t callback,
                                            void *user_data),
@@ -3252,6 +3265,8 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
 
   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) {
     return NULL;
   }
@@ -3365,10 +3380,7 @@ struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
   opts.user_data = user_data;
 #endif
 
-  if (callback == NULL) {
-    MG_SET_PTRPTR(opts.error_string, "handler is required");
-    return NULL;
-  }
+  if (callback == NULL) callback = mg_ev_handler_empty;
 
   MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
 
@@ -4956,7 +4968,7 @@ static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) {
               : 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,
                                           char *identity,
                                           unsigned int max_identity_len,
@@ -5022,10 +5034,10 @@ static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx,
   (void) ctx;
   (void) identity;
   (void) key_str;
-  /* Krypton does not support PSK. */
+  /* Krypton / LibreSSL does not support PSK. */
   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 *ca_cert) {
@@ -5999,20 +6011,29 @@ struct mg_http_proto_data {
   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_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
     struct mg_connect_opts opts, const char *scheme1, const char *scheme2,
     const char *scheme_ssl1, const char *scheme_ssl2, const char *url,
     struct mg_str *path, struct mg_str *user_info, struct mg_str *host);
 
-static struct mg_http_proto_data *mg_http_get_proto_data(
+MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data(
     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;
-  }
+  /* 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(
+    struct mg_connection *c) {
   return (struct mg_http_proto_data *) c->proto_data;
 }
 
@@ -6068,7 +6089,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;
 #if MG_ENABLE_FILESYSTEM
   mg_http_free_proto_data_file(&pd->file);
@@ -6341,7 +6362,8 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
       /* Rate-limited */
     }
     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;
       mg_http_free_proto_data_file(&pd->file);
     }
@@ -6477,12 +6499,12 @@ struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc,
   int matched, matched_max = 0;
   struct mg_http_endpoint *ep;
 
-  if (nc == NULL) {
-    return NULL;
-  }
+  if (nc == NULL) return NULL;
 
   pd = mg_http_get_proto_data(nc);
 
+  if (pd == NULL) return NULL;
+
   ep = pd->endpoints;
   while (ep != NULL) {
     if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) {
@@ -6554,13 +6576,13 @@ void mg_http_handler(struct mg_connection *nc, int ev,
   if (ev == MG_EV_CLOSE) {
 #if MG_ENABLE_HTTP_CGI
     /* 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->flags |= MG_F_CLOSE_IMMEDIATELY;
     }
 #endif
 #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.
        * Finish part and request with an error flag.
@@ -6590,14 +6612,14 @@ void mg_http_handler(struct mg_connection *nc, int ev,
       deliver_chunk(nc, hm, req_len);
       mg_http_call_endpoint_handler(nc, ev2, hm);
     }
-    pd->rcvd = 0;
-    if (pd->endpoint_handler != NULL && pd->endpoint_handler != nc->handler) {
+    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 (pd->file.fp != NULL) {
+  if (pd != NULL && pd->file.fp != NULL) {
     mg_http_transfer_file_data(nc);
   }
 #endif
@@ -6605,7 +6627,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
   mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
 
 #if MG_ENABLE_HTTP_STREAMING_MULTIPART
-  if (pd->mp_stream.boundary != NULL &&
+  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;
@@ -6620,11 +6642,16 @@ void mg_http_handler(struct mg_connection *nc, int ev,
 
   if (ev == MG_EV_RECV) {
     struct mg_str *s;
-    pd->rcvd += *(int *) ev_data;
 
   again:
     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 &&
         (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL &&
         mg_vcasecmp(s, "chunked") == 0) {
@@ -6736,18 +6763,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
       /* If this is a CGI request, we are not done either. */
       if (pd->cgi.cgi_nc != NULL) request_done = 0;
 #endif
-      if (request_done) {
-        /* 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;
-        }
-      }
+      if (request_done && io->len > 0) goto again;
     }
   }
 }
@@ -8887,6 +8903,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
   if (new_ep == NULL) return;
 
   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));
   if (opts.auth_domain != NULL && opts.auth_file != NULL) {
     new_ep->auth_domain = strdup(opts.auth_domain);
@@ -10824,7 +10841,7 @@ static const char *scanto(const char *p, struct mg_str *s) {
 MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   uint8_t header;
   size_t len = 0, len_len = 0;
-  const char *p, *end;
+  const char *p, *end, *eop = &io->buf[io->len];
   unsigned char lc = 0;
   int cmd;
 
@@ -10835,7 +10852,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   /* decode mqtt variable length */
   len = len_len = 0;
   p = io->buf + 1;
-  while ((size_t)(p - io->buf) < io->len) {
+  while (p < eop) {
     lc = *((const unsigned char *) p++);
     len += (lc & 0x7f) << 7 * len_len;
     len_len++;
@@ -10844,9 +10861,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   }
 
   end = p + len;
-  if (lc & 0x80 || len > (io->len - (p - io->buf))) {
-    return MG_MQTT_ERROR_INCOMPLETE_MSG;
-  }
+  if (lc & 0x80 || end > eop) return MG_MQTT_ERROR_INCOMPLETE_MSG;
 
   mm->cmd = cmd;
   mm->qos = MG_MQTT_GET_QOS(header);
@@ -10900,7 +10915,9 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
     case MG_MQTT_CMD_PUBREL:
     case MG_MQTT_CMD_PUBCOMP:
     case MG_MQTT_CMD_SUBACK:
+      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
       mm->message_id = getu16(p);
+      p += 2;
       break;
     case MG_MQTT_CMD_PUBLISH: {
       p = scanto(p, &mm->topic);
@@ -15214,7 +15231,8 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
 #include <lwip/tcp.h>
 #include <lwip/tcpip.h>
 #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
 #include <lwip/tcp_impl.h>
 #endif
@@ -15254,9 +15272,35 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
 #define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
 #endif
 
-#if NO_SYS
-#define tcpip_callback(fn, arg) (fn)(arg)
-typedef void (*tcpip_callback_fn)(void *arg);
+#if !NO_SYS
+#if LWIP_TCPIP_CORE_LOCKING
+/* 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
 
 void mg_lwip_if_init(struct mg_iface *iface);
@@ -15265,7 +15309,8 @@ void mg_lwip_if_add_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);
 
-#if defined(RTOS_SDK) || defined(ESP_PLATFORM)
+// If compiling for Mongoose OS.
+#ifdef MGOS
 extern void mgos_lock();
 extern void mgos_unlock();
 #else
@@ -15434,7 +15479,7 @@ static void mg_lwip_if_connect_tcp_tcpip(void *arg) {
 void mg_lwip_if_connect_tcp(struct mg_connection *nc,
                             const union socket_address *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);
 }
 
 /*
@@ -15527,7 +15572,7 @@ static void mg_lwip_if_connect_udp_tcpip(void *arg) {
 }
 
 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) {
@@ -15613,7 +15658,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) {
   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;
 }
 
@@ -15639,7 +15684,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) {
   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;
 }
 
@@ -15664,7 +15709,7 @@ static void mg_lwip_tcp_write_tcpip(void *arg) {
   if (len == 0) {
     DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen,
          tpcb->unsent, tpcb->unacked));
-    tcpip_callback(tcp_output_tcpip, tpcb);
+    mg_lwip_netif_run_on_tcpip(tcp_output_tcpip, tpcb);
     ctx->ret = 0;
     return;
   }
@@ -15707,7 +15752,7 @@ int mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) {
   struct tcp_pcb *tpcb = cs->pcb.tcp;
   if (tpcb == NULL) return -1;
   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;
 }
 
@@ -15739,7 +15784,7 @@ static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data,
   if (p == NULL) return 0;
   memcpy(p->payload, data, len);
   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;
   pbuf_free(p);
   return (cs->err == ERR_OK ? (int) len : -2);
@@ -15800,7 +15845,7 @@ static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
   mgos_unlock();
   if (res > 0) {
     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;
 }
@@ -15827,7 +15872,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
       tcp_arg(tpcb, NULL);
       DBG(("%p tcp_close %p", nc, tpcb));
       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) {
       struct pbuf *seg = cs->rx_chain;
@@ -15841,7 +15886,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
     struct udp_pcb *upcb = cs->pcb.udp;
     if (upcb != NULL) {
       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));
     MG_FREE(cs);
@@ -16002,6 +16047,10 @@ void mg_lwip_if_init(struct mg_iface *iface) {
   LOG(LL_INFO, ("Mongoose %s, LwIP %u.%u.%u", MG_VERSION, LWIP_VERSION_MAJOR,
                 LWIP_VERSION_MINOR, LWIP_VERSION_REVISION));
   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) {
@@ -16043,7 +16092,7 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
     if (nc->sock != INVALID_SOCKET &&
         !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != 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 (num_timers == 0 || nc->ev_timer_time < min_timer) {
diff --git a/mongoose.h b/mongoose.h
index 0f85b18b61b5289dc471fc32ad38b0f2966fe8a2..28d628555678f30ce2f04b92db8f220b3533b491 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -58,8 +58,9 @@
 #define CS_P_NRF51 12
 #define CS_P_NRF52 10
 #define CS_P_PIC32 11
+#define CS_P_RS14100 18
 #define CS_P_STM32 16
-/* Next id: 18 */
+/* Next id: 19 */
 
 /* If not specified explicitly, we guess platform by defines. */
 #ifndef CS_PLATFORM
@@ -91,6 +92,8 @@
 #elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
     defined(TARGET_IS_TM4C129_RA2)
 #define CS_PLATFORM CS_P_TM4C129
+#elif defined(RS14100)
+#define CS_PLATFORM CS_P_RS14100
 #elif defined(STM32)
 #define CS_PLATFORM CS_P_STM32
 #endif
@@ -125,7 +128,11 @@
 /* Amalgamated: #include "common/platforms/platform_nxp_lpc.h" */
 /* Amalgamated: #include "common/platforms/platform_nxp_kinetis.h" */
 /* Amalgamated: #include "common/platforms/platform_pic32.h" */
+/* Amalgamated: #include "common/platforms/platform_rs14100.h" */
 /* Amalgamated: #include "common/platforms/platform_stm32.h" */
+#if CS_PLATFORM == CS_P_CUSTOM
+#include <platform_custom.h>
+#endif
 
 /* Common stuff */
 
@@ -1907,6 +1914,70 @@ char *inet_ntoa(struct in_addr in);
 
 #endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */
 #ifdef MG_MODULE_LINES
+#line 1 "common/platforms/platform_rs14100.h"
+#endif
+/*
+ * 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_ */
+#ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_stm32.h"
 #endif
 /*
diff --git a/src/common/platform.h b/src/common/platform.h
index af0b4f501b05a23721ed49a91390fa8e14b8d433..af91973fb63a4bd854ed32e8963043345e8e0edf 100644
--- a/src/common/platform.h
+++ b/src/common/platform.h
@@ -22,8 +22,9 @@
 #define CS_P_NRF51 12
 #define CS_P_NRF52 10
 #define CS_P_PIC32 11
+#define CS_P_RS14100 18
 #define CS_P_STM32 16
-/* Next id: 18 */
+/* Next id: 19 */
 
 /* If not specified explicitly, we guess platform by defines. */
 #ifndef CS_PLATFORM
@@ -55,6 +56,8 @@
 #elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
     defined(TARGET_IS_TM4C129_RA2)
 #define CS_PLATFORM CS_P_TM4C129
+#elif defined(RS14100)
+#define CS_PLATFORM CS_P_RS14100
 #elif defined(STM32)
 #define CS_PLATFORM CS_P_STM32
 #endif
@@ -89,7 +92,11 @@
 #include "common/platforms/platform_nxp_lpc.h"
 #include "common/platforms/platform_nxp_kinetis.h"
 #include "common/platforms/platform_pic32.h"
+#include "common/platforms/platform_rs14100.h"
 #include "common/platforms/platform_stm32.h"
+#if CS_PLATFORM == CS_P_CUSTOM
+#include <platform_custom.h>
+#endif
 
 /* Common stuff */
 
diff --git a/src/common/platforms/arm/arm_exc.c b/src/common/platforms/arm/arm_exc.c
index 606ae5ebbe1a6f64d4123dec274f5b7dbb933655..3477e6bf3c10f7c4925071d34b16219546271620 100644
--- a/src/common/platforms/arm/arm_exc.c
+++ b/src/common/platforms/arm/arm_exc.c
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+#include "arm_exc.h"
+
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,33 +33,7 @@
 #define MGOS_ENABLE_CORE_DUMP 1
 #endif
 
-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;
-#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)
+#if __FPU_PRESENT && !defined(MGOS_BOOT_BUILD)
 static void save_s16_s31(uint32_t *dst) {
   __asm volatile(
       "\
@@ -91,17 +67,25 @@ static void print_fpu_regs(const uint32_t *regs, int off, int n) {
 }
 #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,
                             struct arm_gdb_reg_file *rf) {
   char buf[8];
   const char *name;
-  (void) ef;
+#if __MPU_PRESENT
+  MPU->CTRL = 0;  // Disable MPU.
+#endif
   portDISABLE_INTERRUPTS();
+  s_rf = rf;
   switch (isr_no) {
     case 0:
       name = "ThreadMode";
       break;
-
     case 1:
     case 7:
     case 8:
@@ -110,7 +94,6 @@ void arm_exc_handler_bottom(uint8_t isr_no, struct arm_exc_frame *ef,
     case 13:
       name = "Reserved";
       break;
-
     case 2:
       name = "NMI";
       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]);
     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]);
-    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);
-    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));
-#if ARM_HAVE_FPU && !defined(MGOS_BOOT_BUILD)
+#if !defined(MGOS_BOOT_BUILD)
   rf->fpscr = ef->fpscr;
   memcpy((uint8_t *) rf->d, ef->s, sizeof(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
   rf->fpscr = 0;
 #endif
+#endif
 #if MGOS_ENABLE_CORE_DUMP
-  mgos_cd_emit_header();
-  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();
+  mgos_cd_write();
 #endif
 #ifdef MGOS_HALT_ON_EXCEPTION
   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_dev_system_restart();
 #endif
+  (void) ef;
 }
diff --git a/src/common/platforms/arm/arm_exc.h b/src/common/platforms/arm/arm_exc.h
new file mode 100644
index 0000000000000000000000000000000000000000..63a2c1ccbbbdea910a98eaab446457de9046fb25
--- /dev/null
+++ b/src/common/platforms/arm/arm_exc.h
@@ -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);
diff --git a/src/common/platforms/arm/arm_exc_top.S b/src/common/platforms/arm/arm_exc_top.S
index 9ffade47e48f07648db9b1e069eb9fdfb7184bc7..10a7c9ae5a579ecf88ef813196b96e49db1f43ca 100644
--- a/src/common/platforms/arm/arm_exc_top.S
+++ b/src/common/platforms/arm/arm_exc_top.S
@@ -28,12 +28,12 @@ arm_exc_handler_top:
   mrseq r1, msp
   mrsne r1, psp
   // 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)
-  sub sp, #328      // sizeof(arm_gdb_reg_file)
+  sub sp, #208      // sizeof(arm_gdb_reg_file)
 #else
   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
   mov r2, sp
   // r0 -> original sp, r2 -> arm_gdb_reg_file to fill
@@ -63,6 +63,17 @@ arm_exc_handler_top:
   str r3,  [r2, #60]
   ldr r3,  [r1, #28]  // xpsr
   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
   b arm_exc_handler_bottom
diff --git a/src/common/platforms/arm/arm_nsleep100_m3.S b/src/common/platforms/arm/arm_nsleep100_m3.S
index cae920e3c4e5c96d723255fe7fdaad40017678a0..0ce16bf52d7ef49a4c66143e99ccc1d9b412b36f 100644
--- a/src/common/platforms/arm/arm_nsleep100_m3.S
+++ b/src/common/platforms/arm/arm_nsleep100_m3.S
@@ -26,11 +26,7 @@
 .global mgos_nsleep100_impl
 .global mgos_nsleep100_loop_count
 
-#ifdef STM32
-.section .RamFunc.mgos_nsleep100_impl
-#else
-.section .iram.mgos_nsleep100_impl
-#endif
+.section .text.IRAM.mgos_nsleep100_impl
 .type mgos_nsleep100_impl, %function
 .align 4
 
diff --git a/src/common/platforms/arm/arm_nsleep100_m4.S b/src/common/platforms/arm/arm_nsleep100_m4.S
index afb596fafae5986e0b78a75f859f2b49db445777..45cc32b30ebb8822cda8433b7c6bea6e2036e785 100644
--- a/src/common/platforms/arm/arm_nsleep100_m4.S
+++ b/src/common/platforms/arm/arm_nsleep100_m4.S
@@ -26,10 +26,10 @@
 .global mgos_nsleep100_impl
 .global mgos_nsleep100_loop_count
 
-#ifdef STM32
-.section .RamFunc.mgos_nsleep100_impl
-#else
+#if defined(TARGET_IS_CC3200) || defined(TARGET_IS_CC3220)
 .section .iram.mgos_nsleep100_impl
+#else
+.section .text.IRAM.mgos_nsleep100_impl
 #endif
 .type mgos_nsleep100_impl, %function
 .align 4
@@ -38,17 +38,17 @@ mgos_nsleep100_impl:
       ldr     r3, =mgos_nsleep100_loop_count
       ldr     r3, [r3]
       mul     r0, r3
-#ifdef STM32L4
+#if defined(STM32L4)
       mov     r1, #3
 #else
       mov     r1, #6
 #endif
       udiv    r0, r0, r1
-      cbz     r0, xxx
-lxx:
+      cbz     r0, mgos_nsleep100_impl_out
+mgos_nsleep100_impl_loop:
       subs    r0, #1
-      bne     lxx
-xxx:
+      bne     mgos_nsleep100_impl_loop
+mgos_nsleep100_impl_out:
       bx      lr
 .align 4
 .size mgos_nsleep100_impl, . - mgos_nsleep100_impl
diff --git a/src/common/platforms/arm/arm_nsleep100_m7.S b/src/common/platforms/arm/arm_nsleep100_m7.S
index 36c878fdb2bbccd279063d9ca30ba15e7f1125df..be6da6f7b536533bcb242e36435eaf01f011f4df 100644
--- a/src/common/platforms/arm/arm_nsleep100_m7.S
+++ b/src/common/platforms/arm/arm_nsleep100_m7.S
@@ -26,11 +26,7 @@
 .global mgos_nsleep100_impl
 .global mgos_nsleep100_loop_count
 
-#ifdef STM32
-.section .RamFunc.mgos_nsleep100_impl
-#else
-.section .iram.mgos_nsleep100_impl
-#endif
+.section .text.IRAM.mgos_nsleep100_impl
 .type mgos_nsleep100_impl, %function
 .align 4
 
diff --git a/src/common/platforms/lwip/mg_lwip_ev_mgr.c b/src/common/platforms/lwip/mg_lwip_ev_mgr.c
index ab8f19f7830b260607d6335423b33fe8daa751f4..afff2dbd281d91d7aa79ff4cebc8b9a4f933f23f 100644
--- a/src/common/platforms/lwip/mg_lwip_ev_mgr.c
+++ b/src/common/platforms/lwip/mg_lwip_ev_mgr.c
@@ -91,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,
                 LWIP_VERSION_MINOR, LWIP_VERSION_REVISION));
   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) {
@@ -132,7 +136,7 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
     if (nc->sock != INVALID_SOCKET &&
         !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != 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 (num_timers == 0 || nc->ev_timer_time < min_timer) {
diff --git a/src/common/platforms/lwip/mg_lwip_net_if.c b/src/common/platforms/lwip/mg_lwip_net_if.c
index e78d93cea598e37136ea4b012c7b4851253283a3..754ac2dfa386cb52fffa4901cdfdc16c93a54503 100644
--- a/src/common/platforms/lwip/mg_lwip_net_if.c
+++ b/src/common/platforms/lwip/mg_lwip_net_if.c
@@ -24,7 +24,8 @@
 #include <lwip/tcp.h>
 #include <lwip/tcpip.h>
 #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
 #include <lwip/tcp_impl.h>
 #endif
@@ -64,9 +65,35 @@
 #define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
 #endif
 
-#if NO_SYS
-#define tcpip_callback(fn, arg) (fn)(arg)
-typedef void (*tcpip_callback_fn)(void *arg);
+#if !NO_SYS
+#if LWIP_TCPIP_CORE_LOCKING
+/* 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
 
 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);
 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_unlock();
 #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,
                             const union socket_address *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) {
-  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) {
@@ -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) {
   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;
 }
 
@@ -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) {
   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;
 }
 
@@ -474,7 +502,7 @@ static void mg_lwip_tcp_write_tcpip(void *arg) {
   if (len == 0) {
     DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen,
          tpcb->unsent, tpcb->unacked));
-    tcpip_callback(tcp_output_tcpip, tpcb);
+    mg_lwip_netif_run_on_tcpip(tcp_output_tcpip, tpcb);
     ctx->ret = 0;
     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;
   if (tpcb == NULL) return -1;
   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;
 }
 
@@ -549,7 +577,7 @@ static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data,
   if (p == NULL) return 0;
   memcpy(p->payload, data, len);
   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;
   pbuf_free(p);
   return (cs->err == ERR_OK ? (int) len : -2);
@@ -610,7 +638,7 @@ static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
   mgos_unlock();
   if (res > 0) {
     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;
 }
@@ -637,7 +665,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
       tcp_arg(tpcb, NULL);
       DBG(("%p tcp_close %p", nc, tpcb));
       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) {
       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;
     if (upcb != NULL) {
       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));
     MG_FREE(cs);
diff --git a/src/common/platforms/platform_rs14100.h b/src/common/platforms/platform_rs14100.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e9618e572a8d4288e247d72c983dabe47f07109
--- /dev/null
+++ b/src/common/platforms/platform_rs14100.h
@@ -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_ */
diff --git a/src/common/platforms/stm32/LICENSE b/src/common/platforms/stm32/LICENSE
deleted file mode 100644
index 3d464ca1e3106eb6af66f6baf5f1f921cb6d8604..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/LICENSE
+++ /dev/null
@@ -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.
diff --git a/src/common/platforms/stm32/common.c b/src/common/platforms/stm32/common.c
deleted file mode 100644
index 9a201cafde33b00b23e6866b4e1dd58524c8a907..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/common.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/common/platforms/stm32/flash.c b/src/common/platforms/stm32/flash.c
deleted file mode 100644
index 371c6eb1ac46f0aa405e734a0e81c6dda9b19382..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/flash.c
+++ /dev/null
@@ -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;
-}
diff --git a/src/common/platforms/stm32/flash_loader.c b/src/common/platforms/stm32/flash_loader.c
deleted file mode 100644
index b508e8e2b27d770938f3be4e3d3991da349f12fa..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/flash_loader.c
+++ /dev/null
@@ -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;
-}
diff --git a/src/common/platforms/stm32/flash_loader.h b/src/common/platforms/stm32/flash_loader.h
deleted file mode 100644
index a3f072dfebd8640fd5382a1d89cb70a30edee70f..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/flash_loader.h
+++ /dev/null
@@ -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_ */
diff --git a/src/common/platforms/stm32/flasher.go b/src/common/platforms/stm32/flasher.go
deleted file mode 100644
index 36cbad1149eb3e46e93db29d1f4ee7fd7597da95..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/flasher.go
+++ /dev/null
@@ -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/common/go/fwbundle"
-	"github.com/cesanta/errors"
-)
-
-type FlashOpts struct {
-	ShareName string
-	Timeout   time.Duration
-}
-
-func Flash(fw *fwbundle.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
-}
diff --git a/src/common/platforms/stm32/stlink.h b/src/common/platforms/stm32/stlink.h
deleted file mode 100644
index ac34dd067f2c8ae11d5f5caac274235e751b21d5..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/stlink.h
+++ /dev/null
@@ -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 */
diff --git a/src/common/platforms/stm32/stm32_flash.h b/src/common/platforms/stm32/stm32_flash.h
deleted file mode 100644
index 2846ab712eb70edc3634e92706627f1e5e869587..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/stm32_flash.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef STM32_FLASH_H_
-#define STM32_FLASH_H_
-
-int stm32_flash(const char *device_name, void *data, int len);
-
-#endif
diff --git a/src/common/platforms/stm32/usb.c b/src/common/platforms/stm32/usb.c
deleted file mode 100644
index a60e99f8bb4d1575838e72361f863f078ca66419..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/usb.c
+++ /dev/null
@@ -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;
-}
diff --git a/src/common/platforms/stm32/usb.h b/src/common/platforms/stm32/usb.h
deleted file mode 100644
index 6ee4ff3b41dda11ed14ab6d847c037ebccd19f94..0000000000000000000000000000000000000000
--- a/src/common/platforms/stm32/usb.h
+++ /dev/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 */
diff --git a/src/mg_http.c b/src/mg_http.c
index 5cd6f623a62cb15eeb08a656cdd115152aca40be..ef546c3732fd193ba671ddbf5caa9d0fb0e7981c 100644
--- a/src/mg_http.c
+++ b/src/mg_http.c
@@ -177,20 +177,29 @@ struct mg_http_proto_data {
   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_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
     struct mg_connect_opts opts, const char *scheme1, const char *scheme2,
     const char *scheme_ssl1, const char *scheme_ssl2, const char *url,
     struct mg_str *path, struct mg_str *user_info, struct mg_str *host);
 
-static struct mg_http_proto_data *mg_http_get_proto_data(
+MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data(
     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;
-  }
+  /* 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(
+    struct mg_connection *c) {
   return (struct mg_http_proto_data *) c->proto_data;
 }
 
@@ -246,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;
 #if MG_ENABLE_FILESYSTEM
   mg_http_free_proto_data_file(&pd->file);
@@ -519,7 +528,8 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
       /* Rate-limited */
     }
     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;
       mg_http_free_proto_data_file(&pd->file);
     }
@@ -655,12 +665,12 @@ struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc,
   int matched, matched_max = 0;
   struct mg_http_endpoint *ep;
 
-  if (nc == NULL) {
-    return NULL;
-  }
+  if (nc == NULL) return NULL;
 
   pd = mg_http_get_proto_data(nc);
 
+  if (pd == NULL) return NULL;
+
   ep = pd->endpoints;
   while (ep != NULL) {
     if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) {
@@ -732,13 +742,13 @@ void mg_http_handler(struct mg_connection *nc, int ev,
   if (ev == MG_EV_CLOSE) {
 #if MG_ENABLE_HTTP_CGI
     /* 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->flags |= MG_F_CLOSE_IMMEDIATELY;
     }
 #endif
 #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.
        * Finish part and request with an error flag.
@@ -768,14 +778,14 @@ void mg_http_handler(struct mg_connection *nc, int ev,
       deliver_chunk(nc, hm, req_len);
       mg_http_call_endpoint_handler(nc, ev2, hm);
     }
-    pd->rcvd = 0;
-    if (pd->endpoint_handler != NULL && pd->endpoint_handler != nc->handler) {
+    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 (pd->file.fp != NULL) {
+  if (pd != NULL && pd->file.fp != NULL) {
     mg_http_transfer_file_data(nc);
   }
 #endif
@@ -783,7 +793,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
   mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
 
 #if MG_ENABLE_HTTP_STREAMING_MULTIPART
-  if (pd->mp_stream.boundary != NULL &&
+  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;
@@ -798,11 +808,16 @@ void mg_http_handler(struct mg_connection *nc, int ev,
 
   if (ev == MG_EV_RECV) {
     struct mg_str *s;
-    pd->rcvd += *(int *) ev_data;
 
   again:
     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 &&
         (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL &&
         mg_vcasecmp(s, "chunked") == 0) {
@@ -914,18 +929,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
       /* If this is a CGI request, we are not done either. */
       if (pd->cgi.cgi_nc != NULL) request_done = 0;
 #endif
-      if (request_done) {
-        /* 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;
-        }
-      }
+      if (request_done && io->len > 0) goto again;
     }
   }
 }
@@ -3065,6 +3069,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
   if (new_ep == NULL) return;
 
   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));
   if (opts.auth_domain != NULL && opts.auth_file != NULL) {
     new_ep->auth_domain = strdup(opts.auth_domain);
diff --git a/src/mg_internal.h b/src/mg_internal.h
index 0e62448e9d876579798f4db5b7e4129d9298a46f..250b6fe1000faf5910215d453fc7d715d28ddf33 100644
--- a/src/mg_internal.h
+++ b/src/mg_internal.h
@@ -84,6 +84,9 @@ extern void *(*test_calloc)(size_t count, size_t size);
 #if MG_ENABLE_HTTP
 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
  * in the HTTP chunked encoding, by collapsing data chunks to the
diff --git a/src/mg_modules.mk b/src/mg_modules.mk
index b53c53b1bf1c263d671c38dbc48a2ae94e5ffe00..e0e51bc7a683cab7b9621f2c138b8f139ebc101e 100644
--- a/src/mg_modules.mk
+++ b/src/mg_modules.mk
@@ -19,6 +19,7 @@ HEADERS = mg_common.h \
           $(COMMON)/platforms/platform_nxp_lpc.h \
           $(COMMON)/platforms/platform_nxp_kinetis.h \
           $(COMMON)/platforms/platform_pic32.h \
+          $(COMMON)/platforms/platform_rs14100.h \
           $(COMMON)/platforms/platform_stm32.h \
           $(COMMON)/platforms/lwip/mg_lwip.h \
           $(COMMON)/cs_md5.h \
diff --git a/src/mg_mqtt.c b/src/mg_mqtt.c
index 68069e8d1c1ab4fc0ccddbc680ddb6c4d46f182b..9ab66bc632c2aa5c641a0e33239e7ae55ab85fcf 100644
--- a/src/mg_mqtt.c
+++ b/src/mg_mqtt.c
@@ -24,7 +24,7 @@ static const char *scanto(const char *p, struct mg_str *s) {
 MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   uint8_t header;
   size_t len = 0, len_len = 0;
-  const char *p, *end;
+  const char *p, *end, *eop = &io->buf[io->len];
   unsigned char lc = 0;
   int cmd;
 
@@ -35,7 +35,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   /* decode mqtt variable length */
   len = len_len = 0;
   p = io->buf + 1;
-  while ((size_t)(p - io->buf) < io->len) {
+  while (p < eop) {
     lc = *((const unsigned char *) p++);
     len += (lc & 0x7f) << 7 * len_len;
     len_len++;
@@ -44,9 +44,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   }
 
   end = p + len;
-  if (lc & 0x80 || len > (io->len - (p - io->buf))) {
-    return MG_MQTT_ERROR_INCOMPLETE_MSG;
-  }
+  if (lc & 0x80 || end > eop) return MG_MQTT_ERROR_INCOMPLETE_MSG;
 
   mm->cmd = cmd;
   mm->qos = MG_MQTT_GET_QOS(header);
@@ -100,7 +98,9 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
     case MG_MQTT_CMD_PUBREL:
     case MG_MQTT_CMD_PUBCOMP:
     case MG_MQTT_CMD_SUBACK:
+      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
       mm->message_id = getu16(p);
+      p += 2;
       break;
     case MG_MQTT_CMD_PUBLISH: {
       p = scanto(p, &mm->topic);
diff --git a/src/mg_net.c b/src/mg_net.c
index 3b1b7038fccd8b46ef322ac6a542e388ba54010e..12c1b17c4a8738d2155c770e7c1d609908ca08e4 100644
--- a/src/mg_net.c
+++ b/src/mg_net.c
@@ -341,8 +341,8 @@ static int mg_resolve2(const char *host, struct in_addr *ina) {
     return 0;
   }
   for (p = servinfo; p != NULL; p = p->ai_next) {
-    memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *));
-    memcpy(ina, &h->sin_addr, sizeof(ina));
+    memcpy(&h, &p->ai_addr, sizeof(h));
+    memcpy(ina, &h->sin_addr, sizeof(*ina));
   }
   freeaddrinfo(servinfo);
   return 1;
@@ -877,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);
 }
 
+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,
                                      MG_CB(mg_event_handler_t callback,
                                            void *user_data),
@@ -888,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);
 
+  if (callback == NULL) callback = mg_ev_handler_empty;
+
   if ((nc = mg_create_connection(mgr, callback, add_sock_opts)) == NULL) {
     return NULL;
   }
@@ -1001,10 +1013,7 @@ struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
   opts.user_data = user_data;
 #endif
 
-  if (callback == NULL) {
-    MG_SET_PTRPTR(opts.error_string, "handler is required");
-    return NULL;
-  }
+  if (callback == NULL) callback = mg_ev_handler_empty;
 
   MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
 
diff --git a/src/mg_ssl_if_openssl.c b/src/mg_ssl_if_openssl.c
index ef8342fc3c92e12ac59636e8f29a1cc3076bb770..98af8e29c8dc39f6f6a85edea4bdddcc8cc24acb 100644
--- a/src/mg_ssl_if_openssl.c
+++ b/src/mg_ssl_if_openssl.c
@@ -310,7 +310,7 @@ static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) {
               : 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,
                                           char *identity,
                                           unsigned int max_identity_len,
@@ -376,10 +376,10 @@ static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx,
   (void) ctx;
   (void) identity;
   (void) key_str;
-  /* Krypton does not support PSK. */
+  /* Krypton / LibreSSL does not support PSK. */
   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 *ca_cert) {
diff --git a/test/Makefile b/test/Makefile
index 97f41c9ae817ad08fe299d04d13b11a7f40c79e3..b3457bf928dc5e8d03ba1dc758254a2ab805b779 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -11,7 +11,7 @@ COMMON_DIR_DEV = ../../common
 
 # Our dev repo and public mongoose repo have different layouts, so here
 # we make it work on both
-ifneq ("$(wildcard ../../fw)","")
+ifneq ("$(wildcard ../../common.mk)","")
 COMMON_PARENT = ../..
 else
 COMMON_PARENT = $(SRC_DIR)
diff --git a/test/unit_test.c b/test/unit_test.c
index 5901c21c36159c78bd83e13aa68ed51c069af2f1..5eb919cab761df7791938bb7ea952f0e8e60e7af 100644
--- a/test/unit_test.c
+++ b/test/unit_test.c
@@ -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) {
   struct mg_mgr mgr;
   struct mg_bind_opts bopts;
@@ -995,25 +989,21 @@ static const char *test_connection_errors(void) {
   bopts.error_string = &error_string;
 
   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(mg_bind_opt(&mgr, "tcp://8.8.8.8:88", ev_handler_empty, bopts) ==
-         NULL);
+  ASSERT(mg_bind_opt(&mgr, "tcp://8.8.8.8:88", NULL, bopts) == NULL);
   ASSERT_STREQ(error_string, "failed to open listener");
 
 #if MG_ENABLE_SSL
   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");
   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");
   bopts.ssl_cert = NULL;
   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");
 #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;
   (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) {
     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;
       break;
     case MG_EV_CLOSE:
-      (*(int *) nc->listener->user_data)++;
+      DBG(("%p == hello1 close", nc));
+      (*(int *) nc->listener->user_data) += 10;
       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;
       break;
     case MG_EV_CLOSE:
-      (*(int *) nc->listener->user_data)++;
+      DBG(("%p == hello2 close", nc));
+      (*(int *) nc->listener->user_data) += 100;
       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;
       break;
     case MG_EV_CLOSE:
-      (*(int *) nc->listener->user_data)++;
+      DBG(("%p == hello5 close", nc));
+      (*(int *) nc->listener->user_data) += 1000;
       break;
   }
 }
@@ -1823,7 +1817,7 @@ static const char *test_http_endpoints(void) {
 
   mg_mgr_init(&mgr, NULL);
   /* 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/hello2",
                             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 *) "");
   ASSERT_STREQ(buf, "[I am Hello again] 37");
 
-  ASSERT_EQ(close_count, 700);
+  ASSERT_EQ(close_count, 1117);
 
   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);
   mg_set_protocol_http_websocket(nc);
   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);
   ASSERT_EQ(status, 1);
   mg_mgr_free(&mgr);
@@ -4003,6 +3997,7 @@ static const char *test_http_chunk2(void) {
   nc.mgr = &mgr;
   nc.sock = INVALID_SOCKET;
   nc.handler = eh_chunk2;
+  mg_http_create_proto_data(&nc);
   hm.message.len = hm.body.len = ~0;
 
   s_handle_chunk_event = 0;
@@ -4135,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[] =
     "111111111111111111111111111111111111111111111111111111111111111\r\n"
     "111111111111111111111111111111111111111111111111111111111111111\r\n"
@@ -4264,13 +4253,13 @@ static const char *test_http_multipart2(void) {
            "\r\n--Asrf456BGe4h\r\n", b1, b2, b4);
 
   /* 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;
 
   mg_set_protocol_http_websocket(nc_listen);
   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: "
                               "multipart/form-data; boundary=Asrf456BGe4h",
                               multi_part_req)) != NULL);
@@ -4307,7 +4296,7 @@ static const char *test_http_multipart2(void) {
   /* Test interrupted request */
   multi_part_req[1800] = '\0';
   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",
       multi_part_req);
 
@@ -4328,7 +4317,7 @@ static const char *test_http_multipart2(void) {
 
   ASSERT(
       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",
           "\r\n--Asrf456BGe4h\r\n"
           "Content-Disposition: form-data; name=\"d\"; filename=\"small\"\r\n"
@@ -4348,7 +4337,7 @@ static const char *test_http_multipart2(void) {
    * See https://github.com/cesanta/dev/issues/6974
    * 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,
             "POST /test HTTP/1.1\r\n"
             "Connection: keep-alive\r\n"
@@ -4401,7 +4390,7 @@ static const char *test_http_multipart_pipeline(void) {
   snprintf(multi_part_req, sizeof(multi_part_req), multi_part_req_fmt,
            "\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: "
                               "multipart/form-data;boundary=Asrf456BGe4h\r\n"
                               "Connection: keep-alive",
@@ -5685,7 +5674,7 @@ static const char *test_socks(void) {
   mg_set_protocol_http_websocket(c);
 
   /* 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);
 
   /* Create HTTP client that uses socks proxy */