esp_https_ota.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <esp_https_ota.h>
  17. #include <esp_log.h>
  18. #define IMAGE_HEADER_SIZE sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t) + 1
  19. #define DEFAULT_OTA_BUF_SIZE IMAGE_HEADER_SIZE
  20. static const char *TAG = "esp_https_ota";
  21. typedef enum {
  22. ESP_HTTPS_OTA_INIT,
  23. ESP_HTTPS_OTA_BEGIN,
  24. ESP_HTTPS_OTA_IN_PROGRESS,
  25. ESP_HTTPS_OTA_SUCCESS,
  26. } esp_https_ota_state;
  27. struct esp_https_ota_handle {
  28. esp_ota_handle_t update_handle;
  29. const esp_partition_t *update_partition;
  30. esp_http_client_handle_t http_client;
  31. char *ota_upgrade_buf;
  32. size_t ota_upgrade_buf_size;
  33. int binary_file_len;
  34. esp_https_ota_state state;
  35. };
  36. typedef struct esp_https_ota_handle esp_https_ota_t;
  37. static bool process_again(int status_code)
  38. {
  39. switch (status_code) {
  40. case HttpStatus_MovedPermanently:
  41. case HttpStatus_Found:
  42. case HttpStatus_Unauthorized:
  43. return true;
  44. default:
  45. return false;
  46. }
  47. return false;
  48. }
  49. static esp_err_t _http_handle_response_code(esp_http_client_handle_t http_client, int status_code)
  50. {
  51. esp_err_t err;
  52. if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found) {
  53. err = esp_http_client_set_redirection(http_client);
  54. if (err != ESP_OK) {
  55. ESP_LOGE(TAG, "URL redirection Failed");
  56. return err;
  57. }
  58. } else if (status_code == HttpStatus_Unauthorized) {
  59. esp_http_client_add_auth(http_client);
  60. }
  61. char upgrade_data_buf[DEFAULT_OTA_BUF_SIZE];
  62. if (process_again(status_code)) {
  63. while (1) {
  64. int data_read = esp_http_client_read(http_client, upgrade_data_buf, DEFAULT_OTA_BUF_SIZE);
  65. if (data_read < 0) {
  66. ESP_LOGE(TAG, "Error: SSL data read error");
  67. return ESP_FAIL;
  68. } else if (data_read == 0) {
  69. return ESP_OK;
  70. }
  71. }
  72. }
  73. return ESP_OK;
  74. }
  75. static esp_err_t _http_connect(esp_http_client_handle_t http_client)
  76. {
  77. esp_err_t err = ESP_FAIL;
  78. int status_code;
  79. do {
  80. err = esp_http_client_open(http_client, 0);
  81. if (err != ESP_OK) {
  82. ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
  83. return err;
  84. }
  85. esp_http_client_fetch_headers(http_client);
  86. status_code = esp_http_client_get_status_code(http_client);
  87. if (_http_handle_response_code(http_client, status_code) != ESP_OK) {
  88. return ESP_FAIL;
  89. }
  90. } while (process_again(status_code));
  91. return err;
  92. }
  93. static void _http_cleanup(esp_http_client_handle_t client)
  94. {
  95. esp_http_client_close(client);
  96. esp_http_client_cleanup(client);
  97. }
  98. static esp_err_t _ota_write(esp_https_ota_t *https_ota_handle, const void *buffer, size_t buf_len)
  99. {
  100. if (buffer == NULL || https_ota_handle == NULL) {
  101. return ESP_FAIL;
  102. }
  103. esp_err_t err = esp_ota_write(https_ota_handle->update_handle, buffer, buf_len);
  104. if (err != ESP_OK) {
  105. ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%d", err);
  106. } else {
  107. https_ota_handle->binary_file_len += buf_len;
  108. ESP_LOGD(TAG, "Written image length %d", https_ota_handle->binary_file_len);
  109. err = ESP_ERR_HTTPS_OTA_IN_PROGRESS;
  110. }
  111. return err;
  112. }
  113. esp_err_t esp_https_ota_begin(esp_https_ota_config_t *ota_config, esp_https_ota_handle_t *handle)
  114. {
  115. esp_err_t err;
  116. if (handle == NULL || ota_config == NULL || ota_config->http_config == NULL) {
  117. ESP_LOGE(TAG, "esp_https_ota_begin: Invalid argument");
  118. if (handle) {
  119. *handle = NULL;
  120. }
  121. return ESP_ERR_INVALID_ARG;
  122. }
  123. #if !CONFIG_OTA_ALLOW_HTTP
  124. if (!ota_config->http_config->cert_pem) {
  125. ESP_LOGE(TAG, "Server certificate not found in esp_http_client config");
  126. *handle = NULL;
  127. return ESP_ERR_INVALID_ARG;
  128. }
  129. #endif
  130. esp_https_ota_t *https_ota_handle = calloc(1, sizeof(esp_https_ota_t));
  131. if (!https_ota_handle) {
  132. ESP_LOGE(TAG, "Couldn't allocate memory to upgrade data buffer");
  133. *handle = NULL;
  134. return ESP_ERR_NO_MEM;
  135. }
  136. /* Initiate HTTP Connection */
  137. https_ota_handle->http_client = esp_http_client_init(ota_config->http_config);
  138. if (https_ota_handle->http_client == NULL) {
  139. ESP_LOGE(TAG, "Failed to initialise HTTP connection");
  140. err = ESP_FAIL;
  141. goto failure;
  142. }
  143. err = _http_connect(https_ota_handle->http_client);
  144. if (err != ESP_OK) {
  145. ESP_LOGE(TAG, "Failed to establish HTTP connection");
  146. goto http_cleanup;
  147. }
  148. https_ota_handle->update_partition = NULL;
  149. ESP_LOGI(TAG, "Starting OTA...");
  150. https_ota_handle->update_partition = esp_ota_get_next_update_partition(NULL);
  151. if (https_ota_handle->update_partition == NULL) {
  152. ESP_LOGE(TAG, "Passive OTA partition not found");
  153. err = ESP_FAIL;
  154. goto http_cleanup;
  155. }
  156. ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
  157. https_ota_handle->update_partition->subtype, https_ota_handle->update_partition->address);
  158. const int alloc_size = (ota_config->http_config->buffer_size > DEFAULT_OTA_BUF_SIZE) ?
  159. ota_config->http_config->buffer_size : DEFAULT_OTA_BUF_SIZE;
  160. https_ota_handle->ota_upgrade_buf = (char *)malloc(alloc_size);
  161. if (!https_ota_handle->ota_upgrade_buf) {
  162. ESP_LOGE(TAG, "Couldn't allocate memory to upgrade data buffer");
  163. err = ESP_ERR_NO_MEM;
  164. goto http_cleanup;
  165. }
  166. https_ota_handle->ota_upgrade_buf_size = alloc_size;
  167. https_ota_handle->binary_file_len = 0;
  168. *handle = (esp_https_ota_handle_t)https_ota_handle;
  169. https_ota_handle->state = ESP_HTTPS_OTA_BEGIN;
  170. return ESP_OK;
  171. http_cleanup:
  172. _http_cleanup(https_ota_handle->http_client);
  173. failure:
  174. free(https_ota_handle);
  175. *handle = NULL;
  176. return err;
  177. }
  178. esp_err_t esp_https_ota_get_img_desc(esp_https_ota_handle_t https_ota_handle, esp_app_desc_t *new_app_info)
  179. {
  180. esp_https_ota_t *handle = (esp_https_ota_t *)https_ota_handle;
  181. if (handle == NULL || new_app_info == NULL) {
  182. ESP_LOGE(TAG, "esp_https_ota_read_img_desc: Invalid argument");
  183. return ESP_ERR_INVALID_ARG;
  184. }
  185. if (handle->state < ESP_HTTPS_OTA_BEGIN) {
  186. ESP_LOGE(TAG, "esp_https_ota_read_img_desc: Invalid state");
  187. return ESP_FAIL;
  188. }
  189. int data_read_size = IMAGE_HEADER_SIZE;
  190. int data_read = esp_http_client_read(handle->http_client,
  191. handle->ota_upgrade_buf,
  192. data_read_size);
  193. if (data_read < 0) {
  194. return ESP_FAIL;
  195. }
  196. if (data_read >= sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
  197. memcpy(new_app_info, &handle->ota_upgrade_buf[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
  198. handle->binary_file_len += data_read;
  199. } else {
  200. return ESP_FAIL;
  201. }
  202. return ESP_OK;
  203. }
  204. esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
  205. {
  206. esp_https_ota_t *handle = (esp_https_ota_t *)https_ota_handle;
  207. if (handle == NULL) {
  208. ESP_LOGE(TAG, "esp_https_ota_perform: Invalid argument");
  209. return ESP_ERR_INVALID_ARG;
  210. }
  211. if (handle->state < ESP_HTTPS_OTA_BEGIN) {
  212. ESP_LOGE(TAG, "esp_https_ota_perform: Invalid state");
  213. return ESP_FAIL;
  214. }
  215. esp_err_t err;
  216. int data_read;
  217. switch (handle->state) {
  218. case ESP_HTTPS_OTA_BEGIN:
  219. err = esp_ota_begin(handle->update_partition, OTA_SIZE_UNKNOWN, &handle->update_handle);
  220. if (err != ESP_OK) {
  221. ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
  222. return err;
  223. }
  224. handle->state = ESP_HTTPS_OTA_IN_PROGRESS;
  225. /* In case `esp_https_ota_read_img_desc` was invoked first,
  226. then the image data read there should be written to OTA partition
  227. */
  228. if (handle->binary_file_len) {
  229. return _ota_write(handle, (const void *)handle->ota_upgrade_buf, handle->binary_file_len);
  230. }
  231. /* falls through */
  232. case ESP_HTTPS_OTA_IN_PROGRESS:
  233. data_read = esp_http_client_read(handle->http_client,
  234. handle->ota_upgrade_buf,
  235. handle->ota_upgrade_buf_size);
  236. if (data_read == 0) {
  237. ESP_LOGI(TAG, "Connection closed, all data received");
  238. } else if (data_read < 0) {
  239. ESP_LOGE(TAG, "Error: SSL data read error");
  240. return ESP_FAIL;
  241. } else if (data_read > 0) {
  242. return _ota_write(handle, (const void *)handle->ota_upgrade_buf, data_read);
  243. }
  244. handle->state = ESP_HTTPS_OTA_SUCCESS;
  245. break;
  246. default:
  247. ESP_LOGE(TAG, "Invalid ESP HTTPS OTA State");
  248. return ESP_FAIL;
  249. break;
  250. }
  251. return ESP_OK;
  252. }
  253. esp_err_t esp_https_ota_finish(esp_https_ota_handle_t https_ota_handle)
  254. {
  255. esp_https_ota_t *handle = (esp_https_ota_t *)https_ota_handle;
  256. if (handle == NULL) {
  257. return ESP_ERR_INVALID_ARG;
  258. }
  259. if (handle->state < ESP_HTTPS_OTA_BEGIN) {
  260. return ESP_FAIL;
  261. }
  262. esp_err_t err = ESP_OK;
  263. switch (handle->state) {
  264. case ESP_HTTPS_OTA_SUCCESS:
  265. case ESP_HTTPS_OTA_IN_PROGRESS:
  266. err = esp_ota_end(handle->update_handle);
  267. /* falls through */
  268. case ESP_HTTPS_OTA_BEGIN:
  269. if (handle->ota_upgrade_buf) {
  270. free(handle->ota_upgrade_buf);
  271. }
  272. if (handle->http_client) {
  273. _http_cleanup(handle->http_client);
  274. }
  275. break;
  276. default:
  277. ESP_LOGE(TAG, "Invalid ESP HTTPS OTA State");
  278. break;
  279. }
  280. if ((err == ESP_OK) && (handle->state == ESP_HTTPS_OTA_SUCCESS)) {
  281. esp_err_t err = esp_ota_set_boot_partition(handle->update_partition);
  282. if (err != ESP_OK) {
  283. ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%d", err);
  284. }
  285. }
  286. free(handle);
  287. return err;
  288. }
  289. int esp_https_ota_get_image_len_read(esp_https_ota_handle_t https_ota_handle)
  290. {
  291. esp_https_ota_t *handle = (esp_https_ota_t *)https_ota_handle;
  292. if (handle == NULL) {
  293. return -1;
  294. }
  295. if (handle->state < ESP_HTTPS_OTA_IN_PROGRESS) {
  296. return -1;
  297. }
  298. return handle->binary_file_len;
  299. }
  300. esp_err_t esp_https_ota(const esp_http_client_config_t *config)
  301. {
  302. if (!config) {
  303. ESP_LOGE(TAG, "esp_http_client config not found");
  304. return ESP_ERR_INVALID_ARG;
  305. }
  306. esp_https_ota_config_t ota_config = {
  307. .http_config = config,
  308. };
  309. esp_https_ota_handle_t https_ota_handle = NULL;
  310. esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
  311. if (https_ota_handle == NULL) {
  312. return ESP_FAIL;
  313. }
  314. while (1) {
  315. err = esp_https_ota_perform(https_ota_handle);
  316. if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
  317. break;
  318. }
  319. }
  320. esp_err_t ota_finish_err = esp_https_ota_finish(https_ota_handle);
  321. if (err != ESP_OK) {
  322. /* If there was an error in esp_https_ota_perform(),
  323. then it is given more precedence than error in esp_https_ota_finish()
  324. */
  325. return err;
  326. } else if (ota_finish_err != ESP_OK) {
  327. return ota_finish_err;
  328. }
  329. return ESP_OK;
  330. }