]> git.ipfire.org Git - people/arne_f/kernel.git/blame - drivers/bluetooth/btqcomsmd.c
x86/defconfig: Enable CONFIG_USB_XHCI_HCD=y
[people/arne_f/kernel.git] / drivers / bluetooth / btqcomsmd.c
CommitLineData
1511cc75
BA
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>
5052de8d 17#include <linux/rpmsg.h>
1511cc75
BA
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
26struct btqcomsmd {
27 struct hci_dev *hdev;
28
5912d9ca 29 bdaddr_t bdaddr;
5052de8d
BA
30 struct rpmsg_endpoint *acl_channel;
31 struct rpmsg_endpoint *cmd_channel;
1511cc75
BA
32};
33
34static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type,
35 const void *data, size_t count)
36{
37 struct sk_buff *skb;
38
39 /* Use GFP_ATOMIC as we're in IRQ context */
40 skb = bt_skb_alloc(count, GFP_ATOMIC);
41 if (!skb) {
42 hdev->stat.err_rx++;
43 return -ENOMEM;
44 }
45
46 hci_skb_pkt_type(skb) = type;
59ae1d12 47 skb_put_data(skb, data, count);
1511cc75
BA
48
49 return hci_recv_frame(hdev, skb);
50}
51
5052de8d
BA
52static int btqcomsmd_acl_callback(struct rpmsg_device *rpdev, void *data,
53 int count, void *priv, u32 addr)
1511cc75 54{
5052de8d 55 struct btqcomsmd *btq = priv;
1511cc75
BA
56
57 btq->hdev->stat.byte_rx += count;
58 return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
59}
60
5052de8d
BA
61static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
62 int count, void *priv, u32 addr)
1511cc75 63{
5052de8d 64 struct btqcomsmd *btq = priv;
1511cc75
BA
65
66 return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
67}
68
69static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
70{
71 struct btqcomsmd *btq = hci_get_drvdata(hdev);
72 int ret;
73
74 switch (hci_skb_pkt_type(skb)) {
75 case HCI_ACLDATA_PKT:
5052de8d 76 ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
1511cc75
BA
77 hdev->stat.acl_tx++;
78 hdev->stat.byte_tx += skb->len;
79 break;
80 case HCI_COMMAND_PKT:
5052de8d 81 ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
1511cc75
BA
82 hdev->stat.cmd_tx++;
83 break;
84 default:
85 ret = -EILSEQ;
86 break;
87 }
88
e59e19dc
LP
89 if (!ret)
90 kfree_skb(skb);
1511cc75
BA
91
92 return ret;
93}
94
95static int btqcomsmd_open(struct hci_dev *hdev)
96{
97 return 0;
98}
99
100static int btqcomsmd_close(struct hci_dev *hdev)
101{
102 return 0;
103}
104
5912d9ca
LP
105static int btqcomsmd_setup(struct hci_dev *hdev)
106{
107 struct btqcomsmd *btq = hci_get_drvdata(hdev);
108 struct sk_buff *skb;
109 int err;
110
111 skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
112 if (IS_ERR(skb))
113 return PTR_ERR(skb);
114 kfree_skb(skb);
115
116 /* Devices do not have persistent storage for BD address. If no
117 * BD address has been retrieved during probe, mark the device
118 * as having an invalid BD address.
119 */
120 if (!bacmp(&btq->bdaddr, BDADDR_ANY)) {
121 set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
122 return 0;
123 }
124
125 /* When setting a configured BD address fails, mark the device
126 * as having an invalid BD address.
127 */
128 err = qca_set_bdaddr_rome(hdev, &btq->bdaddr);
129 if (err) {
130 set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
131 return 0;
132 }
133
134 return 0;
135}
136
1511cc75
BA
137static int btqcomsmd_probe(struct platform_device *pdev)
138{
139 struct btqcomsmd *btq;
140 struct hci_dev *hdev;
141 void *wcnss;
142 int ret;
143
144 btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
145 if (!btq)
146 return -ENOMEM;
147
148 wcnss = dev_get_drvdata(pdev->dev.parent);
149
150 btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
5052de8d 151 btqcomsmd_acl_callback, btq);
1511cc75
BA
152 if (IS_ERR(btq->acl_channel))
153 return PTR_ERR(btq->acl_channel);
154
155 btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
5052de8d 156 btqcomsmd_cmd_callback, btq);
1511cc75
BA
157 if (IS_ERR(btq->cmd_channel))
158 return PTR_ERR(btq->cmd_channel);
159
1511cc75
BA
160 hdev = hci_alloc_dev();
161 if (!hdev)
162 return -ENOMEM;
163
164 hci_set_drvdata(hdev, btq);
165 btq->hdev = hdev;
166 SET_HCIDEV_DEV(hdev, &pdev->dev);
167
168 hdev->bus = HCI_SMD;
169 hdev->open = btqcomsmd_open;
170 hdev->close = btqcomsmd_close;
171 hdev->send = btqcomsmd_send;
5912d9ca 172 hdev->setup = btqcomsmd_setup;
1511cc75
BA
173 hdev->set_bdaddr = qca_set_bdaddr_rome;
174
175 ret = hci_register_dev(hdev);
176 if (ret < 0) {
177 hci_free_dev(hdev);
178 return ret;
179 }
180
181 platform_set_drvdata(pdev, btq);
182
183 return 0;
184}
185
186static int btqcomsmd_remove(struct platform_device *pdev)
187{
188 struct btqcomsmd *btq = platform_get_drvdata(pdev);
189
190 hci_unregister_dev(btq->hdev);
191 hci_free_dev(btq->hdev);
192
5052de8d
BA
193 rpmsg_destroy_ept(btq->cmd_channel);
194 rpmsg_destroy_ept(btq->acl_channel);
195
1511cc75
BA
196 return 0;
197}
198
199static const struct of_device_id btqcomsmd_of_match[] = {
200 { .compatible = "qcom,wcnss-bt", },
201 { },
202};
747d3f1a 203MODULE_DEVICE_TABLE(of, btqcomsmd_of_match);
1511cc75
BA
204
205static struct platform_driver btqcomsmd_driver = {
206 .probe = btqcomsmd_probe,
207 .remove = btqcomsmd_remove,
208 .driver = {
209 .name = "btqcomsmd",
210 .of_match_table = btqcomsmd_of_match,
211 },
212};
213
214module_platform_driver(btqcomsmd_driver);
215
216MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
217MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
218MODULE_LICENSE("GPL v2");