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;
}