FROMLIST: Bluetooth: Ignore cmd_timeout with HCI_USER_CHANNEL

When using HCI_USER_CHANNEL, hci traffic is expected to be handled by
userspace entirely. However, it is still possible (and sometimes
desirable) to be able to send commands to the controller directly. In
such cases, the kernel command timeout shouldn't do any error handling.

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
(am from https://patchwork.kernel.org/patch/12938973/)
(also found at https://lore.kernel.org/r/20220808110315.1.I5a39052e33f6f3c7406f53b0304a32ccf9f340fa@changeid)

BUG=b:233764522
TEST=run `hcitool cmd 4 1` while stack is running

Change-Id: I5a39052e33f6f3c7406f53b0304a32ccf9f340fa
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3831732
Commit-Queue: Abhishek Pandit-Subedi <abhishekpandit@google.com>
Reviewed-by: Sonny Sasaka <sonnysasaka@chromium.org>
Reviewed-by: Sean Paul <sean@poorly.run>
Reviewed-by: Katherine Lai <laikatherine@chromium.org>
Tested-by: Abhishek Pandit-Subedi <abhishekpandit@google.com>
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index c587f6f..c8c4c9b 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1471,18 +1471,26 @@
 	struct hci_dev *hdev = container_of(work, struct hci_dev,
 					    cmd_timer.work);
 
-	if (hdev->sent_cmd) {
-		struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data;
-		u16 opcode = __le16_to_cpu(sent->opcode);
+	/* Don't trigger the timeout behavior if it happens while we're in
+	 * userchannel mode. Userspace is responsible for handling any command
+	 * timeouts.
+	 */
+	if (!(hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
+	      test_bit(HCI_UP, &hdev->flags))) {
+		if (hdev->sent_cmd) {
+			struct hci_command_hdr *sent =
+				(void *)hdev->sent_cmd->data;
+			u16 opcode = __le16_to_cpu(sent->opcode);
 
-		bt_dev_err(hdev, "command 0x%4.4x tx timeout", opcode);
-	} else {
-		bt_dev_err(hdev, "command tx timeout");
+			bt_dev_err(hdev, "command 0x%4.4x tx timeout", opcode);
+		} else {
+			bt_dev_err(hdev, "command tx timeout");
+		}
+
+		if (hdev->cmd_timeout)
+			hdev->cmd_timeout(hdev);
 	}
 
-	if (hdev->cmd_timeout)
-		hdev->cmd_timeout(hdev);
-
 	atomic_set(&hdev->cmd_cnt, 1);
 	queue_work(hdev->workqueue, &hdev->cmd_work);
 }