// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * common/rtk_fw.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ /****************************************************************************** * Copyright (c) 2020-2022 by Hisi. * All rights reserved. * * Create by zengqiu. 2022-1-10 * ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_ERROR(fmt, args...) {if (log_level) printf(fmt, ##args); } #define USB_DEBUG(fmt, args...) {if (log_level) printf(fmt, ##args); } //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_23K] = { #include CHIP_TYPE_RTL8822BU }; static unsigned char lu8Code_RTL88X2CU[CODE_MAXSIZE_40K] = { #include CHIP_TYPE_RTL88X2CU }; static const bluetooth_dongle usb_dongle[] = { {0x0bda, 0xb720, "8723bu", CHIP_TYPE_RTL8723BU, CODE_MAXSIZE_24K, lu8Code_RTL8723BU}, {0x0bda, 0xd723, "8723du", CHIP_TYPE_RTL8723DU, CODE_MAXSIZE_40K, lu8Code_RTL8723DU}, {0x0bda, 0xb82c, "8822bu", CHIP_TYPE_RTL8822BU, CODE_MAXSIZE_23K, lu8Code_RTL8822BU}, {0x0bda, 0xc82c, "88x2cu", CHIP_TYPE_RTL88X2CU, CODE_MAXSIZE_40K, lu8Code_RTL88X2CU} }; static int num = -1; 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; fw_info->fw_data = malloc(usb_dongle[num].fw_size); memset(fw_info->fw_data, 0, usb_dongle[num].fw_size); 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; size = usb_dongle[num].fw_buf[0] | (usb_dongle[num].fw_buf[1] << 8); USB_DEBUG("%s,%s size is %d\n", __func__, usb_dongle[num].fw_name, size); if (size > usb_dongle[num].fw_size) { USB_ERROR("%s,size is %d firmware is larger than %d\n", __func__, size, usb_dongle[num].fw_size); num = -1; return -1; } memcpy(*buff, &usb_dongle[num].fw_buf[2], size); return size; } 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 (fw_info->evt_hdr->evt == CMD_CMP_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("%s\n", __func__); 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); if (pkt_len == (CMD_HDR_LEN + sizeof(unsigned char))) goto end; } 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; 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; } //USB_DEBUG("%s: Receive acked frag num %d\n", __func__, evt_para->index); ncmd--; if (evt_para->status != 0) { //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 = fw_info->cmd_cmp->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\n", __func__, ever_evt->status, ever_evt->version); return ret_val; } int reset_chip_dev(firmware_info *fw_info) { int ret_val; if (!fw_info) return -ENODEV; fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_CHIP_RESET); 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; } if (!*fw_info->rsp_para) printf("%s success\n", __func__); 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 DEL_CODE // 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; i < USB_MAX_DEVICE; i++) { dev = usb_get_dev_index(i); /* get device */ if (!dev) return; if (dev->devnum != -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 static struct usb_device *qiu_usb_find_device(int devnum) { #ifdef CONFIG_DM_USB struct usb_device *udev; struct udevice *hub; struct uclass *uc; int ret; /* Device addresses start at 1 */ devnum++; ret = uclass_get(UCLASS_USB_HUB, &uc); if (ret) return NULL; uclass_foreach_dev(hub, uc) { struct udevice *dev; if (!device_active(hub)) continue; udev = dev_get_parent_priv(hub); if (udev->devnum == devnum) return udev; for (device_find_first_child(hub, &dev); dev; device_find_next_child(&dev)) { if (!device_active(hub)) continue; udev = dev_get_parent_priv(dev); if (udev->devnum == devnum) return udev; } } #else struct usb_device *udev; int d; for (d = 0; d < USB_MAX_DEVICE; d++) { udev = usb_get_dev_index(d); if (!udev) return NULL; if (udev->devnum == devnum) return udev; } #endif return NULL; } struct usb_device *get_rtl_dev(void) { int i = 0; int size, usb_size; struct usb_device *dev; /* scan all USB Devices */ dev = NULL; for (i = 0; i < USB_MAX_DEVICE; i++) { dev = qiu_usb_find_device(i); if (!dev) { 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); USB_DEBUG("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) { size = sizeof(usb_dongle) / sizeof(bluetooth_dongle); for (usb_size = 0; usb_size < size; usb_size++) { if (le16_to_cpu(dev->descriptor.idVendor == usb_dongle[usb_size].idVendor) && le16_to_cpu(dev->descriptor.idProduct) == usb_dongle[usb_size].idProduct) { num = usb_size; return dev; } } } } printf("dou't find device\n"); return NULL; } int load_rtl_firmware_dev(struct usb_device *dev) { #if DEL_CODE char *reboot_mode = getenv("reboot_mode"); if (!reboot_mode) 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 20221220i_1)\n"); fw_info = firmware_info_init(dev); if (!fw_info) return -1; ret_val = get_firmware(fw_info); printf("++++ZQ:{%s}[%d]...(get_firmware = [%d]).\n", __func__, __LINE__, fw_info->fw_len); if (-1 == ret_val) { printf("++++ZQ:{%s}[%d]...get firmware, errno %d\n", __func__, __LINE__, ret_val); return ret_val; } ret_val = reset_chip_dev(fw_info); if (ret_val < 0) { printf("++++ZQ:{%s}[%d]...chip fail", __func__, __LINE__); return ret_val; } ret_val = check_chip_fw_version(fw_info); USB_DEBUG("++++ZQ:{%s}[%d]...(check_chip_fw_version = [%d]).\n", __func__, __LINE__, ret_val); if (ret_val < 0) { printf("++++ZQ:{%s}[%d]...Failed to check chip fw version, errno %d\n", __func__, __LINE__, ret_val); return ret_val; } ret_val = reset_controller(fw_info); USB_DEBUG("++++ZQ:{%s}[%d]...(reset_controller = [%d]).\n", __func__, __LINE__, ret_val); if (ret_val < 0) { printf("++++ZQ:{%s}[%d]...(reset controller error = [%d]).\n", __func__, __LINE__, ret_val); return ret_val; } ret_val = download_data(fw_info); USB_DEBUG("++++ZQ:{%s}[%d]...(download_data = [%d]).\n", __func__, __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); USB_DEBUG("++++ZQ:{%s}[%d]...(set_wakeup_device = [%d]).\n", __func__, __LINE__, ret_val); //set_bt_wakeup(fw_info); firmware_info_destroy(fw_info); return ret_val; }