@@ -49,6 +49,7 @@ static struct usb_driver btusb_driver;
49
49
#define BTUSB_WRONG_SCO_MTU 0x40
50
50
#define BTUSB_ATH3012 0x80
51
51
#define BTUSB_INTEL 0x100
52
+ #define BTUSB_BCM_PATCHRAM 0x200
52
53
53
54
static const struct usb_device_id btusb_table [] = {
54
55
/* Generic Bluetooth USB device */
@@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = {
111
112
{ USB_VENDOR_AND_INTERFACE_INFO (0x0489 , 0xff , 0x01 , 0x01 ) },
112
113
113
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
118
/* Belkin F8065bf - Broadcom based */
117
119
{ USB_VENDOR_AND_INTERFACE_INFO (0x050d , 0xff , 0x01 , 0x01 ) },
@@ -1381,6 +1383,154 @@ static int btusb_setup_intel(struct hci_dev *hdev)
1381
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
1534
static int btusb_probe (struct usb_interface * intf ,
1385
1535
const struct usb_device_id * id )
1386
1536
{
@@ -1486,6 +1636,9 @@ static int btusb_probe(struct usb_interface *intf,
1486
1636
if (id -> driver_info & BTUSB_BCM92035 )
1487
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
1642
if (id -> driver_info & BTUSB_INTEL )
1490
1643
hdev -> setup = btusb_setup_intel ;
1491
1644
0 commit comments