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>
This commit is contained in:
Bhasker Neti 2012-12-05 13:58:17 +05:30 committed by Iliyan Malchev
parent 07e1499c08
commit cdd2f80725

View file

@ -44,6 +44,19 @@
#define RX_Q_MONITOR (500) /* 500 milli second */ #define RX_Q_MONITOR (500) /* 500 milli second */
#define HCI_REGISTER_SET 0 #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 int hcismd_set;
static DEFINE_SEMAPHORE(hci_smd_enable); static DEFINE_SEMAPHORE(hci_smd_enable);
@ -341,25 +354,47 @@ static void hci_smd_notify_event(void *data, unsigned int event)
break; break;
case SMD_EVENT_OPEN: case SMD_EVENT_OPEN:
BT_INFO("opening HCI-SMD channel :%s", EVENT_CHANNEL); BT_INFO("opening HCI-SMD channel :%s", EVENT_CHANNEL);
hci_smd_open(hdev); BT_DBG("SSR state is : %x", ssr_state);
open_worker = kzalloc(sizeof(*open_worker), GFP_ATOMIC); if ((ssr_state == STATE_SSR_OFF) ||
if (!open_worker) { (ssr_state == STATE_SSR_CHANNEL_OPEN_PENDING)) {
BT_ERR("Out of memory");
break; 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; break;
case SMD_EVENT_CLOSE: case SMD_EVENT_CLOSE:
BT_INFO("Closing HCI-SMD channel :%s", EVENT_CHANNEL); BT_INFO("Closing HCI-SMD channel :%s", EVENT_CHANNEL);
hci_smd_close(hdev); BT_DBG("SSR state is : %x", ssr_state);
reset_worker = kzalloc(sizeof(*reset_worker), GFP_ATOMIC); if ((ssr_state == STATE_SSR_OFF) ||
if (!reset_worker) { (ssr_state == (STATE_SSR_PENDING_INIT))) {
BT_ERR("Out of memory");
break; 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; break;
default: default:
break; break;
@ -403,19 +438,37 @@ static int hci_smd_hci_register_dev(struct hci_smd_data *hsmd)
{ {
struct hci_dev *hdev; struct hci_dev *hdev;
hdev = hsmd->hdev; if (hsmd->hdev)
if (test_and_set_bit(HCI_REGISTER_SET, &hsmd->flags)) { hdev = hsmd->hdev;
BT_ERR("HCI device registered already"); else {
BT_ERR("hdev is NULL");
return 0; 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; return 0;
} }
@ -449,7 +502,10 @@ static int hci_smd_register_smd(struct hci_smd_data *hsmd)
*/ */
setup_timer(&hsmd->rx_q_timer, schedule_timer, setup_timer(&hsmd->rx_q_timer, schedule_timer,
(unsigned long) hsmd->hdev); (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 */ /* Open the SMD Channel and device and register the callback function */
rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS, rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS,
&hsmd->event_channel, hdev, hci_smd_notify_event); &hsmd->event_channel, hdev, hci_smd_notify_event);
@ -478,21 +534,25 @@ static int hci_smd_register_smd(struct hci_smd_data *hsmd)
static void hci_smd_deregister_dev(struct hci_smd_data *hsmd) static void hci_smd_deregister_dev(struct hci_smd_data *hsmd)
{ {
tasklet_kill(&hs.rx_task); 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)) { if (!test_and_clear_bit(HCI_REGISTER_SET, &hsmd->flags)) {
BT_ERR("HCI device un-registered already"); BT_ERR("HCI device un-registered already");
return; } else {
} else
BT_INFO("HCI device un-registration going on"); 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); if (hsmd->hdev) {
hsmd->hdev = NULL; 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.event_channel);
smd_close(hs.data_channel); smd_close(hs.data_channel);
@ -513,23 +573,47 @@ static void hci_dev_restart(struct work_struct *worker)
{ {
down(&hci_smd_enable); down(&hci_smd_enable);
restart_in_progress = 1; 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_deregister_dev(&hs);
hci_smd_register_smd(&hs); hci_smd_register_smd(&hs);
up(&hci_smd_enable); up(&hci_smd_enable);
kfree(worker); kfree(worker);
} }
static void hci_dev_smd_open(struct work_struct *worker) static void hci_dev_smd_open(struct work_struct *worker)
{ {
down(&hci_smd_enable); 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) { if (restart_in_progress == 1) {
/* Allow wcnss to initialize */ /* Allow wcnss to initialize */
restart_in_progress = 0; restart_in_progress = 0;
msleep(10000); msleep(10000);
} }
hci_smd_hci_register_dev(&hs); hci_smd_hci_register_dev(&hs);
up(&hci_smd_enable); up(&hci_smd_enable);
kfree(worker); kfree(worker);
} }
static int hcismd_set_enable(const char *val, struct kernel_param *kp) static int hcismd_set_enable(const char *val, struct kernel_param *kp)
@ -545,14 +629,23 @@ static int hcismd_set_enable(const char *val, struct kernel_param *kp)
if (ret) if (ret)
goto done; goto done;
/* Ignore the all incomming register de-register requests in case of
* SSR is in-progress
*/
switch (hcismd_set) { switch (hcismd_set) {
case 1: case 1:
if (hs.hdev == NULL) if ((hs.hdev == NULL) && (ssr_state == STATE_SSR_OFF))
hci_smd_register_smd(&hs); hci_smd_register_smd(&hs);
else if (ssr_state)
BT_ERR("SSR is in progress,state is : %x", ssr_state);
break; break;
case 0: 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; break;
default: default:
ret = -EFAULT; ret = -EFAULT;
@ -569,6 +662,7 @@ static int __init hci_smd_init(void)
wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND, wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND,
"msm_smd_Tx"); "msm_smd_Tx");
restart_in_progress = 0; restart_in_progress = 0;
ssr_state = STATE_SSR_OFF;
hs.hdev = NULL; hs.hdev = NULL;
return 0; return 0;
} }