1030 lines · 25.7 KB · C
RawDownload
// 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> #include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h> #include "btqca.h" int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,			 enum qca_btsoc_type soc_type){	struct sk_buff *skb;	struct edl_event_hdr *edl;	char cmd;	int err = 0;	u8 event_type = HCI_EV_VENDOR;	u8 rlen = sizeof(*edl) + sizeof(*ver);	u8 rtype = EDL_APP_VER_RES_EVT; 	bt_dev_dbg(hdev, "QCA Version Request"); 	/* 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;	} 	edl = (struct edl_event_hdr *)(skb->data); 	if (edl->cresp != EDL_CMD_REQ_RES_EVT ||	    edl->rtype != rtype) {		bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,			   edl->rtype);		err = -EIO;		goto out;	} 	if (soc_type >= QCA_WCN3991)		memcpy(ver, edl->data + 1, sizeof(*ver));	else		memcpy(ver, &edl->data, sizeof(*ver)); 	bt_dev_info(hdev, "QCA Product ID   :0x%08x",		    le32_to_cpu(ver->product_id));	bt_dev_info(hdev, "QCA SOC Version  :0x%08x",		    le32_to_cpu(ver->soc_id));	bt_dev_info(hdev, "QCA ROM Version  :0x%08x",		    le16_to_cpu(ver->rom_ver));	bt_dev_info(hdev, "QCA Patch Version:0x%08x",		    le16_to_cpu(ver->patch_ver)); 	if (ver->soc_id == 0 || ver->rom_ver == 0)		err = -EILSEQ; out:	kfree_skb(skb);	if (err)		bt_dev_err(hdev, "QCA Failed to get version (%d)", err); 	return err;}EXPORT_SYMBOL_GPL(qca_read_soc_version); static int qca_read_fw_build_info(struct hci_dev *hdev){	struct sk_buff *skb;	struct edl_event_hdr *edl;	char *build_label;	char cmd;	int build_lbl_len, err = 0; 	bt_dev_dbg(hdev, "QCA read fw build info"); 	cmd = EDL_GET_BUILD_INFO_CMD;	skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,				&cmd, 0, HCI_INIT_TIMEOUT);	if (IS_ERR(skb)) {		err = PTR_ERR(skb);		bt_dev_err(hdev, "Reading QCA fw build info failed (%d)",			   err);		return err;	} 	if (skb->len < sizeof(*edl)) {		err = -EILSEQ;		goto out;	} 	edl = (struct edl_event_hdr *)(skb->data); 	if (edl->cresp != EDL_CMD_REQ_RES_EVT ||	    edl->rtype != EDL_GET_BUILD_INFO_CMD) {		bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,			   edl->rtype);		err = -EIO;		goto out;	} 	if (skb->len < sizeof(*edl) + 1) {		err = -EILSEQ;		goto out;	} 	build_lbl_len = edl->data[0]; 	if (skb->len < sizeof(*edl) + 1 + build_lbl_len) {		err = -EILSEQ;		goto out;	} 	build_label = kstrndup(&edl->data[1], build_lbl_len, GFP_KERNEL);	if (!build_label) {		err = -ENOMEM;		goto out;	} 	hci_set_fw_info(hdev, "%s", build_label); 	bt_dev_info(hdev, "QCA FW build version: %s", build_label); 	kfree(build_label);out:	kfree_skb(skb);	return err;} static int qca_send_patch_config_cmd(struct hci_dev *hdev){	const u8 cmd[] = { EDL_PATCH_CONFIG_CMD, 0x01, 0, 0, 0 };	struct sk_buff *skb;	struct edl_event_hdr *edl;	int err; 	bt_dev_dbg(hdev, "QCA Patch config"); 	skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, sizeof(cmd),				cmd, 0, HCI_INIT_TIMEOUT);	if (IS_ERR(skb)) {		err = PTR_ERR(skb);		bt_dev_err(hdev, "Sending QCA Patch config failed (%d)", err);		return err;	} 	if (skb->len != 2) {		bt_dev_err(hdev, "QCA Patch config cmd size mismatch len %d", skb->len);		err = -EILSEQ;		goto out;	} 	edl = (struct edl_event_hdr *)(skb->data); 	if (edl->cresp != EDL_PATCH_CONFIG_RES_EVT || edl->rtype != EDL_PATCH_CONFIG_CMD) {		bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,			   edl->rtype);		err = -EIO;		goto out;	} 	err = 0; out:	kfree_skb(skb);	return err;} static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid){	u8 cmd;	struct sk_buff *skb;	struct edl_event_hdr *edl;	int err = 0; 	cmd = EDL_GET_BID_REQ_CMD;	skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,				&cmd, 0, HCI_INIT_TIMEOUT);	if (IS_ERR(skb)) {		err = PTR_ERR(skb);		bt_dev_err(hdev, "Reading QCA board ID failed (%d)", err);		return err;	} 	edl = skb_pull_data(skb, sizeof(*edl));	if (!edl) {		bt_dev_err(hdev, "QCA read board ID with no header");		err = -EILSEQ;		goto out;	} 	if (edl->cresp != EDL_CMD_REQ_RES_EVT ||	    edl->rtype != EDL_GET_BID_REQ_CMD) {		bt_dev_err(hdev, "QCA Wrong packet: %d %d", edl->cresp, edl->rtype);		err = -EIO;		goto out;	} 	if (skb->len < 3) {		err = -EILSEQ;		goto out;	} 	*bid = (edl->data[1] << 8) + edl->data[2];	bt_dev_dbg(hdev, "%s: bid = %x", __func__, *bid); out:	kfree_skb(skb);	return err;} int qca_send_pre_shutdown_cmd(struct hci_dev *hdev){	struct sk_buff *skb;	int err; 	bt_dev_dbg(hdev, "QCA pre shutdown cmd"); 	skb = __hci_cmd_sync_ev(hdev, QCA_PRE_SHUTDOWN_CMD, 0,				NULL, HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT); 	if (IS_ERR(skb)) {		err = PTR_ERR(skb);		bt_dev_err(hdev, "QCA preshutdown_cmd failed (%d)", err);		return err;	} 	kfree_skb(skb); 	return 0;}EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd); static bool qca_filename_has_extension(const char *filename){	const char *suffix = strrchr(filename, '.'); 	/* File extensions require a dot, but not as the first or last character */	if (!suffix || suffix == filename || *(suffix + 1) == '\0')		return 0; 	/* Avoid matching directories with names that look like files with extensions */	return !strchr(suffix, '/');} static bool qca_get_alt_nvm_file(char *filename, size_t max_size){	char fwname[64];	const char *suffix; 	/* nvm file name has an extension, replace with .bin */	if (qca_filename_has_extension(filename)) {		suffix = strrchr(filename, '.');		strscpy(fwname, filename, suffix - filename + 1);		snprintf(fwname + (suffix - filename),		       sizeof(fwname) - (suffix - filename), ".bin");		/* If nvm file is already the default one, return false to skip the retry. */		if (strcmp(fwname, filename) == 0)			return false; 		snprintf(filename, max_size, "%s", fwname);		return true;	}	return false;} static int qca_tlv_check_data(struct hci_dev *hdev,			       struct qca_fw_config *config,			       u8 *fw_data, size_t fw_size,			       enum qca_btsoc_type soc_type){	const u8 *data;	u32 type_len;	u16 tag_id, tag_len;	int idx, length;	struct tlv_type_hdr *tlv;	struct tlv_type_patch *tlv_patch;	struct tlv_type_nvm *tlv_nvm;	uint8_t nvm_baud_rate = config->user_baud_rate;	u8 type; 	config->dnld_mode = QCA_SKIP_EVT_NONE;	config->dnld_type = QCA_SKIP_EVT_NONE; 	switch (config->type) {	case ELF_TYPE_PATCH:		if (fw_size < 7)			return -EINVAL; 		config->dnld_mode = QCA_SKIP_EVT_VSE_CC;		config->dnld_type = QCA_SKIP_EVT_VSE_CC; 		bt_dev_dbg(hdev, "File Class        : 0x%x", fw_data[4]);		bt_dev_dbg(hdev, "Data Encoding     : 0x%x", fw_data[5]);		bt_dev_dbg(hdev, "File version      : 0x%x", fw_data[6]);		break;	case TLV_TYPE_PATCH:		if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))			return -EINVAL; 		tlv = (struct tlv_type_hdr *)fw_data;		type_len = le32_to_cpu(tlv->type_len);		tlv_patch = (struct tlv_type_patch *)tlv->data; 		/* 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; 		BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);		BT_DBG("Total Length           : %d bytes",		       le32_to_cpu(tlv_patch->total_size));		BT_DBG("Patch Data Length      : %d bytes",		       le32_to_cpu(tlv_patch->data_length));		BT_DBG("Signing Format Version : 0x%x",		       tlv_patch->format_version);		BT_DBG("Signature Algorithm    : 0x%x",		       tlv_patch->signature);		BT_DBG("Download mode          : 0x%x",		       tlv_patch->download_mode);		BT_DBG("Reserved               : 0x%x",		       tlv_patch->reserved1);		BT_DBG("Product ID             : 0x%04x",		       le16_to_cpu(tlv_patch->product_id));		BT_DBG("Rom Build Version      : 0x%04x",		       le16_to_cpu(tlv_patch->rom_build));		BT_DBG("Patch Version          : 0x%04x",		       le16_to_cpu(tlv_patch->patch_version));		BT_DBG("Reserved               : 0x%x",		       le16_to_cpu(tlv_patch->reserved2));		BT_DBG("Patch Entry Address    : 0x%x",		       le32_to_cpu(tlv_patch->entry));		break; 	case TLV_TYPE_NVM:		if (fw_size < sizeof(struct tlv_type_hdr))			return -EINVAL; 		tlv = (struct tlv_type_hdr *)fw_data; 		type_len = le32_to_cpu(tlv->type_len);		length = type_len >> 8;		type = type_len & 0xff; 		/* 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; 			tlv++; 			type_len = le32_to_cpu(tlv->type_len);			length = type_len >> 8;			type = type_len & 0xff;		} 		BT_DBG("TLV Type\t\t : 0x%x", type);		BT_DBG("Length\t\t : %d bytes", length); 		if (type != 2)			break; 		if (fw_size < length + (tlv->data - fw_data))			return -EINVAL; 		idx = 0;		data = tlv->data;		while (idx + sizeof(struct tlv_type_nvm) <= length) {			tlv_nvm = (struct tlv_type_nvm *)(data + idx); 			tag_id = le16_to_cpu(tlv_nvm->tag_id);			tag_len = le16_to_cpu(tlv_nvm->tag_len); 			if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)				return -EINVAL; 			/* Update NVM tags as needed */			switch (tag_id) {			case EDL_TAG_ID_BD_ADDR:				if (tag_len != sizeof(bdaddr_t))					return -EINVAL; 				memcpy(&config->bdaddr, tlv_nvm->data, sizeof(bdaddr_t)); 				break; 			case EDL_TAG_ID_HCI:				if (tag_len < 3)					return -EINVAL; 				/* HCI transport layer parameters				 * enabling software inband sleep				 * onto controller side.				 */				tlv_nvm->data[0] |= 0x80; 				/* UART Baud Rate */				if (soc_type >= QCA_WCN3991)					tlv_nvm->data[1] = nvm_baud_rate;				else					tlv_nvm->data[2] = nvm_baud_rate; 				break; 			case EDL_TAG_ID_DEEP_SLEEP:				if (tag_len < 1)					return -EINVAL; 				/* Sleep enable mask				 * enabling deep sleep feature on controller.				 */				tlv_nvm->data[0] |= 0x01; 				break;			} 			idx += sizeof(struct tlv_type_nvm) + tag_len;		}		break; 	default:		BT_ERR("Unknown TLV type %d", config->type);		return -EINVAL;	} 	return 0;} static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,				const u8 *data, enum qca_tlv_dnld_mode mode,				enum qca_btsoc_type soc_type){	struct sk_buff *skb;	struct edl_event_hdr *edl;	struct tlv_seg_resp *tlv_resp;	u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];	int err = 0;	u8 event_type = HCI_EV_VENDOR;	u8 rlen = (sizeof(*edl) + sizeof(*tlv_resp));	u8 rtype = EDL_TVL_DNLD_RES_EVT; 	cmd[0] = EDL_PATCH_TLV_REQ_CMD;	cmd[1] = seg_size;	memcpy(cmd + 2, data, seg_size); 	if (mode == QCA_SKIP_EVT_VSE_CC || mode == QCA_SKIP_EVT_VSE)		return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,				      cmd); 	/* 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;	} 	skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,				event_type, HCI_INIT_TIMEOUT);	if (IS_ERR(skb)) {		err = PTR_ERR(skb);		bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err);		return err;	} 	if (skb->len != rlen) {		bt_dev_err(hdev, "QCA TLV response size mismatch");

Lines 1–500 of 1030.