diff options
Diffstat (limited to 'drivers/bluetooth/btusb.c')
-rw-r--r-- | drivers/bluetooth/btusb.c | 155 |
1 files changed, 154 insertions, 1 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a7dfbf9a3afb..a1c80b0c7663 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c | |||
@@ -49,6 +49,7 @@ static struct usb_driver btusb_driver; | |||
49 | #define BTUSB_WRONG_SCO_MTU 0x40 | 49 | #define BTUSB_WRONG_SCO_MTU 0x40 |
50 | #define BTUSB_ATH3012 0x80 | 50 | #define BTUSB_ATH3012 0x80 |
51 | #define BTUSB_INTEL 0x100 | 51 | #define BTUSB_INTEL 0x100 |
52 | #define BTUSB_BCM_PATCHRAM 0x200 | ||
52 | 53 | ||
53 | static const struct usb_device_id btusb_table[] = { | 54 | static const struct usb_device_id btusb_table[] = { |
54 | /* Generic Bluetooth USB device */ | 55 | /* Generic Bluetooth USB device */ |
@@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = { | |||
111 | { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) }, | 112 | { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) }, |
112 | 113 | ||
113 | /* Broadcom devices with vendor specific id */ | 114 | /* Broadcom devices with vendor specific id */ |
114 | { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) }, | 115 | { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01), |
116 | .driver_info = BTUSB_BCM_PATCHRAM }, | ||
115 | 117 | ||
116 | /* Belkin F8065bf - Broadcom based */ | 118 | /* Belkin F8065bf - Broadcom based */ |
117 | { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) }, | 119 | { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) }, |
@@ -1381,6 +1383,154 @@ exit_mfg_deactivate: | |||
1381 | return 0; | 1383 | return 0; |
1382 | } | 1384 | } |
1383 | 1385 | ||
1386 | static int btusb_setup_bcm_patchram(struct hci_dev *hdev) | ||
1387 | { | ||
1388 | struct btusb_data *data = hci_get_drvdata(hdev); | ||
1389 | struct usb_device *udev = data->udev; | ||
1390 | char fw_name[64]; | ||
1391 | const struct firmware *fw; | ||
1392 | const u8 *fw_ptr; | ||
1393 | size_t fw_size; | ||
1394 | const struct hci_command_hdr *cmd; | ||
1395 | const u8 *cmd_param; | ||
1396 | u16 opcode; | ||
1397 | struct sk_buff *skb; | ||
1398 | struct hci_rp_read_local_version *ver; | ||
1399 | long ret; | ||
1400 | |||
1401 | snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd", | ||
1402 | udev->product ? udev->product : "BCM", | ||
1403 | le16_to_cpu(udev->descriptor.idVendor), | ||
1404 | le16_to_cpu(udev->descriptor.idProduct)); | ||
1405 | |||
1406 | ret = request_firmware(&fw, fw_name, &hdev->dev); | ||
1407 | if (ret < 0) { | ||
1408 | BT_INFO("%s: BCM: patch %s not found", hdev->name, | ||
1409 | fw_name); | ||
1410 | return 0; | ||
1411 | } | ||
1412 | |||
1413 | /* Reset */ | ||
1414 | skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); | ||
1415 | if (IS_ERR(skb)) { | ||
1416 | ret = PTR_ERR(skb); | ||
1417 | BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); | ||
1418 | goto done; | ||
1419 | } | ||
1420 | kfree_skb(skb); | ||
1421 | |||
1422 | /* Read Local Version Info */ | ||
1423 | skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, | ||
1424 | HCI_INIT_TIMEOUT); | ||
1425 | if (IS_ERR(skb)) { | ||
1426 | ret = PTR_ERR(skb); | ||
1427 | BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", | ||
1428 | hdev->name, ret); | ||
1429 | goto done; | ||
1430 | } | ||
1431 | |||
1432 | if (skb->len != sizeof(*ver)) { | ||
1433 | BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", | ||
1434 | hdev->name); | ||
1435 | kfree_skb(skb); | ||
1436 | ret = -EIO; | ||
1437 | goto done; | ||
1438 | } | ||
1439 | |||
1440 | ver = (struct hci_rp_read_local_version *) skb->data; | ||
1441 | BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x " | ||
1442 | "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, | ||
1443 | ver->lmp_ver, ver->lmp_subver); | ||
1444 | kfree_skb(skb); | ||
1445 | |||
1446 | /* Start Download */ | ||
1447 | skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); | ||
1448 | if (IS_ERR(skb)) { | ||
1449 | ret = PTR_ERR(skb); | ||
1450 | BT_ERR("%s: BCM: Download Minidrv command failed (%ld)", | ||
1451 | hdev->name, ret); | ||
1452 | goto reset_fw; | ||
1453 | } | ||
1454 | kfree_skb(skb); | ||
1455 | |||
1456 | /* 50 msec delay after Download Minidrv completes */ | ||
1457 | msleep(50); | ||
1458 | |||
1459 | fw_ptr = fw->data; | ||
1460 | fw_size = fw->size; | ||
1461 | |||
1462 | while (fw_size >= sizeof(*cmd)) { | ||
1463 | cmd = (struct hci_command_hdr *) fw_ptr; | ||
1464 | fw_ptr += sizeof(*cmd); | ||
1465 | fw_size -= sizeof(*cmd); | ||
1466 | |||
1467 | if (fw_size < cmd->plen) { | ||
1468 | BT_ERR("%s: BCM: patch %s is corrupted", | ||
1469 | hdev->name, fw_name); | ||
1470 | ret = -EINVAL; | ||
1471 | goto reset_fw; | ||
1472 | } | ||
1473 | |||
1474 | cmd_param = fw_ptr; | ||
1475 | fw_ptr += cmd->plen; | ||
1476 | fw_size -= cmd->plen; | ||
1477 | |||
1478 | opcode = le16_to_cpu(cmd->opcode); | ||
1479 | |||
1480 | skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, | ||
1481 | HCI_INIT_TIMEOUT); | ||
1482 | if (IS_ERR(skb)) { | ||
1483 | ret = PTR_ERR(skb); | ||
1484 | BT_ERR("%s: BCM: patch command %04x failed (%ld)", | ||
1485 | hdev->name, opcode, ret); | ||
1486 | goto reset_fw; | ||
1487 | } | ||
1488 | kfree_skb(skb); | ||
1489 | } | ||
1490 | |||
1491 | /* 250 msec delay after Launch Ram completes */ | ||
1492 | msleep(250); | ||
1493 | |||
1494 | reset_fw: | ||
1495 | /* Reset */ | ||
1496 | skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); | ||
1497 | if (IS_ERR(skb)) { | ||
1498 | ret = PTR_ERR(skb); | ||
1499 | BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); | ||
1500 | goto done; | ||
1501 | } | ||
1502 | kfree_skb(skb); | ||
1503 | |||
1504 | /* Read Local Version Info */ | ||
1505 | skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, | ||
1506 | HCI_INIT_TIMEOUT); | ||
1507 | if (IS_ERR(skb)) { | ||
1508 | ret = PTR_ERR(skb); | ||
1509 | BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", | ||
1510 | hdev->name, ret); | ||
1511 | goto done; | ||
1512 | } | ||
1513 | |||
1514 | if (skb->len != sizeof(*ver)) { | ||
1515 | BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", | ||
1516 | hdev->name); | ||
1517 | kfree_skb(skb); | ||
1518 | ret = -EIO; | ||
1519 | goto done; | ||
1520 | } | ||
1521 | |||
1522 | ver = (struct hci_rp_read_local_version *) skb->data; | ||
1523 | BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x " | ||
1524 | "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, | ||
1525 | ver->lmp_ver, ver->lmp_subver); | ||
1526 | kfree_skb(skb); | ||
1527 | |||
1528 | done: | ||
1529 | release_firmware(fw); | ||
1530 | |||
1531 | return ret; | ||
1532 | } | ||
1533 | |||
1384 | static int btusb_probe(struct usb_interface *intf, | 1534 | static int btusb_probe(struct usb_interface *intf, |
1385 | const struct usb_device_id *id) | 1535 | const struct usb_device_id *id) |
1386 | { | 1536 | { |
@@ -1486,6 +1636,9 @@ static int btusb_probe(struct usb_interface *intf, | |||
1486 | if (id->driver_info & BTUSB_BCM92035) | 1636 | if (id->driver_info & BTUSB_BCM92035) |
1487 | hdev->setup = btusb_setup_bcm92035; | 1637 | hdev->setup = btusb_setup_bcm92035; |
1488 | 1638 | ||
1639 | if (id->driver_info & BTUSB_BCM_PATCHRAM) | ||
1640 | hdev->setup = btusb_setup_bcm_patchram; | ||
1641 | |||
1489 | if (id->driver_info & BTUSB_INTEL) | 1642 | if (id->driver_info & BTUSB_INTEL) |
1490 | hdev->setup = btusb_setup_intel; | 1643 | hdev->setup = btusb_setup_intel; |
1491 | 1644 | ||