in btqca.c [445:530]
static int qca_download_firmware(struct hci_dev *hdev,
struct qca_fw_config *config,
enum qca_btsoc_type soc_type,
u8 rom_ver)
{
const struct firmware *fw;
u8 *data;
const u8 *segment;
int ret, size, remain, i = 0;
bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
ret = request_firmware(&fw, config->fwname, &hdev->dev);
if (ret) {
/* For WCN6750, if mbn file is not present then check for
* tlv file.
*/
if (soc_type == QCA_WCN6750 && config->type == ELF_TYPE_PATCH) {
bt_dev_dbg(hdev, "QCA Failed to request file: %s (%d)",
config->fwname, ret);
config->type = TLV_TYPE_PATCH;
snprintf(config->fwname, sizeof(config->fwname),
"qca/msbtfw%02x.tlv", rom_ver);
bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
ret = request_firmware(&fw, config->fwname, &hdev->dev);
if (ret) {
bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
config->fwname, ret);
return ret;
}
} else {
bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
config->fwname, ret);
return ret;
}
}
size = fw->size;
data = vmalloc(fw->size);
if (!data) {
bt_dev_err(hdev, "QCA Failed to allocate memory for file: %s",
config->fwname);
release_firmware(fw);
return -ENOMEM;
}
memcpy(data, fw->data, size);
release_firmware(fw);
qca_tlv_check_data(hdev, config, data, soc_type);
segment = data;
remain = size;
while (remain > 0) {
int segsize = min(MAX_SIZE_PER_TLV_SEGMENT, remain);
bt_dev_dbg(hdev, "Send segment %d, size %d", i++, segsize);
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);
out:
vfree(data);
return ret;
}