/* SPDX-License-Identifier: GPL-2.0 */ #include "usb_boot.h" #define DOWNLOAD_TX_CHN 2 #define DOWNLOAD_RX_CHN 18 static int ackncmp(const char *cs, const char *ct, size_t count) { unsigned char c1, c2; while (count) { c1 = *cs++; c2 = *ct++; if (c1 != c2) return c1 < c2 ? -1 : 1; count--; } return 0; } static int usb_download_sent_command(const char *command, unsigned int command_len, const char *ack, unsigned int ack_len) { struct wcn_usb_ep *tx_ep = wcn_usb_store_get_epFRchn(DOWNLOAD_TX_CHN); struct wcn_usb_ep *rx_ep = wcn_usb_store_get_epFRchn(DOWNLOAD_RX_CHN); int actual_len; int ret; char *command_temp; void *data; int data_size = 128; if (!tx_ep || !rx_ep) { wcn_usb_err("no ep\n"); return -EIO; } data = kzalloc(data_size, GFP_KERNEL); if (!data) return -ENOMEM; command_temp = kmalloc(command_len, GFP_KERNEL); if (!command_temp) { ret = -ENOMEM; goto OUT_FREE_DATA; } memcpy(command_temp, command, command_len); /* sent command */ ret = wcn_usb_msg(tx_ep, command_temp, command_len, &actual_len, 3000); if (ret || actual_len != command_len) { wcn_usb_err("wcn %s sent msg is error [ret %d actual_len %d command_len %d\n", __func__, ret, actual_len, command_len); ret = -EIO; goto OUT; } if (ack == NULL) goto OUT; /* get ack and compare */ ret = wcn_usb_msg(rx_ep, data, data_size, &actual_len, 3000); if (ret || ack_len > actual_len || ackncmp(ack, data, ack_len)) { wcn_usb_err("wcn %s ack is not ok\n", __func__); print_hex_dump(KERN_DEBUG, "WCN ack", 0, 16, 1, data, data_size, 1); print_hex_dump(KERN_DEBUG, "WCN command", 0, 16, 1, command, command_len, 1); ret = -EIO; goto OUT; } OUT: kfree(command_temp); OUT_FREE_DATA: kfree(data); return ret; } static unsigned short crc16_xmodem_cal(unsigned char *buf, unsigned int buf_len) { unsigned int i; unsigned short crc = 0; while (buf_len-- != 0) { for (i = 0x80; i != 0; i = i >> 1) { if ((crc & 0x8000) != 0) { crc = crc << 1; crc = crc ^ 0x1021; } else { crc = crc << 1; } if ((*buf & i) != 0) crc = crc ^ 0x1021; } buf++; } return crc; } static const char get_version[] = { 0x7E }; static const char get_version_ack[] = { 0x7E, 0x00, 0x81, 0x00, 0x06, 0x53, 0x50, 0x52, 0x44, 0x33, 0x00, 0x57, 0x0A, 0x7E}; static const char connect[] = { 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E}; static const char common_ack[] = { 0x7E, 0x00, 0x80, 0x00, 0x00, 0x3B, 0x5A, 0x7E}; static const char end_download[] = { 0x7E, 0x00, 0x03, 0x00, 0x00, 0x59, 0x50, 0x7E}; unsigned short usb_download_command_replace(char *command, unsigned short len) { unsigned short tail = len - 1; unsigned short i; unsigned short j; /* ho head and no tail */ for (i = len - 2; i > 0; i--) { if (command[i] == 0x7E || command[i] == 0x7D) { tail += 1; for (j = tail; j > i; j--) command[j] = command[j - 1]; command[i + 1] = 0x50 | (command[i + 1] & 0x0F); command[i] = 0x7D; } } return tail + 1; } int usb_download_command_start_download(unsigned int addr, unsigned int len) { unsigned short command_len = 16; char command[20] = {0x7E,/* head */ 0x00, 0x01, /*command type */ 0x00, 0x08, /*for command data len */ 0x00, 0x00, 0x00, 0x00,/*addr*/ 0x00, 0x00, 0x00, 0x00,/*data len*/ 0x00, 0x00, /*for crc*/ 0x7E /*tail*/}; *((u32 *)&command[5]) = cpu_to_be32(addr); *((u32 *)&command[9]) = cpu_to_be32(len); *((u16 *)&command[command_len - 3]) = cpu_to_be16(crc16_xmodem_cal(&command[1], command_len - 4)); command_len = usb_download_command_replace(command, 16); return usb_download_sent_command(command, command_len, common_ack, sizeof(common_ack)); } int usb_download_command_exec(unsigned int addr) { unsigned short command_len = 12; char command[16] = {0x7E,/* head */ 0x00, 0x04, /*command type */ 0x00, 0x04, /*command len */ 0x00, 0x00, 0x00, 0x00, /*addr*/ 0x00, 0x00, /*for crc*/ 0x7E /*tail*/}; *((u32 *)&command[5]) = cpu_to_be32(addr); *((u16 *)&command[command_len - 3]) = cpu_to_be16(crc16_xmodem_cal(&command[1], command_len - 4)); command_len = usb_download_command_replace(command, 12); return usb_download_sent_command(command, command_len, common_ack, sizeof(common_ack)); } #define usb_download_command_get_version() \ usb_download_sent_command(get_version, sizeof(get_version), \ get_version_ack, sizeof(get_version_ack)) #define usb_download_command_connect() \ usb_download_sent_command(connect, sizeof(connect), \ common_ack, sizeof(common_ack)) #define usb_download_command_end_download() \ usb_download_sent_command(end_download, sizeof(end_download), \ common_ack, sizeof(common_ack)) int marlin_get_version(void) { return usb_download_command_get_version(); } int marlin_connet(void) { return usb_download_command_connect(); } int marlin_firmware_download_start_usb(void) { int ret; ret = usb_download_command_get_version(); if (ret) { wcn_usb_err("%s start command is error\n", __func__); return ret; } ret = usb_download_command_connect(); if (ret) { wcn_usb_err("%s connect command is error\n", __func__); return ret; } return 0; } int marlin_firmware_download_exec_usb(unsigned int addr) { int ret; ret = usb_download_command_exec(addr); if (ret) { wcn_usb_err("%s exec command is error\n", __func__); return ret; } return 0; } int marlin_firmware_download_usb(unsigned int addr, const void *buf, unsigned int len, unsigned int packet_max) { int ret; ret = usb_download_command_start_download(addr, len); if (ret) { wcn_usb_err("start download command is error\n"); return ret; } ret = usb_download_sent_command(buf, len, common_ack, sizeof(common_ack)); if (ret) { wcn_usb_err("donwload img is error\n"); return ret; } ret = usb_download_command_end_download(); if (ret) { wcn_usb_err("end donwload command is error\n"); return ret; } return 0; } int marlin_firmware_get_chip_id(void *buf, unsigned int buf_size) { int ret; unsigned short command_len = 8; char command[16] = {0x7E,/* head */ 0x00, 0x08, /*command type */ 0x00, 0x00, /*command len */ 0x00, 0x00, /*for crc*/ 0x7E /*tail*/}; struct wcn_usb_ep *rx_ep = wcn_usb_store_get_epFRchn(DOWNLOAD_RX_CHN); int actual_len; char *temp_buf; #define temp_buf_size 16 if (!rx_ep) { wcn_usb_err("%s no rx_ep\n", __func__); return -EIO; } temp_buf = kzalloc(temp_buf_size, GFP_KERNEL); if (!temp_buf) return -ENOMEM; *((u16 *)&command[command_len - 3]) = cpu_to_be16(crc16_xmodem_cal(&command[1], command_len - 4)); command_len = usb_download_command_replace(command, 8); ret = usb_download_sent_command(command, command_len, NULL, 0); if (ret) { wcn_usb_err("%s send command error\n", __func__); kfree(temp_buf); return -EIO; } ret = wcn_usb_msg(rx_ep, temp_buf, temp_buf_size, &actual_len, 3000); if (ret) { wcn_usb_err("%s get chip id error\n", __func__); kfree(temp_buf); return ret; } if (temp_buf[0] != 0x7e || temp_buf[2] != 0x08) { wcn_usb_err("%s get chip id error\n", __func__); print_hex_dump(KERN_ERR, "WCN ack", 0, 16, 1, temp_buf, temp_buf_size, 1); kfree(temp_buf); return ret; } memcpy(buf, &temp_buf[5], buf_size); kfree(temp_buf); return ret; } int marlin_dump_from_romcode_usb(unsigned int addr, void *buf, int len) { int ret; unsigned short command_len = 16; char command[20] = {0x7E,/* head */ 0x00, 0x09, /*command type */ 0x00, 0x08, /*for command data len */ 0x00, 0x00, 0x00, 0x00,/*addr*/ 0x00, 0x00, 0x00, 0x00,/*data len*/ 0x00, 0x00, /*for crc*/ 0x7E /*tail*/}; struct wcn_usb_ep *rx_ep = wcn_usb_store_get_epFRchn(DOWNLOAD_RX_CHN); int actual_len; if (!rx_ep) { wcn_usb_err("%s no rx_ep\n", __func__); return -EIO; } wcn_usb_info("%s addr is 0x%x len is 0x%x\n", __func__, addr, len); *((u32 *)&command[5]) = cpu_to_be32(addr); *((u32 *)&command[9]) = cpu_to_be32(len); *((u16 *)&command[command_len - 3]) = cpu_to_be16(crc16_xmodem_cal(&command[1], command_len - 4)); command_len = usb_download_command_replace(command, 16); ret = usb_download_sent_command(command, command_len, NULL, 0); if (ret) { wcn_usb_err("%s send command error\n", __func__); return -EIO; } ret = wcn_usb_msg(rx_ep, buf, len, &actual_len, 3000); if (ret) wcn_usb_err("%s dump memory error\n", __func__); return ret; } int marlin_dump_read_usb(unsigned int addr, char *buf, int len) { int ret; static int first_pac; if (first_pac == 0) { first_pac = 1; marlin_firmware_download_start_usb(); } ret = marlin_dump_from_romcode_usb(addr, buf, len); return ret; }