Commit 50862ee5 authored by Marcel Holtmann's avatar Marcel Holtmann

Bluetooth: btbcm: Export patchram download as separate function

This isolates the Broadcom patchram download procedure as separate
function so that it can be easily used from USB and UART based drivers.
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent 16e5c47a
...@@ -95,6 +95,78 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) ...@@ -95,6 +95,78 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
} }
EXPORT_SYMBOL_GPL(btbcm_set_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) static int btbcm_reset(struct hci_dev *hdev)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -198,12 +270,8 @@ static const struct { ...@@ -198,12 +270,8 @@ static const struct {
int btbcm_setup_patchram(struct hci_dev *hdev) 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]; char fw_name[64];
u16 opcode, subver, rev, pid, vid; u16 subver, rev, pid, vid;
const char *hw_name = NULL; const char *hw_name = NULL;
struct sk_buff *skb; struct sk_buff *skb;
struct hci_rp_read_local_version *ver; struct hci_rp_read_local_version *ver;
...@@ -273,74 +341,19 @@ int btbcm_setup_patchram(struct hci_dev *hdev) ...@@ -273,74 +341,19 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
hw_name ? : "BCM", (subver & 0x7000) >> 13, hw_name ? : "BCM", (subver & 0x7000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
err = request_firmware(&fw, fw_name, &hdev->dev); err = btbcm_patchram(hdev, fw_name);
if (err < 0) { if (err == -ENOENT)
BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
return 0; 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 */ /* Reset */
err = btbcm_reset(hdev); err = btbcm_reset(hdev);
if (err) if (err)
goto done; return err;
/* Read Local Version Info */ /* Read Local Version Info */
skb = btbcm_read_local_version(hdev); skb = btbcm_read_local_version(hdev);
if (IS_ERR(skb)) { if (IS_ERR(skb))
err = PTR_ERR(skb); return PTR_ERR(skb);
goto done;
}
ver = (struct hci_rp_read_local_version *)skb->data; ver = (struct hci_rp_read_local_version *)skb->data;
rev = le16_to_cpu(ver->hci_rev); rev = le16_to_cpu(ver->hci_rev);
...@@ -355,10 +368,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev) ...@@ -355,10 +368,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
done: return 0;
release_firmware(fw);
return err;
} }
EXPORT_SYMBOL_GPL(btbcm_setup_patchram); EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
int btbcm_check_bdaddr(struct hci_dev *hdev); int btbcm_check_bdaddr(struct hci_dev *hdev);
int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); 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_patchram(struct hci_dev *hdev);
int btbcm_setup_apple(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) ...@@ -41,6 +42,11 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
return -EOPNOTSUPP; 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) static inline int btbcm_setup_patchram(struct hci_dev *hdev)
{ {
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment