#include "httpserver.h" #include "lwip/def.h" #include "lwip/tcp.h" #include "fsdata.c" #include "main.h" #include "flash_if.h" #include "common_config.h" #include "conf.h" #include "settings_api.h" #include "crc.h" #include "board.h" #include "netconf.h" #ifdef SLAVEBRD_ENABLE #include "stm32sprog.h" #endif #ifdef LCD_ENABLE #include "lcd.h" #endif #include "led.h" #include "systick.h" #include #include "tinystdio.h" #undef DBG #define DBG if(0) /* Uncomment this macro to compleately skip any flash read/write operations/ USE THIS ONLY FOR DEBUG AND TESTING PURPOSES! NEVER UNKOMMENT FOR RELEASE BUILDS! */ // #define DUMMY_PROG #ifdef DUMMY_PROG /* Disable main fw flash read/write operations */ #define FLASH_If_Write(X, ...) #define FLASH_If_Erase(X, ...) #define CRC_Read() 0x00 #define CRC_Calculate(X, ...) 0x00 /* Disable daughter fw flash read/write operations */ #define stmProg(X, ...) #define stmRebootForFlash(X, ...) #define stmConnect(X, ...) ok #define stmGetDevParams(X, ...) ok #define stmEraseFW(X, ...) ok #define stmReadFlashCrc() 0x00 #define stmCalcFlashCrc(X, ...) 0x00 #endif /* DUMMY_PROG*/ #define FILENAME_MAX_LEN 30 #define BOUNDARY_MAX_LEN 70 #define BLOCK_PROG_TIMEOUT 20000 #define COOKIES_MAX_LEN 64 #define COOKIES_SET_ATTEMPTS 2 #define BD_CONNECT_RETRY 3 /* Max HTTP file name length including "/" */ #define HTTP_FNAME_LEN 32 /* Max HTTP Etag field length */ #define MAX_ETAG_LEN 48 #define DB_FW_SIZE DB_CPU_FLASH_END_ADDRESS - DB_CPU_FLASH_FIRST_PAGE_ADDRESS + 1 #define HTTP_RET_UPLOAD_ERROR() do {\ memset(sendBuf, 0, 32);\ strcpy(sendBuf, HTTP_200_OK);\ strcat(sendBuf, "0");\ hs->file = sendBuf;\ hs->left = strlen(sendBuf);\ send_data(pcb, hs);\ tcp_sent(pcb, http_sent);\ htmlpage = UploadErrorPage;\ SET_FWINVALID_FLAG();\ } while(0) #define HTTP_RET_UPLOAD_ERROR_NO_FLASH_ERASE() do {\ memset(sendBuf, 0, 32);\ strcpy(sendBuf, HTTP_200_OK);\ strcat(sendBuf, "0");\ hs->file = sendBuf;\ hs->left = strlen(sendBuf);\ send_data(pcb, hs);\ tcp_sent(pcb, http_sent);\ htmlpage = UploadErrorPage;\ } while(0) static char sendBuf[512]; static char tempBuf[HW_REV_OFFSET + HW_REV_LEN]; extern bool fDoneReset; extern bool fUpload; extern bool fInvalidFw; char lcdbuf[32] = {0}; static vu32 DataFlag = 0; static vu32 BoundaryFlag = 0; static vu32 size = 0; static __IO uint32_t FlashWriteAddress; static u32 TotalReceived = 0; static char LeftBytesTab[4]; static u8 LeftBytes = 0; static uint32_t BrowserFlag = 0; static uint32_t ContentOffset = 0; static __IO uint32_t TotalData = 0; static uint32_t FwDestSize = 0; static bool fEraseFlash = true; uint8_t update_timeout = UPDATE_TIMEOUT; struct http_state { char *file; u32_t left; }; typedef enum { fw_unknown, fw_main_board, fw_daughter_board } fw_type_t; static fw_type_t fw_type = fw_unknown; htmlpageState htmlpage; /* Content Length */ static const char Content_Length[17] = { 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, }; static const char If_None_Match[] = "If-None-Match: "; static const char Etag[] = "ETag: "; const char HTTP_200_OK[] = "HTTP/1.1 200 OK\r\n\r\n"; const char HTTP_304_NOT_MODIFIED[] = "HTTP/1.1 304 Not Modified\r\n\r\n"; const char HTTP_200_OK_TEXT_HTML[] = "HTTP/1.1 200 OK\r\nContent-Type:text/html"; const char Redirect_Head[] = "\

"; uint16_t printLen = 0; char printBuf[1500]; uint32_t oldAdd = USER_FLASH_FIRST_PAGE_ADDRESS; uint32_t deltaAdd = 0; #ifdef SLAVEBRD_ENABLE uint32_t stm32addr = DB_CPU_FLASH_FIRST_PAGE_ADDRESS; static void IAP_HTTP_stmProg(char *data, uint32_t len); bool IAP_HTTP_stmConnect(void); #endif static uint32_t Parse_Content_Length(char *data, uint32_t len); void Parse_IfNonMatch(char *data, uint32_t len, char *value); static uint32_t Parse_Header(char *data, uint32_t len, const char *field, uint32_t flen, char *value); static bool Parse_Filename(const char *data, uint32_t len, fw_type_t *fw_type, uint32_t *fw_size); char *Parce_Boundary(const char *data, uint32_t len, char *dst, uint8_t dstlen); static void IAP_HTTP_writedata(char *data, uint32_t len); void ProgTimeout_Handler(); /* file must be allocated by caller and will be filled in by the function. */ static int fs_open(char *name, struct fs_file *file); bool send_file(char *filename, char *pnonmatch, struct tcp_pcb *pcb, struct http_state *hs, struct fs_file *file); void send_index_page(struct tcp_pcb *pcb, struct http_state *hs, struct pbuf *p, bool upload); static void PeriodicHandle(uint8_t progress); /* Get Cookie parameter value */ bool GetCookieValue(char *inStr, char *paramName, char *paramValue, uint8_t *paramLen); /* Check if Cookie is set or not */ bool CookieIsSet(char *inStr, uint16_t inLen, char *paramName); // Implements the GNU function memmem much faster (2x) than the standard memmem char *memmemory(char *haystack, int hlen, char *needle, int nlen) { if (nlen > hlen) return 0; int i = 0, j = 0; switch (nlen) { // we have a few specialized compares for certain needle sizes case 0: // no needle? just give the haystack return haystack; case 1: // just use memchr for 1-byte needle return memchr(haystack, needle[0], hlen); case 2: // use 16-bit compares for 2-byte needles for (i = 0; i < hlen - nlen + 1; i++) { if (*(uint16_t *)(haystack + i) == *(uint16_t *)needle) { return haystack + i; } } break; case 4: // use 32-bit compares for 4-byte needles for (i = 0; i < hlen - nlen + 1; i++) { if (*(uint32_t *)(haystack + i) == *(uint32_t *)needle) { return haystack + i; } } break; /* actually slower on my 32-bit machine case 8: // use 64-bit compares for 8-byte needles for (i=0; ileft) { len = tcp_sndbuf(pcb); } else { len = hs->left; } err = tcp_write(pcb, hs->file, len, 0); if (err == ERR_OK) { hs->file += len; hs->left -= len; } } /** * @brief tcp poll callback function * @param arg: pointer to an argument to be passed to callback function * @param pcb: pointer on tcp_pcb structure * @retval err_t */ static err_t http_poll(void *arg, struct tcp_pcb *pcb) { if (arg == NULL) { tcp_close(pcb); } else { send_data(pcb, (struct http_state *)arg); } return ERR_OK; } /** * @brief callback function called after a successfull TCP data packet transmission * @param arg: pointer to an argument to be passed to callback function * @param pcb: pointer on tcp_pcb structure * @param len * @retval err : LwIP error code */ static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { struct http_state *hs; hs = arg; (void)len; if (hs->left > 0) { send_data(pcb, hs); } else { close_conn(pcb, hs); if (htmlpage == UploadDonePage) { #ifndef DUMMY_PROG fDoneReset = true; fUpload = false; #endif } else if (htmlpage == UploadErrorPage) { /* Don't reboot if update was failed, just reload main page */ fUpload = false; } } return ERR_OK; } /** * @brief callback function for handling TCP HTTP traffic * @param arg: pointer to an argument structure to be passed to callback function * @param pcb: pointer to a tcp_pcb structure * @param p: pointer to a packet buffer * @param err: LwIP error code * @retval err */ /* goback.cgi - возврат в основную прошивку */ /* upload.cgi - загрузка новой прошивки */ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { int32_t i, len = 0; static uint32_t temp_len = 0; uint32_t DataOffset, CrcRead = 0, CrcCalc = 0; char *data, *ptr; struct fs_file file = {0, 0}; struct http_state *hs; static bool hw_validated = false; char fname[HTTP_FNAME_LEN]; uint8_t fname_len; static struct tcp_pcb *upload_pcb = NULL; static cookie_attempt = 0; hs = arg; if (err == ERR_OK && p != NULL) { /* Inform TCP that we have taken the data */ tcp_recved(pcb, p->tot_len); if (hs->file == NULL) { data = p->payload; len = p->tot_len; printLen = p->tot_len > 500 ? 500 : p->tot_len; memcpy(printBuf, p->payload, printLen); // DBG printf("\r\n[%d] %s...\r\n", p->tot_len, printBuf); // DBG printf("pcb: %p (%p)\r\n", pcb, upload_pcb); /* process HTTP GET requests */ if (strncmp(data, "GET /", 5) == 0 && pcb != upload_pcb) { /* Возврат в основную прошивку. Сбрасываем флаг loadmode, * сохраняем настройки и перезагружаемся */ if (strncmp(data, "GET /goback.cgi", 15) == 0) { /* Check if we realy can go back. Return to IAP if fw update was failed */ if (!fInvalidFw) { /* Set loadMode = 0 */ RTC_WriteBackupRegister(RTC_BKP_DR1, 0); } else { /* Set loadMode = 1 */ RTC_WriteBackupRegister(RTC_BKP_DR1, 1); } #ifdef SLAVEBRD_ENABLE /* Reboot daughter board */ stmReboot(); #endif /* Self reboot */ NVIC_SystemReset(); } /* Статус обновления и таймаут до перезагрузки */ else if (strncmp(data, "GET /status.cgi", 15) == 0) { memset(sendBuf, 0, sizeof(sendBuf)); /* Headers для поддержки saffari */ sprintf(sendBuf, "HTTP/1.1 200 OK\r\nContent-Type:text/json\r\n\r\n"); char buf[32]; uint8_t update_state = 0; if (upload_pcb != NULL && fw_type == fw_unknown) { update_state = 1; } else if (upload_pcb != NULL && fw_type == fw_daughter_board) { update_state = 2; } else if (upload_pcb != NULL && fw_type == fw_main_board) { update_state = 3; } else { update_state = 0; } strcat(sendBuf, "{"); sprintf(buf, "\"upload\":\"%u\",", (unsigned int)update_state); strcat(sendBuf, buf); sprintf(buf, "\"progress\":\"%u\",", (unsigned int)(TotalReceived * 100 / size)); strcat(sendBuf, buf); sprintf(buf, "\"wrtmr\":\"%u\",", timer_GetCountdown(&ProgTimeout_Handler) / 1000); strcat(sendBuf, buf); sprintf(buf, "\"updtmr\":\"%u\",", update_timeout); strcat(sendBuf, buf); #ifdef SLAVEBRD_ENABLE sprintf(buf, "\"file1\":\"%s\",", DAUGHTER_FW_NAME); strcat(sendBuf, buf); #endif sprintf(buf, "\"file2\":\"%s\",", MAIN_FW_NAME); strcat(sendBuf, buf); /* sprintf(buf, "\"rev\":\"%s\",", REVISION); strcat(sendBuf, buf); */ sprintf(buf, "\"invalidfw\":%s", fInvalidFw ? "true" : "false"); strcat(sendBuf, buf); strcat(sendBuf, "}"); hs->file = sendBuf; hs->left = strlen(sendBuf); pbuf_free(p); send_data(pcb, hs); tcp_sent(pcb, http_sent); } else if ((strncmp(data, "GET /upload.html", 16) == 0) && (!CookieIsSet(p->payload, p->tot_len, "file1=") || !CookieIsSet(p->payload, p->tot_len, "file2="))) { if (cookie_attempt < COOKIES_SET_ATTEMPTS) { /* Cookies not found. Try to set. */ send_index_page(pcb, hs, p, upload_pcb != NULL); cookie_attempt++; } else { cookie_attempt = 0; /* Send Cookie set error message */ strcpy(sendBuf, HTTP_200_OK_TEXT_HTML); strcat(sendBuf, "\r\n\r\n"); strcat(sendBuf, Redirect_Head); strcat(sendBuf, ""); strcat(sendBuf, Msg_Body); strcat(sendBuf, "Включите файлы cookie в браузере и попробуйте еще раз.

\r\n\r\n"); hs->file = sendBuf; hs->left = strlen(sendBuf); pbuf_free(p); send_data(pcb, hs); tcp_sent(pcb, http_sent); } } /* Check common GET request */ else if (GetFileName(data, fname, &fname_len)) { char nonmatch[MAX_ETAG_LEN]; char *pnonmatch = NULL; /* Parce If-Non_Match value */ uint8_t nonmatch_len = Parse_Header(data, p->tot_len, If_None_Match, 15, nonmatch); if (nonmatch_len < MAX_ETAG_LEN && nonmatch_len > 0) { printf("If_None_Match: %s\r\n", nonmatch); pnonmatch = nonmatch; } if (send_file(fname, pnonmatch, pcb, hs, &file)) { pbuf_free(p); } else { send_index_page(pcb, hs, p, upload_pcb != NULL); } } /* Send default index page */ else { send_index_page(pcb, hs, p, upload_pcb != NULL); } } /* Process POST request for file upload and incoming data packets after POST request */ /* Tested MTU sizes: 400 - 1500. Sizes < 400 not supported currently. */ else if ((strncmp(data, "POST /upload.cgi", 16) == 0 && upload_pcb == NULL) || (DataFlag >= 1 && pcb == upload_pcb)) { static char boundary[BOUNDARY_MAX_LEN]; static char boundary_part[BOUNDARY_MAX_LEN]; static char *pbound = NULL; fUpload = true; DataOffset = 0; #ifndef DUMMY_PROG DBG printf("\r\n[%u] ______\r\n", (unsigned int)len); #endif /* POST Packet received */ if (DataFlag == 0) { BrowserFlag = 0; TotalReceived = 0; BoundaryFlag = 0; temp_len = 0; upload_pcb = pcb; hw_validated = false; fw_type = fw_unknown; /* Reset block receive and prog timeout */ timer_Restart(&ProgTimeout_Handler); /* Parse packet for Content-length field */ size = Parse_Content_Length(data, p->tot_len); DBG printf("Content_Length: %u\r\n", (unsigned int)size); /* Parse packet for the boundary field */ pbound = Parce_Boundary(data, p->tot_len, boundary, sizeof(boundary)); DBG printf("boundary: %s\r\n", pbound ? boundary : "header not found"); /* Find the boundary */ if (pbound != NULL) { for (i = 0; i < len; i++) { if (strncmp((char *)(data + i), boundary, strlen(boundary)) == 0) { ContentOffset = i; BoundaryFlag = 1; DBG printf("boundary found\r\n"); /* Parse packet for "\r\n\r\n" field */ for (int32_t j = 0; j < len; j++) { if (strncmp((char *)(data + i + j), "\r\n\r\n", 4) == 0) { DataOffset = i + j + 4; break; } } } } } /* TODO Handle partially received boundary */ if (BoundaryFlag) { TotalReceived = len - ContentOffset; } DBG { printf("Boundary: %s\r\n", BoundaryFlag ? "yes" : "no"); printf("ContentOffset: %u\r\n", (unsigned int)ContentOffset); printf("DataOffset: %u\r\n", (unsigned int)DataOffset); printf("TotalReceived: %u/%u\r\n", (unsigned int)TotalReceived, (unsigned int)size); } if (fw_type == fw_unknown) { Parse_Filename(data, len, &fw_type, &FwDestSize); } /* No data in the first POST packet */ if (DataOffset == 0) { DataFlag++; BrowserFlag = 1; pbuf_free(p); return ERR_OK; } } if (((DataFlag == 1) && (BrowserFlag == 1)) || ((DataFlag == 0) && (BrowserFlag == 0))) { if ((DataFlag == 0) && (BrowserFlag == 0)) { DataFlag++; } else if ((DataFlag == 1) && (BrowserFlag == 1)) { /* Find the boundary */ for (i = 0; i < len; i++) { if (pbound != NULL) { if (strncmp((char *)(data + i), boundary, strlen(boundary)) == 0 || BoundaryFlag == 1) { /* Parse packet for "\r\n\r\n" field */ for (int32_t j = 0; j < len; j++) { if (strncmp((char *)(data + i + j), "\r\n\r\n", 4) == 0) { DataOffset = i + j + 4; break; } } break; } } } /* TODO Handle partially received boundary */ TotalReceived += len; DataFlag++; } if (fw_type == fw_unknown) { Parse_Filename(data, len, &fw_type, &FwDestSize); } DBG { printf("ContentOffset: %u\r\n", (unsigned int)ContentOffset); printf("DataOffset: %u\r\n", (unsigned int)DataOffset); printf("TotalReceived: %u/%u\r\n", (unsigned int)TotalReceived, (unsigned int)size); } /* Get next packet */ if (!DataOffset) { DataFlag--; pbuf_free(p); return ERR_OK; } if (fw_type == fw_unknown) { htmlpage = FileUploadPage; /* No filename, in this case reload upload page */ fs_open("/index.html", &file); hs->file = file.data; hs->left = file.len; pbuf_free(p); send_data(pcb, hs); tcp_sent(pcb, http_sent); DataFlag = 0; upload_pcb = NULL; printf("Error: Unknown firmware\r\n"); #ifdef LCD_ENABLE LCD_PrintAligned(7, alignCENTER, "Ошибка!"); #endif return ERR_OK; } TotalData = 0; } /* DataFlag >1 => the packet is data only */ else { TotalReceived += len; } ptr = (char *)(data + DataOffset); len -= DataOffset; /* Update total data received counter */ TotalData += len; #ifndef DUMMY_PROG DBG printf("TotalData: +%u %u/%u\r\n", (unsigned int)len, (unsigned int)TotalData, (unsigned int)FwDestSize); DBG printf("%u/%u\r\n", (unsigned int)TotalReceived, (unsigned int)size); #endif /* Check if last data packet */ if (TotalReceived == size) { /* Check if boundary was splited into two packets. Get second part. */ /* Data transfer sequence should end with "\r\n" + boundary + "--\rn" (6 extra bytes) */ if ((pbound) && (p->tot_len <= strlen(boundary) + 6)) { /* No data in packet */ TotalData = TotalData - len; len = 0; } else { char *bptr = memmemory(data, p->tot_len, boundary, strlen(boundary)); // DBG printf("boundary: %s\r\n", boundary); if (bptr != NULL) { /* Substruct length of "\r\n" - 2 bytes */ uint32_t last_len = bptr - data - 2; TotalData = TotalData - len + last_len; len = last_len; } else { printf("Boundary not found\r\n"); } } } /* Not the last data packet */ else if (TotalReceived < size) { /* Check if boundary was splited into two packets. Get first part. */ /* Data transfer sequence should end with "\r\n" + boundary + "--\rn" (6 extra bytes) */ if ((pbound) && (size - TotalReceived < strlen(boundary) + 6)) { uint16_t tail_len = size - TotalReceived; uint16_t boundary_len = strlen(boundary) + 6; /* Find "\r\n" or "\r" depending on a current packet length */ uint8_t marker_len = (tail_len - boundary_len >= 2) ? 2 : 1; char *ptr = memmemory(data + p->tot_len - boundary_len, boundary_len, marker_len == 2 ? "\r\n" : "\r", marker_len); if (ptr != NULL) { DBG { /* Boundary first part is not actually used, just for debug output */ uint32_t buflen = len - (uint32_t)(ptr + marker_len - data); printf("buflen: %u\r\n", (unsigned int)buflen); if (buflen > BOUNDARY_MAX_LEN) buflen = BOUNDARY_MAX_LEN; memcpy(boundary_part, ptr + marker_len, buflen); printf("boundary part: %s\r\n", boundary_part); } uint32_t last_len = ptr - data; TotalData = TotalData - len + last_len; len = last_len; } else { DBG printf("Boundary part not found!\r\n"); } } /* Validate uploaded binary */ if ((TotalData >= HW_REV_OFFSET + HW_REV_LEN) && (!hw_validated)) { char rev[HW_REV_LEN]; if (fw_type == fw_main_board) { strncpy(rev, HW_REV, HW_REV_LEN); } #ifdef SLAVEBRD_ENABLE else if (fw_type == fw_daughter_board) { strncpy(rev, HW_REV_UPPER, HW_REV_LEN); } #endif else { strncpy(rev, "-", HW_REV_LEN); } char bin_rev[HW_REV_LEN]; memset(bin_rev, 0, sizeof(bin_rev)); uint32_t first_len = temp_len > HW_REV_OFFSET ? temp_len - HW_REV_OFFSET : 0; /* Copy 1st part (if not empty) */ memcpy(bin_rev, tempBuf + HW_REV_OFFSET, first_len); /* Copy 2nd part */ memcpy(bin_rev + first_len, ptr + HW_REV_OFFSET - (temp_len - first_len), HW_REV_LEN - first_len); if (strncmp(bin_rev, rev, HW_REV_LEN) != 0) { printf("HW version (%s) mismatch: found %s\r\n", rev, bin_rev); printf("Update aborted!\r\n"); #ifdef LCD_ENABLE LCD_PrintAligned(7, alignCENTER, "Ошибка!"); #endif DataFlag = 0; upload_pcb = NULL; HTTP_RET_UPLOAD_ERROR_NO_FLASH_ERASE(); pbuf_free(p); return ERR_OK; } else { printf("HW version (%s) validated\r\n", bin_rev); hw_validated = true; printf("State: Erasing...\r\n"); SET_FWINVALID_FLAG(); #ifdef LCD_ENABLE LCD_ClearRow(7); #ifdef SLAVEBRD_ENABLE sprintf(lcdbuf, "Очистка %d/2 ", fw_type == fw_main_board ? 2 : 1); LCD_PrintAligned(5, alignCENTER, lcdbuf); #else LCD_PrintAligned(5, alignCENTER, "Очистка..."); #endif #endif switch (fw_type) { case fw_main_board: /* Erase flash */ if (fEraseFlash) { FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS); fEraseFlash = false; } /* Init flash */ FLASH_If_Init(); FlashWriteAddress = USER_FLASH_FIRST_PAGE_ADDRESS; break; #ifdef SLAVEBRD_ENABLE case fw_daughter_board: stm32addr = DB_CPU_FLASH_FIRST_PAGE_ADDRESS; uint8_t retries = 0; /* Try to connect to stm32 */ while ((!IAP_HTTP_stmConnect()) && (retries++ < (BD_CONNECT_RETRY - 1))) { ; } /* Error if no device found */ if (retries >= BD_CONNECT_RETRY) { #ifdef LCD_ENABLE LCD_PrintAligned(7, alignCENTER, "Ошибка!"); #endif DataFlag = 0; upload_pcb = NULL; HTTP_RET_UPLOAD_ERROR(); pbuf_free(p); return ERR_OK; } break; #endif default: break; } #ifdef LCD_ENABLE LCD_ClearRow(7); #ifdef SLAVEBRD_ENABLE sprintf(lcdbuf, "Запись %d/2", fw_type == fw_main_board ? 2 : 1); LCD_PrintAligned(5, alignCENTER, lcdbuf); #else LCD_PrintAligned(5, alignCENTER, "Запись..."); #endif #endif /*indicate start of flash programming */ printf("\r\nState: Programming..\r\n"); } } } if (hw_validated) { /* Check fw data size overflow */ if (TotalData > FwDestSize) { printf("FW binary size mismatch. Aborting update..\r\n"); printf("FW dest size %u was %u\r\n", (unsigned int)FwDestSize, (unsigned int)TotalData); HTTP_RET_UPLOAD_ERROR_NO_FLASH_ERASE(); } else { static uint8_t progress = 0; if (progress != TotalReceived * 100 / size) { progress = TotalReceived * 100 / size; #ifndef DUMMY_PROG printf("Writing: %u%%\r\n", (unsigned int)progress); #ifdef LCD_ENABLE LCD_PrintBar(7, (unsigned int)(100 * TotalReceived / size)); #endif #endif } /* Reset block prog timeout */ timer_Restart(&ProgTimeout_Handler); /* Write data in flash */ if (len) { switch (fw_type) { case fw_main_board: if (temp_len) { IAP_HTTP_writedata(tempBuf, temp_len); temp_len = 0; } IAP_HTTP_writedata(ptr, len); break; #ifdef SLAVEBRD_ENABLE case fw_daughter_board: if (temp_len) { IAP_HTTP_stmProg(tempBuf, temp_len); temp_len = 0; } IAP_HTTP_stmProg(ptr, len); break; #endif default: break; } } } } else { /* Save first part */ memcpy(tempBuf + temp_len, ptr, len); temp_len += len; DBG printf("Stashed: %u\r\n", (unsigned int)temp_len); } if (TotalReceived == size) { /* Stop block receive timer */ timer_Stop(&ProgTimeout_Handler); DataFlag = 0; upload_pcb = NULL; printf("State: Verifying..\r\n"); #ifdef LCD_ENABLE LCD_ClearRow(7); #ifdef SLAVEBRD_ENABLE sprintf(lcdbuf, "Проверка CRC %d/2", fw_type == fw_main_board ? 2 : 1); LCD_PrintAligned(5, alignCENTER, lcdbuf); #else LCD_PrintAligned(5, alignCENTER, "Проверка CRC..."); #endif #endif switch (fw_type) { case fw_main_board: CrcRead = CRC_Read(); CrcCalc = CRC_Calculate(&PeriodicHandle); break; #ifdef SLAVEBRD_ENABLE case fw_daughter_board: CrcRead = stmReadFlashCrc(); CrcCalc = stmCalcFlashCrc(&PeriodicHandle); break; #endif default: break; } printf("State: Prog Finished\r\n\r\n"); printf("Tot bytes received: %u bytes\r\n", (unsigned int)TotalData); printf("CRC read: 0x%X\r\n", (unsigned int)CrcRead); printf("CRC calcucated: 0x%X\r\n", (unsigned int)CrcCalc); /* Check CRC */ if (CrcRead == CrcCalc) { printf("CRC check: Pass\r\n"); /* Reset block receive and prog timeout */ timer_Restart(&ProgTimeout_Handler); memset(sendBuf, 0, 32); strcpy(sendBuf, HTTP_200_OK); strcat(sendBuf, "1"); hs->file = sendBuf; hs->left = strlen(sendBuf); send_data(pcb, hs); tcp_sent(pcb, http_sent); #ifdef LCD_ENABLE LCD_PrintAligned(7, alignCENTER, "Успешно!"); #endif switch (fw_type) { case fw_main_board: /* Run main FW */ htmlpage = UploadDonePage; break; #ifdef SLAVEBRD_ENABLE case fw_daughter_board: timer_ChangeFrequency(&LED_Blinky_Green, 250); /* Nothing to do */ break; #endif default: break; } } else { #ifdef LCD_ENABLE LCD_PrintAligned(7, alignCENTER, "Ошибка!"); #endif switch (fw_type) { case fw_main_board: /* Erase flash */ /* That's a way not to boot wrong FW next time if update was failed */ FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS); fEraseFlash = false; break; #ifdef SLAVEBRD_ENABLE case fw_daughter_board: /* We can boot device if upper board update was failed. */ break; #endif default: break; } printf("CRC check: Fail\r\n"); HTTP_RET_UPLOAD_ERROR(); } } pbuf_free(p); } else { /* Bad HTTP requests */ printf("Bad HTTP request\r\n"); pbuf_free(p); close_conn(pcb, hs); } } else { pbuf_free(p); close_conn(pcb, hs); } } else if (err == ERR_OK && p == NULL) { /* Received empty frame */ printf("Received empty frame\r\n"); close_conn(pcb, hs); } return ERR_OK; } /** * @brief callback function on TCP connection setup ( on port 80) * @param arg: pointer to an argument structure to be passed to callback function * @param pcb: pointer to a tcp_pcb structure * ¶m err: Lwip stack error code * @retval err */ static err_t http_accept(void *arg, struct tcp_pcb *pcb, err_t err) { struct http_state *hs; (void)err; (void)arg; /* Allocate memory for the structure that holds the state of the connection */ hs = mem_malloc(sizeof(struct http_state)); if (hs == NULL) { return ERR_MEM; } /* Initialize the structure. */ hs->file = NULL; hs->left = 0; /* Tell TCP that this is the structure we wish to be passed for our callbacks. */ tcp_arg(pcb, hs); /* Tell TCP that we wish to be informed of incoming data by a call to the http_recv() function. */ tcp_recv(pcb, http_recv); tcp_err(pcb, conn_err); tcp_poll(pcb, http_poll, 10); return ERR_OK; } /** * @brief intialize HTTP webserver * @param none * @retval none */ void IAP_httpd_init(void) { struct tcp_pcb *pcb; /*create new pcb*/ pcb = tcp_new(); /* bind HTTP traffic to pcb */ tcp_bind(pcb, IP_ADDR_ANY, 80); /* start listening on port 80 */ pcb = tcp_listen(pcb); /* define callback function for TCP connection setup */ tcp_accept(pcb, http_accept); /* Set block receive and prog timeout */ timer_AddFunction(BLOCK_PROG_TIMEOUT, &ProgTimeout_Handler); timer_Stop(&ProgTimeout_Handler); } /** * @brief Opens a file defined in fsdata.c ROM filesystem * @param name : pointer to a file name * @param file : pointer to a fs_file structure * @retval 1 if success, 0 if fail */ static int fs_open(char *name, struct fs_file *file) { struct fsdata_file_noconst *f; for (f = (struct fsdata_file_noconst *)FS_ROOT; f != NULL; f = (struct fsdata_file_noconst *)f->next) { if (!strcmp(name, f->name)) { file->data = f->data; file->len = f->len; return 1; } } return 0; } /** * @brief sends file from flash FS * @param filename: pointer to the file name to send * @param pnonmatch: pointer to the If-Non_Match value * @param pcb: pointer to a tcp_pcb struct * @param hs: pointer to a http_state struct * @param file: pointer to a fs_file struct * @retval */ bool send_file(char *filename, char *pnonmatch, struct tcp_pcb *pcb, struct http_state *hs, struct fs_file *file) { int res = 0; char etag[MAX_ETAG_LEN]; char *petag = NULL; res = fs_open(filename, file); if (res == 0) { return false; } /* Find Etag value */ uint8_t etag_len = Parse_Header(file->data, file->len, Etag, 6, etag); if (etag_len < MAX_ETAG_LEN && etag_len > 0) { printf("Etag: %s\r\n", etag); petag = etag; } /* Compare Etag and If-Non-Match fields */ if (pnonmatch && petag && (strcmp(pnonmatch, petag) == 0)) { /* Send 304 code */ sprintf(sendBuf, HTTP_304_NOT_MODIFIED); printf(sendBuf); hs->file = sendBuf; hs->left = strlen(sendBuf); } else { /* Send file */ printf("%s\r\n\r\n", filename); hs->file = file->data; hs->left = file->len; } send_data(pcb, hs); tcp_sent(pcb, http_sent); return true; } /** * @brief * @retval None */ void send_index_page(struct tcp_pcb *pcb, struct http_state *hs, struct pbuf *p, bool upload) { char buf[32]; /* Send cookie */ strcpy(sendBuf, "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n"); strcat(sendBuf, "Set-Cookie: upload="); sprintf(buf, "%d", (unsigned char)upload); strcat(sendBuf, buf); strcat(sendBuf, "\r\nSet-Cookie: wrtmr="); sprintf(buf, "%d", timer_GetCountdown(&ProgTimeout_Handler) / 1000); strcat(sendBuf, buf); strcat(sendBuf, "\r\nSet-Cookie: updtmr="); sprintf(buf, "%d", update_timeout); strcat(sendBuf, buf); strcat(sendBuf, "\r\nSet-Cookie: file1="); #ifdef SLAVEBRD_ENABLE strcat(sendBuf, DAUGHTER_FW_NAME); #else strcat(sendBuf, "NONE"); #endif strcat(sendBuf, "\r\nSet-Cookie: file2="); char file_name_buf[40]; memset(file_name_buf, 0, sizeof(file_name_buf)); strcpy(file_name_buf, MAIN_FW_NAME); if(strcmp(file_name_buf, "NONE") != 0){ strcat(sendBuf, MAIN_FW_NAME); } else { strcat(sendBuf, " "); } strcat(sendBuf, "\r\n\r\n\r\n\r\n"); htmlpage = LoginPage; hs->file = sendBuf; hs->left = strlen(sendBuf); pbuf_free(p); send_data(pcb, hs); tcp_sent(pcb, http_sent); } /** * @brief Extract the Content_Length data from HTML data * @param data : pointer on receive packet buffer * @param len : buffer length * @retval size : Content_length in numeric format */ static uint32_t Parse_Content_Length(char *data, uint32_t len) { uint32_t i = 0, size = 0, S = 1; int32_t j = 0; char sizestring[6], *ptr; uint32_t ContentLengthOffset = 0; /* find Content-Length data in packet buffer */ for (i = 0; i < len; i++) { if (strncmp((char *)(data + i), Content_Length, 16) == 0) { ContentLengthOffset = i + 16; break; } } /* read Content-Length value */ if (ContentLengthOffset) { i = 0; ptr = (char *)(data + ContentLengthOffset); while (*(ptr + i) != 0x0d && (i < len - ContentLengthOffset)) { sizestring[i] = *(ptr + i); i++; ContentLengthOffset++; } if (i > 0) { /* transform string data into numeric format */ for (j = i - 1; j >= 0; j--) { size += (sizestring[j] - 0x30) * S; S = S * 10; } } } return size; } /** * @brief Extract the custom field data from HTML data * @param data : pointer on receive packet buffer * @param len : buffer length * @param field : field name * @param flen : field name length * @retval value : pointer for field data */ static uint32_t Parse_Header(char *data, uint32_t len, const char *field, uint32_t flen, char *value) { uint32_t i = 0, size = 0; char *ptr; uint32_t Offset = 0; /* Find field name in data buffer */ for (i = 0; i < len; i++) { if (strncmp((char *)(data + i), field, flen) == 0) { Offset = i + flen; break; } } /* Copy Field value */ if (Offset) { i = 0; ptr = (char *)(data + Offset); while (*(ptr + i) != 0x0d && (i < len - Offset)) { value[i] = *(ptr + i); i++; } value[i] = '\0'; size = i; } return size; } /** * @brief * @retval None */ char *Parce_Boundary(const char *data, uint32_t len, char *dst, uint8_t dstlen) { char *ptr = NULL; char *boundary = NULL; uint8_t i = 0; for (uint32_t j = 0; j < len; j++) { if (strncmp((char *)(data + j), "boundary=", 9) == 0) { boundary = (char *)data + j + 9; break; } } if (!boundary) return NULL; *dst++ = '-'; *dst++ = '-'; ptr = boundary; while ((*ptr != 0x0d) && (i < dstlen - 4)) { *dst++ = *ptr++; i++; } //*dst++ = '-'; //*dst++ = '-'; *dst = '\0'; if (i > 0) return boundary; else return NULL; } static bool Parse_Filename(const char *data, uint32_t len, fw_type_t *fw_type, uint32_t *fw_size) { /* Parse packet for the filename field */ uint32_t FilenameOffset = 0; char filename[FILENAME_MAX_LEN]; char file_name_buf[40]; memset(file_name_buf, 0, sizeof(file_name_buf)); strcpy(file_name_buf, MAIN_FW_NAME); if(strcmp(file_name_buf, "NONE") == 0){ *fw_type = fw_main_board; *fw_size = MAIN_FW_SIZE; return true; } for (uint32_t i = 0; i < len; i++) { if (strncmp((char *)(data + i), "filename=", 9) == 0) { FilenameOffset = i + 10; break; } } if (FilenameOffset) { uint32_t i = 0; while ((*(data + FilenameOffset + i) != 0x22) && (i < FILENAME_MAX_LEN) && (FilenameOffset + i <= len)) { filename[i] = *(data + FilenameOffset + i); i++; } filename[i] = 0x0; printf("File: %s\r\n", filename); /* Decide which FW is uploaded */ if (strncmp(filename, MAIN_FW_NAME, 16) == 0) { *fw_type = fw_main_board; *fw_size = MAIN_FW_SIZE; printf("Updating main board FW..\r\n"); } #ifdef SLAVEBRD_ENABLE else if (strncmp(filename, DAUGHTER_FW_NAME, 16) == 0) { *fw_type = fw_daughter_board; *fw_size = DB_FW_SIZE; printf("Updating daughter FW..\r\n"); } #endif else { *fw_type = fw_unknown; } return true; } return false; } /** * @brief * @retval None */ bool GetFileName(char *inStr, char *fileName, uint8_t *fileNameLen) { char *beginValue = NULL; char *endValue = NULL; int len = 0; char *strPtr = NULL; strPtr = strstr(inStr, "GET"); if (strPtr == NULL) { strPtr = strstr(inStr, "POST"); } if (strPtr == NULL) { *fileNameLen = 0; return false; } else { beginValue = strpbrk(strPtr, "/"); endValue = strpbrk(beginValue, " "); if (endValue == NULL) { *fileNameLen = 0; return false; } len = endValue - beginValue; if (len < HTTP_FNAME_LEN) { strncpy(fileName, beginValue, len); *fileNameLen = len; fileName[len] = '\0'; return true; } else { return false; } } } /** * @brief writes received data in flash * @param ptr: data pointer * @param len: data length * @retval none */ void IAP_HTTP_writedata(char *ptr, uint32_t len) { uint32_t count, i = 0, j = 0; /* check if any left bytes from previous packet transfer*/ /* if it is the case do a concat with new data to create a 32-bit word */ if (LeftBytes) { while (LeftBytes <= 3) { if (len > (j + 1)) { LeftBytesTab[LeftBytes++] = *(ptr + j); } else { LeftBytesTab[LeftBytes++] = 0xFF; } j++; } FLASH_If_Write(&FlashWriteAddress, (u32 *)(LeftBytesTab), 1); LeftBytes = 0; /* update data pointer */ ptr = (char *)(ptr + j); len = len - j; } /* write received bytes into flash */ count = len / 4; /* check if remaining bytes < 4 */ i = len % 4; if (i > 0) { if (TotalReceived != size) { /* store bytes in LeftBytesTab */ LeftBytes = 0; for (; i > 0; i--) LeftBytesTab[LeftBytes++] = *(char *)(ptr + len - i); } else count++; } FLASH_If_Write(&FlashWriteAddress, (u32 *)ptr, count); } uint32_t ReturnFlashWriteAddress(void) { return FlashWriteAddress; } #ifdef SLAVEBRD_ENABLE /** * @brief Connect stm32 */ bool IAP_HTTP_stmConnect(void) { bool ok = true; stmRebootForFlash(); /* Connect */ ok = stmConnect(); if (!ok) { printf("Error: device not detected.\n"); return false; } else printf("Device connected\n"); /* Get params */ ok = stmGetDevParams(); if (!ok) { printf("Error: Device not supported.\n"); return false; } /* Erase */ printf("Erasing.. "); ok = stmEraseFW(); if (ok) printf("OK\n"); else { printf("Erase error!\n"); return false; } stm32addr = DB_CPU_FLASH_FIRST_PAGE_ADDRESS; return true; } /** * @brief Prog received data to connected stm32 on uart * @param ptr: data pointer * @param len: data length * @retval none */ void IAP_HTTP_stmProg(char *ptr, uint32_t len) { uint32_t count, i, j = 0; /* check if any left bytes from previous packet transfer*/ /* if it is the case do a concat with new data to create a 32-bit word */ if (LeftBytes) { while (LeftBytes <= 3) { if (len > (j + 1)) { LeftBytesTab[LeftBytes++] = *(ptr + j); } else { LeftBytesTab[LeftBytes++] = 0xFF; } j++; } stmProg(&stm32addr, (uint8_t *)(LeftBytesTab), 4); LeftBytes = 0; /* update data pointer */ ptr = (char *)(ptr + j); len = len - j; } /* write received bytes into flash */ count = len / 4; /* check if remaining bytes < 4 */ i = len % 4; if (i > 0) { if (TotalReceived != size) { /* store bytes in LeftBytesTab */ LeftBytes = 0; for (; i > 0; i--) { LeftBytesTab[LeftBytes++] = *(char *)(ptr + len - i); } } else { count++; } } stmProg(&stm32addr, (uint8_t *)ptr, count * 4); } #endif void ProgTimeout_Handler() { /* If connection was broken during update, * abort process, erase main board flash and restart IAP */ printf("Program timeout. Seems that connection was broken. Reboot...\r\n"); SET_FWINVALID_FLAG(); #ifdef LCD_ENABLE LCD_PrintAligned(5, alignCENTER, "Таймаут обновления"); LCD_PrintAligned(7, alignCENTER, "Перезагрузка.."); #endif switch (fw_type) { case fw_main_board: /* Erase flash */ FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS); break; #ifdef SLAVEBRD_ENABLE case fw_daughter_board: /*TODO Should we erase upper board here? */ break; #endif default: break; } #ifdef SLAVEBRD_ENABLE /* Reboot daughter board */ stmReboot(); #endif /* Self reboot */ NVIC_SystemReset(); } static void PeriodicHandle(uint8_t progress) { (void)progress; #ifdef LCD_ENABLE LCD_PrintBar(7, progress); #endif /* Handle periodic timers for LwIP */ LwIP_Periodic_Handle(0); } /* Get Cookie parameter value */ bool GetCookieValue(char *inStr, char *paramName, char *paramValue, uint8_t *paramLen) { char *beginValue = 0; char *endValue = 0; char *endValueTemp = 0; int len = 0; char *strPtr = strstr(inStr, paramName); if (strPtr != NULL) { beginValue = strpbrk(strPtr, "="); endValue = strpbrk(strPtr, ";"); endValueTemp = strpbrk(strPtr, "\r"); if (endValueTemp != 0 && endValueTemp < endValue) { endValue = endValueTemp; } if (endValue == 0) { endValue = strpbrk(strPtr, "\n"); } len = endValue - beginValue - 1; *endValue = '0'; *beginValue = '0'; if (paramValue && paramLen) { strncpy(paramValue, beginValue + 1, len); *paramLen = len; } return true; } else { return false; } } /* Check if Cookie is set or not */ bool CookieIsSet(char *inStr, uint16_t inLen, char *paramName) { static char CookieBuf[COOKIES_MAX_LEN]; if (!inStr) return false; char *CookiePtr = lwip_strnstr(inStr, "Cookie:", inLen); if (CookiePtr) { char *paramPtr = lwip_strnstr(CookiePtr, paramName, inLen - (inStr - CookiePtr)); if (paramPtr) { strncpy(CookieBuf, paramPtr, sizeof(CookieBuf)); if (GetCookieValue(CookieBuf, paramName, NULL, NULL)) { return true; } } } return false; } /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/