2 * ESP32 OTA update protocol handler
4 * Copyright (C) 2017 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 #include <esp_partition.h>
24 #include <esp_ota_ops.h>
27 struct per_session_data__esplws_ota {
30 char result[LWS_PRE + 512];
33 esp_ota_handle_t otahandle;
34 const esp_partition_t *part;
37 TimerHandle_t reboot_timer;
40 struct per_vhost_data__esplws_ota {
41 struct lws_context *context;
42 struct lws_vhost *vhost;
43 const struct lws_protocols *protocol;
46 static const char * const ota_param_names[] = {
50 enum enum_ota_param_names {
54 static void ota_reboot_timer_cb(TimerHandle_t t)
59 const esp_partition_t *
62 const esp_partition_t *bootpart, *part = NULL;
63 esp_partition_iterator_t i;
65 bootpart = lws_esp_ota_get_boot_partition();
66 i = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
68 part = esp_partition_get(i);
70 /* cannot update ourselves */
74 if (part->subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MIN ||
75 part->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_MIN +
76 ESP_PARTITION_SUBTYPE_APP_OTA_MAX)
82 i = esp_partition_next(i);
86 lwsl_err("Can't find good OTA part\n");
89 lwsl_notice("Directing OTA to part type %d/%d start 0x%x\n",
90 part->type, part->subtype,
91 (uint32_t)part->address);
97 ota_file_upload_cb(void *data, const char *name, const char *filename,
98 char *buf, int len, enum lws_spa_fileupload_states state)
100 struct per_session_data__esplws_ota *pss =
101 (struct per_session_data__esplws_ota *)data;
105 lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename);
106 strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
107 if (strcmp(name, "ota"))
110 pss->part = ota_choose_part();
114 if (esp_ota_begin(pss->part, (long)-1, &pss->otahandle) != ESP_OK) {
115 lwsl_err("OTA: Failed to begin\n");
119 pss->file_length = 0;
122 case LWS_UFS_FINAL_CONTENT:
123 case LWS_UFS_CONTENT:
124 if (pss->file_length + len > pss->part->size) {
125 lwsl_err("OTA: incoming file too large\n");
129 lwsl_debug("writing 0x%lx... 0x%lx\n",
130 pss->part->address + pss->file_length,
131 pss->part->address + pss->file_length + len);
132 if (esp_ota_write(pss->otahandle, buf, len) != ESP_OK) {
133 lwsl_err("OTA: Failed to write\n");
136 pss->file_length += len;
138 if (state == LWS_UFS_CONTENT)
141 lwsl_notice("LWS_UFS_FINAL_CONTENT\n");
142 if (esp_ota_end(pss->otahandle) != ESP_OK) {
143 lwsl_err("OTA: end failed\n");
147 if (esp_ota_set_boot_partition(pss->part) != ESP_OK) {
148 lwsl_err("OTA: set boot part failed\n");
152 pss->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, NULL,
153 ota_reboot_timer_cb);
154 xTimerStart(pss->reboot_timer, 0);
162 callback_esplws_ota(struct lws *wsi, enum lws_callback_reasons reason,
163 void *user, void *in, size_t len)
165 struct per_session_data__esplws_ota *pss =
166 (struct per_session_data__esplws_ota *)user;
167 struct per_vhost_data__esplws_ota *vhd =
168 (struct per_vhost_data__esplws_ota *)
169 lws_protocol_vh_priv_get(lws_get_vhost(wsi),
170 lws_get_protocol(wsi));
171 unsigned char buf[LWS_PRE + 384], *start = buf + LWS_PRE - 1, *p = start,
172 *end = buf + sizeof(buf) - 1;
177 case LWS_CALLBACK_PROTOCOL_INIT:
178 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
179 lws_get_protocol(wsi),
180 sizeof(struct per_vhost_data__esplws_ota));
181 vhd->context = lws_get_context(wsi);
182 vhd->protocol = lws_get_protocol(wsi);
183 vhd->vhost = lws_get_vhost(wsi);
186 case LWS_CALLBACK_PROTOCOL_DESTROY:
191 /* OTA POST handling */
193 case LWS_CALLBACK_HTTP_BODY:
194 /* create the POST argument parser if not already existing */
195 //lwsl_notice("LWS_CALLBACK_HTTP_BODY (ota) %d %d %p\n", (int)pss->file_length, (int)len, pss->spa);
197 pss->spa = lws_spa_create(wsi, ota_param_names,
198 ARRAY_SIZE(ota_param_names), 4096,
199 ota_file_upload_cb, pss);
203 pss->filename[0] = '\0';
204 pss->file_length = 0;
207 /* let it parse the POST data */
208 if (lws_spa_process(pss->spa, in, len))
212 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
213 lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION (ota)\n");
214 /* call to inform no more payload data coming */
215 lws_spa_finalize(pss->spa);
217 pss->result_len = snprintf(pss->result + LWS_PRE, sizeof(pss->result) - LWS_PRE - 1,
218 "Rebooting after OTA update");
220 if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
223 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
224 (unsigned char *)"text/html", 9, &p, end))
226 if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end))
228 if (lws_finalize_http_header(wsi, &p, end))
231 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
235 lws_callback_on_writable(wsi);
238 case LWS_CALLBACK_HTTP_WRITEABLE:
239 lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",
241 n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
242 pss->result_len, LWS_WRITE_HTTP);
246 if (lws_http_transaction_completed(wsi))
249 /* stop further service so we don't serve the probe GET to see if we rebooted */
254 case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
255 /* called when our wsi user_space is going to be destroyed */
257 lws_spa_destroy(pss->spa);
272 #define LWS_PLUGIN_PROTOCOL_ESPLWS_OTA \
275 callback_esplws_ota, \
276 sizeof(struct per_session_data__esplws_ota), \