Bluetooth: Fix init sequence for some CSR based controllers
authorJohan Hedberg <johan.hedberg@intel.com>
Thu, 1 Mar 2012 19:35:55 +0000 (21:35 +0200)
committerJohan Hedberg <johan.hedberg@intel.com>
Thu, 1 Mar 2012 19:44:55 +0000 (21:44 +0200)
Some CSR controllers will generate a spontaneous reset during init and
just eat up any pending command without sending a command complete for
it. This patch solves the issue by just resending whatever was the last
sent command. hci_send_cmd is not used since we need to bypass all other
commands in the send queue.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/hci_core.c

index db484a8e73649cedd60a952ab42af54a83a7f10d..d3ddc0ba9cd4fc3839ab0a489c8ceeb652fe1212 100644 (file)
@@ -82,8 +82,28 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result)
        /* If this is the init phase check if the completed command matches
         * the last init command, and if not just return.
         */
-       if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd)
+       if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) {
+               struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data;
+               struct sk_buff *skb;
+
+               /* Some CSR based controllers generate a spontaneous
+                * reset complete event during init and any pending
+                * command will never be completed. In such a case we
+                * need to resend whatever was the last sent
+                * command.
+                */
+
+               if (cmd != HCI_OP_RESET || sent->opcode == HCI_OP_RESET)
+                       return;
+
+               skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC);
+               if (skb) {
+                       skb_queue_head(&hdev->cmd_q, skb);
+                       queue_work(hdev->workqueue, &hdev->cmd_work);
+               }
+
                return;
+       }
 
        if (hdev->req_status == HCI_REQ_PEND) {
                hdev->req_result = result;