Bluetooth: Added SSR state machine in hci_smd sriver

This is to handle back to back SSR and also spurious
notifications from the smd and the BT on/off or Airplane
mode toggling while SSR is in progress.

CRs-fixed: 425768
Change-Id: I851d8d785c2ff5445d3b8aa95225f1cb63654002
Signed-off-by: Bhasker Neti <bneti@codeaurora.org>
Signed-off-by: Mallikarjuna GB <gbmalli@codeaurora.org>
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index 6030520..9b60bda 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -44,6 +44,19 @@
 #define RX_Q_MONITOR		(500)	/* 500 milli second */
 #define HCI_REGISTER_SET	0
 
+/* SSR state machine to take care of back to back SSR requests
+ * and handling the incomming BT on/off,Airplane mode toggling and
+ * also spuriour SMD open notification while one SSr is in progress
+ */
+#define STATE_SSR_ON 0x1
+#define STATE_SSR_START 0x02
+#define STATE_SSR_CHANNEL_OPEN_PENDING 0x04
+#define STATE_SSR_PENDING_INIT  0x08
+#define STATE_SSR_COMPLETE 0x00
+#define STATE_SSR_OFF STATE_SSR_COMPLETE
+
+static int ssr_state = STATE_SSR_OFF;
+
 
 static int hcismd_set;
 static DEFINE_SEMAPHORE(hci_smd_enable);
@@ -341,25 +354,47 @@
 		break;
 	case SMD_EVENT_OPEN:
 		BT_INFO("opening HCI-SMD channel :%s", EVENT_CHANNEL);
-		hci_smd_open(hdev);
-		open_worker = kzalloc(sizeof(*open_worker), GFP_ATOMIC);
-		if (!open_worker) {
-			BT_ERR("Out of memory");
-			break;
+		BT_DBG("SSR state is : %x", ssr_state);
+		if ((ssr_state == STATE_SSR_OFF) ||
+			(ssr_state == STATE_SSR_CHANNEL_OPEN_PENDING)) {
+
+			hci_smd_open(hdev);
+			open_worker = kzalloc(sizeof(*open_worker), GFP_ATOMIC);
+			if (!open_worker) {
+				BT_ERR("Out of memory");
+				break;
+			}
+			if (ssr_state == STATE_SSR_CHANNEL_OPEN_PENDING) {
+				ssr_state = STATE_SSR_PENDING_INIT;
+				BT_INFO("SSR state is : %x", ssr_state);
+			}
+			INIT_WORK(open_worker, hci_dev_smd_open);
+			schedule_work(open_worker);
+
 		}
-		INIT_WORK(open_worker, hci_dev_smd_open);
-		schedule_work(open_worker);
 		break;
 	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;
+		BT_DBG("SSR state is : %x", ssr_state);
+		if ((ssr_state == STATE_SSR_OFF) ||
+			(ssr_state == (STATE_SSR_PENDING_INIT))) {
+
+			hci_smd_close(hdev);
+			reset_worker = kzalloc(sizeof(*reset_worker),
+							GFP_ATOMIC);
+			if (!reset_worker) {
+				BT_ERR("Out of memory");
+				break;
+			}
+			ssr_state = STATE_SSR_ON;
+			BT_INFO("SSR state is : %x", ssr_state);
+			INIT_WORK(reset_worker, hci_dev_restart);
+			schedule_work(reset_worker);
+
+		} else if (ssr_state & STATE_SSR_ON) {
+				BT_ERR("SSR state is : %x", ssr_state);
 		}
-		INIT_WORK(reset_worker, hci_dev_restart);
-		schedule_work(reset_worker);
+
 		break;
 	default:
 		break;
@@ -403,19 +438,37 @@
 {
 	struct hci_dev *hdev;
 
-	hdev = hsmd->hdev;
-	if (test_and_set_bit(HCI_REGISTER_SET, &hsmd->flags)) {
-		BT_ERR("HCI device registered already");
+	if (hsmd->hdev)
+		hdev = hsmd->hdev;
+	else {
+		BT_ERR("hdev is NULL");
 		return 0;
-	} else
-		BT_INFO("HCI device registration is starting");
-	if (hci_register_dev(hdev) < 0) {
-		BT_ERR("Can't register HCI device");
-		hci_free_dev(hdev);
-		hsmd->hdev = NULL;
-		clear_bit(HCI_REGISTER_SET, &hsmd->flags);
-		return -ENODEV;
 	}
+	/* Allow the incomming SSR even the prev one at PENDING INIT STATE
+	 * since clenup need to be started again from the beging and ignore
+	 *  or bypass the prev one
+	 */
+	if ((ssr_state == STATE_SSR_OFF) ||
+			(ssr_state == STATE_SSR_PENDING_INIT)) {
+
+		if (test_and_set_bit(HCI_REGISTER_SET, &hsmd->flags)) {
+			BT_ERR("HCI device registered already");
+			return 0;
+		} else
+			BT_INFO("HCI device registration is starting");
+		if (hci_register_dev(hdev) < 0) {
+			BT_ERR("Can't register HCI device");
+			hci_free_dev(hdev);
+			hsmd->hdev = NULL;
+			clear_bit(HCI_REGISTER_SET, &hsmd->flags);
+			return -ENODEV;
+		}
+		if (ssr_state == STATE_SSR_PENDING_INIT) {
+			ssr_state = STATE_SSR_COMPLETE;
+			BT_INFO("SSR state is : %x", ssr_state);
+		}
+	} else if (ssr_state)
+		BT_ERR("Registration called in invalid context");
 	return 0;
 }
 
@@ -449,7 +502,10 @@
 	 */
 	setup_timer(&hsmd->rx_q_timer, schedule_timer,
 			(unsigned long) hsmd->hdev);
-
+	if (ssr_state == STATE_SSR_START) {
+		ssr_state = STATE_SSR_CHANNEL_OPEN_PENDING;
+		BT_INFO("SSR state is : %x", ssr_state);
+	}
 	/* Open the SMD Channel and device and register the callback function */
 	rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS,
 			&hsmd->event_channel, hdev, hci_smd_notify_event);
@@ -478,21 +534,25 @@
 static void hci_smd_deregister_dev(struct hci_smd_data *hsmd)
 {
 	tasklet_kill(&hs.rx_task);
-
+	if (ssr_state)
+		BT_DBG("SSR state is : %x", ssr_state);
+	/* Though the hci_smd driver is not registered with the hci
+	 * need to close the opened channels as a part of cleaup
+	 */
 	if (!test_and_clear_bit(HCI_REGISTER_SET, &hsmd->flags)) {
 		BT_ERR("HCI device un-registered already");
-		return;
-	} else
+	} else {
 		BT_INFO("HCI device un-registration going on");
-	if (hsmd->hdev) {
-		if (hci_unregister_dev(hsmd->hdev) < 0)
-			BT_ERR("Can't unregister HCI device %s",
-				hsmd->hdev->name);
 
-		hci_free_dev(hsmd->hdev);
-		hsmd->hdev = NULL;
+		if (hsmd->hdev) {
+			if (hci_unregister_dev(hsmd->hdev) < 0)
+				BT_ERR("Can't unregister HCI device %s",
+					hsmd->hdev->name);
+
+			hci_free_dev(hsmd->hdev);
+			hsmd->hdev = NULL;
+		}
 	}
-
 	smd_close(hs.event_channel);
 	smd_close(hs.data_channel);
 
@@ -513,23 +573,47 @@
 {
 	down(&hci_smd_enable);
 	restart_in_progress = 1;
+	BT_DBG("SSR state is : %x", ssr_state);
+
+	if (ssr_state == STATE_SSR_ON) {
+		ssr_state = STATE_SSR_START;
+		BT_INFO("SSR state is : %x", ssr_state);
+	} else {
+		BT_ERR("restart triggered in wrong context");
+		up(&hci_smd_enable);
+		kfree(worker);
+		return;
+	}
 	hci_smd_deregister_dev(&hs);
 	hci_smd_register_smd(&hs);
 	up(&hci_smd_enable);
 	kfree(worker);
+
 }
 
 static void hci_dev_smd_open(struct work_struct *worker)
 {
 	down(&hci_smd_enable);
+	if (ssr_state)
+		BT_DBG("SSR state is : %x", ssr_state);
+
+	if ((ssr_state != STATE_SSR_OFF) &&
+			(ssr_state  !=  (STATE_SSR_PENDING_INIT))) {
+		up(&hci_smd_enable);
+		kfree(worker);
+		return;
+	}
+
 	if (restart_in_progress == 1) {
 		/* Allow wcnss to initialize */
 		restart_in_progress = 0;
 		msleep(10000);
 	}
+
 	hci_smd_hci_register_dev(&hs);
 	up(&hci_smd_enable);
 	kfree(worker);
+
 }
 
 static int hcismd_set_enable(const char *val, struct kernel_param *kp)
@@ -545,14 +629,23 @@
 	if (ret)
 		goto done;
 
+	/* Ignore the all incomming register de-register requests in case of
+	 * SSR is in-progress
+	 */
 	switch (hcismd_set) {
 
 	case 1:
-		if (hs.hdev == NULL)
+		if ((hs.hdev == NULL) && (ssr_state == STATE_SSR_OFF))
 			hci_smd_register_smd(&hs);
+		else if (ssr_state)
+			BT_ERR("SSR is in progress,state is : %x", ssr_state);
+
 	break;
 	case 0:
-		hci_smd_deregister_dev(&hs);
+		if (ssr_state == STATE_SSR_OFF)
+			hci_smd_deregister_dev(&hs);
+		else if (ssr_state)
+			BT_ERR("SSR is in progress,state is : %x", ssr_state);
 	break;
 	default:
 		ret = -EFAULT;
@@ -569,6 +662,7 @@
 	wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND,
 			 "msm_smd_Tx");
 	restart_in_progress = 0;
+	ssr_state = STATE_SSR_OFF;
 	hs.hdev = NULL;
 	return 0;
 }