827 lines
23 KiB
C
827 lines
23 KiB
C
/*
|
|
Copyright 2023 Quectel Wireless Solutions Co.,Ltd
|
|
|
|
Quectel hereby grants customers of Quectel a license to use, modify,
|
|
distribute and publish the Software in binary form provided that
|
|
customers shall have no right to reverse engineer, reverse assemble,
|
|
decompile or reduce to source code form any portion of the Software.
|
|
Under no circumstances may customers modify, demonstrate, use, deliver
|
|
or disclose any portion of the Software in source code form.
|
|
*/
|
|
|
|
#include "usb_linux.h"
|
|
#include "hostdl_packet.h"
|
|
|
|
#define true (1 == 1)
|
|
#define false (1 != 1)
|
|
|
|
#define MAX_SEND_BUFFER_SIZE 1280
|
|
#define MAX_RECEIVE_BUFFER_SIZE 1280
|
|
unsigned char g_Transmit_Buffer[MAX_SEND_BUFFER_SIZE];
|
|
int g_Transmit_Length;
|
|
|
|
unsigned char g_Receive_Buffer[MAX_RECEIVE_BUFFER_SIZE];
|
|
int g_Receive_Bytes;
|
|
|
|
static void *stream_usb_handle;
|
|
|
|
static void dump_buffer(unsigned char *buff, int len)
|
|
{
|
|
int i = 0;
|
|
|
|
dbg_time("dump buffer: %d bytes\n", len);
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
dbg_time("%02x ", buff[i]);
|
|
}
|
|
dbg_time("\nend\n");
|
|
}
|
|
|
|
#define CRC_16_L_SEED 0xFFFF
|
|
#define CRC_TAB_SIZE 256 /* 2^CRC_TAB_BITS */
|
|
#define CRC_16_L_POLYNOMIAL 0x8408
|
|
|
|
static const uint16_t crc_16_l_table[CRC_TAB_SIZE] = {
|
|
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3,
|
|
0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
|
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399,
|
|
0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
|
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50,
|
|
0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
|
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e,
|
|
0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
|
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5,
|
|
0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
|
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693,
|
|
0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
|
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a,
|
|
0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
|
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710,
|
|
0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
|
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df,
|
|
0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
|
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595,
|
|
0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
|
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c,
|
|
0x3de3, 0x2c6a, 0x1ef1, 0x0f78};
|
|
|
|
unsigned short crc_16_l_calc(unsigned char *buf_ptr, int len)
|
|
{
|
|
int data, crc_16;
|
|
for (crc_16 = CRC_16_L_SEED; len >= 8; len -= 8, buf_ptr++)
|
|
{
|
|
crc_16 = crc_16_l_table[(crc_16 ^ *buf_ptr) & 0x00ff] ^ (crc_16 >> 8);
|
|
}
|
|
if (len != 0)
|
|
{
|
|
data = ((int)(*buf_ptr)) << (16 - 8);
|
|
|
|
while (len-- != 0)
|
|
{
|
|
if (((crc_16 ^ data) & 0x01) != 0)
|
|
{
|
|
crc_16 >>= 1;
|
|
crc_16 ^= CRC_16_L_POLYNOMIAL;
|
|
}
|
|
else
|
|
{
|
|
crc_16 >>= 1;
|
|
}
|
|
|
|
data >>= 1;
|
|
}
|
|
}
|
|
return (~crc_16);
|
|
}
|
|
|
|
void compute_reply_crc()
|
|
{
|
|
unsigned short crc = crc_16_l_calc(g_Transmit_Buffer, g_Transmit_Length * 8);
|
|
g_Transmit_Buffer[g_Transmit_Length] = crc & 0xFF;
|
|
g_Transmit_Buffer[g_Transmit_Length + 1] = crc >> 8;
|
|
g_Transmit_Length += 2;
|
|
}
|
|
|
|
static void compose_packet(unsigned char cmd, unsigned char *parameter, uint32_t parameter_len,
|
|
unsigned char *data, uint32_t data_len)
|
|
{
|
|
uint32_t i;
|
|
|
|
g_Transmit_Buffer[0] = cmd;
|
|
if (parameter == NULL) parameter_len = 0;
|
|
if (data == NULL) data_len = 0;
|
|
for (i = 0; i < parameter_len; i++)
|
|
{
|
|
g_Transmit_Buffer[1 + i] = parameter[i];
|
|
}
|
|
for (i = 0; i < data_len; i++)
|
|
{
|
|
g_Transmit_Buffer[1 + parameter_len + i] = data[i];
|
|
}
|
|
g_Transmit_Length = 1 + parameter_len + data_len;
|
|
g_Transmit_Buffer[g_Transmit_Length] = 0;
|
|
}
|
|
|
|
static unsigned char stream_tx_buf[1280];
|
|
#define CHECK_FOR_DATA() \
|
|
do \
|
|
{ \
|
|
} while (0)
|
|
#define TRANSMIT_BYTE(_byte) \
|
|
do \
|
|
{ \
|
|
stream_tx_buf[j++] = _byte; \
|
|
} while (0)
|
|
|
|
static int send_packet(int flag)
|
|
{
|
|
int i;
|
|
int ch;
|
|
int j;
|
|
|
|
j = 0;
|
|
|
|
CHECK_FOR_DATA();
|
|
|
|
/* Since we don't know how long it's been. */
|
|
if (!!flag)
|
|
{
|
|
TRANSMIT_BYTE(0x7E);
|
|
}
|
|
|
|
for (i = 0; i < g_Transmit_Length; i++)
|
|
{
|
|
/* we only need to check once every 31 characters, since RX and TX
|
|
* run at about the same speed, and our RX FIFO is 64 characters
|
|
*/
|
|
if ((i & 31) == 31) CHECK_FOR_DATA();
|
|
|
|
ch = g_Transmit_Buffer[i];
|
|
|
|
if (ch == 0x7E || ch == 0x7D)
|
|
{
|
|
TRANSMIT_BYTE(0x7D);
|
|
TRANSMIT_BYTE(0x20 ^ ch); /*lint !e734 */
|
|
}
|
|
else
|
|
{
|
|
TRANSMIT_BYTE(ch); /*lint !e734 */
|
|
}
|
|
}
|
|
|
|
CHECK_FOR_DATA();
|
|
TRANSMIT_BYTE(0x7E);
|
|
|
|
#if 0
|
|
/* Hack for USB protocol. If we have an exact multiple of the USB frame
|
|
* size, then the last frame will not be sent out. The USB standard says
|
|
* that a "short packet" needs to be sent to flush the data. Two flag
|
|
* characters can serve as the short packet. Doing it this way, we only
|
|
* perform this test once on every entire packet from the target, so the
|
|
* over head is not too much.
|
|
*/
|
|
if ((j%512) == 0)
|
|
{
|
|
TRANSMIT_BYTE (0x7E);
|
|
TRANSMIT_BYTE (0x7E);
|
|
}
|
|
#endif
|
|
|
|
return (qusb_noblock_write(stream_usb_handle, stream_tx_buf, j, j, 3000, 1) == j) ? 0 : -1;
|
|
}
|
|
|
|
static int remove_escape_hdlc_flag(unsigned char *buffer, int len)
|
|
{
|
|
int i = 0;
|
|
int index = 0;
|
|
int escape = 0;
|
|
// dump_buffer(buffer, len);
|
|
if (len == 0) return 0;
|
|
// ignore the first HDLC FLAG bytes
|
|
while (buffer[i] == 0x7e)
|
|
{
|
|
i++;
|
|
}
|
|
// all bytes is HDLC FLAG
|
|
if (i == len) return 0;
|
|
for (; i < len; i++)
|
|
{
|
|
if (buffer[i] == 0x7D)
|
|
{
|
|
escape = 1;
|
|
continue;
|
|
}
|
|
if (escape == 1)
|
|
{
|
|
escape = 0;
|
|
buffer[i] ^= 0x20;
|
|
}
|
|
buffer[index++] = buffer[i];
|
|
}
|
|
buffer[index] = 0;
|
|
// dump_buffer(buffer, index);
|
|
return index;
|
|
}
|
|
|
|
static int receive_packet(void)
|
|
{
|
|
int bytesread = 0;
|
|
unsigned char *buff = g_Receive_Buffer;
|
|
if (buff == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int idx = 0;
|
|
do
|
|
{
|
|
bytesread =
|
|
qusb_noblock_read(stream_usb_handle, &buff[idx], MAX_RECEIVE_BUFFER_SIZE, 0, 3000);
|
|
if (bytesread == 0)
|
|
{
|
|
// timeout may be error
|
|
dbg_time("%s timeout\n", __FUNCTION__);
|
|
break;
|
|
}
|
|
// dump_buffer(&buff[idx], bytesread);
|
|
idx += bytesread;
|
|
if (buff[idx - 1] == 0x7e)
|
|
{
|
|
// check the packet whether valid.
|
|
g_Receive_Bytes = remove_escape_hdlc_flag(buff, idx);
|
|
if (g_Receive_Bytes == 0)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
} while (1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handle_hello(void)
|
|
{
|
|
static const char host_header[] = "QCOM fast download protocol host";
|
|
// static const char target_header[] = "QCOM fast download protocol targ";
|
|
// char string1[64];
|
|
// int size;
|
|
int err;
|
|
dbg_time("%s\n", __func__);
|
|
|
|
memset(&g_Transmit_Buffer[0], 0, sizeof(g_Transmit_Buffer));
|
|
g_Transmit_Buffer[HELLO_CMD_OFFSET] = HELLO_REQ;
|
|
memcpy(&g_Transmit_Buffer[HELLO_MAGIC_NUM_OFFSET], host_header, 32);
|
|
g_Transmit_Buffer[HELLO_MAX_VER_OFFSET] = STREAM_DLOAD_MAX_VER;
|
|
g_Transmit_Buffer[HELLO_MIN_VER_OFFSET] = STREAM_DLOAD_MIN_VER;
|
|
g_Transmit_Buffer[HELLO_MAX_DATA_SZ_1_OFFSET] = 0;
|
|
g_Transmit_Length = 36;
|
|
|
|
compute_reply_crc();
|
|
send_packet(1);
|
|
|
|
int timeout = 5;
|
|
do
|
|
{
|
|
err = receive_packet();
|
|
if (err == 1)
|
|
{
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x02: return 1;
|
|
case 0x0d: continue;
|
|
default:
|
|
// dump_buffer(g_Receive_Buffer, 64);
|
|
return 0;
|
|
}
|
|
}
|
|
else if (err == -1)
|
|
{
|
|
dbg_time("error = %d, strerr = %s\n", errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
timeout--;
|
|
} while (timeout);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handle_security_mode(unsigned char trusted)
|
|
{
|
|
dbg_time("%s trusted = %d\n", __func__, trusted);
|
|
compose_packet(0x17, &trusted, 1, NULL, 0);
|
|
compute_reply_crc();
|
|
send_packet(1);
|
|
int timeout = 5;
|
|
do
|
|
{
|
|
if (receive_packet() == 1)
|
|
{
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x18: return 1;
|
|
default: return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
if (timeout == 0)
|
|
{
|
|
dbg_time("%s timeout\n", __FUNCTION__);
|
|
break; // return 0; -Werror,-Wunreachable-code-return
|
|
}
|
|
}
|
|
} while (1);
|
|
return 0;
|
|
}
|
|
/*
|
|
set download flag in module, quectel custom command,
|
|
if flag : reboot, module will enter DM
|
|
if not flag: reboot normal
|
|
*/
|
|
static int handle_quectel_download_flag(unsigned char mode)
|
|
{
|
|
// byte mode = 1;
|
|
compose_packet(0x60, &mode, 1, NULL, 0);
|
|
compute_reply_crc();
|
|
send_packet(1);
|
|
int timeout = 5;
|
|
do
|
|
{
|
|
if (receive_packet() == 1)
|
|
{
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x61:
|
|
switch (g_Receive_Buffer[1])
|
|
{
|
|
case 0x00: return 1;
|
|
default: return 0;
|
|
}
|
|
break;
|
|
case 0x0E: dbg_time("Invalid command"); return 2;
|
|
default: dump_buffer(g_Receive_Buffer, 64); return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
if (timeout == 0)
|
|
{
|
|
dbg_time("%s timeout\n", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
static const char *stream_firehose_dir;
|
|
static int stread_fread(const char *filename, void **pp_filebuf)
|
|
{
|
|
int filesize = 0;
|
|
FILE *fp;
|
|
char fullpath[MAX_PATH * 2];
|
|
|
|
snprintf(fullpath, sizeof(fullpath), "%.240s/../%.240s", stream_firehose_dir, filename);
|
|
fp = fopen(fullpath, "rb");
|
|
if (fp == NULL)
|
|
{
|
|
dbg_time("fail to fope %s, errno: %d (%s)\n", fullpath, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
filesize = ftell(fp);
|
|
|
|
*pp_filebuf = malloc(filesize);
|
|
if (pp_filebuf == NULL)
|
|
{
|
|
dbg_time("fail to malloc %d, errno: %d (%s)\n", filesize, errno, strerror(errno));
|
|
if (fp)
|
|
{
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
filesize = fread(*pp_filebuf, 1, filesize, fp);
|
|
fclose(fp);
|
|
|
|
dbg_time("%s filename=%s, filesize=%d\n", __func__, filename, filesize);
|
|
return filesize;
|
|
}
|
|
|
|
static int handle_parti_tbl(unsigned char override)
|
|
{
|
|
int timeout = 5;
|
|
int filesize;
|
|
void *filebuf;
|
|
const char *partition_path = "partition.mbn";
|
|
dbg_time("%s override = %d\n", __func__, override);
|
|
|
|
filesize = stread_fread(partition_path, &filebuf);
|
|
if (filesize <= 0)
|
|
{
|
|
if (filebuf)
|
|
{
|
|
free(filebuf);
|
|
filebuf = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
compose_packet(0x19, &override, 1, filebuf, filesize);
|
|
compute_reply_crc();
|
|
send_packet(1);
|
|
free(filebuf);
|
|
|
|
do
|
|
{
|
|
if (receive_packet() == 1)
|
|
{
|
|
dbg_time("handle_parti_tbl command = %02x, status = %02x\n", g_Receive_Buffer[0],
|
|
g_Receive_Buffer[1]);
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x1a:
|
|
switch (g_Receive_Buffer[1])
|
|
{
|
|
case 0x00: return 1;
|
|
case 0x01: // 0x1 this means that the original partition is different from
|
|
// the current partition,try to send partition
|
|
return 0;
|
|
case 0x02: // 0x2 Partition table format not recognized, does not accept
|
|
// override
|
|
return 0;
|
|
case 0x03: // 0x3 Erase operation failed
|
|
return 0;
|
|
break;
|
|
default: return 0;
|
|
}
|
|
default: return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
if (timeout == 0)
|
|
{
|
|
dbg_time("%s timeout\n", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
static int handle_reset(void)
|
|
{
|
|
dbg_time("%s\n", __func__);
|
|
compose_packet(0x0b, NULL, 0, NULL, 0);
|
|
compute_reply_crc();
|
|
send_packet(1);
|
|
#if 1
|
|
return 1;
|
|
#else
|
|
int timeout = 5;
|
|
do
|
|
{
|
|
if (receive_packet() == 1)
|
|
{
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x0c: return 1;
|
|
case 0x0d: continue;
|
|
default: dump_buffer(g_Receive_Buffer, 64); return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
if (timeout == 0)
|
|
{
|
|
dbg_time("%s timeout\n", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
}
|
|
} while (1);
|
|
#endif
|
|
}
|
|
|
|
/******pkt_open_multi_image*******/
|
|
|
|
static void pkt_open_multi_image(unsigned char mode, unsigned char *data, uint32_t size)
|
|
{
|
|
compose_packet(0x1b, &mode, 1, data, size);
|
|
compute_reply_crc();
|
|
}
|
|
|
|
static int handle_openmulti(uint32_t size, unsigned char *data)
|
|
{
|
|
int timeout = 5;
|
|
unsigned char mode = 0x0e;
|
|
|
|
pkt_open_multi_image(mode, data, size);
|
|
send_packet(1);
|
|
do
|
|
{
|
|
if (receive_packet() == 1)
|
|
{
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x1c: return 1;
|
|
case 0x0d: continue;
|
|
default: return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
if (timeout == 0)
|
|
{
|
|
dbg_time("%s timeout\n", __FUNCTION__);
|
|
break;
|
|
}
|
|
}
|
|
} while (1);
|
|
return 0;
|
|
}
|
|
|
|
/******pkt_write_multi_image*******/
|
|
static void pkt_write_multi_image(uint32_t addr, unsigned char *data, uint16_t size)
|
|
{
|
|
unsigned char parameter[4] = {(unsigned char)(addr)&0xff, (unsigned char)(addr >> 8) & 0xff,
|
|
(unsigned char)(addr >> 16) & 0xff,
|
|
(unsigned char)(addr >> 24) & 0xff};
|
|
compose_packet(0x07, parameter, 4, data, size);
|
|
compute_reply_crc();
|
|
}
|
|
|
|
static int handle_write(unsigned char *data, uint32_t size)
|
|
{
|
|
// uint32_t total_size;
|
|
uint32_t addr = 0;
|
|
uint32_t writesize;
|
|
uint32_t buffer_size = 1024;
|
|
// int loop = 1;
|
|
int retry_cnt = 3; // if send failed,send again
|
|
int ret;
|
|
|
|
// total_size = size;
|
|
while (size)
|
|
{
|
|
writesize = (size < buffer_size) ? size : buffer_size;
|
|
|
|
pkt_write_multi_image(addr, data, writesize);
|
|
start_send_packet:
|
|
ret = send_packet(1);
|
|
if (0 != ret)
|
|
{
|
|
dbg_time("io read/write failed\n");
|
|
return 0;
|
|
}
|
|
if (receive_packet() == 1)
|
|
{
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x08:
|
|
size -= writesize;
|
|
addr += writesize;
|
|
// retry_cnt=5;
|
|
break;
|
|
default:
|
|
goto retry_send_packet;
|
|
// return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retry_send_packet:
|
|
retry_cnt--;
|
|
if (retry_cnt > 0)
|
|
{
|
|
goto start_send_packet;
|
|
}
|
|
else
|
|
{
|
|
dbg_time("value is [0x%02x]", g_Receive_Buffer[0]);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
/******PARTITION*******/
|
|
static int handle_close(void)
|
|
{
|
|
int timeout = 5;
|
|
compose_packet(0x15, NULL, 0, NULL, 0);
|
|
compute_reply_crc();
|
|
send_packet(1);
|
|
|
|
do
|
|
{
|
|
if (receive_packet() == 1)
|
|
{
|
|
switch (g_Receive_Buffer[0])
|
|
{
|
|
case 0x16: return 1;
|
|
default: return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
if (timeout == 0)
|
|
{
|
|
dbg_time("%s timeout\n", __FUNCTION__);
|
|
break;
|
|
}
|
|
}
|
|
} while (1);
|
|
return 0;
|
|
}
|
|
|
|
static int do_flash_mbn(const char *partion, const char *filepath)
|
|
{
|
|
int result = false;
|
|
void *filebuf = NULL;
|
|
int filesize = 0;
|
|
|
|
dbg_time("%s %s\n", __func__, partion);
|
|
|
|
if (filepath)
|
|
{
|
|
filesize = stread_fread(filepath, &filebuf);
|
|
if (filesize <= 0)
|
|
{
|
|
if (filebuf)
|
|
{
|
|
free(filebuf);
|
|
filebuf = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
filesize = 4 * 1024;
|
|
filebuf = (unsigned char *)malloc(filesize);
|
|
if (filebuf == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
memset(filebuf, 0x00, filesize);
|
|
}
|
|
|
|
result = handle_openmulti(strlen(partion) + 1, (unsigned char *)partion);
|
|
if (result == false)
|
|
{
|
|
dbg_time("%s open failed\n", partion);
|
|
goto __fail;
|
|
}
|
|
|
|
dbg_time("sending '%s' (%dKB)\n", partion, (int)(filesize / 1024));
|
|
|
|
result = handle_write(filebuf, filesize);
|
|
if (result == false)
|
|
{
|
|
dbg_time("%s download failed\n", partion);
|
|
goto __fail;
|
|
}
|
|
|
|
result = handle_close();
|
|
if (result == false)
|
|
{
|
|
dbg_time("%s close failed.\n", partion);
|
|
goto __fail;
|
|
}
|
|
|
|
dbg_time("OKAY\n");
|
|
|
|
__fail:
|
|
free(filebuf);
|
|
|
|
return result;
|
|
}
|
|
|
|
int stream_download(const char *firehose_dir, void *usb_handle, unsigned qusb_zlp_mode)
|
|
{
|
|
(void)qusb_zlp_mode;
|
|
stream_usb_handle = usb_handle;
|
|
stream_firehose_dir = firehose_dir;
|
|
|
|
if (handle_hello() == false)
|
|
{
|
|
dbg_time("Send hello command fail\n");
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
hello packet will set dload flag in module, when upgrade interrup, restart module,module will
|
|
enter dm(quectel sbl)
|
|
*/
|
|
if (handle_security_mode(1) == false)
|
|
{
|
|
dbg_time("Send trust command fail\n");
|
|
return false;
|
|
}
|
|
|
|
if (handle_parti_tbl(0) == false)
|
|
{
|
|
dbg_time("----------------------------------\n");
|
|
dbg_time("Detect partition mismatch.\n");
|
|
dbg_time("Download parition with override.\n");
|
|
dbg_time("----------------------------------\n");
|
|
|
|
if (handle_parti_tbl(1) == false)
|
|
{
|
|
dbg_time("override failed. \n");
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
partition is not match, the download flag will be clear, so set it again, reset will clear
|
|
it
|
|
*/
|
|
if (handle_quectel_download_flag(1) == false)
|
|
{
|
|
dbg_time("Set Quectel download flag failed\n");
|
|
}
|
|
else
|
|
{
|
|
dbg_time("Set Quectel download flag successfully\n");
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
if (do_flash_mbn("0:SBL", "sbl1.mbn") == false)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (handle_reset() == false)
|
|
{
|
|
dbg_time("Send reset command failed\n");
|
|
return false;
|
|
}
|
|
|
|
dbg_time("%s successful\n", __func__);
|
|
|
|
return true;
|
|
}
|
|
|
|
// retrieve module soft revision
|
|
|
|
typedef struct
|
|
{
|
|
unsigned char cmd_code;
|
|
unsigned char version;
|
|
unsigned char reserved[2];
|
|
unsigned char msm[4];
|
|
unsigned char mobile_modle_number[4];
|
|
unsigned char mobile_software_revision[1];
|
|
} __attribute__((packed)) extended_build_id_response_t;
|
|
|
|
int retrieve_soft_revision(void *usb_handle, uint8_t *mobile_software_revision, unsigned length)
|
|
{
|
|
/*
|
|
80-v1294-1_yyd_serial_interface_control_document_(icd)_for_cdma_dual-mode_subscriber_station_data
|
|
3.4.122 Extended Build ID
|
|
*/
|
|
uint8_t req1[] = {0x7E, 0x7C, 0x93, 0x49, 0x7E};
|
|
int ret;
|
|
uint8_t *rx_buff = malloc(2048);
|
|
|
|
memset(mobile_software_revision, 0x00, length);
|
|
|
|
if (rx_buff == NULL) return 0;
|
|
|
|
ret = qusb_noblock_write(usb_handle, req1, sizeof(req1), sizeof(req1), 1000, 0);
|
|
if (ret > 0)
|
|
{
|
|
ret = qusb_noblock_read(usb_handle, rx_buff, 2048, 1, 3000);
|
|
if (ret > 0)
|
|
{
|
|
if (rx_buff[0] == 0x7C && rx_buff[ret - 1] == 0x7E)
|
|
{
|
|
extended_build_id_response_t *rsp = (extended_build_id_response_t *)rx_buff;
|
|
(void)length;
|
|
memcpy(mobile_software_revision, rsp->mobile_software_revision,
|
|
strlen((const char *)rsp->mobile_software_revision));
|
|
}
|
|
}
|
|
}
|
|
|
|
free(rx_buff);
|
|
return (mobile_software_revision[0] != '\0');
|
|
}
|