diff options
Diffstat (limited to 'drivers/bluetooth/btusb.c')
| -rw-r--r-- | drivers/bluetooth/btusb.c | 328 | 
1 files changed, 304 insertions, 24 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 18bc94718711..2a8e2bb038f5 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -540,6 +540,10 @@ static const struct usb_device_id blacklist_table[] = {  	/* Realtek 8852BE Bluetooth devices */  	{ USB_DEVICE(0x0cb8, 0xc559), .driver_info = BTUSB_REALTEK |  						     BTUSB_WIDEBAND_SPEECH }, +	{ USB_DEVICE(0x0bda, 0x887b), .driver_info = BTUSB_REALTEK | +						     BTUSB_WIDEBAND_SPEECH }, +	{ USB_DEVICE(0x13d3, 0x3571), .driver_info = BTUSB_REALTEK | +						     BTUSB_WIDEBAND_SPEECH },  	/* Realtek Bluetooth devices */  	{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), @@ -558,6 +562,9 @@ static const struct usb_device_id blacklist_table[] = {  	{ USB_DEVICE(0x043e, 0x310c), .driver_info = BTUSB_MEDIATEK |  						     BTUSB_WIDEBAND_SPEECH |  						     BTUSB_VALID_LE_STATES }, +	{ USB_DEVICE(0x04ca, 0x3801), .driver_info = BTUSB_MEDIATEK | +						     BTUSB_WIDEBAND_SPEECH | +						     BTUSB_VALID_LE_STATES },  	/* Additional MediaTek MT7668 Bluetooth devices */  	{ USB_DEVICE(0x043e, 0x3109), .driver_info = BTUSB_MEDIATEK | @@ -612,6 +619,9 @@ static const struct usb_device_id blacklist_table[] = {  	{ USB_DEVICE(0x0489, 0xe0e2), .driver_info = BTUSB_MEDIATEK |  						     BTUSB_WIDEBAND_SPEECH |  						     BTUSB_VALID_LE_STATES }, +	{ USB_DEVICE(0x0489, 0xe0e4), .driver_info = BTUSB_MEDIATEK | +						     BTUSB_WIDEBAND_SPEECH | +						     BTUSB_VALID_LE_STATES },  	{ USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK |  						     BTUSB_WIDEBAND_SPEECH |  						     BTUSB_VALID_LE_STATES }, @@ -723,6 +733,16 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {  	{}  }; +struct qca_dump_info { +	/* fields for dump collection */ +	u16 id_vendor; +	u16 id_product; +	u32 fw_version; +	u32 controller_id; +	u32 ram_dump_size; +	u16 ram_dump_seqno; +}; +  #define BTUSB_MAX_ISOC_FRAMES	10  #define BTUSB_INTR_RUNNING	0 @@ -742,6 +762,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {  #define BTUSB_WAKEUP_AUTOSUSPEND	14  #define BTUSB_USE_ALT3_FOR_WBS	15  #define BTUSB_ALT6_CONTINUOUS_TX	16 +#define BTUSB_HW_SSR_ACTIVE	17  struct btusb_data {  	struct hci_dev       *hdev; @@ -804,6 +825,8 @@ struct btusb_data {  	int oob_wake_irq;   /* irq for out-of-band wake-on-bt */  	unsigned cmd_timeout_cnt; + +	struct qca_dump_info qca_dump;  };  static void btusb_reset(struct hci_dev *hdev) @@ -894,6 +917,11 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev)  	struct btusb_data *data = hci_get_drvdata(hdev);  	struct gpio_desc *reset_gpio = data->reset_gpio; +	if (test_bit(BTUSB_HW_SSR_ACTIVE, &data->flags)) { +		bt_dev_info(hdev, "Ramdump in progress, defer cmd_timeout"); +		return; +	} +  	if (++data->cmd_timeout_cnt < 5)  		return; @@ -1050,21 +1078,11 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)  		hci_skb_expect(skb) -= len;  		if (skb->len == HCI_ACL_HDR_SIZE) { -			__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);  			__le16 dlen = hci_acl_hdr(skb)->dlen; -			__u8 type;  			/* Complete ACL header */  			hci_skb_expect(skb) = __le16_to_cpu(dlen); -			/* Detect if ISO packet has been sent over bulk */ -			if (hci_conn_num(data->hdev, ISO_LINK)) { -				type = hci_conn_lookup_type(data->hdev, -							    hci_handle(handle)); -				if (type == ISO_LINK) -					hci_skb_pkt_type(skb) = HCI_ISODATA_PKT; -			} -  			if (skb_tailroom(skb) < hci_skb_expect(skb)) {  				kfree_skb(skb);  				skb = NULL; @@ -2386,16 +2404,47 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,  	return btusb_recv_bulk(data, buffer, count);  } +static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) +{ +	struct intel_tlv *tlv = (void *)&skb->data[5]; + +	/* The first event is always an event type TLV */ +	if (tlv->type != INTEL_TLV_TYPE_ID) +		goto recv_frame; + +	switch (tlv->val[0]) { +	case INTEL_TLV_SYSTEM_EXCEPTION: +	case INTEL_TLV_FATAL_EXCEPTION: +	case INTEL_TLV_DEBUG_EXCEPTION: +	case INTEL_TLV_TEST_EXCEPTION: +		/* Generate devcoredump from exception */ +		if (!hci_devcd_init(hdev, skb->len)) { +			hci_devcd_append(hdev, skb); +			hci_devcd_complete(hdev); +		} else { +			bt_dev_err(hdev, "Failed to generate devcoredump"); +			kfree_skb(skb); +		} +		return 0; +	default: +		bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]); +	} + +recv_frame: +	return hci_recv_frame(hdev, skb); +} +  static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)  { -	if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { -		struct hci_event_hdr *hdr = (void *)skb->data; +	struct hci_event_hdr *hdr = (void *)skb->data; +	const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 }; -		if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && -		    hdr->plen > 0) { -			const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; -			unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; +	if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && +	    hdr->plen > 0) { +		const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; +		unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; +		if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {  			switch (skb->data[2]) {  			case 0x02:  				/* When switching to the operational firmware @@ -2414,6 +2463,15 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)  				break;  			}  		} + +		/* Handle all diagnostics events separately. May still call +		 * hci_recv_frame. +		 */ +		if (len >= sizeof(diagnostics_hdr) && +		    memcmp(&skb->data[2], diagnostics_hdr, +			   sizeof(diagnostics_hdr)) == 0) { +			return btusb_intel_diagnostics(hdev, skb); +		}  	}  	return hci_recv_frame(hdev, skb); @@ -3254,6 +3312,202 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,  	return 0;  } +#define QCA_MEMDUMP_ACL_HANDLE 0x2EDD +#define QCA_MEMDUMP_SIZE_MAX  0x100000 +#define QCA_MEMDUMP_VSE_CLASS 0x01 +#define QCA_MEMDUMP_MSG_TYPE 0x08 +#define QCA_MEMDUMP_PKT_SIZE 248 +#define QCA_LAST_SEQUENCE_NUM 0xffff + +struct qca_dump_hdr { +	u8 vse_class; +	u8 msg_type; +	__le16 seqno; +	u8 reserved; +	union { +		u8 data[0]; +		struct { +			__le32 ram_dump_size; +			u8 data0[0]; +		} __packed; +	}; +} __packed; + + +static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ +	char buf[128]; +	struct btusb_data *btdata = hci_get_drvdata(hdev); + +	snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n", +			btdata->qca_dump.controller_id); +	skb_put_data(skb, buf, strlen(buf)); + +	snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n", +			btdata->qca_dump.fw_version); +	skb_put_data(skb, buf, strlen(buf)); + +	snprintf(buf, sizeof(buf), "Driver: %s\nVendor: qca\n", +			btusb_driver.name); +	skb_put_data(skb, buf, strlen(buf)); + +	snprintf(buf, sizeof(buf), "VID: 0x%x\nPID:0x%x\n", +			btdata->qca_dump.id_vendor, btdata->qca_dump.id_product); +	skb_put_data(skb, buf, strlen(buf)); + +	snprintf(buf, sizeof(buf), "Lmp Subversion: 0x%x\n", +			hdev->lmp_subver); +	skb_put_data(skb, buf, strlen(buf)); +} + +static void btusb_coredump_qca(struct hci_dev *hdev) +{ +	static const u8 param[] = { 0x26 }; +	struct sk_buff *skb; + +	skb = __hci_cmd_sync(hdev, 0xfc0c, 1, param, HCI_CMD_TIMEOUT); +	if (IS_ERR(skb)) +		bt_dev_err(hdev, "%s: triggle crash failed (%ld)", __func__, PTR_ERR(skb)); +	kfree_skb(skb); +} + +/* + * ==0: not a dump pkt. + * < 0: fails to handle a dump pkt + * > 0: otherwise. + */ +static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ +	int ret = 1; +	u8 pkt_type; +	u8 *sk_ptr; +	unsigned int sk_len; +	u16 seqno; +	u32 dump_size; + +	struct hci_event_hdr *event_hdr; +	struct hci_acl_hdr *acl_hdr; +	struct qca_dump_hdr *dump_hdr; +	struct btusb_data *btdata = hci_get_drvdata(hdev); +	struct usb_device *udev = btdata->udev; + +	pkt_type = hci_skb_pkt_type(skb); +	sk_ptr = skb->data; +	sk_len = skb->len; + +	if (pkt_type == HCI_ACLDATA_PKT) { +		acl_hdr = hci_acl_hdr(skb); +		if (le16_to_cpu(acl_hdr->handle) != QCA_MEMDUMP_ACL_HANDLE) +			return 0; +		sk_ptr += HCI_ACL_HDR_SIZE; +		sk_len -= HCI_ACL_HDR_SIZE; +		event_hdr = (struct hci_event_hdr *)sk_ptr; +	} else { +		event_hdr = hci_event_hdr(skb); +	} + +	if ((event_hdr->evt != HCI_VENDOR_PKT) +		|| (event_hdr->plen != (sk_len - HCI_EVENT_HDR_SIZE))) +		return 0; + +	sk_ptr += HCI_EVENT_HDR_SIZE; +	sk_len -= HCI_EVENT_HDR_SIZE; + +	dump_hdr = (struct qca_dump_hdr *)sk_ptr; +	if ((sk_len < offsetof(struct qca_dump_hdr, data)) +		|| (dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) +	    || (dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE)) +		return 0; + +	/*it is dump pkt now*/ +	seqno = le16_to_cpu(dump_hdr->seqno); +	if (seqno == 0) { +		set_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags); +		dump_size = le32_to_cpu(dump_hdr->ram_dump_size); +		if (!dump_size || (dump_size > QCA_MEMDUMP_SIZE_MAX)) { +			ret = -EILSEQ; +			bt_dev_err(hdev, "Invalid memdump size(%u)", +				   dump_size); +			goto out; +		} + +		ret = hci_devcd_init(hdev, dump_size); +		if (ret < 0) { +			bt_dev_err(hdev, "memdump init error(%d)", ret); +			goto out; +		} + +		btdata->qca_dump.ram_dump_size = dump_size; +		btdata->qca_dump.ram_dump_seqno = 0; +		sk_ptr += offsetof(struct qca_dump_hdr, data0); +		sk_len -= offsetof(struct qca_dump_hdr, data0); + +		usb_disable_autosuspend(udev); +		bt_dev_info(hdev, "%s memdump size(%u)\n", +			    (pkt_type == HCI_ACLDATA_PKT) ? "ACL" : "event", +			    dump_size); +	} else { +		sk_ptr += offsetof(struct qca_dump_hdr, data); +		sk_len -= offsetof(struct qca_dump_hdr, data); +	} + +	if (!btdata->qca_dump.ram_dump_size) { +		ret = -EINVAL; +		bt_dev_err(hdev, "memdump is not active"); +		goto out; +	} + +	if ((seqno > btdata->qca_dump.ram_dump_seqno + 1) && (seqno != QCA_LAST_SEQUENCE_NUM)) { +		dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - btdata->qca_dump.ram_dump_seqno - 1); +		hci_devcd_append_pattern(hdev, 0x0, dump_size); +		bt_dev_err(hdev, +			   "expected memdump seqno(%u) is not received(%u)\n", +			   btdata->qca_dump.ram_dump_seqno, seqno); +		btdata->qca_dump.ram_dump_seqno = seqno; +		kfree_skb(skb); +		return ret; +	} + +	skb_pull(skb, skb->len - sk_len); +	hci_devcd_append(hdev, skb); +	btdata->qca_dump.ram_dump_seqno++; +	if (seqno == QCA_LAST_SEQUENCE_NUM) { +		bt_dev_info(hdev, +				"memdump done: pkts(%u), total(%u)\n", +				btdata->qca_dump.ram_dump_seqno, btdata->qca_dump.ram_dump_size); + +		hci_devcd_complete(hdev); +		goto out; +	} +	return ret; + +out: +	if (btdata->qca_dump.ram_dump_size) +		usb_enable_autosuspend(udev); +	btdata->qca_dump.ram_dump_size = 0; +	btdata->qca_dump.ram_dump_seqno = 0; +	clear_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags); + +	if (ret < 0) +		kfree_skb(skb); +	return ret; +} + +static int btusb_recv_acl_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ +	if (handle_dump_pkt_qca(hdev, skb)) +		return 0; +	return hci_recv_frame(hdev, skb); +} + +static int btusb_recv_evt_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ +	if (handle_dump_pkt_qca(hdev, skb)) +		return 0; +	return hci_recv_frame(hdev, skb); +} + +  #define QCA_DFU_PACKET_LEN	4096  #define QCA_GET_TARGET_VERSION	0x09 @@ -3588,6 +3842,9 @@ static int btusb_setup_qca(struct hci_dev *hdev)  	if (err < 0)  		return err; +	btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); +	btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version); +  	if (!(status & QCA_SYSCFG_UPDATED)) {  		err = btusb_setup_qca_load_nvm(hdev, &ver, info);  		if (err < 0) @@ -3841,13 +4098,9 @@ static int btusb_probe(struct usb_interface *intf,  	BT_DBG("intf %p id %p", intf, id); -	/* interface numbers are hardcoded in the spec */ -	if (intf->cur_altsetting->desc.bInterfaceNumber != 0) { -		if (!(id->driver_info & BTUSB_IFNUM_2)) -			return -ENODEV; -		if (intf->cur_altsetting->desc.bInterfaceNumber != 2) -			return -ENODEV; -	} +	if ((id->driver_info & BTUSB_IFNUM_2) && +	    (intf->cur_altsetting->desc.bInterfaceNumber != 2)) +		return -ENODEV;  	ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber; @@ -4022,7 +4275,7 @@ static int btusb_probe(struct usb_interface *intf,  	/* Combined Intel Device setup to support multiple setup routine */  	if (id->driver_info & BTUSB_INTEL_COMBINED) { -		err = btintel_configure_setup(hdev); +		err = btintel_configure_setup(hdev, btusb_driver.name);  		if (err)  			goto out_free_dev; @@ -4081,6 +4334,11 @@ static int btusb_probe(struct usb_interface *intf,  	}  	if (id->driver_info & BTUSB_QCA_WCN6855) { +		data->qca_dump.id_vendor = id->idVendor; +		data->qca_dump.id_product = id->idProduct; +		data->recv_event = btusb_recv_evt_qca; +		data->recv_acl = btusb_recv_acl_qca; +		hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL);  		data->setup_on_usb = btusb_setup_qca;  		hdev->shutdown = btusb_shutdown_qca;  		hdev->set_bdaddr = btusb_set_bdaddr_wcn6855; @@ -4112,6 +4370,9 @@ static int btusb_probe(struct usb_interface *intf,  	if (id->driver_info & BTUSB_ACTIONS_SEMI) {  		/* Support is advertised, but not implemented */  		set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); +		set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); +		set_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks); +		set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks);  	}  	if (!reset) @@ -4399,6 +4660,17 @@ done:  }  #endif +#ifdef CONFIG_DEV_COREDUMP +static void btusb_coredump(struct device *dev) +{ +	struct btusb_data *data = dev_get_drvdata(dev); +	struct hci_dev *hdev = data->hdev; + +	if (hdev->dump.coredump) +		hdev->dump.coredump(hdev); +} +#endif +  static struct usb_driver btusb_driver = {  	.name		= "btusb",  	.probe		= btusb_probe, @@ -4410,6 +4682,14 @@ static struct usb_driver btusb_driver = {  	.id_table	= btusb_table,  	.supports_autosuspend = 1,  	.disable_hub_initiated_lpm = 1, + +#ifdef CONFIG_DEV_COREDUMP +	.drvwrap = { +		.driver = { +			.coredump = btusb_coredump, +		}, +	}, +#endif  };  module_usb_driver(btusb_driver);  |