lwsws: remove no longer extant D option from help string
[platform/upstream/libwebsockets.git] / plugins / protocol_esp32_lws_ota.c
1 /*
2  * ESP32 OTA update protocol handler
3  *
4  * Copyright (C) 2017 Andy Green <andy@warmcat.com>
5  *
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.
10  *
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.
15  *
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,
19  *  MA  02110-1301  USA 
20  *
21  */
22 #include <string.h>
23 #include <esp_partition.h>
24 #include <esp_ota_ops.h>
25 #include <nvs.h>
26
27 struct per_session_data__esplws_ota {
28         struct lws_spa *spa;
29         char filename[32];
30         char result[LWS_PRE + 512];
31         int result_len;
32         int filename_length;
33         esp_ota_handle_t otahandle;
34         const esp_partition_t *part;
35         long file_length;
36         nvs_handle nvh;
37         TimerHandle_t reboot_timer;
38 };
39
40 struct per_vhost_data__esplws_ota {
41         struct lws_context *context;
42         struct lws_vhost *vhost;
43         const struct lws_protocols *protocol;
44 };
45
46 static const char * const ota_param_names[] = {
47         "upload",
48 };
49
50 enum enum_ota_param_names {
51         EPN_UPLOAD,
52 };
53
54 static void ota_reboot_timer_cb(TimerHandle_t t)
55 {
56         esp_restart();
57 }
58
59 const esp_partition_t *
60 ota_choose_part(void)
61 {
62         const esp_partition_t *bootpart, *part = NULL;
63         esp_partition_iterator_t i;
64
65         bootpart = lws_esp_ota_get_boot_partition();
66         i = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
67         while (i) {
68                 part = esp_partition_get(i);
69
70                 /* cannot update ourselves */
71                 if (part == bootpart)
72                         goto next;
73
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)
77                         goto next;
78
79                 break;
80
81 next:
82                 i = esp_partition_next(i);
83         }
84
85         if (!i) {
86                 lwsl_err("Can't find good OTA part\n");
87                 return NULL;
88         }
89         lwsl_notice("Directing OTA to part type %d/%d start 0x%x\n",
90                         part->type, part->subtype,
91                         (uint32_t)part->address);
92
93         return part;
94 }
95
96 static int
97 ota_file_upload_cb(void *data, const char *name, const char *filename,
98                char *buf, int len, enum lws_spa_fileupload_states state)
99 {
100         struct per_session_data__esplws_ota *pss =
101                         (struct per_session_data__esplws_ota *)data;
102
103         switch (state) {
104         case LWS_UFS_OPEN:
105                 lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename);
106                 strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
107                 if (strcmp(name, "ota"))
108                         return 1;
109
110                 pss->part = ota_choose_part();
111                 if (!pss->part)
112                         return 1;
113
114                 if (esp_ota_begin(pss->part, (long)-1, &pss->otahandle) != ESP_OK) {
115                         lwsl_err("OTA: Failed to begin\n");
116                         return 1;
117                 }
118
119                 pss->file_length = 0;
120                 break;
121
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");
126                         return 1;
127                 }
128
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");
134                         return 1;
135                 }
136                 pss->file_length += len;
137
138                 if (state == LWS_UFS_CONTENT)
139                         break;
140
141                 lwsl_notice("LWS_UFS_FINAL_CONTENT\n");
142                 if (esp_ota_end(pss->otahandle) != ESP_OK) {
143                         lwsl_err("OTA: end failed\n");
144                         return 1;
145                 }
146
147                 if (esp_ota_set_boot_partition(pss->part) != ESP_OK) {
148                         lwsl_err("OTA: set boot part failed\n");
149                         return 1;
150                 }
151
152                 pss->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, NULL,
153                                                  ota_reboot_timer_cb);
154                 xTimerStart(pss->reboot_timer, 0);
155                 break;
156         }
157
158         return 0;
159 }
160
161 static int
162 callback_esplws_ota(struct lws *wsi, enum lws_callback_reasons reason,
163                     void *user, void *in, size_t len)
164 {
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;
173         int n;
174
175         switch (reason) {
176
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);
184                 break;
185
186         case LWS_CALLBACK_PROTOCOL_DESTROY:
187                 if (!vhd)
188                         break;
189                 break;
190
191         /* OTA POST handling */
192
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);
196                 if (!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);
200                         if (!pss->spa)
201                                 return -1;
202
203                         pss->filename[0] = '\0';
204                         pss->file_length = 0;
205                 }
206
207                 /* let it parse the POST data */
208                 if (lws_spa_process(pss->spa, in, len))
209                         return -1;
210                 break;
211
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);
216
217                 pss->result_len = snprintf(pss->result + LWS_PRE, sizeof(pss->result) - LWS_PRE - 1,
218                         "Rebooting after OTA update");
219
220                 if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
221                         goto bail;
222
223                 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
224                                 (unsigned char *)"text/html", 9, &p, end))
225                         goto bail;
226                 if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end))
227                         goto bail;
228                 if (lws_finalize_http_header(wsi, &p, end))
229                         goto bail;
230
231                 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
232                 if (n < 0)
233                         goto bail;
234
235                 lws_callback_on_writable(wsi);
236                 break;
237
238         case LWS_CALLBACK_HTTP_WRITEABLE:
239                 lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",
240                            pss->result_len);
241                 n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
242                               pss->result_len, LWS_WRITE_HTTP);
243                 if (n < 0)
244                         return 1;
245
246                 if (lws_http_transaction_completed(wsi))
247                         return 1;
248
249                 /* stop further service so we don't serve the probe GET to see if we rebooted */
250                 while (1);
251
252                 break;
253
254         case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
255                 /* called when our wsi user_space is going to be destroyed */
256                 if (pss->spa) {
257                         lws_spa_destroy(pss->spa);
258                         pss->spa = NULL;
259                 }
260                 break;
261
262         default:
263                 break;
264         }
265
266         return 0;
267
268 bail:
269         return 1;
270 }
271
272 #define LWS_PLUGIN_PROTOCOL_ESPLWS_OTA \
273         { \
274                 "esplws-ota", \
275                 callback_esplws_ota, \
276                 sizeof(struct per_session_data__esplws_ota), \
277                 4096, 0, NULL, 900 \
278         }
279