/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * common/rtk_fw.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ /****************************************************************************** * Copyright (c) 2009-2015 by Hisi. * All rights reserved. * * Create by Chenzetian. 2015-11-16 * ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #define USB_ERROR(fmt, args...) printf(fmt, ##args) #define USB_DEBUG(fmt, args...) printf(fmt, ##args) #define CHIP_TYPE_RTL8723BU "fw_8723b_b.txt" #define CHIP_TYPE_RTL8723DU "fw_8723du_c.txt" #define CHIP_TYPE_RTL8822BU "fw_8822b_b.txt" #define CODE_MAXSIZE_24K (1024*24) //24K #define CODE_MAXSIZE_40K (1024*40) //40K #define code_maxsize 23639//56 * 10K int fw_8723bu=0; int fw_8723du=0; int fw_8822bu=0; //patch txt formate: total length(2 bytes) || fw patch || config patch static unsigned char lu8Code_RTL8723BU[CODE_MAXSIZE_24K] = { #include CHIP_TYPE_RTL8723BU }; static unsigned char lu8Code_RTL8723DU[CODE_MAXSIZE_40K] = { #include CHIP_TYPE_RTL8723DU }; static unsigned char lu8Code_RTL8822BU[code_maxsize] = { #include CHIP_TYPE_RTL8822BU }; firmware_info *firmware_info_init(struct usb_device *dev) { struct usb_device *udev = dev; firmware_info *fw_info; USB_DEBUG("%s[%d]: start.\n", __func__, __LINE__); fw_info = malloc(sizeof(*fw_info)); memset(fw_info,0,sizeof(*fw_info)); if (!fw_info) return NULL; if (fw_8723bu) { fw_info->fw_data = malloc(CODE_MAXSIZE_24K); memset(fw_info->fw_data,0,CODE_MAXSIZE_24K); } if (fw_8723du) { fw_info->fw_data = malloc(CODE_MAXSIZE_40K); memset(fw_info->fw_data,0,CODE_MAXSIZE_40K); } if (fw_8822bu) { fw_info->fw_data = malloc(code_maxsize); memset(fw_info->fw_data,0,code_maxsize); //USB_DEBUG("zengqiu %s[%d]: start.\n", __func__, __LINE__); } if (!fw_info->fw_data) { free(fw_info); return NULL; } fw_info->send_pkt = malloc(PKT_LEN); memset(fw_info->send_pkt,0,PKT_LEN); if (!fw_info->send_pkt) { free(fw_info->fw_data); free(fw_info); return NULL; } fw_info->rcv_pkt = malloc(PKT_LEN); memset(fw_info->rcv_pkt,0,PKT_LEN); if (!fw_info->rcv_pkt) { free(fw_info->fw_data); free(fw_info->send_pkt); free(fw_info); return NULL; } fw_info->patch_entry = NULL; fw_info->intf = NULL; fw_info->udev = udev; fw_info->pipe_in = usb_rcvintpipe(fw_info->udev, INTR_EP); fw_info->pipe_out = usb_sndctrlpipe(fw_info->udev, CTRL_EP); fw_info->cmd_hdr = (struct hci_command_hdr *)(fw_info->send_pkt); fw_info->evt_hdr = (struct hci_event_hdr *)(fw_info->rcv_pkt); fw_info->cmd_cmp = (struct hci_ev_cmd_complete *)(fw_info->rcv_pkt + EVT_HDR_LEN); fw_info->req_para = fw_info->send_pkt + CMD_HDR_LEN; fw_info->rsp_para = fw_info->rcv_pkt + EVT_HDR_LEN + CMD_CMP_LEN; return fw_info; } void firmware_info_destroy(firmware_info *fw_info) { free(fw_info->fw_data); free(fw_info->rcv_pkt); free(fw_info->send_pkt); free(fw_info); } int load_firmware(firmware_info *fw_info, unsigned char **buff) { unsigned int size; if (fw_8723bu) { size = lu8Code_RTL8723BU[0] | (lu8Code_RTL8723BU[1] << 8); USB_DEBUG("%s,size is %d\n",__func__, size); if (size >= CODE_MAXSIZE_24K) { USB_ERROR("%s,size is %d firmware is larger than %d\n",__func__, size, CODE_MAXSIZE_24K); return -1; } memcpy(*buff, &lu8Code_RTL8723BU[2],size); fw_8723bu = 0; return size; } if (fw_8723du) { size = lu8Code_RTL8723DU[0] | (lu8Code_RTL8723DU[1] << 8); USB_DEBUG("%s,size is %d\n",__func__, size); if (size >= CODE_MAXSIZE_40K) { USB_ERROR("%s,size is %d firmware is larger than %d\n",__func__, size, CODE_MAXSIZE_40K); return -1; } memcpy(*buff, &lu8Code_RTL8723DU[2],size); fw_8723du= 0; return size; } if (fw_8822bu) { size = lu8Code_RTL8822BU[0] | (lu8Code_RTL8822BU[1] << 8); USB_DEBUG("%s,size is %d\n",__func__, size); memcpy(*buff, &lu8Code_RTL8822BU[2],size); fw_8822bu = 0; return size; } return 0; } int send_hci_cmd(firmware_info *fw_info) { int ret_val; ret_val = usb_control_msg( fw_info->udev, fw_info->pipe_out, 0, USB_TYPE_CLASS, 0, 0, (void *)(fw_info->send_pkt), fw_info->pkt_len, MSG_TO); return ret_val; } int rcv_hci_evt(firmware_info *fw_info) { int /*ret_len = 0,*/ ret_val = 0; int i; while (1) { for (i = 0; i < 5; i++) { ret_val = usb_submit_int_msg( fw_info->udev, fw_info->pipe_in, (void *)(fw_info->rcv_pkt), PKT_LEN, MSG_TO); if (ret_val >= 0) break; } /*USB_DEBUG("fw_info->rcv_pkt : 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \n", fw_info->rcv_pkt[0], fw_info->rcv_pkt[1], fw_info->rcv_pkt[2], fw_info->rcv_pkt[3], fw_info->rcv_pkt[4], fw_info->rcv_pkt[5], fw_info->rcv_pkt[6]);*/ if (ret_val < 0) return ret_val; if (CMD_CMP_EVT == fw_info->evt_hdr->evt) { if (fw_info->cmd_hdr->opcode == fw_info->cmd_cmp->opcode) return 0; } } } int get_firmware(firmware_info *fw_info) { /* patch_info *patch_entry = fw_info->patch_entry; */ fw_info->fw_len = load_firmware(fw_info, &fw_info->fw_data); USB_DEBUG("%s[%d]: fw_len=%d\n", __func__, __LINE__, fw_info->fw_len); if (fw_info->fw_len <= 0) return -1; return 0; } int reset_controller(firmware_info* fw_info) { int ret_val; USB_DEBUG("reset_controller \n"); if (!fw_info) return -ENODEV; fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_FORCE_RESET_AND_PATCHABLE); fw_info->cmd_hdr->plen = 0; fw_info->pkt_len = CMD_HDR_LEN; ret_val = send_hci_cmd(fw_info); if (ret_val < 0) { USB_DEBUG("%s: Failed to send hci cmd 0x%04x, errno %d \n", __func__, fw_info->cmd_hdr->opcode, ret_val); return ret_val; } mdelay(200); USB_DEBUG("%s: Wait fw reset for 200ms \n",__func__); return ret_val; } int download_data(firmware_info *fw_info) { download_cp *cmd_para; download_rp *evt_para; unsigned char *pcur; int pkt_len, frag_num, frag_len; int i, ret_val; int ncmd = 1, step = 1; //USB_DEBUG("%s: start\n", __func__); cmd_para = (download_cp *)fw_info->req_para; evt_para = (download_rp *)fw_info->rsp_para; pcur = fw_info->fw_data; pkt_len = CMD_HDR_LEN + sizeof(download_cp); frag_num = fw_info->fw_len / PATCH_SEG_MAX + 1; frag_len = PATCH_SEG_MAX; cmd_para->index = 0; for (i = 0; i < frag_num; i++) { if (i == (frag_num - 1)) { cmd_para->index |= DATA_END; frag_len = fw_info->fw_len % PATCH_SEG_MAX; pkt_len -= (PATCH_SEG_MAX - frag_len); } fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE); fw_info->cmd_hdr->plen = sizeof(unsigned char) + frag_len; fw_info->pkt_len = pkt_len; memcpy(cmd_para->data, pcur, frag_len); if (step > 0) { ret_val = send_hci_cmd(fw_info); if (ret_val < 0) { //USB_ERROR("%s: Failed to send frag num %d\n", __func__, cmd_para->index); return ret_val; } else //USB_DEBUG("%s: Send frag num %d\n", __func__, cmd_para->index); if (--step > 0 && i < frag_num - 1) { //USB_DEBUG("%s: Continue to send frag num %d\n", __func__, cmd_para->index + 1); pcur += PATCH_SEG_MAX; continue; } } while (ncmd > 0) { ret_val = rcv_hci_evt(fw_info); if (ret_val < 0) { //USB_ERROR("%s: rcv_hci_evt err %d\n", __func__, ret_val); return ret_val; } else { //USB_DEBUG("%s: Receive acked frag num %d\n", __func__, evt_para->index); ncmd--; } if (0 != evt_para->status) { //USB_ERROR("%s: Receive acked frag num %d, err status %d\n", // __func__, ret_val, evt_para->status); return -1; } if ((evt_para->index & DATA_END) || (evt_para->index == frag_num - 1)) { //USB_DEBUG("%s: Receive last acked index %d\n", __func__, evt_para->index); goto end; } } cmd_para->index ++; if (cmd_para->index == 0x80) cmd_para->index = 1; ncmd = step = fw_info->cmd_cmp->ncmd; pcur += PATCH_SEG_MAX; //USB_DEBUG("%s: HCI command packet num %d\n", __func__, ncmd); } /* * It is tricky that Host cannot receive DATA_END index from BT * controller, at least for 8723au. We are doomed if failed. */ end: // USB_DEBUG("%s: done, sent %d frag pkts, received %d frag events\n", // __func__, cmd_para->index, evt_para->index); return fw_info->fw_len; } int set_wakeup_device(firmware_info* fw_info) { struct rtk_eversion_evt *ever_evt; int ret_val; static unsigned char bdaddr[7] = {0x00, 0xd7, 0xe3,0x3e,0xf8, 0xe6,0xa0}; if (!fw_info) return -ENODEV; fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_SET_WAKE_UP_DEVICE); fw_info->cmd_hdr->plen = 7; memcpy(fw_info->req_para, bdaddr, 7); fw_info->pkt_len = CMD_HDR_LEN + 7; ret_val = send_hci_cmd(fw_info); if (ret_val < 0) { USB_ERROR("%s: Failed to send hci cmd 0x%04x, errno %d\n", __func__, fw_info->cmd_hdr->opcode, ret_val); return ret_val; } ret_val = rcv_hci_evt(fw_info); if (ret_val < 0) { USB_ERROR("%s: Failed to receive hci event, errno %d\n", __func__, ret_val); return ret_val; } ever_evt = (struct rtk_eversion_evt *)(fw_info->rsp_para); USB_DEBUG("%s: status %d, eversion %d", __func__, ever_evt->status, ever_evt->version); return ret_val; } int check_chip_fw_version(firmware_info* fw_info) { struct rtk_localversion_evt *ever_evt; int ret_val; if (!fw_info) return -ENODEV; fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_READ_LMP_VERSION); fw_info->cmd_hdr->plen = 0; fw_info->pkt_len = CMD_HDR_LEN; ret_val = send_hci_cmd(fw_info); if (ret_val < 0) { USB_ERROR("%s: Failed to send hci cmd 0x%04x, errno %d\n", __func__, fw_info->cmd_hdr->opcode, ret_val); return ret_val; } ret_val = rcv_hci_evt(fw_info); if (ret_val < 0) { USB_ERROR("%s: Failed to receive hci event, errno %d\n", __func__, ret_val); return ret_val; } ever_evt = (struct rtk_localversion_evt *)(fw_info->rsp_para); USB_DEBUG("%s: status %x \n", __func__, ever_evt->status); USB_DEBUG("%s: hci_version %x \n", __func__, ever_evt->hci_version); USB_DEBUG("%s: hci_revision %x \n", __func__, ever_evt->hci_revision); USB_DEBUG("%s: lmp_version %x \n", __func__, ever_evt->lmp_version); USB_DEBUG("%s: lmp_subversion %x \n", __func__, ever_evt->lmp_subversion); USB_DEBUG("%s: lmp_manufacture %x \n", __func__, ever_evt->lmp_manufacture); return ret_val; } #if 0 // just fro reference void load_rtl_firmware(void){ firmware_info *fw_info; int ret_val,i; struct usb_device *dev; usb_init(); /* scan all USB Devices */ dev = NULL; for (i=0;idevnum != -1) { if (!strcmp(dev->mf,"Realtek") && !strcmp(dev->prod,"Bluetooth Radio")) { /* Download BT Fireware */ printf("Now Download BT Firmware.\n"); fw_info = firmware_info_init(dev); get_firmware(fw_info); ret_val = download_data(fw_info); if (ret_val > 0) printf("Download fw patch done, fw len %d\n",ret_val); mdelay(10); set_wakeup_device(fw_info); // set_bt_wakeup(fw_info); firmware_info_destroy(fw_info); } } } } #endif struct usb_device * get_rtl_dev(void){ int i; struct usb_device *dev; /* scan all USB Devices */ dev = NULL; for (i = 0; i < USB_MAX_DEVICE; i++) { dev=usb_get_dev_index(i); /* get device */ if (dev == NULL) { printf("usb dev is null!!!!\n"); return NULL; } printf("i=%d dev->devnum:%d dev->mf:%s dev->prod:%s\n", i, dev->devnum, dev->mf, dev->prod); printf("vid is %x, pid is %x \n",le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); /* AML change for haier to match 8723bu bt */ if (dev->devnum != -1) { if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda && le16_to_cpu(dev->descriptor.idProduct) == 0xb720){ fw_8723bu = 1; return dev; } if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda && le16_to_cpu(dev->descriptor.idProduct) == 0xd723){ fw_8723du = 1; return dev; } if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda && le16_to_cpu(dev->descriptor.idProduct) == 0xb82c){ fw_8822bu = 1; return dev; } } } return NULL; } int load_rtl_firmware_dev(struct usb_device *dev) { #if 0 char *reboot_mode = getenv("reboot_mode"); if (reboot_mode == NULL) return 0; if (reboot_mode&& strcmp("cold_boot", reboot_mode)) return 0; #endif firmware_info *fw_info; int ret_val=-1; /* Download BT Fireware */ USB_DEBUG("Now Download RTK BT Firmware. (version 20181220i_1)\n"); fw_info = firmware_info_init(dev); if (NULL == fw_info) return -1; ret_val = get_firmware(fw_info); printf("++++MB:{%s}[%d]...(get_firmware = [%d]).\n", __FUNCTION__, __LINE__, ret_val); if (-1 == ret_val) { printf("++++MB:{%s}[%d]...get firmware, errno %d \n", __FUNCTION__, __LINE__, ret_val); return ret_val; } ret_val = check_chip_fw_version(fw_info); printf("++++MB:{%s}[%d]...(check_chip_fw_version = [%d]).\n", __FUNCTION__, __LINE__, ret_val); if (ret_val < 0) { printf("++++MB:{%s}[%d]...Failed to check chip fw version, errno %d \n", __FUNCTION__, __LINE__, ret_val); return ret_val; } ret_val = reset_controller(fw_info); printf("++++MB:{%s}[%d]...(reset_controller = [%d]).\n", __FUNCTION__, __LINE__, ret_val); if (ret_val < 0) { printf("++++MB:{%s}[%d]...(reset controller error = [%d]).\n", __FUNCTION__, __LINE__, ret_val); return ret_val; } ret_val = download_data(fw_info); printf("++++MB:{%s}[%d]...(download_data = [%d]).\n", __FUNCTION__, __LINE__, ret_val); if (ret_val > 0) printf("Download fw patch done, fw len %d\n",ret_val); else { firmware_info_destroy(fw_info); return ret_val; } mdelay(10); ret_val = set_wakeup_device(fw_info); printf("++++MB:{%s}[%d]...(set_wakeup_device = [%d]).\n", __FUNCTION__, __LINE__, ret_val); //set_bt_wakeup(fw_info); firmware_info_destroy(fw_info); return ret_val; }