/***************************************************************************//** * @file main.c * @brief Bluetotoh over-the-air (OTA) device firmware update (DFU) example for x86 host using Network Co-Processor (NCP) * @version 3.0.0 ******************************************************************************* * # License * Copyright 2020 Silicon Laboratories Inc. www.silabs.com ******************************************************************************* * * The licensor of this software is Silicon Laboratories Inc. Your use of this * software is governed by the terms of Silicon Labs Master Software License * Agreement (MSLA) available at * www.silabs.com/about-us/legal/master-software-license-agreement. This * software is distributed to you in Source Code format and is governed by the * sections of the MSLA applicable to Source Code. * ******************************************************************************/ /** * This an example application that demonstrates how to make a Bluetooth Over-the-Air (OTA) * firmware update. * * To use this application you must have a WSTK configured into NCP mode connected to your * PC and it is used as a Bluetooth radio to push the OTA update to a remote device. * */ #include #include #include #include #include #include #include #include #include "sl_bt_api.h" #include "sl_bt_ncp_host.h" #include "uart.h" #include "system.h" SL_BT_API_DEFINE(); /** * Configurable parameters that can be modified to match the test setup. */ /** The serial port to use for BGAPI communication. */ static char* uart_port = NULL; /** The baud rate to use. */ static uint32_t baud_rate = 0; /** Usage string */ #define OTA_DFU_USAGE "OTA_DFU_Usage: %s ota [serial port] [baud rate] [ota file] [remote address] [(optional) force write without response: 0 / 1] [optional max mtu]\n\n" /**/ /** dfu file to upload*/ FILE *dfu_file = NULL; /** remote device address*/ static uint8_t remote_address_type; static bd_addr remote_address; static bd_addr remote_public_address; static uint8_t addr_found = 0; /** Force using write without response commands */ static uint32_t force_write_without_rsp = 1; /*Bluetooth connection*/ uint8_t ble_connection; /*found OTA descriptors*/ uint32_t ota_gatt_service_handle; uint16_t ota_control_characteristic; uint16_t ota_data_characteristic; uint16_t ota_version_characteristic; uint16_t apploader_version_characteristic; uint16_t bootloader_version_characteristic; uint16_t application_version_characteristic; /*OTA UUIDS*/ uint8_t uuid_ota_service[] = { 0xf0, 0x19, 0x21, 0xb4, 0x47, 0x8f, 0xa4, 0xbf, 0xa1, 0x4f, 0x63, 0xfd, 0xee, 0xd6, 0x14, 0x1d }; //1d14d6ee-fd63-4fa1-bfa4-8f47b42119f0 uint8_t uuid_ota_control[] = { 0x63, 0x60, 0x32, 0xe0, 0x37, 0x5e, 0xa4, 0x88, 0x53, 0x4e, 0x6d, 0xfb, 0x64, 0x35, 0xbf, 0xf7 }; //F7BF3564-FB6D-4E53-88A4-5E37E0326063 uint8_t uuid_ota_data[] = { 0x53, 0xa1, 0x81, 0x1f, 0x58, 0x2c, 0xd0, 0xa5, 0x45, 0x40, 0xfc, 0x34, 0xf3, 0x27, 0x42, 0x98 }; //984227F3-34FC-4045-A5D0-2C581F81A153 uint8_t uuid_bootloader_version[] = { 0xfe, 0x5a, 0x24, 0xe1, 0x2b, 0xaa, 0xa5, 0xb2, 0xe9, 0x46, 0x17, 0xe9, 0x0a, 0x5c, 0xf0, 0x25 };//25f05c0a-e917-46e9-b2a5-aa2be1245afe uint8_t uuid_apploader_version[] = { 0x9f, 0x3e, 0xe2, 0x2e, 0x0e, 0xcf, 0xff, 0xbf, 0x1e, 0x45, 0xca, 0x8c, 0x68, 0x23, 0x4a, 0x4f };//4f4a2368-8cca-451e-bfff-cf0e2ee23e9f uint8_t uuid_ota_version[] = { 0x16, 0x53, 0x1e, 0xc4, 0x4c, 0xba, 0xad, 0x9d, 0x32, 0x4b, 0x68, 0x08, 0xcf, 0x7b, 0xc0, 0x4c };//4cc07bcf-0868-4b32-9dad-ba4cc41e5316 uint8_t uuid_application_version[] = { 0xf8, 0x92, 0x7a, 0xac, 0x96, 0xcd, 0xa9, 0xbf, 0xf2, 0x49, 0xc1, 0x4a, 0x11, 0xcc, 0x77, 0x0d };//0d77cc11-4ac1-49f2-bfa9-cd96ac7a92f8 /*Error macro*/ #define ERROR_EXIT(...) do { printf(__VA_ARGS__); exit(EXIT_FAILURE); } while (0) #define GAP_ADDR_TYPE 0x1b /* Enumeration of possible OTA states */ enum ota_states { OTA_INIT, // default state OTA_CONNECT, // connect to remote device OTA_FIND_SERVICES, // find OTA control&data attributes OTA_FIND_CHARACTERISTICS, // find OTA control&data attributes OTA_READ_OTA_DATA_PROPERTIES, // Read OTA Data characteristic properties OTA_BEGIN, // Begin OTA process OTA_UPLOAD_WITHOUT_RSP, // Upload data to remote device using write without response OTA_UPLOAD_WITH_RSP, // Upload data to remote device using write with response OTA_END, // End current OTA process OTA_RESTART, // End current OTA process OTA_RESET_TO_DFU, // Reset to DFU mode OTA_SCAN, // Scan for device OTA_READ_BOOTLOADER_VERSION, //Read bootloader version OTA_READ_APPLOADER_VERSION, //Read apploader version OTA_READ_OTA_VERSION, //Read OTA version OTA_READ_APPLICATION_VERSION, //Read application version } ota_state = OTA_INIT; void ota_change_state(enum ota_states new_state); ///DFU #define MAX_DFU_PACKET 256 uint8_t dfu_data[MAX_DFU_PACKET]; bool dfu_resync = false; size_t dfu_toload = 0; size_t dfu_total = 0; size_t dfu_current_pos = 0; time_t dfu_start_time; #define MAX_MTU 247 #define MIN_MTU 23 uint32_t mtu = MIN_MTU; uint32_t max_mtu = MAX_MTU; uint8_t ota_data_properties = 0; uint32_t bootloader_version = -1; uint32_t application_version = -1; uint16_t apploader_version[4] = { 0 }; uint8_t ota_version = 0; // /** * UART DFU Configuration */ #define SOH 0x01 #define STX 0x02 #define EOT 0x04 #define ACK 0x06 #define NAK 0x15 #define CAN 0x18 #define CTRLZ 0x1A #define MAXRETRANS 25 /** Usage string */ #define UART_DFU_USAGE "\nUART_DFU_Usage: %s uart [serial port] [baud rate] [dfu file] \n" #define MAX_UART_DFU_PACKET 132 unsigned char txbuff[MAX_UART_DFU_PACKET]; uint8_t rxbuff; void app_init(int argc, char* argv[]); void upload_dfu_file(void); void sync_dfu_boot(void); int dfu_read_size(void); int xmodemTransmit(char *src, int srcsz); static void on_message_send(uint32_t msg_len, uint8_t* msg_data); static const unsigned short crc16tab[256]= { 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 }; unsigned short crc16_ccitt(const void *buf, int len) { register int counter; register unsigned short crc = 0; for( counter = 0; counter < len; counter++) crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *(char *)buf++)&0x00FF]; return crc; } static int _inbyte(void) // msec timeout { int dataRead; dataRead = uartRxNonBlocking(1, &rxbuff); // dataRead = uartRx(1, &rxbuff); if(dataRead != 0) printf("inbyte is 0x%x, %c \n", rxbuff, rxbuff); return rxbuff ; } static void _outbyte(int c) { uartTx(1, (uint8_t*)&c); // printf("outbyte is 0x%x, %c \n", c, c); } static void flushinput(void) { while (_inbyte() >= 0) ; } int get_option(void) { int option; do{ // scanf("%d", &option); option = getchar(); // if(option < '1' || option > '3'){ // printf("wrong value, please retry \n"); // } }while(option < '1' || option > '3'); return option; } int UART_hw_init(int argc, char* argv[]) { if (argc < 5) { printf(UART_DFU_USAGE, argv[0]); printf(OTA_DFU_USAGE, argv[0]); exit(EXIT_FAILURE); } /** * Handle the command-line arguments. */ //filename dfu_file = fopen(argv[4], "rb"); if (dfu_file == NULL) { printf("cannot open file %s\n", argv[4]); exit(-1); } baud_rate = atoi(argv[3]); uart_port = argv[2]; if (!uart_port || !baud_rate || !dfu_file) { printf(UART_DFU_USAGE, argv[0]); exit(EXIT_FAILURE); } /** * Initialise the serial port. */ return uartOpen((int8_t*)uart_port, baud_rate, 1, 100); } void menu_init(void) { int option; printf("\r\nMenu\r\n"); printf("1. upload gbl\r\n"); printf("2. run\r\n"); printf("3. ebl info\r\n"); printf("BL >"); option = get_option(); switch(option){ case '1': _outbyte('1'); // MENU start INIT_TRANSFER printf("begin upload \n"); break; case '2': _outbyte('2'); // MENU start INIT_TRANSFER sl_bt_dfu_reset(0); printf("begin boot \n"); break; case '3': _outbyte('3'); // MENU start INIT_TRANSFER printf("return to MENU \n"); break; default: _outbyte('3'); // MENU start INIT_TRANSFER printf("return to MENU \n"); break; } } int main_uart(int argc, char* argv[]) { // Initialize Silicon Labs device, system, service(s) and protocol stack(s). // Note that if the kernel is present, processing task(s) will be created by // this call. sl_system_init(); // Initialize the application. For example, create periodic timer(s) or // task(s) if the kernel is present. app_init(argc, argv); // Do not remove this call: Silicon Labs components process action routine // must be called from the super loop. // sl_system_process_action(); upload_dfu_file(); } void upload_dfu_file() { int st; /** get dfu file size*/ if (dfu_read_size()) { printf("Error, DFU file read failed\n"); exit(EXIT_FAILURE); } /** move target to dfu mode*/ sync_dfu_boot(); printf ("Prepare your terminal emulator to receive data now...\n"); uint8_t dfu_data[dfu_total]; if (fread(dfu_data, 1, dfu_total, dfu_file) != dfu_total) { printf("File read failure\n"); exit(EXIT_FAILURE); } printf ("Read the DFU file successfully.\n"); st = xmodemTransmit((char *)dfu_data, dfu_total); if (st < 0) { printf ("Xmodem transmit error: status: %d\n", st); fflush(stdout); } else { printf ("Xmodem successfully transmitted %d bytes\n", st); fflush(stdout); } printf("fclose, close the dfu file \r\n"); fclose(dfu_file); menu_init(); // sl_bt_dfu_reset(0); printf("Close the uart port \r\n"); if(uartClose() != 0) // close the uart handler { printf("Close the uart port successfully\n"); } } void sync_dfu_boot() { printf("Syncing"); fflush(stdout); do{ printf("."); fflush(stdout); // reset the device, and make it enter bootloader mode. printf("->->->-> sl_bt_system_reset ...\n"); sl_bt_system_reset(1); //get_action() menu_init(); int receivechar[100]; char transferInitStr[12] = "begin upload"; int i; for(uint8_t try=0; try < 50; try++) { for(i = 0; i < 100; i++) { receivechar[i] = _inbyte(); } for(int i = 0; i < 100; i++) { if (receivechar[i] == 'b') { if(memcmp(&receivechar[i], &transferInitStr[0],12) == 0) { printf("DFU OK\n"); fflush(stdout); return; } else return; } } if(i == 15) return; } // sleep 1s, and then try to reset the device again sleep(1); } while(1); } /***************************************************************************//** * Application initialisation. ******************************************************************************/ void app_init(int argc, char* argv[]) { SL_BT_API_INITIALIZE_NONBLOCK(on_message_send, uartRx, uartRxPeek); ////////////////////////////////////////// // Add your application init code here! // ////////////////////////////////////////// if (UART_hw_init(argc, argv) < 0) { printf("HW init failure\n"); exit(EXIT_FAILURE); } } /***************************************************************************//** * Application process actions. ******************************************************************************/ void app_process_action(void) { ////////////////////////////////////////// // Add your application tick code here! // ////////////////////////////////////////// } int xmodemTransmit(char *src, int srcsz) { // unsigned char xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */ int maxPayloadSize, crc = -1; // unsigned char packetno = 1; int i= 0; uint32_t packetno = 1; uint32_t packetLen = 0; static uint32_t sendOffset = 0; uint16_t retry; uint8_t rspVal = 0; // The response from device after receiving one xmodem packet printf("---------------------\n"); for(;;) { for( retry = 0; retry < 100; ++retry) { // printf("retry %d\n", retry); if ((rspVal = _inbyte()) >= 0) { switch (rspVal) { case 'C': crc = 1; printf("go to start_trans ->->->- \n"); goto start_trans; case NAK: crc = 0; goto start_trans; case CAN: if ((rspVal = _inbyte()) == CAN) { _outbyte(ACK); flushinput(); return -1; /* canceled by remote */ } break; default: break; } } } printf(">>>return and reset >>>\n"); _outbyte(CAN); _outbyte(CAN); _outbyte(CAN); // flushinput(); return -2; /* no sync */ for(;;) { start_trans: printf("\n\n>> start trans <<\n"); #ifdef TRANSMIT_XMODEM_1K txbuff[0] = STX; maxPayloadSize = 1024; #else txbuff[0] = SOH; maxPayloadSize = 128; #endif txbuff[1] = packetno; txbuff[2] = ~packetno; packetLen = srcsz - sendOffset; if (packetLen > maxPayloadSize) packetLen = maxPayloadSize; printf("srcsz %d, sendOffset %d, packetLen %d \n", srcsz, sendOffset, packetLen); if (packetLen > 0) { memset (&txbuff[3], 0, maxPayloadSize); memcpy (&txbuff[3], &src[sendOffset], packetLen); if (packetLen < maxPayloadSize){ txbuff[3+packetLen] = CTRLZ; printf("---- It's a short packet, packet length is %d---- \n", packetLen); } if (crc) { unsigned short ccrc = crc16_ccitt(&txbuff[3], maxPayloadSize); txbuff[maxPayloadSize + 3] = (ccrc >> 8) & 0xFF; txbuff[maxPayloadSize + 4] = ccrc & 0xFF; printf("CRC is 0x%04x\n", ccrc); } else { unsigned char ccks = 0; // check sum for (i = 3; i < maxPayloadSize + 3; ++i) { ccks += txbuff[i]; } txbuff[maxPayloadSize+3] = ccks; } for (retry = 0; retry < MAXRETRANS; ++retry) { // If didn't get ACK from remote device, will try to re-transmit the packet up to MAXRETRANS times printf("retry %d, pakcet dump:\n", retry); for (i = 0; i < maxPayloadSize+4+(crc?1:0); ++i) { _outbyte(txbuff[i]); if(i!=0 && i%16 == 0) printf("\n"); printf("0x%02x ", txbuff[i]); } printf("\r\npacket number = %d\r\n", packetno); // if ((c = _inbyte()) >= 0 ) { rspVal = _inbyte(); printf("response value is 0x%x (0x01 SOH; 0x04 EOT; 0x06 ACK; 0x15 NAK; 0x18 CAN)\n", rspVal); if (rspVal >= 0 ) { switch (rspVal) { case ACK: ++packetno; // sendOffset += maxPayloadSize; sendOffset += packetLen; // goto start_trans; case CAN: if ((rspVal = _inbyte()) == CAN) { _outbyte(ACK); // flushinput(); // cheng return -1; /* canceled by remote */ } break; case NAK: default: break; } } } printf("Didn't get response from remote device after MAXRETRANS retry to the specified packet\r\n"); _outbyte(CAN); _outbyte(CAN); _outbyte(CAN); // flushinput(); // cheng return -4; /* xmit error */ } else { // end of transfer for (retry = 0; retry < 10; ++retry) { _outbyte(EOT); if ((rspVal = _inbyte()) == ACK) break; } //flushinput(); return (rspVal == ACK)?sendOffset:-5; } } } } int dfu_read_size() { if (fseek(dfu_file, 0L, SEEK_END)) { return -1; } dfu_total = dfu_toload = ftell(dfu_file); if (fseek(dfu_file, 0L, SEEK_SET)) { return -1; } printf("Bytes to send:%d\n", (int)dfu_toload); fflush(stdout); return 0; } void send_dfu_block() { time_t ti; size_t dfu_size; sl_bt_msg_t evt; while (dfu_toload > 0) { sl_status_t sc; uint16_t sent_len; dfu_size = dfu_toload > (mtu - 3) ? (mtu - 3) : dfu_toload; if (fread(dfu_data, 1, dfu_size, dfu_file) != dfu_size) { printf("File read failure\n"); exit(-1); } do { sc = sl_bt_pop_event(&evt); sc = sl_bt_gatt_write_characteristic_value_without_response(ble_connection, ota_data_characteristic, dfu_size, dfu_data, &sent_len); } while (sc != SL_STATUS_OK); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed:0x%x", __FUNCTION__, sc); } dfu_current_pos += dfu_size; dfu_toload -= dfu_size; ti = time(NULL); if (ti != dfu_start_time && dfu_total > 0) { printf("\r%d%% %.2fkbit/s ", (int)(100 * dfu_current_pos / dfu_total), dfu_current_pos * 8.0 / 1000.0 / difftime(ti, dfu_start_time)); } } printf("\n"); printf("time: %.2fs", difftime(ti, dfu_start_time)); printf("\n"); ota_change_state(OTA_END); } void send_dfu_packet_with_confirmation() { time_t ti; size_t dfu_size; if (dfu_toload > 0) { sl_status_t sc; dfu_size = dfu_toload > (mtu - 3) ? (mtu - 3) : dfu_toload; if (fread(dfu_data, 1, dfu_size, dfu_file) != dfu_size) { printf("File read failure\n"); exit(-1); } sc = sl_bt_gatt_write_characteristic_value(ble_connection, ota_data_characteristic, dfu_size, dfu_data); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed:0x%x", __FUNCTION__, sc); } dfu_current_pos += dfu_size; dfu_toload -= dfu_size; ti = time(NULL); if (ti != dfu_start_time && dfu_total > 0) { printf("\r%d%% %.2fkbit/s ", (int)(100 * dfu_current_pos / dfu_total), dfu_current_pos * 8.0 / 1000.0 / difftime(ti, dfu_start_time)); } } else { ti = time(NULL); printf("\n"); printf("time: %.2fs", difftime(ti, dfu_start_time)); printf("\n"); ota_change_state(OTA_END); } } /* */ void sync_boot() { sl_status_t sc; sl_bt_msg_t evt; // Flush std output fflush(stdout); // Reset NCP to ensure it gets into a defined state. // Once the chip successfully boots, boot event should be received. sl_bt_system_reset(0); do { sc = sl_bt_pop_event(&evt); if (!sc) { switch (SL_BT_MSG_ID(evt.header)) { case sl_bt_evt_system_boot_id: printf("System rebooted\n"); fflush(stdout); sl_bt_evt_system_boot_t *p = \ &evt.data.evt_system_boot; printf("NCP version: v%d.%d.%d-b%d\n", p->major, p->minor, p->patch, p->build); return; } } } while (1); } /* */ void ota_change_state(enum ota_states new_state) { ota_state = new_state; switch (ota_state) { case OTA_END: { printf("Finishing DFU block..."); fflush(stdout); { sl_status_t sc; sc = sl_bt_gatt_write_characteristic_value(ble_connection, ota_control_characteristic, 1, (uint8_t*)"\x03"); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed,0x%x", __FUNCTION__, sc); } } } break; case OTA_UPLOAD_WITHOUT_RSP: send_dfu_block(); break; case OTA_UPLOAD_WITH_RSP: send_dfu_packet_with_confirmation(); break; case OTA_BEGIN: { sl_status_t sc; sc = sl_bt_gatt_write_characteristic_value(ble_connection, ota_control_characteristic, 1, (uint8_t*)"\0"); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed,0x%x", __FUNCTION__, sc); } printf("DFU mode..."); fflush(stdout); dfu_start_time = time(NULL); } break; case OTA_FIND_CHARACTERISTICS: { sl_status_t sc; sc = sl_bt_gatt_discover_characteristics(ble_connection, ota_gatt_service_handle); if (sc) { ERROR_EXIT("Error, characteristics discover failed,0x%x", sc); } printf("Discovering characteristics..."); fflush(stdout); ota_control_characteristic = 0xFFFF; ota_data_characteristic = 0xFFFF; bootloader_version_characteristic = 0xFFFF; apploader_version_characteristic = 0xFFFF; ota_version_characteristic = 0xFFFF; } break; case OTA_READ_OTA_DATA_PROPERTIES: { sl_status_t sc; sc = sl_bt_gatt_read_characteristic_value_from_offset(ble_connection, ota_data_characteristic - 1, 0, 1); if (sc) { ERROR_EXIT("Error,%s, characteristic read failed,0x%x", __FUNCTION__, sc); } } break; case OTA_FIND_SERVICES: { sl_status_t sc; sc = sl_bt_gatt_discover_primary_services_by_uuid(ble_connection, sizeof(uuid_ota_service), uuid_ota_service); if (sc) { ERROR_EXIT("Error, service discover failed,0x%x", sc); } printf("Discovering services..."); fflush(stdout); ota_gatt_service_handle = 0xFFFFFFFF; } break; case OTA_SCAN: { addr_found = 0; sl_bt_scanner_start(gap_1m_phy, scanner_discover_generic); printf("Scanning..."); fflush(stdout); } break; case OTA_CONNECT: { sl_status_t sc; uint16_t set_mtu; uint8_t connection; sc = sl_bt_gatt_set_max_mtu(max_mtu, &set_mtu); if (sc) { ERROR_EXIT("Error, set max MTU failed,0x%x", sc); } //set default connection parameters sc = sl_bt_connection_set_default_parameters(6, 6, 0, 300, 0, 0xffff); if (sc) { ERROR_EXIT("Error, set default connection parameters failed,0x%x", sc); } //move to connect state, connect to device address sc = sl_bt_connection_open(remote_address, remote_address_type, gap_1m_phy, &connection); if (sc) { ERROR_EXIT("Error, open failed,0x%x", sc); } ble_connection = connection; printf("Connecting..."); fflush(stdout); } break; case OTA_INIT: sync_boot(); { sl_status_t sc; bd_addr address; uint8_t address_type; sc = sl_bt_system_get_identity_address(&address, &address_type); if (sc) { ERROR_EXIT("Error, failed to get Bluetooth address,0x%x", sc); } printf("Local %s address: %02x:%02x:%02x:%02x:%02x:%02x\n", address_type ? "static random" : "public device", address.addr[5], address.addr[4], address.addr[3], address.addr[2], address.addr[1], address.addr[0]); } if (dfu_read_size()) { ERROR_EXIT("Error, DFU file read failed\n"); } ota_change_state(OTA_SCAN); break; case OTA_RESET_TO_DFU: { sl_status_t sc; sc = sl_bt_gatt_write_characteristic_value(ble_connection, ota_control_characteristic, 1, (uint8_t*)"\0"); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed,0x%x", __FUNCTION__, sc); } } break; case OTA_READ_OTA_VERSION: { sl_status_t sc; sc = sl_bt_gatt_read_characteristic_value(ble_connection, ota_version_characteristic); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed,0x%x", __FUNCTION__, sc); } } break; case OTA_READ_APPLOADER_VERSION: { sl_status_t sc; sc = sl_bt_gatt_read_characteristic_value(ble_connection, apploader_version_characteristic); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed,0x%x", __FUNCTION__, sc); } } break; case OTA_READ_BOOTLOADER_VERSION: { sl_status_t sc; sc = sl_bt_gatt_read_characteristic_value(ble_connection, bootloader_version_characteristic); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed,0x%x", __FUNCTION__, sc); } } break; case OTA_READ_APPLICATION_VERSION: { sl_status_t sc; sc = sl_bt_gatt_read_characteristic_value(ble_connection, application_version_characteristic); if (sc) { ERROR_EXIT("Error,%s, characteristic write failed,0x%x", __FUNCTION__, sc); } } break; default: break; } } /** * Function called when a message needs to be written to the serial port. * @param msg_len Length of the message. * @param msg_data Message data, including the header. * @param data_len Optional variable data length. * @param data Optional variable data. */ static void on_message_send(uint32_t msg_len, uint8_t* msg_data) { /** Variable for storing function return values. */ int ret; #if DEBUG printf("on_message_send()\n"); #endif /* DEBUG */ ret = uartTx(msg_len, msg_data); if (ret < 0) { printf("on_message_send() - failed to write to serial port %s, ret: %d, errno: %d\n", uart_port, ret, errno); exit(EXIT_FAILURE); } } void print_address(bd_addr address) { for (int i = 5; i >= 0; i--) { printf("%02x", address.addr[i]); if (i > 0) { printf(":"); } } } static int parse_scan_data(uint8array *data, bd_addr *addr) { uint8_t i = 0; while (i < data->len) { if (data->data[i + 1] == GAP_ADDR_TYPE) { memcpy(addr, &data->data[i + 3], sizeof(bd_addr)); return 0; } else { i += data->data[i] + 1; } } return -1; } int parse_address(const char *str, bd_addr *addr) { int a[6]; int i; i = sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x", &a[5], &a[4], &a[3], &a[2], &a[1], &a[0] ); if (i != 6) { return -1; } for (i = 0; i < 6; i++) { addr->addr[i] = (uint8_t)a[i]; } return 0; } int hw_init(int argc, char* argv[]) { if (argc < 6) { printf(UART_DFU_USAGE, argv[0]); printf(OTA_DFU_USAGE, argv[0]); exit(EXIT_FAILURE); } /** * Handle the command-line arguments. */ //filename dfu_file = fopen(argv[4], "rb"); if (dfu_file == NULL) { printf("cannot open file %s\n", argv[4]); exit(-1); } //baudrate baud_rate = atoi(argv[3]); //uart port uart_port = argv[2]; //remote address if (parse_address(argv[5], &remote_public_address)) { printf("Unable to parse address %s", argv[5]); exit(EXIT_FAILURE); } if (argc >= 7) { force_write_without_rsp = atoi(argv[5]); } if (argc == 8) { max_mtu = atoi(argv[7]); if (max_mtu > MAX_MTU) { max_mtu = MAX_MTU; } else if (max_mtu < MIN_MTU) { max_mtu = MIN_MTU; } printf("MTU set to %u\n", max_mtu); } /** * Initialise the serial port. */ return uartOpen((int8_t*)uart_port, baud_rate, 1, 100); } /** * The main program. */ int main_ota(int argc, char* argv[]) { sl_bt_msg_t evt; sl_bt_msg_t *p = &evt; /** * Initialize BGLIB with our output function for sending messages. */ SL_BT_API_INITIALIZE_NONBLOCK(on_message_send, uartRx, uartRxPeek); if (hw_init(argc, argv) < 0) { printf("HW init failure\n"); exit(EXIT_FAILURE); } ota_change_state(OTA_INIT); while (1) { sl_bt_wait_event(&evt); if (p && SL_BT_MSG_ID(p->header) == sl_bt_evt_gatt_mtu_exchanged_id) { mtu = p->data.evt_gatt_mtu_exchanged.mtu; printf("ATT MTU exchanged: %d\n", mtu); continue; } switch (ota_state) { case OTA_END: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_connection_closed_id: printf("OK\n"); fflush(stdout); exit(EXIT_SUCCESS); break; case sl_bt_evt_gatt_procedure_completed_id: if (p->data.evt_gatt_procedure_completed.result) { ERROR_EXIT("Error, OTA DFU failed,0x%x", p->data.evt_gatt_procedure_completed.result); } printf("OK\n"); fflush(stdout); printf("Closing connection..."); fflush(stdout); { sl_status_t sc; sc = sl_bt_connection_close(ble_connection); if (sc) { ERROR_EXIT("Error,%s, close failed,0x%x", __FUNCTION__, sc); } } break; default: break; } break; case OTA_UPLOAD_WITHOUT_RSP: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_procedure_completed_id: if (p->data.evt_gatt_procedure_completed.result) { ERROR_EXIT("procedure failed:0x%x\r\n", p->data.evt_gatt_procedure_completed.result); } send_dfu_block(); break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_UPLOAD_WITH_RSP: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_procedure_completed_id: if (p->data.evt_gatt_procedure_completed.result) { ERROR_EXIT("procedure failed:0x%x\r\n", p->data.evt_gatt_procedure_completed.result); } send_dfu_packet_with_confirmation(); break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_BEGIN: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_procedure_completed_id: printf("OK\n"); fflush(stdout); if ((ota_data_properties & 0x0C) == 0) { ERROR_EXIT("Wrong supported OTA Data properties\r\n"); } else { if (force_write_without_rsp == 1 && ota_data_properties & 0x04) { //Write without response is supported and forced printf("OTA DFU - write without response \n"); fflush(stdout); ota_change_state(OTA_UPLOAD_WITHOUT_RSP); } else { printf("OTA DFU - write with response \n"); fflush(stdout); ota_change_state(OTA_UPLOAD_WITH_RSP); } } break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_CONNECT: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_connection_opened_id: printf("OK\n"); fflush(stdout); ota_change_state(OTA_FIND_SERVICES); break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_FIND_SERVICES: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_procedure_completed_id: if (ota_gatt_service_handle == 0xFFFFFFFF) { ERROR_EXIT("Error, no valid OTA service found"); } printf("OK\n"); fflush(stdout); ota_change_state(OTA_FIND_CHARACTERISTICS); break; case sl_bt_evt_gatt_service_id: ota_gatt_service_handle = p->data.evt_gatt_service.service; break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_FIND_CHARACTERISTICS: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_procedure_completed_id: if (ota_control_characteristic == 0xFFFF) { ERROR_EXIT("Error, no valid OTA characteristics found"); } else if (ota_data_characteristic == 0xFFFF) { printf("\nRestarting target."); ota_change_state(OTA_RESET_TO_DFU); break; } printf("OK\n Control handle:%d\n Data handle:%d\n", ota_control_characteristic, ota_data_characteristic); fflush(stdout); if (ota_version_characteristic != 0xFFFF) { ota_change_state(OTA_READ_OTA_VERSION); } else { ota_change_state(OTA_READ_OTA_DATA_PROPERTIES); } break; case sl_bt_evt_gatt_characteristic_id: if (p->data.evt_gatt_characteristic.uuid.len == sizeof(uuid_ota_control) && !memcmp(p->data.evt_gatt_characteristic.uuid.data, uuid_ota_control, sizeof(uuid_ota_control))) { ota_control_characteristic = p->data.evt_gatt_characteristic.characteristic; } else if (p->data.evt_gatt_characteristic.uuid.len == sizeof(uuid_ota_data) && !memcmp(p->data.evt_gatt_characteristic.uuid.data, uuid_ota_data, sizeof(uuid_ota_data))) { ota_data_characteristic = p->data.evt_gatt_characteristic.characteristic; } else if (p->data.evt_gatt_characteristic.uuid.len == sizeof(uuid_bootloader_version) && !memcmp(p->data.evt_gatt_characteristic.uuid.data, uuid_bootloader_version, sizeof(uuid_bootloader_version))) { bootloader_version_characteristic = p->data.evt_gatt_characteristic.characteristic; } else if (p->data.evt_gatt_characteristic.uuid.len == sizeof(uuid_apploader_version) && !memcmp(p->data.evt_gatt_characteristic.uuid.data, uuid_apploader_version, sizeof(uuid_apploader_version))) { apploader_version_characteristic = p->data.evt_gatt_characteristic.characteristic; } else if (p->data.evt_gatt_characteristic.uuid.len == sizeof(uuid_ota_version) && !memcmp(p->data.evt_gatt_characteristic.uuid.data, uuid_ota_version, sizeof(uuid_ota_version))) { ota_version_characteristic = p->data.evt_gatt_characteristic.characteristic; } else if (p->data.evt_gatt_characteristic.uuid.len == sizeof(uuid_application_version) && !memcmp(p->data.evt_gatt_characteristic.uuid.data, uuid_application_version, sizeof(uuid_bootloader_version))) { application_version_characteristic = p->data.evt_gatt_characteristic.characteristic; } break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_READ_OTA_DATA_PROPERTIES: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_characteristic_value_id: if (p->data.evt_gatt_characteristic_value.value.len == 1) { ota_data_properties = p->data.evt_gatt_characteristic_value.value.data[0]; } printf(" OTA Data characteristic properties:0x%02x\n", ota_data_properties); fflush(stdout); break; case sl_bt_evt_gatt_procedure_completed_id: ota_change_state(OTA_BEGIN); break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_RESET_TO_DFU: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_connection_closed_id: printf("\nConnection closed, retrying. (Remote device booting in DFU mode)\n"); fflush(stdout); ota_change_state(OTA_SCAN); break; default: break; } break; case OTA_SCAN: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_scanner_scan_report_id: if (!addr_found) { bd_addr addr; if (!memcmp(&p->data.evt_scanner_scan_report.address, &remote_public_address, sizeof(bd_addr))) { memcpy(&remote_address, &p->data.evt_scanner_scan_report.address, sizeof(bd_addr)); remote_address_type = p->data.evt_scanner_scan_report.address_type; addr_found = 1; } else if (parse_scan_data(&p->data.evt_scanner_scan_report.data, &addr) == 0) { if (!memcmp(&addr, &remote_public_address, sizeof(bd_addr))) { memcpy(&remote_address, &p->data.evt_scanner_scan_report.address, sizeof(bd_addr)); remote_address_type = p->data.evt_scanner_scan_report.address_type; addr_found = 1; } } if (addr_found) { sl_bt_scanner_stop(); printf("OK\n"); fflush(stdout); printf("Device address found, connecting.\n"); fflush(stdout); ota_change_state(OTA_CONNECT); } } break; default: break; } break; case OTA_READ_OTA_VERSION: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_characteristic_value_id: if (p->data.evt_gatt_characteristic_value.value.len == 1) { memcpy(&ota_version, p->data.evt_gatt_characteristic_value.value.data, p->data.evt_gatt_characteristic_value.value.len); printf(" OTA protocol version:0x%02x\n", ota_version); fflush(stdout); } break; case sl_bt_evt_gatt_procedure_completed_id: ota_change_state(OTA_READ_APPLOADER_VERSION); break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_READ_APPLOADER_VERSION: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_characteristic_value_id: if (p->data.evt_gatt_characteristic_value.value.len == 8) { memcpy(&apploader_version, p->data.evt_gatt_characteristic_value.value.data, p->data.evt_gatt_characteristic_value.value.len); if (ota_version >= 3) { printf(" Apploader version:%d.%d.%d.%d\n", apploader_version[0], apploader_version[1], apploader_version[2], apploader_version[3]); fflush(stdout); } else { printf(" Bluetooth stack version:%d.%d.%d.%d\n", apploader_version[0], apploader_version[1], apploader_version[2], apploader_version[3]); fflush(stdout); } } break; case sl_bt_evt_gatt_procedure_completed_id: if (ota_version >= 3) { ota_change_state(OTA_READ_BOOTLOADER_VERSION); } else { ota_change_state(OTA_READ_OTA_DATA_PROPERTIES); } break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_READ_BOOTLOADER_VERSION: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_characteristic_value_id: if (p->data.evt_gatt_characteristic_value.value.len == 4) { memcpy(&bootloader_version, p->data.evt_gatt_characteristic_value.value.data, p->data.evt_gatt_characteristic_value.value.len); printf(" Bootloader version:0x%08x\n", bootloader_version); fflush(stdout); } break; case sl_bt_evt_gatt_procedure_completed_id: if (bootloader_version == 0) { ERROR_EXIT("\nError, no bootloader present"); } ota_change_state(OTA_READ_APPLICATION_VERSION); break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; case OTA_READ_APPLICATION_VERSION: switch (SL_BT_MSG_ID(p->header)) { case sl_bt_evt_gatt_characteristic_value_id: if (p->data.evt_gatt_characteristic_value.value.len == 4) { memcpy(&application_version, p->data.evt_gatt_characteristic_value.value.data, p->data.evt_gatt_characteristic_value.value.len); printf(" Application version:0x%08x\n", application_version); fflush(stdout); } break; case sl_bt_evt_gatt_procedure_completed_id: ota_change_state(OTA_READ_OTA_DATA_PROPERTIES); break; case sl_bt_evt_connection_closed_id: ERROR_EXIT("\nError, Connection closed, reason 0x%x", p->data.evt_connection_closed.reason); break; default: break; } break; default: break; } } fclose(dfu_file); return 0; } int main(int argc, char* argv[]) { if (strcmp(argv[1],"uart") == 0) { main_uart(argc, argv); }else if (strcmp(argv[1],"ota") == 0) { main_ota(argc, argv); } else{ printf(UART_DFU_USAGE, argv[0]); printf(OTA_DFU_USAGE, argv[0]); } }