Bluetooth: Clear role switch pending flag
authorKuba Pawlak <kubax.t.pawlak@intel.com>
Thu, 6 Nov 2014 18:36:52 +0000 (19:36 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 6 Nov 2014 18:38:42 +0000 (19:38 +0100)
If role switch was rejected by the controller and HCI Event: Command Status
returned with status "Command Disallowed" (0x0C) the flag
HCI_CONN_RSWITCH_PEND remains set. No further role switches are
possible as this flag prevents us from sending any new HCI Switch Role
requests and the only way to clear it is to receive a valid
HCI Event Switch Role.

This patch clears the flag if command was rejected.

2013-01-01 00:03:44.209913 < HCI Command: Switch Role (0x02|0x000b) plen 7
    bdaddr BC:C6:DB:C4:6F:79 role 0x00
    Role: Master
2013-01-01 00:03:44.210867 > HCI Event: Command Status (0x0f) plen 4
    Switch Role (0x02|0x000b) status 0x0c ncmd 1
    Error: Command Disallowed

Signed-off-by: Kuba Pawlak <kubax.t.pawlak@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/hci_event.c

index 2f02ff0ed781f88085394acc7a582c2765091d5e..73d9bb4a2c1e5ab2c891050adf95ed6e43ef1f3a 100644 (file)
@@ -1947,6 +1947,29 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
+{
+       struct hci_cp_switch_role *cp;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       if (!status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_SWITCH_ROLE);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+       if (conn)
+               clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -2886,6 +2909,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_exit_sniff_mode(hdev, ev->status);
                break;
 
+       case HCI_OP_SWITCH_ROLE:
+               hci_cs_switch_role(hdev, ev->status);
+               break;
+
        case HCI_OP_DISCONNECT:
                hci_cs_disconnect(hdev, ev->status);
                break;