summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/btqcomsmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth/btqcomsmd.c')
-rw-r--r--drivers/bluetooth/btqcomsmd.c91
1 files changed, 69 insertions, 22 deletions
diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c
index d00c4fdae924..d2e13fcb6bab 100644
--- a/drivers/bluetooth/btqcomsmd.c
+++ b/drivers/bluetooth/btqcomsmd.c
@@ -1,20 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/rpmsg.h>
+#include <linux/of.h>
+
#include <linux/soc/qcom/wcnss_ctrl.h>
#include <linux/platform_device.h>
@@ -62,6 +56,7 @@ static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
{
struct btqcomsmd *btq = priv;
+ btq->hdev->stat.byte_rx += count;
return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
}
@@ -73,19 +68,29 @@ static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
+ if (ret) {
+ hdev->stat.err_tx++;
+ break;
+ }
hdev->stat.acl_tx++;
hdev->stat.byte_tx += skb->len;
break;
case HCI_COMMAND_PKT:
ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
+ if (ret) {
+ hdev->stat.err_tx++;
+ break;
+ }
hdev->stat.cmd_tx++;
+ hdev->stat.byte_tx += skb->len;
break;
default:
ret = -EILSEQ;
break;
}
- kfree_skb(skb);
+ if (!ret)
+ kfree_skb(skb);
return ret;
}
@@ -100,6 +105,38 @@ static int btqcomsmd_close(struct hci_dev *hdev)
return 0;
}
+static int btqcomsmd_setup(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+ kfree_skb(skb);
+
+ /* Devices do not have persistent storage for BD address. Retrieve
+ * it from the firmware node property.
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+
+ return 0;
+}
+
+static int btqcomsmd_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ int ret;
+
+ ret = qca_set_bdaddr_rome(hdev, bdaddr);
+ if (ret)
+ return ret;
+
+ /* The firmware stops responding for a while after setting the bdaddr,
+ * causing timeouts for subsequent commands. Sleep a bit to avoid this.
+ */
+ usleep_range(1000, 10000);
+ return 0;
+}
+
static int btqcomsmd_probe(struct platform_device *pdev)
{
struct btqcomsmd *btq;
@@ -120,12 +157,16 @@ static int btqcomsmd_probe(struct platform_device *pdev)
btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
btqcomsmd_cmd_callback, btq);
- if (IS_ERR(btq->cmd_channel))
- return PTR_ERR(btq->cmd_channel);
+ if (IS_ERR(btq->cmd_channel)) {
+ ret = PTR_ERR(btq->cmd_channel);
+ goto destroy_acl_channel;
+ }
hdev = hci_alloc_dev();
- if (!hdev)
- return -ENOMEM;
+ if (!hdev) {
+ ret = -ENOMEM;
+ goto destroy_cmd_channel;
+ }
hci_set_drvdata(hdev, btq);
btq->hdev = hdev;
@@ -135,20 +176,28 @@ static int btqcomsmd_probe(struct platform_device *pdev)
hdev->open = btqcomsmd_open;
hdev->close = btqcomsmd_close;
hdev->send = btqcomsmd_send;
- hdev->set_bdaddr = qca_set_bdaddr_rome;
+ hdev->setup = btqcomsmd_setup;
+ hdev->set_bdaddr = btqcomsmd_set_bdaddr;
ret = hci_register_dev(hdev);
- if (ret < 0) {
- hci_free_dev(hdev);
- return ret;
- }
+ if (ret < 0)
+ goto hci_free_dev;
platform_set_drvdata(pdev, btq);
return 0;
+
+hci_free_dev:
+ hci_free_dev(hdev);
+destroy_cmd_channel:
+ rpmsg_destroy_ept(btq->cmd_channel);
+destroy_acl_channel:
+ rpmsg_destroy_ept(btq->acl_channel);
+
+ return ret;
}
-static int btqcomsmd_remove(struct platform_device *pdev)
+static void btqcomsmd_remove(struct platform_device *pdev)
{
struct btqcomsmd *btq = platform_get_drvdata(pdev);
@@ -157,8 +206,6 @@ static int btqcomsmd_remove(struct platform_device *pdev)
rpmsg_destroy_ept(btq->cmd_channel);
rpmsg_destroy_ept(btq->acl_channel);
-
- return 0;
}
static const struct of_device_id btqcomsmd_of_match[] = {