Skip to content

Commit 1511cc7

Browse files
anderssonholtmann
authored andcommitted
Bluetooth: Introduce Qualcomm WCNSS SMD based HCI driver
The Qualcomm WCNSS chip provides two SMD channels to the BT core; one for command and one for event packets. This driver exposes the two channels as a hci device. Signed-off-by: Bjorn Andersson <[email protected]> Signed-off-by: Bjorn Andersson <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 65010e6 commit 1511cc7

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

drivers/bluetooth/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,4 +331,16 @@ config BT_WILINK
331331
Say Y here to compile support for Texas Instrument's WiLink7 driver
332332
into the kernel or say M to compile it as module (btwilink).
333333

334+
config BT_QCOMSMD
335+
tristate "Qualcomm SMD based HCI support"
336+
depends on QCOM_SMD
337+
select BT_QCA
338+
help
339+
Qualcomm SMD based HCI driver.
340+
This driver is used to bridge HCI data onto the shared memory
341+
channels to the WCNSS core.
342+
343+
Say Y here to compile support for HCI over Qualcomm SMD into the
344+
kernel or say M to compile as a module.
345+
334346
endmenu

drivers/bluetooth/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o
2020
obj-$(CONFIG_BT_MRVL) += btmrvl.o
2121
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
2222
obj-$(CONFIG_BT_WILINK) += btwilink.o
23+
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
2324
obj-$(CONFIG_BT_BCM) += btbcm.o
2425
obj-$(CONFIG_BT_RTL) += btrtl.o
2526
obj-$(CONFIG_BT_QCA) += btqca.o

drivers/bluetooth/btqcomsmd.c

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Copyright (c) 2016, Linaro Ltd.
3+
* Copyright (c) 2015, Sony Mobile Communications Inc.
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License version 2 and
7+
* only version 2 as published by the Free Software Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*/
14+
15+
#include <linux/module.h>
16+
#include <linux/slab.h>
17+
#include <linux/soc/qcom/smd.h>
18+
#include <linux/soc/qcom/wcnss_ctrl.h>
19+
#include <linux/platform_device.h>
20+
21+
#include <net/bluetooth/bluetooth.h>
22+
#include <net/bluetooth/hci_core.h>
23+
24+
#include "btqca.h"
25+
26+
struct btqcomsmd {
27+
struct hci_dev *hdev;
28+
29+
struct qcom_smd_channel *acl_channel;
30+
struct qcom_smd_channel *cmd_channel;
31+
};
32+
33+
static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type,
34+
const void *data, size_t count)
35+
{
36+
struct sk_buff *skb;
37+
38+
/* Use GFP_ATOMIC as we're in IRQ context */
39+
skb = bt_skb_alloc(count, GFP_ATOMIC);
40+
if (!skb) {
41+
hdev->stat.err_rx++;
42+
return -ENOMEM;
43+
}
44+
45+
hci_skb_pkt_type(skb) = type;
46+
memcpy(skb_put(skb, count), data, count);
47+
48+
return hci_recv_frame(hdev, skb);
49+
}
50+
51+
static int btqcomsmd_acl_callback(struct qcom_smd_channel *channel,
52+
const void *data, size_t count)
53+
{
54+
struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
55+
56+
btq->hdev->stat.byte_rx += count;
57+
return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
58+
}
59+
60+
static int btqcomsmd_cmd_callback(struct qcom_smd_channel *channel,
61+
const void *data, size_t count)
62+
{
63+
struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
64+
65+
return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
66+
}
67+
68+
static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
69+
{
70+
struct btqcomsmd *btq = hci_get_drvdata(hdev);
71+
int ret;
72+
73+
switch (hci_skb_pkt_type(skb)) {
74+
case HCI_ACLDATA_PKT:
75+
ret = qcom_smd_send(btq->acl_channel, skb->data, skb->len);
76+
hdev->stat.acl_tx++;
77+
hdev->stat.byte_tx += skb->len;
78+
break;
79+
case HCI_COMMAND_PKT:
80+
ret = qcom_smd_send(btq->cmd_channel, skb->data, skb->len);
81+
hdev->stat.cmd_tx++;
82+
break;
83+
default:
84+
ret = -EILSEQ;
85+
break;
86+
}
87+
88+
kfree_skb(skb);
89+
90+
return ret;
91+
}
92+
93+
static int btqcomsmd_open(struct hci_dev *hdev)
94+
{
95+
return 0;
96+
}
97+
98+
static int btqcomsmd_close(struct hci_dev *hdev)
99+
{
100+
return 0;
101+
}
102+
103+
static int btqcomsmd_probe(struct platform_device *pdev)
104+
{
105+
struct btqcomsmd *btq;
106+
struct hci_dev *hdev;
107+
void *wcnss;
108+
int ret;
109+
110+
btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
111+
if (!btq)
112+
return -ENOMEM;
113+
114+
wcnss = dev_get_drvdata(pdev->dev.parent);
115+
116+
btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
117+
btqcomsmd_acl_callback);
118+
if (IS_ERR(btq->acl_channel))
119+
return PTR_ERR(btq->acl_channel);
120+
121+
btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
122+
btqcomsmd_cmd_callback);
123+
if (IS_ERR(btq->cmd_channel))
124+
return PTR_ERR(btq->cmd_channel);
125+
126+
qcom_smd_set_drvdata(btq->acl_channel, btq);
127+
qcom_smd_set_drvdata(btq->cmd_channel, btq);
128+
129+
hdev = hci_alloc_dev();
130+
if (!hdev)
131+
return -ENOMEM;
132+
133+
hci_set_drvdata(hdev, btq);
134+
btq->hdev = hdev;
135+
SET_HCIDEV_DEV(hdev, &pdev->dev);
136+
137+
hdev->bus = HCI_SMD;
138+
hdev->open = btqcomsmd_open;
139+
hdev->close = btqcomsmd_close;
140+
hdev->send = btqcomsmd_send;
141+
hdev->set_bdaddr = qca_set_bdaddr_rome;
142+
143+
ret = hci_register_dev(hdev);
144+
if (ret < 0) {
145+
hci_free_dev(hdev);
146+
return ret;
147+
}
148+
149+
platform_set_drvdata(pdev, btq);
150+
151+
return 0;
152+
}
153+
154+
static int btqcomsmd_remove(struct platform_device *pdev)
155+
{
156+
struct btqcomsmd *btq = platform_get_drvdata(pdev);
157+
158+
hci_unregister_dev(btq->hdev);
159+
hci_free_dev(btq->hdev);
160+
161+
return 0;
162+
}
163+
164+
static const struct of_device_id btqcomsmd_of_match[] = {
165+
{ .compatible = "qcom,wcnss-bt", },
166+
{ },
167+
};
168+
169+
static struct platform_driver btqcomsmd_driver = {
170+
.probe = btqcomsmd_probe,
171+
.remove = btqcomsmd_remove,
172+
.driver = {
173+
.name = "btqcomsmd",
174+
.of_match_table = btqcomsmd_of_match,
175+
},
176+
};
177+
178+
module_platform_driver(btqcomsmd_driver);
179+
180+
MODULE_AUTHOR("Bjorn Andersson <[email protected]>");
181+
MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
182+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)