// SPDX-License-Identifier: GPL-2.0-only /* * Bluetooth supports for Qualcomm Atheros chips * * Copyright (c) 2015 The Linux Foundation. All rights reserved.
*/ #include <linux/module.h> #include <linux/firmware.h> #include <linux/vmalloc.h>
/* Unlike other SoC's sending version command response as payload to * VSE event. WCN3991 sends version command response as a payload to * command complete event.
*/ if (soc_type >= QCA_WCN3991) {
event_type = 0;
rlen += 1;
rtype = EDL_PATCH_VER_REQ_CMD;
}
cmd = EDL_PATCH_VER_REQ_CMD;
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
&cmd, event_type, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "Reading QCA version information failed (%d)",
err); return err;
}
if (skb->len != rlen) {
bt_dev_err(hdev, "QCA Version size mismatch len %d", skb->len);
err = -EILSEQ; goto out;
}
/* For Rome version 1.1 to 3.1, all segment commands * are acked by a vendor specific event (VSE). * For Rome >= 3.2, the download mode field indicates * if VSE is skipped by the controller. * In case VSE is skipped, only the last segment is acked.
*/
config->dnld_mode = tlv_patch->download_mode;
config->dnld_type = config->dnld_mode;
/* Some NVM files have more than one set of tags, only parse * the first set when it has type 2 for now. When there is * more than one set there is an enclosing header of type 4.
*/ if (type == 4) { if (fw_size < 2 * sizeof(struct tlv_type_hdr)) return -EINVAL;
/* Unlike other SoC's sending version command response as payload to * VSE event. WCN3991 sends version command response as a payload to * command complete event.
*/ if (soc_type >= QCA_WCN3991) {
event_type = 0;
rlen = sizeof(*edl);
rtype = EDL_PATCH_TLV_REQ_CMD;
}
remain -= segsize; /* The last segment is always acked regardless download mode */ if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
config->dnld_mode = QCA_SKIP_EVT_NONE;
ret = qca_tlv_send_segment(hdev, segsize, segment,
config->dnld_mode, soc_type); if (ret) goto out;
segment += segsize;
}
/* Latest qualcomm chipsets are not sending a command complete event * for every fw packet sent. They only respond with a vendor specific * event for the last packet. This optimization in the chip will * decrease the BT in initialization time. Here we will inject a command * complete event to avoid a command timeout error message.
*/ if (config->dnld_type == QCA_SKIP_EVT_VSE_CC ||
config->dnld_type == QCA_SKIP_EVT_VSE)
ret = qca_inject_cmd_complete_event(hdev);
/* Set the default value to variant and prefix */
variant = "";
prefix = "b";
if (soc_type == QCA_QCA2066)
prefix = "";
if (soc_type == QCA_WCN6855 || soc_type == QCA_QCA2066) { /* If the chip is manufactured by GlobalFoundries */ if ((le32_to_cpu(ver.soc_id) & QCA_HSP_GF_SOC_MASK) == QCA_HSP_GF_SOC_ID)
variant = "g";
}
bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
config.user_baud_rate = baudrate;
/* Firmware files to download are based on ROM version. * ROM version is derived from last two bytes of soc_ver.
*/ if (soc_type == QCA_WCN3988)
rom_ver = ((soc_ver & 0x00000f00) >> 0x05) | (soc_ver & 0x0000000f); else
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
if (soc_type == QCA_WCN6750)
qca_send_patch_config_cmd(hdev);
/* Download rampatch file */
config.type = TLV_TYPE_PATCH; if (rampatch_name) {
snprintf(config.fwname, sizeof(config.fwname), "qca/%s", rampatch_name);
} else { switch (soc_type) { case QCA_WCN3950:
snprintf(config.fwname, sizeof(config.fwname), "qca/cmbtfw%02x.tlv", rom_ver); break; case QCA_WCN3990: case QCA_WCN3991: case QCA_WCN3998:
snprintf(config.fwname, sizeof(config.fwname), "qca/crbtfw%02x.tlv", rom_ver); break; case QCA_WCN3988:
snprintf(config.fwname, sizeof(config.fwname), "qca/apbtfw%02x.tlv", rom_ver); break; case QCA_QCA2066:
snprintf(config.fwname, sizeof(config.fwname), "qca/hpbtfw%02x.tlv", rom_ver); break; case QCA_QCA6390:
snprintf(config.fwname, sizeof(config.fwname), "qca/htbtfw%02x.tlv", rom_ver); break; case QCA_WCN6750: /* Choose mbn file by default.If mbn file is not found * then choose tlv file
*/
config.type = ELF_TYPE_PATCH;
snprintf(config.fwname, sizeof(config.fwname), "qca/msbtfw%02x.mbn", rom_ver); break; case QCA_WCN6855:
snprintf(config.fwname, sizeof(config.fwname), "qca/hpbtfw%02x.tlv", rom_ver); break; case QCA_WCN7850:
snprintf(config.fwname, sizeof(config.fwname), "qca/hmtbtfw%02x.tlv", rom_ver); break; default:
snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.bin", soc_ver);
}
}
/* Give the controller some time to get ready to receive the NVM */
msleep(10);
if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850)
qca_read_fw_board_id(hdev, &boardid);
/* Download NVM configuration */
config.type = TLV_TYPE_NVM; if (firmware_name) { /* The firmware name has an extension, use it directly */ if (qca_filename_has_extension(firmware_name)) {
snprintf(config.fwname, sizeof(config.fwname), "qca/%s", firmware_name);
} else {
qca_read_fw_board_id(hdev, &boardid);
qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
firmware_name, soc_type, ver, 0, boardid);
}
} else { switch (soc_type) { case QCA_WCN3950: if (le32_to_cpu(ver.soc_id) == QCA_WCN3950_SOC_ID_T)
variant = "t"; elseif (le32_to_cpu(ver.soc_id) == QCA_WCN3950_SOC_ID_S)
variant = "s";
snprintf(config.fwname, sizeof(config.fwname), "qca/cmnv%02x%s.bin", rom_ver, variant); break; case QCA_WCN3990: case QCA_WCN3991: case QCA_WCN3998: if (le32_to_cpu(ver.soc_id) == QCA_WCN3991_SOC_ID)
variant = "u";
switch (soc_type) { case QCA_WCN3991: case QCA_QCA2066: case QCA_QCA6390: case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850:
err = qca_disable_soc_logging(hdev); if (err < 0) return err; break; default: break;
}
/* WCN399x and WCN6750 supports the Microsoft vendor extension with 0xFD70 as the * VsMsftOpCode.
*/ switch (soc_type) { case QCA_WCN3950: case QCA_WCN3988: case QCA_WCN3990: case QCA_WCN3991: case QCA_WCN3998: case QCA_WCN6750:
hci_set_msft_opcode(hdev, 0xFD70); break; default: break;
}
/* Perform HCI reset */
err = qca_send_reset(hdev); if (err < 0) {
bt_dev_err(hdev, "QCA Failed to run HCI_RESET (%d)", err); return err;
}
switch (soc_type) { case QCA_WCN3991: case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850: /* get fw build info */
err = qca_read_fw_build_info(hdev); if (err < 0) return err; break; default: break;
}
err = qca_check_bdaddr(hdev, &config); if (err) return err;
bt_dev_info(hdev, "QCA setup on UART is completed");
return 0;
}
EXPORT_SYMBOL_GPL(qca_uart_setup);
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
bdaddr_t bdaddr_swapped; struct sk_buff *skb; int err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.