Bluetooth: Wait for smd open before registering with hci
During subsystem restart we need to wait for the WCNSS to come up
and open the smd port before sending any data (command) to it
otherwise the data is lost and it will result in hci_cmd_timeout.
If there was a mechanism in place to issue subsystem_restart on
hci_cmd_timeout, it would result in repetitive subsystem_restart
without this fix.
CRs-fixed: 346227
Change-Id: I5b6098ad017455346953c63e6acef7b12eb44814
Signed-off-by: Sunny Kapdi <sunnyk@codeaurora.org>
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index 332922e..8f51e2d 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -48,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_smd_open(struct work_struct *worker);
static void hci_dev_restart(struct work_struct *worker);
struct hci_smd_data {
@@ -315,6 +316,8 @@
struct hci_dev *hdev = hs.hdev;
struct hci_smd_data *hsmd = &hs;
struct work_struct *reset_worker;
+ struct work_struct *open_worker;
+
int len = 0;
if (!hdev) {
@@ -334,6 +337,13 @@
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;
+ }
+ 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);
@@ -384,9 +394,24 @@
}
-static int hci_smd_register_dev(struct hci_smd_data *hsmd)
+static int hci_smd_hci_register_dev(struct hci_smd_data *hsmd)
{
- static struct hci_dev *hdev;
+ struct hci_dev *hdev;
+
+ hdev = hsmd->hdev;
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ hci_free_dev(hdev);
+ hsmd->hdev = NULL;
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int hci_smd_register_smd(struct hci_smd_data *hsmd)
+{
+ struct hci_dev *hdev;
int rc;
/* Initialize and register HCI device */
@@ -421,7 +446,7 @@
if (rc < 0) {
BT_ERR("Cannot open the command channel");
hci_free_dev(hdev);
- hdev = NULL;
+ hsmd->hdev = NULL;
return -ENODEV;
}
@@ -430,18 +455,13 @@
if (rc < 0) {
BT_ERR("Failed to open the Data channel");
hci_free_dev(hdev);
- hdev = NULL;
+ hsmd->hdev = NULL;
return -ENODEV;
}
/* Disable the read interrupts on the channel */
smd_disable_read_intr(hsmd->event_channel);
smd_disable_read_intr(hsmd->data_channel);
- if (hci_register_dev(hdev) < 0) {
- BT_ERR("Can't register HCI device");
- hci_free_dev(hdev);
- return -ENODEV;
- }
return 0;
}
@@ -478,7 +498,15 @@
{
mutex_lock(&hci_smd_enable);
hci_smd_deregister_dev(&hs);
- hci_smd_register_dev(&hs);
+ hci_smd_register_smd(&hs);
+ mutex_unlock(&hci_smd_enable);
+ kfree(worker);
+}
+
+static void hci_dev_smd_open(struct work_struct *worker)
+{
+ mutex_lock(&hci_smd_enable);
+ hci_smd_hci_register_dev(&hs);
mutex_unlock(&hci_smd_enable);
kfree(worker);
}
@@ -497,7 +525,7 @@
switch (hcismd_set) {
case 1:
- hci_smd_register_dev(&hs);
+ hci_smd_register_smd(&hs);
break;
case 0:
hci_smd_deregister_dev(&hs);