From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> During setup callback the code would attempt to send Read Version which sometimes can fail due to ncmd being set to 0 which would block any command from being send which is why INTEL_BROKEN_INITIAL_NCMD was introduced. Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> --- drivers/bluetooth/btintel.c | 70 ++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index a19ebe47bd95..b787540a1031 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -430,18 +430,18 @@ int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug) } EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg); +static struct sk_buff *btintel_read_version_skb(struct hci_dev *hdev, + u32 plen, const char *param) +{ + return __hci_cmd_sync(hdev, 0xfc05, plen, param, HCI_CMD_TIMEOUT); +} + int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver) { struct sk_buff *skb; - skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { - bt_dev_err(hdev, "Reading Intel version information failed (%ld)", - PTR_ERR(skb)); - return PTR_ERR(skb); - } - - if (!skb || skb->len != sizeof(*ver)) { + skb = btintel_read_version_skb(hdev, 0, NULL); + if (IS_ERR(skb) || skb->len != sizeof(*ver)) { bt_dev_err(hdev, "Intel version event size mismatch"); kfree_skb(skb); return -EILSEQ; @@ -655,12 +655,9 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, struct sk_buff *skb; const u8 param[1] = { 0xFF }; - if (!version) - return -EINVAL; - - skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT); + skb = btintel_read_version_skb(hdev, sizeof(param), param); if (IS_ERR(skb)) { - bt_dev_err(hdev, "Reading Intel version information failed (%ld)", + bt_dev_err(hdev, "Intel Read Version command failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } @@ -672,12 +669,44 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, return -EIO; } - btintel_parse_version_tlv(hdev, version, skb); + if (version) + btintel_parse_version_tlv(hdev, version, skb); kfree_skb(skb); return 0; } +static struct sk_buff *btintel_read_version_setup(struct hci_dev *hdev) +{ + int err; + struct sk_buff *skb; + const u8 param[1] = { 0xFF }; + + skb = btintel_read_version_skb(hdev, sizeof(param), param); + if (!IS_ERR(skb)) + goto done; + + /* Attempt to reset if the command times out since this could be + * because the ncmd is set to 0 therefore no command will be able to be + * sent. + */ + err = hci_reset_sync(hdev); + if (err) + return ERR_PTR(err); + + skb = btintel_read_version_skb(hdev, sizeof(param), param); + if (IS_ERR(skb)) + return skb; + +done: + if (skb->data[0]) { + kfree_skb(skb); + return ERR_PTR(-EIO); + } + + return skb; +} + /* ------- REGMAP IBT SUPPORT ------- */ #define IBT_REG_MODE_8BIT 0x00 @@ -2821,7 +2850,6 @@ static void btintel_print_fseq_info(struct hci_dev *hdev) static int btintel_setup_combined(struct hci_dev *hdev) { - const u8 param[1] = { 0xFF }; struct intel_version ver; struct intel_version_tlv ver_tlv; struct sk_buff *skb; @@ -2862,18 +2890,10 @@ static int btintel_setup_combined(struct hci_dev *hdev) * command has a parameter and returns a correct version information. * So, it uses new format to support both legacy and new format. */ - skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT); + skb = btintel_read_version_setup(hdev); if (IS_ERR(skb)) { - bt_dev_err(hdev, "Reading Intel version command failed (%ld)", + bt_dev_err(hdev, "Intel Read Version command failed (%ld)", PTR_ERR(skb)); - return PTR_ERR(skb); - } - - /* Check the status */ - if (skb->data[0]) { - bt_dev_err(hdev, "Intel Read Version command failed (%02x)", - skb->data[0]); - err = -EIO; goto exit_error; } -- 2.44.0