diff options
Diffstat (limited to 'drivers/bluetooth')
| -rw-r--r-- | drivers/bluetooth/ath3k.c | 4 | ||||
| -rw-r--r-- | drivers/bluetooth/bt3c_cs.c | 3 | ||||
| -rw-r--r-- | drivers/bluetooth/btbcm.c | 148 | ||||
| -rw-r--r-- | drivers/bluetooth/btbcm.h | 6 | ||||
| -rw-r--r-- | drivers/bluetooth/btusb.c | 406 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_ath.c | 98 | 
6 files changed, 556 insertions, 109 deletions
| diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 288547a3c566..8c81af6dbe06 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -88,6 +88,7 @@ static const struct usb_device_id ath3k_table[] = {  	{ USB_DEVICE(0x04CA, 0x3007) },  	{ USB_DEVICE(0x04CA, 0x3008) },  	{ USB_DEVICE(0x04CA, 0x300b) }, +	{ USB_DEVICE(0x04CA, 0x300f) },  	{ USB_DEVICE(0x04CA, 0x3010) },  	{ USB_DEVICE(0x0930, 0x0219) },  	{ USB_DEVICE(0x0930, 0x0220) }, @@ -104,6 +105,7 @@ static const struct usb_device_id ath3k_table[] = {  	{ USB_DEVICE(0x0cf3, 0xe003) },  	{ USB_DEVICE(0x0CF3, 0xE004) },  	{ USB_DEVICE(0x0CF3, 0xE005) }, +	{ USB_DEVICE(0x0CF3, 0xE006) },  	{ USB_DEVICE(0x13d3, 0x3362) },  	{ USB_DEVICE(0x13d3, 0x3375) },  	{ USB_DEVICE(0x13d3, 0x3393) }, @@ -143,6 +145,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {  	{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, @@ -158,6 +161,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {  	{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 4f7e8d400bc0..6de97b3871b0 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -227,7 +227,6 @@ static void bt3c_receive(struct bt3c_info *info)  	iobase = info->p_dev->resource[0]->start;  	avail = bt3c_read(iobase, 0x7006); -	//printk("bt3c_cs: receiving %d bytes\n", avail);  	bt3c_address(iobase, 0x7480);  	while (size < avail) { @@ -250,7 +249,6 @@ static void bt3c_receive(struct bt3c_info *info)  			bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);  			inb(iobase + DATA_H); -			//printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);  			switch (bt_cb(info->rx_skb)->pkt_type) { @@ -364,7 +362,6 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)  			if (stat & 0x0001)  				bt3c_receive(info);  			if (stat & 0x0002) { -				//BT_ERR("Ack (stat=0x%04x)", stat);  				clear_bit(XMIT_SENDING, &(info->tx_state));  				bt3c_write_wakeup(info);  			} diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index d0741f3ed7ec..4bba86677adc 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -95,6 +95,78 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)  }  EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); +int btbcm_patchram(struct hci_dev *hdev, const char *firmware) +{ +	const struct hci_command_hdr *cmd; +	const struct firmware *fw; +	const u8 *fw_ptr; +	size_t fw_size; +	struct sk_buff *skb; +	u16 opcode; +	int err; + +	err = request_firmware(&fw, firmware, &hdev->dev); +	if (err < 0) { +		BT_INFO("%s: BCM: Patch %s not found", hdev->name, firmware); +		return err; +	} + +	/* Start Download */ +	skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); +	if (IS_ERR(skb)) { +		err = PTR_ERR(skb); +		BT_ERR("%s: BCM: Download Minidrv command failed (%d)", +		       hdev->name, err); +		goto done; +	} +	kfree_skb(skb); + +	/* 50 msec delay after Download Minidrv completes */ +	msleep(50); + +	fw_ptr = fw->data; +	fw_size = fw->size; + +	while (fw_size >= sizeof(*cmd)) { +		const u8 *cmd_param; + +		cmd = (struct hci_command_hdr *)fw_ptr; +		fw_ptr += sizeof(*cmd); +		fw_size -= sizeof(*cmd); + +		if (fw_size < cmd->plen) { +			BT_ERR("%s: BCM: Patch %s is corrupted", hdev->name, +			       firmware); +			err = -EINVAL; +			goto done; +		} + +		cmd_param = fw_ptr; +		fw_ptr += cmd->plen; +		fw_size -= cmd->plen; + +		opcode = le16_to_cpu(cmd->opcode); + +		skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, +				     HCI_INIT_TIMEOUT); +		if (IS_ERR(skb)) { +			err = PTR_ERR(skb); +			BT_ERR("%s: BCM: Patch command %04x failed (%d)", +			       hdev->name, opcode, err); +			goto done; +		} +		kfree_skb(skb); +	} + +	/* 250 msec delay after Launch Ram completes */ +	msleep(250); + +done: +	release_firmware(fw); +	return err; +} +EXPORT_SYMBOL(btbcm_patchram); +  static int btbcm_reset(struct hci_dev *hdev)  {  	struct sk_buff *skb; @@ -198,12 +270,8 @@ static const struct {  int btbcm_setup_patchram(struct hci_dev *hdev)  { -	const struct hci_command_hdr *cmd; -	const struct firmware *fw; -	const u8 *fw_ptr; -	size_t fw_size;  	char fw_name[64]; -	u16 opcode, subver, rev, pid, vid; +	u16 subver, rev, pid, vid;  	const char *hw_name = NULL;  	struct sk_buff *skb;  	struct hci_rp_read_local_version *ver; @@ -273,74 +341,19 @@ int btbcm_setup_patchram(struct hci_dev *hdev)  		hw_name ? : "BCM", (subver & 0x7000) >> 13,  		(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); -	err = request_firmware(&fw, fw_name, &hdev->dev); -	if (err < 0) { -		BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name); +	err = btbcm_patchram(hdev, fw_name); +	if (err == -ENOENT)  		return 0; -	} - -	/* Start Download */ -	skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); -	if (IS_ERR(skb)) { -		err = PTR_ERR(skb); -		BT_ERR("%s: BCM: Download Minidrv command failed (%d)", -		       hdev->name, err); -		goto reset; -	} -	kfree_skb(skb); - -	/* 50 msec delay after Download Minidrv completes */ -	msleep(50); - -	fw_ptr = fw->data; -	fw_size = fw->size; - -	while (fw_size >= sizeof(*cmd)) { -		const u8 *cmd_param; - -		cmd = (struct hci_command_hdr *)fw_ptr; -		fw_ptr += sizeof(*cmd); -		fw_size -= sizeof(*cmd); - -		if (fw_size < cmd->plen) { -			BT_ERR("%s: BCM: patch %s is corrupted", hdev->name, -			       fw_name); -			err = -EINVAL; -			goto reset; -		} -		cmd_param = fw_ptr; -		fw_ptr += cmd->plen; -		fw_size -= cmd->plen; - -		opcode = le16_to_cpu(cmd->opcode); - -		skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, -				     HCI_INIT_TIMEOUT); -		if (IS_ERR(skb)) { -			err = PTR_ERR(skb); -			BT_ERR("%s: BCM: patch command %04x failed (%d)", -			       hdev->name, opcode, err); -			goto reset; -		} -		kfree_skb(skb); -	} - -	/* 250 msec delay after Launch Ram completes */ -	msleep(250); - -reset:  	/* Reset */  	err = btbcm_reset(hdev);  	if (err) -		goto done; +		return err;  	/* Read Local Version Info */  	skb = btbcm_read_local_version(hdev); -	if (IS_ERR(skb)) { -		err = PTR_ERR(skb); -		goto done; -	} +	if (IS_ERR(skb)) +		return PTR_ERR(skb);  	ver = (struct hci_rp_read_local_version *)skb->data;  	rev = le16_to_cpu(ver->hci_rev); @@ -355,10 +368,7 @@ reset:  	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); -done: -	release_firmware(fw); - -	return err; +	return 0;  }  EXPORT_SYMBOL_GPL(btbcm_setup_patchram); diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h index 34268ae3eb46..eb6ab5f9483d 100644 --- a/drivers/bluetooth/btbcm.h +++ b/drivers/bluetooth/btbcm.h @@ -25,6 +25,7 @@  int btbcm_check_bdaddr(struct hci_dev *hdev);  int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); +int btbcm_patchram(struct hci_dev *hdev, const char *firmware);  int btbcm_setup_patchram(struct hci_dev *hdev);  int btbcm_setup_apple(struct hci_dev *hdev); @@ -41,6 +42,11 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)  	return -EOPNOTSUPP;  } +static inline int btbcm_patchram(struct hci_dev *hdev, const char *firmware) +{ +	return -EOPNOTSUPP; +} +  static inline int btbcm_setup_patchram(struct hci_dev *hdev)  {  	return 0; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index de7b236eeae7..3c10d4dfe9a7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -24,6 +24,7 @@  #include <linux/module.h>  #include <linux/usb.h>  #include <linux/firmware.h> +#include <asm/unaligned.h>  #include <net/bluetooth/bluetooth.h>  #include <net/bluetooth/hci_core.h> @@ -57,6 +58,7 @@ static struct usb_driver btusb_driver;  #define BTUSB_AMP		0x4000  #define BTUSB_QCA_ROME		0x8000  #define BTUSB_BCM_APPLE		0x10000 +#define BTUSB_REALTEK		0x20000  static const struct usb_device_id btusb_table[] = {  	/* Generic Bluetooth USB device */ @@ -184,6 +186,7 @@ static const struct usb_device_id blacklist_table[] = {  	{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, @@ -200,6 +203,7 @@ static const struct usb_device_id blacklist_table[] = {  	{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, @@ -216,6 +220,7 @@ static const struct usb_device_id blacklist_table[] = {  	{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },  	/* QCA ROME chipset */ +	{ USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME },  	{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },  	{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME }, @@ -288,6 +293,28 @@ static const struct usb_device_id blacklist_table[] = {  	{ USB_VENDOR_AND_INTERFACE_INFO(0x8087, 0xe0, 0x01, 0x01),  	  .driver_info = BTUSB_IGNORE }, +	/* Realtek Bluetooth devices */ +	{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), +	  .driver_info = BTUSB_REALTEK }, + +	/* Additional Realtek 8723AE Bluetooth devices */ +	{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK }, + +	/* Additional Realtek 8723BE Bluetooth devices */ +	{ USB_DEVICE(0x0489, 0xe085), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x0489, 0xe08b), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3410), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3416), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK }, + +	/* Additional Realtek 8821AE Bluetooth devices */ +	{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3458), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK }, +	{ USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK }, +  	{ }	/* Terminating entry */  }; @@ -892,7 +919,7 @@ static int btusb_open(struct hci_dev *hdev)  	 */  	if (data->setup_on_usb) {  		err = data->setup_on_usb(hdev); -		if (err <0) +		if (err < 0)  			return err;  	} @@ -1345,6 +1372,378 @@ static int btusb_setup_csr(struct hci_dev *hdev)  	return ret;  } +#define RTL_FRAG_LEN 252 + +struct rtl_download_cmd { +	__u8 index; +	__u8 data[RTL_FRAG_LEN]; +} __packed; + +struct rtl_download_response { +	__u8 status; +	__u8 index; +} __packed; + +struct rtl_rom_version_evt { +	__u8 status; +	__u8 version; +} __packed; + +struct rtl_epatch_header { +	__u8 signature[8]; +	__le32 fw_version; +	__le16 num_patches; +} __packed; + +#define RTL_EPATCH_SIGNATURE	"Realtech" +#define RTL_ROM_LMP_3499	0x3499 +#define RTL_ROM_LMP_8723A	0x1200 +#define RTL_ROM_LMP_8723B	0x8723 +#define RTL_ROM_LMP_8821A	0x8821 +#define RTL_ROM_LMP_8761A	0x8761 + +static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) +{ +	struct rtl_rom_version_evt *rom_version; +	struct sk_buff *skb; +	int ret; + +	/* Read RTL ROM version command */ +	skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT); +	if (IS_ERR(skb)) { +		BT_ERR("%s: Read ROM version failed (%ld)", +		       hdev->name, PTR_ERR(skb)); +		return PTR_ERR(skb); +	} + +	if (skb->len != sizeof(*rom_version)) { +		BT_ERR("%s: RTL version event length mismatch", hdev->name); +		kfree_skb(skb); +		return -EIO; +	} + +	rom_version = (struct rtl_rom_version_evt *)skb->data; +	BT_INFO("%s: rom_version status=%x version=%x", +		hdev->name, rom_version->status, rom_version->version); + +	ret = rom_version->status; +	if (ret == 0) +		*version = rom_version->version; + +	kfree_skb(skb); +	return ret; +} + +static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver, +				   const struct firmware *fw, +				   unsigned char **_buf) +{ +	const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 }; +	struct rtl_epatch_header *epatch_info; +	unsigned char *buf; +	int i, ret, len; +	size_t min_size; +	u8 opcode, length, data, rom_version = 0; +	int project_id = -1; +	const unsigned char *fwptr, *chip_id_base; +	const unsigned char *patch_length_base, *patch_offset_base; +	u32 patch_offset = 0; +	u16 patch_length, num_patches; +	const u16 project_id_to_lmp_subver[] = { +		RTL_ROM_LMP_8723A, +		RTL_ROM_LMP_8723B, +		RTL_ROM_LMP_8821A, +		RTL_ROM_LMP_8761A +	}; + +	ret = rtl_read_rom_version(hdev, &rom_version); +	if (ret) +		return -bt_to_errno(ret); + +	min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3; +	if (fw->size < min_size) +		return -EINVAL; + +	fwptr = fw->data + fw->size - sizeof(extension_sig); +	if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) { +		BT_ERR("%s: extension section signature mismatch", hdev->name); +		return -EINVAL; +	} + +	/* Loop from the end of the firmware parsing instructions, until +	 * we find an instruction that identifies the "project ID" for the +	 * hardware supported by this firwmare file. +	 * Once we have that, we double-check that that project_id is suitable +	 * for the hardware we are working with. +	 */ +	while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) { +		opcode = *--fwptr; +		length = *--fwptr; +		data = *--fwptr; + +		BT_DBG("check op=%x len=%x data=%x", opcode, length, data); + +		if (opcode == 0xff) /* EOF */ +			break; + +		if (length == 0) { +			BT_ERR("%s: found instruction with length 0", +			       hdev->name); +			return -EINVAL; +		} + +		if (opcode == 0 && length == 1) { +			project_id = data; +			break; +		} + +		fwptr -= length; +	} + +	if (project_id < 0) { +		BT_ERR("%s: failed to find version instruction", hdev->name); +		return -EINVAL; +	} + +	if (project_id >= ARRAY_SIZE(project_id_to_lmp_subver)) { +		BT_ERR("%s: unknown project id %d", hdev->name, project_id); +		return -EINVAL; +	} + +	if (lmp_subver != project_id_to_lmp_subver[project_id]) { +		BT_ERR("%s: firmware is for %x but this is a %x", hdev->name, +		       project_id_to_lmp_subver[project_id], lmp_subver); +		return -EINVAL; +	} + +	epatch_info = (struct rtl_epatch_header *)fw->data; +	if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) { +		BT_ERR("%s: bad EPATCH signature", hdev->name); +		return -EINVAL; +	} + +	num_patches = le16_to_cpu(epatch_info->num_patches); +	BT_DBG("fw_version=%x, num_patches=%d", +	       le32_to_cpu(epatch_info->fw_version), num_patches); + +	/* After the rtl_epatch_header there is a funky patch metadata section. +	 * Assuming 2 patches, the layout is: +	 * ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2 +	 * +	 * Find the right patch for this chip. +	 */ +	min_size += 8 * num_patches; +	if (fw->size < min_size) +		return -EINVAL; + +	chip_id_base = fw->data + sizeof(struct rtl_epatch_header); +	patch_length_base = chip_id_base + (sizeof(u16) * num_patches); +	patch_offset_base = patch_length_base + (sizeof(u16) * num_patches); +	for (i = 0; i < num_patches; i++) { +		u16 chip_id = get_unaligned_le16(chip_id_base + +						 (i * sizeof(u16))); +		if (chip_id == rom_version + 1) { +			patch_length = get_unaligned_le16(patch_length_base + +							  (i * sizeof(u16))); +			patch_offset = get_unaligned_le32(patch_offset_base + +							  (i * sizeof(u32))); +			break; +		} +	} + +	if (!patch_offset) { +		BT_ERR("%s: didn't find patch for chip id %d", +		       hdev->name, rom_version); +		return -EINVAL; +	} + +	BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i); +	min_size = patch_offset + patch_length; +	if (fw->size < min_size) +		return -EINVAL; + +	/* Copy the firmware into a new buffer and write the version at +	 * the end. +	 */ +	len = patch_length; +	buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4); + +	*_buf = buf; +	return len; +} + +static int rtl_download_firmware(struct hci_dev *hdev, +				 const unsigned char *data, int fw_len) +{ +	struct rtl_download_cmd *dl_cmd; +	int frag_num = fw_len / RTL_FRAG_LEN + 1; +	int frag_len = RTL_FRAG_LEN; +	int ret = 0; +	int i; + +	dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL); +	if (!dl_cmd) +		return -ENOMEM; + +	for (i = 0; i < frag_num; i++) { +		struct rtl_download_response *dl_resp; +		struct sk_buff *skb; + +		BT_DBG("download fw (%d/%d)", i, frag_num); + +		dl_cmd->index = i; +		if (i == (frag_num - 1)) { +			dl_cmd->index |= 0x80; /* data end */ +			frag_len = fw_len % RTL_FRAG_LEN; +		} +		memcpy(dl_cmd->data, data, frag_len); + +		/* Send download command */ +		skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd, +				     HCI_INIT_TIMEOUT); +		if (IS_ERR(skb)) { +			BT_ERR("%s: download fw command failed (%ld)", +			       hdev->name, PTR_ERR(skb)); +			ret = -PTR_ERR(skb); +			goto out; +		} + +		if (skb->len != sizeof(*dl_resp)) { +			BT_ERR("%s: download fw event length mismatch", +			       hdev->name); +			kfree_skb(skb); +			ret = -EIO; +			goto out; +		} + +		dl_resp = (struct rtl_download_response *)skb->data; +		if (dl_resp->status != 0) { +			kfree_skb(skb); +			ret = bt_to_errno(dl_resp->status); +			goto out; +		} + +		kfree_skb(skb); +		data += RTL_FRAG_LEN; +	} + +out: +	kfree(dl_cmd); +	return ret; +} + +static int btusb_setup_rtl8723a(struct hci_dev *hdev) +{ +	struct btusb_data *data = dev_get_drvdata(&hdev->dev); +	struct usb_device *udev = interface_to_usbdev(data->intf); +	const struct firmware *fw; +	int ret; + +	BT_INFO("%s: rtl: loading rtl_bt/rtl8723a_fw.bin", hdev->name); +	ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &udev->dev); +	if (ret < 0) { +		BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name); +		return ret; +	} + +	if (fw->size < 8) { +		ret = -EINVAL; +		goto out; +	} + +	/* Check that the firmware doesn't have the epatch signature +	 * (which is only for RTL8723B and newer). +	 */ +	if (!memcmp(fw->data, RTL_EPATCH_SIGNATURE, 8)) { +		BT_ERR("%s: unexpected EPATCH signature!", hdev->name); +		ret = -EINVAL; +		goto out; +	} + +	ret = rtl_download_firmware(hdev, fw->data, fw->size); + +out: +	release_firmware(fw); +	return ret; +} + +static int btusb_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, +				const char *fw_name) +{ +	struct btusb_data *data = dev_get_drvdata(&hdev->dev); +	struct usb_device *udev = interface_to_usbdev(data->intf); +	unsigned char *fw_data = NULL; +	const struct firmware *fw; +	int ret; + +	BT_INFO("%s: rtl: loading %s", hdev->name, fw_name); +	ret = request_firmware(&fw, fw_name, &udev->dev); +	if (ret < 0) { +		BT_ERR("%s: Failed to load %s", hdev->name, fw_name); +		return ret; +	} + +	ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data); +	if (ret < 0) +		goto out; + +	ret = rtl_download_firmware(hdev, fw_data, ret); +	kfree(fw_data); +	if (ret < 0) +		goto out; + +out: +	release_firmware(fw); +	return ret; +} + +static int btusb_setup_realtek(struct hci_dev *hdev) +{ +	struct sk_buff *skb; +	struct hci_rp_read_local_version *resp; +	u16 lmp_subver; + +	skb = btusb_read_local_version(hdev); +	if (IS_ERR(skb)) +		return -PTR_ERR(skb); + +	resp = (struct hci_rp_read_local_version *)skb->data; +	BT_INFO("%s: rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x " +		"lmp_subver=%04x", hdev->name, resp->hci_ver, resp->hci_rev, +		resp->lmp_ver, resp->lmp_subver); + +	lmp_subver = le16_to_cpu(resp->lmp_subver); +	kfree_skb(skb); + +	/* Match a set of subver values that correspond to stock firmware, +	 * which is not compatible with standard btusb. +	 * If matched, upload an alternative firmware that does conform to +	 * standard btusb. Once that firmware is uploaded, the subver changes +	 * to a different value. +	 */ +	switch (lmp_subver) { +	case RTL_ROM_LMP_8723A: +	case RTL_ROM_LMP_3499: +		return btusb_setup_rtl8723a(hdev); +	case RTL_ROM_LMP_8723B: +		return btusb_setup_rtl8723b(hdev, lmp_subver, +					    "rtl_bt/rtl8723b_fw.bin"); +	case RTL_ROM_LMP_8821A: +		return btusb_setup_rtl8723b(hdev, lmp_subver, +					    "rtl_bt/rtl8821a_fw.bin"); +	case RTL_ROM_LMP_8761A: +		return btusb_setup_rtl8723b(hdev, lmp_subver, +					    "rtl_bt/rtl8761a_fw.bin"); +	default: +		BT_INFO("rtl: assuming no firmware upload needed."); +		return 0; +	} +} +  static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,  						       struct intel_version *ver)  { @@ -2577,7 +2976,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)  	int i, err;  	err = btusb_qca_send_vendor_req(hdev, QCA_GET_TARGET_VERSION, &ver, -				        sizeof(ver)); +					sizeof(ver));  	if (err < 0)  		return err; @@ -2776,6 +3175,9 @@ static int btusb_probe(struct usb_interface *intf,  		hdev->set_bdaddr = btusb_set_bdaddr_ath3012;  	} +	if (id->driver_info & BTUSB_REALTEK) +		hdev->setup = btusb_setup_realtek; +  	if (id->driver_info & BTUSB_AMP) {  		/* AMP controllers do not support SCO packets */  		data->isoc = NULL; diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index 1b3f8647ea2f..ec8fa0e0f036 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -95,7 +95,6 @@ static void ath_hci_uart_work(struct work_struct *work)  	hci_uart_tx_wakeup(hu);  } -/* Initialize protocol */  static int ath_open(struct hci_uart *hu)  {  	struct ath_struct *ath; @@ -116,8 +115,7 @@ static int ath_open(struct hci_uart *hu)  	return 0;  } -/* Flush protocol data */ -static int ath_flush(struct hci_uart *hu) +static int ath_close(struct hci_uart *hu)  {  	struct ath_struct *ath = hu->priv; @@ -125,11 +123,17 @@ static int ath_flush(struct hci_uart *hu)  	skb_queue_purge(&ath->txq); +	kfree_skb(ath->rx_skb); + +	cancel_work_sync(&ath->ctxtsw); + +	hu->priv = NULL; +	kfree(ath); +  	return 0;  } -/* Close protocol */ -static int ath_close(struct hci_uart *hu) +static int ath_flush(struct hci_uart *hu)  {  	struct ath_struct *ath = hu->priv; @@ -137,19 +141,65 @@ static int ath_close(struct hci_uart *hu)  	skb_queue_purge(&ath->txq); -	kfree_skb(ath->rx_skb); +	return 0; +} -	cancel_work_sync(&ath->ctxtsw); +static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ +	struct sk_buff *skb; +	u8 buf[10]; +	int err; + +	buf[0] = 0x01; +	buf[1] = 0x01; +	buf[2] = 0x00; +	buf[3] = sizeof(bdaddr_t); +	memcpy(buf + 4, bdaddr, sizeof(bdaddr_t)); + +	skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT); +	if (IS_ERR(skb)) { +		err = PTR_ERR(skb); +		BT_ERR("%s: Change address command failed (%d)", +		       hdev->name, err); +		return err; +	} +	kfree_skb(skb); -	hu->priv = NULL; -	kfree(ath); +	return 0; +} + +static int ath_setup(struct hci_uart *hu) +{ +	BT_DBG("hu %p", hu); + +	hu->hdev->set_bdaddr = ath_set_bdaddr;  	return 0;  } +static const struct h4_recv_pkt ath_recv_pkts[] = { +	{ H4_RECV_ACL,   .recv = hci_recv_frame }, +	{ H4_RECV_SCO,   .recv = hci_recv_frame }, +	{ H4_RECV_EVENT, .recv = hci_recv_frame }, +}; + +static int ath_recv(struct hci_uart *hu, const void *data, int count) +{ +	struct ath_struct *ath = hu->priv; + +	ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count, +				  ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); +	if (IS_ERR(ath->rx_skb)) { +		int err = PTR_ERR(ath->rx_skb); +		BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); +		return err; +	} + +	return count; +} +  #define HCI_OP_ATH_SLEEP 0xFC04 -/* Enqueue frame for transmittion */  static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)  {  	struct ath_struct *ath = hu->priv; @@ -159,8 +209,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)  		return 0;  	} -	/* -	 * Update power management enable flag with parameters of +	/* Update power management enable flag with parameters of  	 * HCI sleep enable vendor specific HCI command.  	 */  	if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { @@ -190,37 +239,16 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu)  	return skb_dequeue(&ath->txq);  } -static const struct h4_recv_pkt ath_recv_pkts[] = { -	{ H4_RECV_ACL,   .recv = hci_recv_frame }, -	{ H4_RECV_SCO,   .recv = hci_recv_frame }, -	{ H4_RECV_EVENT, .recv = hci_recv_frame }, -}; - -/* Recv data */ -static int ath_recv(struct hci_uart *hu, const void *data, int count) -{ -	struct ath_struct *ath = hu->priv; - -	ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count, -				  ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); -	if (IS_ERR(ath->rx_skb)) { -		int err = PTR_ERR(ath->rx_skb); -		BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); -		return err; -	} - -	return count; -} -  static const struct hci_uart_proto athp = {  	.id		= HCI_UART_ATH3K,  	.name		= "ATH3K",  	.open		= ath_open,  	.close		= ath_close, +	.flush		= ath_flush, +	.setup		= ath_setup,  	.recv		= ath_recv,  	.enqueue	= ath_enqueue,  	.dequeue	= ath_dequeue, -	.flush		= ath_flush,  };  int __init ath_init(void) |