| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <termios.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <pthread.h> |
| |
| #include "gnss_6228.h" |
| #include "gnss_utils.h" |
| #include "mbtk_str.h" |
| #include "mbtk_log.h" |
| #include "mbtk_utils.h" |
| |
| typedef enum { |
| GNSS_6228_STATE_NON, |
| GNSS_6228_STATE_WAIT_YC, |
| GNSS_6228_STATE_BL_DL, |
| GNSS_6228_STATE_FW_DL, |
| GNSS_6228_STATE_SETTING |
| } gnss_6228_state_enum; |
| |
| typedef enum { |
| XMODEM_STATE_C_NAK_WAIT, |
| XMODEM_STATE_DATA_SENDING, |
| XMODEM_STATE_EOT_SENDING, |
| XMODEM_STATE_C_NAK_GET, |
| XMODEM_STATE_DATA_ACK_GET, |
| XMODEM_STATE_DATA_NAK_GET, |
| XMODEM_STATE_EOT_ACK_GET, |
| XMODEM_STATE_EOT_NAK_GET, |
| XMODEM_STATE_CAN_GET, |
| } xmodem_state_enum; |
| |
| #define VMIN 6 |
| #define GPS_DEV "/sys/devices/soc.0/d4000000.apb/mbtk-dev-op/gps_power" |
| #define GNSS_6228_BOOTLOADER "/etc/mbtk/bootloader_r3.0.0_build6773_uartboot_921600.pkg" |
| #define GNSS_6228_FIRMWARE "/etc/mbtk/UC6228CI-R3.5.2.19Build4370_mfg.pkg" |
| |
| #define GNSS_SET_TIMEOUT 3000 // 3s |
| |
| #define UART_BITRATE_DL_BL 230400 |
| #define UART_BITRATE_DL_FW 921600 |
| #define UART_BITRATE_NMEA_DEF_FW 9600 // Default bitrate. |
| #define UART_BITRATE_NMEA 460800 |
| |
| #define GNSS_6228_WAIT_YC_COUNT 40 |
| |
| #define XMODEM_SOH 0x01 |
| #define XMODEM_STX 0x02 |
| #define XMODEM_EOT 0x04 |
| #define XMODEM_ACK 0x06 |
| #define XMODEM_NAK 0x15 |
| #define XMODEM_CAN 0x18 |
| #define XMODEM_CRC_CHR 'C' |
| #define XMODEM_CRC_SIZE 2 /* Crc_High Byte + Crc_Low Byte */ |
| #define XMODEM_FRAME_ID_SIZE 2 /* Frame_Id + 255-Frame_Id */ |
| #define XMODEM_DATA_SIZE_SOH 128 /* for Xmodem protocol */ |
| #define XMODEM_DATA_SIZE_STX 1024 /* for 1K xmodem protocol */ |
| #define USE_1K_XMODEM 1 /* 1 for use 1k_xmodem 0 for xmodem */ |
| #define TIMEOUT_USEC 0 |
| #define TIMEOUT_SEC 10 |
| |
| #if (USE_1K_XMODEM) |
| #define XMODEM_DATA_SIZE XMODEM_DATA_SIZE_STX |
| #define XMODEM_HEAD XMODEM_STX |
| #else |
| #define XMODEM_DATA_SIZE XMODEM_DATA_SIZE_SOH |
| #define XMODEM_HEAD XMODEM_SOH |
| #endif |
| #define XMODEM_PACK_SIZE (XMODEM_DATA_SIZE + XMODEM_CRC_SIZE + XMODEM_FRAME_ID_SIZE + 1) |
| |
| |
| |
| |
| // Default bitrate. |
| static gnss_6228_state_enum gnss_state = GNSS_6228_STATE_WAIT_YC; |
| static xmodem_state_enum xmodem_state = XMODEM_STATE_C_NAK_WAIT; |
| static pthread_cond_t read_cond; |
| static pthread_mutex_t read_mutex; |
| static char xmodem_pack_buff[XMODEM_PACK_SIZE]; |
| static int xmodem_pack_index = 0; |
| static void *gnss_set_rsp_ptr = NULL; |
| static gnss_err_enum gnss_set_result = GNSS_ERR_OK; |
| |
| |
| int gnss_write(int fd, const void *data, int data_len); |
| |
| static void gnss_set_timer_cb(int signo) |
| { |
| if(gnss_state == GNSS_6228_STATE_SETTING) { |
| pthread_mutex_lock(&read_mutex); |
| pthread_cond_signal(&read_cond); |
| pthread_mutex_unlock(&read_mutex); |
| gnss_set_result = GNSS_ERR_TIMEOUT; |
| } |
| return; |
| } |
| |
| int gnss_init_config(int fd) |
| { |
| char *send_buf = "$ANTSTAT,1\r\n"; |
| gnss_write(fd, send_buf, strlen(send_buf)); |
| send_buf = "$ANTSTAT1\r\n"; |
| gnss_write(fd, send_buf, strlen(send_buf)); |
| send_buf = "$PDTINFO\r\n"; |
| gnss_write(fd, send_buf, strlen(send_buf)); |
| send_buf = "$CFGNMEA\r\n"; |
| gnss_write(fd, send_buf, strlen(send_buf)); |
| send_buf = "$CFGPRT,1\r\n"; |
| gnss_write(fd, send_buf, strlen(send_buf)); |
| send_buf = "$CFGAID,0\r\n"; |
| gnss_write(fd, send_buf, strlen(send_buf)); |
| return 0; |
| } |
| |
| static void gnss_cmd_rsp_process(const void *data, int data_len) { |
| const char *ptr = (const char*)data; |
| LOGD("Setting RSP : %s", ptr); |
| if(memcmp(ptr, "$PDTINFO,", 9) == 0) { |
| if(gnss_set_rsp_ptr) { |
| gnss_6228_dev_info_t *dev_info = (gnss_6228_dev_info_t*)gnss_set_rsp_ptr; |
| if(6 == gnss_nmea_sscanf(ptr + 9, dev_info->pdtName, dev_info->Config, |
| dev_info->hwVer, dev_info->fwVer, dev_info->PN, dev_info->SN)) { |
| LOGD("$PDTINFO : %s,%s,%s,%s,%s,%s", dev_info->pdtName, dev_info->Config, |
| dev_info->hwVer, dev_info->fwVer, dev_info->PN, dev_info->SN); |
| } else { |
| LOGW("Parse $PDTINFO error."); |
| } |
| } |
| } |
| |
| |
| else if(memcmp(ptr, "$OK", 3) == 0 || memcmp(ptr, "$Fail", 5) == 0) |
| { |
| // $Fail,0*3E |
| if(memcmp(ptr, "$Fail", 5) == 0) { |
| int code = atoi(ptr + 6); |
| if(code == 1) { |
| gnss_set_result = GNSS_ERR_CHECKSUM; |
| } else if(code == 0) { |
| gnss_set_result = GNSS_ERR_ARG; |
| } else { |
| gnss_set_result = GNSS_ERR_UNKNOWN; |
| } |
| } |
| |
| mbtk_timer_clear(); |
| |
| pthread_mutex_lock(&read_mutex); |
| pthread_cond_signal(&read_cond); |
| pthread_mutex_unlock(&read_mutex); |
| } |
| } |
| |
| int gnss_6228_dev_open() |
| { |
| int fd, ret; |
| fd = open(GPS_DEV, O_RDWR | O_TRUNC, 0644); |
| if(fd < 0) |
| { |
| LOGE("[%s] file [%s] open error\n", __FUNCTION__, GPS_DEV); |
| return -1; |
| } |
| ret = write(fd, "on", 2); |
| if (ret < 0) |
| { |
| LOGE("%s: error writing to file!\n", __FUNCTION__); |
| close(fd); |
| return -2; |
| } |
| |
| close(fd); |
| return 0; |
| } |
| |
| int gnss_6228_dev_close(int uart_fd) |
| { |
| int fd, ret; |
| fd = open(GPS_DEV, O_RDWR | O_TRUNC, 0644); |
| if(fd < 0) |
| { |
| LOGE("[%s] file [%s] open error\n", __FUNCTION__, GPS_DEV); |
| return -1; |
| } |
| ret = write(fd, "off", 3); |
| if (ret < 0) |
| { |
| LOGE("%s: error writing to file!\n", __FUNCTION__); |
| close(fd); |
| return -2; |
| } |
| |
| close(fd); |
| return 0; |
| } |
| |
| int gnss_6228_open(const char *dev) |
| { |
| pthread_mutex_init(&read_mutex, NULL); |
| pthread_cond_init(&read_cond, NULL); |
| return gnss_port_open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY, UART_BITRATE_NMEA_DEF_FW, TRUE); |
| } |
| |
| int gnss_6228_close(int fd) |
| { |
| pthread_mutex_destroy(&read_mutex); |
| pthread_cond_destroy(&read_cond); |
| return gnss_port_close(fd); |
| } |
| |
| static void xmodem_state_change(const void *data, int data_len) |
| { |
| // log_hex("XModem-RECV", data, data_len); |
| |
| pthread_mutex_lock(&read_mutex); |
| const char *ptr = (const char*)data; |
| if(ptr[0] == XMODEM_CAN) { |
| LOGD("GET CAN message."); |
| xmodem_state = XMODEM_STATE_CAN_GET; |
| pthread_cond_signal(&read_cond); |
| pthread_mutex_unlock(&read_mutex); |
| return; |
| } |
| |
| switch(xmodem_state) { |
| case XMODEM_STATE_C_NAK_WAIT: |
| { |
| #if 0 |
| if(ptr[0] == XMODEM_CRC_CHR || ptr[0] == XMODEM_NAK) { |
| LOGD("GET C/NAK(X-Modem start...)"); |
| xmodem_state = XMODEM_STATE_C_NAK_GET; |
| pthread_cond_signal(&read_cond); |
| } |
| #else |
| if(ptr[0] == XMODEM_CRC_CHR) { |
| LOGD("GET C (X-Modem start...)"); |
| xmodem_state = XMODEM_STATE_C_NAK_GET; |
| pthread_cond_signal(&read_cond); |
| } |
| #endif |
| break; |
| } |
| case XMODEM_STATE_DATA_SENDING: |
| { |
| if(ptr[0] == XMODEM_NAK) { |
| xmodem_state = XMODEM_STATE_DATA_NAK_GET; |
| pthread_cond_signal(&read_cond); |
| } else if(ptr[0] == XMODEM_ACK) { |
| xmodem_state = XMODEM_STATE_DATA_ACK_GET; |
| pthread_cond_signal(&read_cond); |
| } |
| break; |
| } |
| case XMODEM_STATE_EOT_SENDING: |
| { |
| if(ptr[0] == XMODEM_NAK) { |
| xmodem_state = XMODEM_STATE_EOT_NAK_GET; |
| pthread_cond_signal(&read_cond); |
| } else if(ptr[0] == XMODEM_ACK) { |
| xmodem_state = XMODEM_STATE_EOT_ACK_GET; |
| pthread_cond_signal(&read_cond); |
| } |
| break; |
| } |
| default: |
| { |
| break; |
| } |
| } |
| pthread_mutex_unlock(&read_mutex); |
| } |
| |
| void gnss_6228_dl_read_cb(const void *data, int data_len) |
| { |
| // const char *buff = (const char*)data; |
| // int index = 0; |
| switch(gnss_state) { |
| case GNSS_6228_STATE_WAIT_YC: |
| { |
| #if 0 |
| static bool found_y = FALSE; |
| while(index < data_len) { |
| if(found_y) { |
| if(buff[index] == 'C') { // Found "YC" |
| gnss_state = GNSS_6228_STATE_BL_DL; |
| bool found_y = FALSE; // Should reset. |
| LOGD("Get YC"); |
| break; |
| } else { |
| found_y = FALSE; // Is "Yxxx" and not "YC". |
| } |
| } else { |
| if(buff[index] == 'Y') { // Only found "Y" |
| found_y = TRUE; |
| } |
| } |
| index++; |
| } |
| #else |
| if(strstr(data, "YC")) { |
| gnss_state = GNSS_6228_STATE_BL_DL; |
| LOGD("Get YC"); |
| } |
| #endif |
| break; |
| } |
| case GNSS_6228_STATE_BL_DL: |
| case GNSS_6228_STATE_FW_DL: |
| { |
| xmodem_state_change(data, data_len); |
| break; |
| } |
| default: |
| { |
| LOGW("Unknown 6228 state:%d", gnss_state); |
| break; |
| } |
| } |
| } |
| |
| void gnss_6228_set_cb(const void *data, int data_len) |
| { |
| // const char *buff = (const char*)data; |
| switch(gnss_state) { |
| case GNSS_6228_STATE_SETTING: |
| { |
| gnss_cmd_rsp_process(data, data_len); |
| break; |
| } |
| default: |
| { |
| break; |
| } |
| } |
| } |
| |
| |
| static int gnss_xmodem_pack_send(int uart_fd, int pack_index, char *data, int data_len) |
| { |
| if(data_len > XMODEM_DATA_SIZE) { |
| LOGE("X-Modem data lenght error:%d", data_len); |
| return -1; |
| } |
| |
| if(pack_index != xmodem_pack_index) { |
| memset(xmodem_pack_buff, 0x1A, XMODEM_PACK_SIZE); |
| xmodem_pack_buff[0] = XMODEM_HEAD; // 帧开始字符 |
| xmodem_pack_buff[1] = (char)pack_index; // 信息包序号 |
| xmodem_pack_buff[2] = (char)(255 - xmodem_pack_buff[1]); // 信息包序号的补码 |
| |
| memcpy(xmodem_pack_buff + 3, data, data_len); |
| |
| uint16 crc_value = get_crc16(xmodem_pack_buff + 3, XMODEM_DATA_SIZE); // 16位crc校验 |
| xmodem_pack_buff[XMODEM_DATA_SIZE+3] = (unsigned char)(crc_value >> 8);// 高八位数据 |
| xmodem_pack_buff[XMODEM_DATA_SIZE+4] = (unsigned char)(crc_value); //低八位数据 |
| xmodem_pack_index = pack_index; |
| } |
| |
| return gnss_write(uart_fd, xmodem_pack_buff, XMODEM_PACK_SIZE); |
| } |
| |
| static int gnss_xmodem_send(int fd, char *file_name) |
| { |
| xmodem_state = XMODEM_STATE_C_NAK_WAIT; |
| |
| bool running = TRUE; |
| int result = 0; |
| xmodem_pack_index = 0; |
| int pack_index = 1; |
| int file_fd = open(file_name, O_RDONLY); |
| if(file_fd <= 0) { |
| LOGE("Can't open (%s) or not exist!(errno=%d)", file_name, errno); |
| return -1; |
| } |
| char buff[XMODEM_DATA_SIZE]; |
| int len = 0; |
| |
| pthread_mutex_lock(&read_mutex); |
| while(running) { |
| //LOGD("Waitting..."); |
| pthread_cond_wait(&read_cond, &read_mutex); |
| //LOGD("Continue : %d", xmodem_state); |
| switch(xmodem_state) |
| { |
| case XMODEM_STATE_C_NAK_GET: |
| case XMODEM_STATE_DATA_ACK_GET: |
| { |
| len = read(file_fd, buff, XMODEM_DATA_SIZE); |
| if(len > 0) { |
| if(XMODEM_PACK_SIZE != gnss_xmodem_pack_send(fd, pack_index, buff, len)) { |
| LOGE(
"Send xmodem package fail."); |
| running = FALSE; |
| result = -1; |
| } |
| xmodem_state = XMODEM_STATE_DATA_SENDING; |
| } else { |
| LOGW("Read file complete[len = %d, errno = %d]", len, errno); |
| //running = FALSE; |
| char end_cmd = XMODEM_EOT; |
| if(gnss_write(fd, &end_cmd, 1) != 1) { |
| LOGE("Send EOT fail."); |
| //running = FALSE; |
| } else { |
| xmodem_state = XMODEM_STATE_EOT_SENDING; |
| } |
| } |
| pack_index++; |
| break; |
| } |
| case XMODEM_STATE_DATA_NAK_GET: |
| { |
| // Retry |
| if(XMODEM_PACK_SIZE != gnss_xmodem_pack_send(fd, pack_index - 1, NULL, XMODEM_DATA_SIZE)) { |
| LOGE(
"Retry send xmodem package fail."); |
| running = FALSE; |
| result = -1; |
| } |
| xmodem_state = XMODEM_STATE_DATA_SENDING; |
| break; |
| } |
| |
| case XMODEM_STATE_EOT_ACK_GET: |
| { |
| LOGD("Send EOT success."); |
| running = FALSE; |
| break; |
| } |
| case XMODEM_STATE_EOT_NAK_GET: |
| { |
| LOGW("Send EOT fail, retry..."); |
| char end_cmd = XMODEM_EOT; |
| if(gnss_write(fd, &end_cmd, 1) != 1) { |
| LOGE("Send EOT fail."); |
| //running = FALSE; |
| } else { |
| xmodem_state = XMODEM_STATE_EOT_SENDING; |
| } |
| break; |
| } |
| case XMODEM_STATE_CAN_GET: |
| { |
| LOGD("Recv CAN message."); |
| running = FALSE; |
| break; |
| } |
| default: |
| { |
| LOGE("Unknown X-Modem state : %d", xmodem_state); |
| running = FALSE; |
| result = -1; |
| break; |
| } |
| } |
| } |
| pthread_mutex_unlock(&read_mutex); |
| |
| if(file_fd > 0) { |
| close(file_fd); |
| } |
| return result; |
| } |
| |
| int gnss_6228_fw_dl(int fd, const char *fw_name, const char *dev) |
| { |
| // Set bootloader baudrate. |
| gnss_set_baudrate(fd, uart_baud_get(UART_BITRATE_DL_BL)); |
| |
| gnss_state = GNSS_6228_STATE_WAIT_YC; |
| int wait_yc_num = 0; |
| while(gnss_state == GNSS_6228_STATE_WAIT_YC && wait_yc_num < GNSS_6228_WAIT_YC_COUNT) { |
| mbtk_write(fd, "M!T", 3); |
| usleep(500); |
| wait_yc_num++; |
| } |
| |
| if(wait_yc_num >= GNSS_6228_WAIT_YC_COUNT) { |
| LOGE("Wait YC timeout : %d / %d", wait_yc_num, GNSS_6228_WAIT_YC_COUNT); |
| return -1; |
| } |
| |
| // Start download bootloader. |
| LOGD("Start download bootloader : %s", GNSS_6228_BOOTLOADER); |
| gnss_state = GNSS_6228_STATE_BL_DL; |
| if(gnss_xmodem_send(fd, GNSS_6228_BOOTLOADER)) { |
| LOGE("Download bootloader fail."); |
| return -1; |
| } |
| LOGD("Bootloader download success."); |
| |
| // Set firmware baudrate. |
| gnss_set_baudrate(fd, uart_baud_get(UART_BITRATE_DL_FW)); |
| |
| // Start download firmware. |
| LOGD("Start download firmware : %s", GNSS_6228_FIRMWARE); |
| gnss_state = GNSS_6228_STATE_FW_DL; |
| if(gnss_xmodem_send(fd, GNSS_6228_FIRMWARE)) { |
| LOGE("Download firmware fail."); |
| return -1; |
| } |
| LOGD("Firmware download success."); |
| |
| // Set NMEA baudrate. |
| gnss_set_baudrate(fd, uart_baud_get(UART_BITRATE_NMEA)); |
| |
| return 0; |
| } |
| |
| gnss_err_enum gnss_6228_set(int fd, const char *cmd, void *cmd_rsp, int cmd_rsp_len) |
| { |
| gnss_state = GNSS_6228_STATE_SETTING; |
| gnss_set_rsp_ptr = cmd_rsp; |
| gnss_set_result = GNSS_ERR_OK; |
| mbtk_timer_set(gnss_set_timer_cb, GNSS_SET_TIMEOUT); |
| |
| char cmd_tmp[128] = {0}; |
| snprintf(cmd_tmp, sizeof(cmd_tmp), "%s\r\n", cmd); |
| gnss_write(fd, cmd_tmp, strlen(cmd_tmp)); |
| |
| pthread_mutex_lock(&read_mutex); |
| pthread_cond_wait(&read_cond, &read_mutex); |
| pthread_mutex_unlock(&read_mutex); |
| |
| gnss_state = GNSS_6228_STATE_NON; |
| return gnss_set_result; |
| } |
| |