Merge "bluetooth: Reset BT stack when wcnss chip resets" into msm-3.0
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index dbd1bd4..332922e 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -25,6 +25,7 @@
 #include <linux/string.h>
 #include <linux/skbuff.h>
 #include <linux/wakelock.h>
+#include <linux/workqueue.h>
 #include <linux/uaccess.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -47,6 +48,7 @@
 static int hcismd_set_enable(const char *val, struct kernel_param *kp);
 module_param_call(hcismd_set, hcismd_set_enable, NULL, &hcismd_set, 0644);
 
+static void hci_dev_restart(struct work_struct *worker);
 
 struct hci_smd_data {
 	struct hci_dev *hdev;
@@ -312,6 +314,7 @@
 {
 	struct hci_dev *hdev = hs.hdev;
 	struct hci_smd_data *hsmd = &hs;
+	struct work_struct *reset_worker;
 	int len = 0;
 
 	if (!hdev) {
@@ -335,6 +338,13 @@
 	case SMD_EVENT_CLOSE:
 		BT_INFO("Closing HCI-SMD channel :%s", EVENT_CHANNEL);
 		hci_smd_close(hdev);
+		reset_worker = kzalloc(sizeof(*reset_worker), GFP_ATOMIC);
+		if (!reset_worker) {
+			BT_ERR("Out of memory");
+			break;
+		}
+		INIT_WORK(reset_worker, hci_dev_restart);
+		schedule_work(reset_worker);
 		break;
 	default:
 		break;
@@ -464,6 +474,15 @@
 	}
 }
 
+static void hci_dev_restart(struct work_struct *worker)
+{
+	mutex_lock(&hci_smd_enable);
+	hci_smd_deregister_dev(&hs);
+	hci_smd_register_dev(&hs);
+	mutex_unlock(&hci_smd_enable);
+	kfree(worker);
+}
+
 static int hcismd_set_enable(const char *val, struct kernel_param *kp)
 {
 	int ret = 0;