diff options
| author | Marcel Holtmann <marcel@holtmann.org> | 2015-04-06 01:52:13 -0400 |
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2015-04-07 12:47:11 -0400 |
| commit | 1c8ba6d013c553bd13c50c139d652daac3348685 (patch) | |
| tree | 3e443a70dd7b9b56789ce1560faae3d4a79ab826 /drivers/bluetooth | |
| parent | 3e0ac12a1a610b4ab47282a25ee5945064228e35 (diff) | |
Bluetooth: btbcm: Add support for Broadcom controller setup
To unify the controller setup of Broadcom devices between USB and UART
transport, add the patchram download support into the Broadcom module.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Diffstat (limited to 'drivers/bluetooth')
| -rw-r--r-- | drivers/bluetooth/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/bluetooth/btbcm.c | 258 | ||||
| -rw-r--r-- | drivers/bluetooth/btbcm.h | 13 |
3 files changed, 272 insertions, 0 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 9f68e11c1671..0801649a3b41 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig | |||
| @@ -4,6 +4,7 @@ menu "Bluetooth device drivers" | |||
| 4 | 4 | ||
| 5 | config BT_BCM | 5 | config BT_BCM |
| 6 | tristate | 6 | tristate |
| 7 | select FW_LOADER | ||
| 7 | 8 | ||
| 8 | config BT_HCIBTUSB | 9 | config BT_HCIBTUSB |
| 9 | tristate "HCI USB driver" | 10 | tristate "HCI USB driver" |
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 20c744a9a3f1..17565ab610f4 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c | |||
| @@ -22,6 +22,8 @@ | |||
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
| 25 | #include <linux/firmware.h> | ||
| 26 | #include <asm/unaligned.h> | ||
| 25 | 27 | ||
| 26 | #include <net/bluetooth/bluetooth.h> | 28 | #include <net/bluetooth/bluetooth.h> |
| 27 | #include <net/bluetooth/hci_core.h> | 29 | #include <net/bluetooth/hci_core.h> |
| @@ -93,6 +95,262 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) | |||
| 93 | } | 95 | } |
| 94 | EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); | 96 | EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); |
| 95 | 97 | ||
| 98 | static int btbcm_reset(struct hci_dev *hdev) | ||
| 99 | { | ||
| 100 | struct sk_buff *skb; | ||
| 101 | |||
| 102 | skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); | ||
| 103 | if (IS_ERR(skb)) { | ||
| 104 | int err = PTR_ERR(skb); | ||
| 105 | BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err); | ||
| 106 | return err; | ||
| 107 | } | ||
| 108 | kfree_skb(skb); | ||
| 109 | |||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev) | ||
| 114 | { | ||
| 115 | struct sk_buff *skb; | ||
| 116 | |||
| 117 | skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, | ||
| 118 | HCI_INIT_TIMEOUT); | ||
| 119 | if (IS_ERR(skb)) { | ||
| 120 | BT_ERR("%s: BCM: Reading local version info failed (%ld)", | ||
| 121 | hdev->name, PTR_ERR(skb)); | ||
| 122 | return skb; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (skb->len != sizeof(struct hci_rp_read_local_version)) { | ||
| 126 | BT_ERR("%s: BCM: Local version length mismatch", hdev->name); | ||
| 127 | kfree_skb(skb); | ||
| 128 | return ERR_PTR(-EIO); | ||
| 129 | } | ||
| 130 | |||
| 131 | return skb; | ||
| 132 | } | ||
| 133 | |||
| 134 | static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev) | ||
| 135 | { | ||
| 136 | struct sk_buff *skb; | ||
| 137 | |||
| 138 | skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); | ||
| 139 | if (IS_ERR(skb)) { | ||
| 140 | BT_ERR("%s: BCM: Read verbose config info failed (%ld)", | ||
| 141 | hdev->name, PTR_ERR(skb)); | ||
| 142 | return skb; | ||
| 143 | } | ||
| 144 | |||
| 145 | if (skb->len != 7) { | ||
| 146 | BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name); | ||
| 147 | kfree_skb(skb); | ||
| 148 | return ERR_PTR(-EIO); | ||
| 149 | } | ||
| 150 | |||
| 151 | return skb; | ||
| 152 | } | ||
| 153 | |||
| 154 | static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev) | ||
| 155 | { | ||
| 156 | struct sk_buff *skb; | ||
| 157 | |||
| 158 | skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT); | ||
| 159 | if (IS_ERR(skb)) { | ||
| 160 | BT_ERR("%s: BCM: Read USB product info failed (%ld)", | ||
| 161 | hdev->name, PTR_ERR(skb)); | ||
| 162 | return skb; | ||
| 163 | } | ||
| 164 | |||
| 165 | if (skb->len != 5) { | ||
| 166 | BT_ERR("%s: BCM: USB product length mismatch", hdev->name); | ||
| 167 | kfree_skb(skb); | ||
| 168 | return ERR_PTR(-EIO); | ||
| 169 | } | ||
| 170 | |||
| 171 | return skb; | ||
| 172 | } | ||
| 173 | |||
| 174 | static const struct { | ||
| 175 | u16 subver; | ||
| 176 | const char *name; | ||
| 177 | } bcm_subver_table[] = { | ||
| 178 | { 0x210b, "BCM43142A0" }, /* 001.001.011 */ | ||
| 179 | { 0x2112, "BCM4314A0" }, /* 001.001.018 */ | ||
| 180 | { 0x2118, "BCM20702A0" }, /* 001.001.024 */ | ||
| 181 | { 0x2126, "BCM4335A0" }, /* 001.001.038 */ | ||
| 182 | { 0x220e, "BCM20702A1" }, /* 001.002.014 */ | ||
| 183 | { 0x230f, "BCM4354A2" }, /* 001.003.015 */ | ||
| 184 | { 0x4106, "BCM4335B0" }, /* 002.001.006 */ | ||
| 185 | { 0x410e, "BCM20702B0" }, /* 002.001.014 */ | ||
| 186 | { 0x6109, "BCM4335C0" }, /* 003.001.009 */ | ||
| 187 | { 0x610c, "BCM4354" }, /* 003.001.012 */ | ||
| 188 | { } | ||
| 189 | }; | ||
| 190 | |||
| 191 | int btbcm_setup_patchram(struct hci_dev *hdev) | ||
| 192 | { | ||
| 193 | const struct hci_command_hdr *cmd; | ||
| 194 | const struct firmware *fw; | ||
| 195 | const u8 *fw_ptr; | ||
| 196 | size_t fw_size; | ||
| 197 | char fw_name[64]; | ||
| 198 | u16 opcode, subver, rev, pid, vid; | ||
| 199 | const char *hw_name = NULL; | ||
| 200 | struct sk_buff *skb; | ||
| 201 | struct hci_rp_read_local_version *ver; | ||
| 202 | int i, err; | ||
| 203 | |||
| 204 | /* Reset */ | ||
| 205 | err = btbcm_reset(hdev); | ||
| 206 | if (err) | ||
| 207 | return err; | ||
| 208 | |||
| 209 | /* Read Local Version Info */ | ||
| 210 | skb = btbcm_read_local_version(hdev); | ||
| 211 | if (IS_ERR(skb)) | ||
| 212 | return PTR_ERR(skb); | ||
| 213 | |||
| 214 | ver = (struct hci_rp_read_local_version *)skb->data; | ||
| 215 | rev = le16_to_cpu(ver->hci_rev); | ||
| 216 | subver = le16_to_cpu(ver->lmp_subver); | ||
| 217 | kfree_skb(skb); | ||
| 218 | |||
| 219 | /* Read Verbose Config Version Info */ | ||
| 220 | skb = btbcm_read_verbose_config(hdev); | ||
| 221 | if (IS_ERR(skb)) | ||
| 222 | return PTR_ERR(skb); | ||
| 223 | |||
| 224 | BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); | ||
| 225 | kfree_skb(skb); | ||
| 226 | |||
| 227 | /* Read USB Product Info */ | ||
| 228 | skb = btbcm_read_usb_product(hdev); | ||
| 229 | if (IS_ERR(skb)) | ||
| 230 | return PTR_ERR(skb); | ||
| 231 | |||
| 232 | vid = get_unaligned_le16(skb->data + 1); | ||
| 233 | pid = get_unaligned_le16(skb->data + 3); | ||
| 234 | kfree_skb(skb); | ||
| 235 | |||
| 236 | for (i = 0; bcm_subver_table[i].name; i++) { | ||
| 237 | if (subver == bcm_subver_table[i].subver) { | ||
| 238 | hw_name = bcm_subver_table[i].name; | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, | ||
| 244 | hw_name ? : "BCM", (subver & 0x7000) >> 13, | ||
| 245 | (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); | ||
| 246 | |||
| 247 | snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd", | ||
| 248 | hw_name ? : "BCM", vid, pid); | ||
| 249 | |||
| 250 | err = request_firmware(&fw, fw_name, &hdev->dev); | ||
| 251 | if (err < 0) { | ||
| 252 | BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name); | ||
| 253 | return 0; | ||
| 254 | } | ||
| 255 | |||
| 256 | /* Start Download */ | ||
| 257 | skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); | ||
| 258 | if (IS_ERR(skb)) { | ||
| 259 | err = PTR_ERR(skb); | ||
| 260 | BT_ERR("%s: BCM: Download Minidrv command failed (%d)", | ||
| 261 | hdev->name, err); | ||
| 262 | goto reset; | ||
| 263 | } | ||
| 264 | kfree_skb(skb); | ||
| 265 | |||
| 266 | /* 50 msec delay after Download Minidrv completes */ | ||
| 267 | msleep(50); | ||
| 268 | |||
| 269 | fw_ptr = fw->data; | ||
| 270 | fw_size = fw->size; | ||
| 271 | |||
| 272 | while (fw_size >= sizeof(*cmd)) { | ||
| 273 | const u8 *cmd_param; | ||
| 274 | |||
| 275 | cmd = (struct hci_command_hdr *)fw_ptr; | ||
| 276 | fw_ptr += sizeof(*cmd); | ||
| 277 | fw_size -= sizeof(*cmd); | ||
| 278 | |||
| 279 | if (fw_size < cmd->plen) { | ||
| 280 | BT_ERR("%s: BCM: patch %s is corrupted", hdev->name, | ||
| 281 | fw_name); | ||
| 282 | err = -EINVAL; | ||
| 283 | goto reset; | ||
| 284 | } | ||
| 285 | |||
| 286 | cmd_param = fw_ptr; | ||
| 287 | fw_ptr += cmd->plen; | ||
| 288 | fw_size -= cmd->plen; | ||
| 289 | |||
| 290 | opcode = le16_to_cpu(cmd->opcode); | ||
| 291 | |||
| 292 | skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, | ||
| 293 | HCI_INIT_TIMEOUT); | ||
| 294 | if (IS_ERR(skb)) { | ||
| 295 | err = PTR_ERR(skb); | ||
| 296 | BT_ERR("%s: BCM: patch command %04x failed (%d)", | ||
| 297 | hdev->name, opcode, err); | ||
| 298 | goto reset; | ||
| 299 | } | ||
| 300 | kfree_skb(skb); | ||
| 301 | } | ||
| 302 | |||
| 303 | /* 250 msec delay after Launch Ram completes */ | ||
| 304 | msleep(250); | ||
| 305 | |||
| 306 | reset: | ||
| 307 | /* Reset */ | ||
| 308 | err = btbcm_reset(hdev); | ||
| 309 | if (err) | ||
| 310 | goto done; | ||
| 311 | |||
| 312 | /* Read Local Version Info */ | ||
| 313 | skb = btbcm_read_local_version(hdev); | ||
| 314 | if (IS_ERR(skb)) { | ||
| 315 | err = PTR_ERR(skb); | ||
| 316 | goto done; | ||
| 317 | } | ||
| 318 | |||
| 319 | ver = (struct hci_rp_read_local_version *)skb->data; | ||
| 320 | rev = le16_to_cpu(ver->hci_rev); | ||
| 321 | subver = le16_to_cpu(ver->lmp_subver); | ||
| 322 | kfree_skb(skb); | ||
| 323 | |||
| 324 | BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, | ||
| 325 | hw_name ? : "BCM", (subver & 0x7000) >> 13, | ||
| 326 | (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); | ||
| 327 | |||
| 328 | btbcm_check_bdaddr(hdev); | ||
| 329 | |||
| 330 | done: | ||
| 331 | release_firmware(fw); | ||
| 332 | |||
| 333 | return err; | ||
| 334 | } | ||
| 335 | EXPORT_SYMBOL_GPL(btbcm_setup_patchram); | ||
| 336 | |||
| 337 | int btbcm_setup_apple(struct hci_dev *hdev) | ||
| 338 | { | ||
| 339 | struct sk_buff *skb; | ||
| 340 | |||
| 341 | /* Read Verbose Config Version Info */ | ||
| 342 | skb = btbcm_read_verbose_config(hdev); | ||
| 343 | if (IS_ERR(skb)) | ||
| 344 | return PTR_ERR(skb); | ||
| 345 | |||
| 346 | BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1], | ||
| 347 | get_unaligned_le16(skb->data + 5)); | ||
| 348 | kfree_skb(skb); | ||
| 349 | |||
| 350 | return 0; | ||
| 351 | } | ||
| 352 | EXPORT_SYMBOL_GPL(btbcm_setup_apple); | ||
| 353 | |||
| 96 | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); | 354 | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); |
| 97 | MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION); | 355 | MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION); |
| 98 | MODULE_VERSION(VERSION); | 356 | MODULE_VERSION(VERSION); |
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h index 813f7e7e191d..34268ae3eb46 100644 --- a/drivers/bluetooth/btbcm.h +++ b/drivers/bluetooth/btbcm.h | |||
| @@ -26,6 +26,9 @@ | |||
| 26 | int btbcm_check_bdaddr(struct hci_dev *hdev); | 26 | int btbcm_check_bdaddr(struct hci_dev *hdev); |
| 27 | int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); | 27 | int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); |
| 28 | 28 | ||
| 29 | int btbcm_setup_patchram(struct hci_dev *hdev); | ||
| 30 | int btbcm_setup_apple(struct hci_dev *hdev); | ||
| 31 | |||
| 29 | #else | 32 | #else |
| 30 | 33 | ||
| 31 | static inline int btbcm_check_bdaddr(struct hci_dev *hdev) | 34 | static inline int btbcm_check_bdaddr(struct hci_dev *hdev) |
| @@ -38,4 +41,14 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) | |||
| 38 | return -EOPNOTSUPP; | 41 | return -EOPNOTSUPP; |
| 39 | } | 42 | } |
| 40 | 43 | ||
| 44 | static inline int btbcm_setup_patchram(struct hci_dev *hdev) | ||
| 45 | { | ||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline int btbcm_setup_apple(struct hci_dev *hdev) | ||
| 50 | { | ||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | |||
| 41 | #endif | 54 | #endif |
