Skip to content

Commit

Permalink
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/linville/wireless-next into for-davem
  • Loading branch information
John W. Linville committed Jun 2, 2014
2 parents 96b2e73 + a715c7d commit fcb2c0d
Show file tree
Hide file tree
Showing 88 changed files with 2,174 additions and 997 deletions.
2 changes: 1 addition & 1 deletion Documentation/devicetree/bindings/net/nfc/st21nfca.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
* STMicroelectronics SAS. ST21NFCA NFC Controller

Required properties:
- compatible: Should be "st,st21nfca-i2c".
- compatible: Should be "st,st21nfca_i2c".
- clock-frequency: I²C work frequency.
- reg: address on the bus
- interrupt-parent: phandle for the interrupt gpio controller
Expand Down
155 changes: 154 additions & 1 deletion drivers/bluetooth/btusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_WRONG_SCO_MTU 0x40
#define BTUSB_ATH3012 0x80
#define BTUSB_INTEL 0x100
#define BTUSB_BCM_PATCHRAM 0x200

static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
Expand Down Expand Up @@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },

/* Broadcom devices with vendor specific id */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
.driver_info = BTUSB_BCM_PATCHRAM },

/* Belkin F8065bf - Broadcom based */
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
Expand Down Expand Up @@ -1381,6 +1383,154 @@ static int btusb_setup_intel(struct hci_dev *hdev)
return 0;
}

static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct usb_device *udev = data->udev;
char fw_name[64];
const struct firmware *fw;
const u8 *fw_ptr;
size_t fw_size;
const struct hci_command_hdr *cmd;
const u8 *cmd_param;
u16 opcode;
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
long ret;

snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd",
udev->product ? udev->product : "BCM",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));

ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_INFO("%s: BCM: patch %s not found", hdev->name,
fw_name);
return 0;
}

/* Reset */
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
goto done;
}
kfree_skb(skb);

/* Read Local Version Info */
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
hdev->name, ret);
goto done;
}

if (skb->len != sizeof(*ver)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
hdev->name);
kfree_skb(skb);
ret = -EIO;
goto done;
}

ver = (struct hci_rp_read_local_version *) skb->data;
BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
ver->lmp_ver, ver->lmp_subver);
kfree_skb(skb);

/* Start Download */
skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
hdev->name, ret);
goto reset_fw;
}
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)) {
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);
ret = -EINVAL;
goto reset_fw;
}

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)) {
ret = PTR_ERR(skb);
BT_ERR("%s: BCM: patch command %04x failed (%ld)",
hdev->name, opcode, ret);
goto reset_fw;
}
kfree_skb(skb);
}

/* 250 msec delay after Launch Ram completes */
msleep(250);

reset_fw:
/* Reset */
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
goto done;
}
kfree_skb(skb);

/* Read Local Version Info */
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
hdev->name, ret);
goto done;
}

if (skb->len != sizeof(*ver)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
hdev->name);
kfree_skb(skb);
ret = -EIO;
goto done;
}

ver = (struct hci_rp_read_local_version *) skb->data;
BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
ver->lmp_ver, ver->lmp_subver);
kfree_skb(skb);

done:
release_firmware(fw);

return ret;
}

static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
Expand Down Expand Up @@ -1486,6 +1636,9 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_BCM92035)
hdev->setup = btusb_setup_bcm92035;

if (id->driver_info & BTUSB_BCM_PATCHRAM)
hdev->setup = btusb_setup_bcm_patchram;

if (id->driver_info & BTUSB_INTEL)
hdev->setup = btusb_setup_intel;

Expand Down
Loading

0 comments on commit fcb2c0d

Please sign in to comment.