Bluetooth: Ignore CC events not matching the last HCI command
authorJoão Paulo Rechi Vita <jprvita@gmail.com>
Thu, 2 May 2019 02:01:52 +0000 (10:01 +0800)
committerMarcel Holtmann <marcel@holtmann.org>
Sun, 5 May 2019 17:29:04 +0000 (19:29 +0200)
This commit makes the kernel not send the next queued HCI command until
a command complete arrives for the last HCI command sent to the
controller. This change avoids a problem with some buggy controllers
(seen on two SKUs of QCA9377) that send an extra command complete event
for the previous command after the kernel had already sent a new HCI
command to the controller.

The problem was reproduced when starting an active scanning procedure,
where an extra command complete event arrives for the LE_SET_RANDOM_ADDR
command. When this happends the kernel ends up not processing the
command complete for the following commmand, LE_SET_SCAN_PARAM, and
ultimately behaving as if a passive scanning procedure was being
performed, when in fact controller is performing an active scanning
procedure. This makes it impossible to discover BLE devices as no device
found events are sent to userspace.

This problem is reproducible on 100% of the attempts on the affected
controllers. The extra command complete event can be seen at timestamp
27.420131 on the btmon logs bellow.

Bluetooth monitor ver 5.50
= Note: Linux version 5.0.0+ (x86_64)                                  0.352340
= Note: Bluetooth subsystem version 2.22                               0.352343
= New Index: 80:C5:F2:8F:87:84 (Primary,USB,hci0)               [hci0] 0.352344
= Open Index: 80:C5:F2:8F:87:84                                 [hci0] 0.352345
= Index Info: 80:C5:F2:8F:87:84 (Qualcomm)                      [hci0] 0.352346
@ MGMT Open: bluetoothd (privileged) version 1.14             {0x0001} 0.352347
@ MGMT Open: btmon (privileged) version 1.14                  {0x0002} 0.352366
@ MGMT Open: btmgmt (privileged) version 1.14                {0x0003} 27.302164
@ MGMT Command: Start Discovery (0x0023) plen 1       {0x0003} [hci0] 27.302310
        Address type: 0x06
          LE Public
          LE Random
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6   #1 [hci0] 27.302496
        Address: 15:60:F2:91:B2:24 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                 #2 [hci0] 27.419117
      LE Set Random Address (0x08|0x0005) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7  #3 [hci0] 27.419244
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                 #4 [hci0] 27.420131
      LE Set Random Address (0x08|0x0005) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2      #5 [hci0] 27.420259
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                 #6 [hci0] 27.420969
      LE Set Scan Parameters (0x08|0x000b) ncmd 1
        Status: Success (0x00)
> HCI Event: Command Complete (0x0e) plen 4                 #7 [hci0] 27.421983
      LE Set Scan Enable (0x08|0x000c) ncmd 1
        Status: Success (0x00)
@ MGMT Event: Command Complete (0x0001) plen 4        {0x0003} [hci0] 27.422059
      Start Discovery (0x0023) plen 1
        Status: Success (0x00)
        Address type: 0x06
          LE Public
          LE Random
@ MGMT Event: Discovering (0x0013) plen 2             {0x0003} [hci0] 27.422067
        Address type: 0x06
          LE Public
          LE Random
        Discovery: Enabled (0x01)
@ MGMT Event: Discovering (0x0013) plen 2             {0x0002} [hci0] 27.422067
        Address type: 0x06
          LE Public
          LE Random
        Discovery: Enabled (0x01)
@ MGMT Event: Discovering (0x0013) plen 2             {0x0001} [hci0] 27.422067
        Address type: 0x06
          LE Public
          LE Random
        Discovery: Enabled (0x01)

Signed-off-by: João Paulo Rechi Vita <jprvita@endlessm.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci.h
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_request.c
net/bluetooth/hci_request.h

index fbba43e9bef5b9959626cac81248a5ac20e0f384..9a5330eed7944fca7a7b013edf1d5d99c4d93128 100644 (file)
@@ -282,6 +282,7 @@ enum {
        HCI_FORCE_BREDR_SMP,
        HCI_FORCE_STATIC_ADDR,
        HCI_LL_RPA_RESOLUTION,
+       HCI_CMD_PENDING,
 
        __HCI_NUM_FLAGS,
 };
index 3d9175f130b363ac1557b410ee2444fed101d823..b81bf53c5ac4a564491e71fcbdec4474903eaed5 100644 (file)
@@ -4381,6 +4381,9 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
                return;
        }
 
+       /* If we reach this point this event matches the last command sent */
+       hci_dev_clear_flag(hdev, HCI_CMD_PENDING);
+
        /* If the command succeeded and there's still more commands in
         * this request the request is not yet complete.
         */
@@ -4491,6 +4494,8 @@ static void hci_cmd_work(struct work_struct *work)
 
                hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
                if (hdev->sent_cmd) {
+                       if (hci_req_status_pend(hdev))
+                               hci_dev_set_flag(hdev, HCI_CMD_PENDING);
                        atomic_dec(&hdev->cmd_cnt);
                        hci_send_frame(hdev, skb);
                        if (test_bit(HCI_RESET, &hdev->flags))
index 66b631ab0d35335667dcab06cb78e5f41b49d025..9e4fcf406d9cd773e02b62998eb1cf387e3e622d 100644 (file)
@@ -3404,6 +3404,12 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
        hci_req_cmd_complete(hdev, *opcode, *status, req_complete,
                             req_complete_skb);
 
+       if (hci_dev_test_flag(hdev, HCI_CMD_PENDING)) {
+               bt_dev_err(hdev,
+                          "unexpected event for opcode 0x%4.4x", *opcode);
+               return;
+       }
+
        if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
                queue_work(hdev->workqueue, &hdev->cmd_work);
 }
@@ -3511,6 +3517,12 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
                hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
                                     req_complete_skb);
 
+       if (hci_dev_test_flag(hdev, HCI_CMD_PENDING)) {
+               bt_dev_err(hdev,
+                          "unexpected event for opcode 0x%4.4x", *opcode);
+               return;
+       }
+
        if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
                queue_work(hdev->workqueue, &hdev->cmd_work);
 }
index ca73d36cc149460e8d3eb08b9b291b1a7a396ef3..e9a95ed654915dd976f5a6bcb81af9dc764ff9ba 100644 (file)
@@ -46,6 +46,11 @@ void hci_req_purge(struct hci_request *req)
        skb_queue_purge(&req->cmd_q);
 }
 
+bool hci_req_status_pend(struct hci_dev *hdev)
+{
+       return hdev->req_status == HCI_REQ_PEND;
+}
+
 static int req_run(struct hci_request *req, hci_req_complete_t complete,
                   hci_req_complete_skb_t complete_skb)
 {
index 692cc8b133682ab749b73cf34fe0f5da4c6276b4..55b2050cc9ff0557f3aec21573ad97af57dc76f4 100644 (file)
@@ -37,6 +37,7 @@ struct hci_request {
 
 void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
 void hci_req_purge(struct hci_request *req);
+bool hci_req_status_pend(struct hci_dev *hdev);
 int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
 int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete);
 void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,