Bluetooth: Call shutdown for HCI_USER_CHANNEL
authorAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Tue, 27 Sep 2022 20:17:20 +0000 (13:17 -0700)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 28 Sep 2022 19:16:12 +0000 (12:16 -0700)
Some drivers depend on shutdown being called for proper operation.
Unset HCI_USER_CHANNEL and call the full close routine since shutdown is
complementary to setup.

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/hci_sync.c

index 422f7c6911d9fbc6688d6f0c9c77023a662a9bd5..15c75ef4c271d1227fa33d56cd47b54a5a7ff2fd 100644 (file)
@@ -4727,6 +4727,31 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
        BT_DBG("All LE pending actions cleared");
 }
 
+static int hci_dev_shutdown(struct hci_dev *hdev)
+{
+       int err = 0;
+       /* Similar to how we first do setup and then set the exclusive access
+        * bit for userspace, we must first unset userchannel and then clean up.
+        * Otherwise, the kernel can't properly use the hci channel to clean up
+        * the controller (some shutdown routines require sending additional
+        * commands to the controller for example).
+        */
+       bool was_userchannel =
+               hci_dev_test_and_clear_flag(hdev, HCI_USER_CHANNEL);
+
+       if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
+           test_bit(HCI_UP, &hdev->flags)) {
+               /* Execute vendor specific shutdown routine */
+               if (hdev->shutdown)
+                       err = hdev->shutdown(hdev);
+       }
+
+       if (was_userchannel)
+               hci_dev_set_flag(hdev, HCI_USER_CHANNEL);
+
+       return err;
+}
+
 int hci_dev_close_sync(struct hci_dev *hdev)
 {
        bool auto_off;
@@ -4746,13 +4771,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)
                hdev->adv_instance_timeout = 0;
        }
 
-       if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
-           !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
-           test_bit(HCI_UP, &hdev->flags)) {
-               /* Execute vendor specific shutdown routine */
-               if (hdev->shutdown)
-                       err = hdev->shutdown(hdev);
-       }
+       err = hci_dev_shutdown(hdev);
 
        if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
                cancel_delayed_work_sync(&hdev->cmd_timer);