2 * Copyright (c) 2016, Linaro Ltd.
3 * Copyright (c) 2015, Sony Mobile Communications Inc.
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.
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.
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>
21 #include <net/bluetooth/bluetooth.h>
22 #include <net/bluetooth/hci_core.h>
29 struct qcom_smd_channel
*acl_channel
;
30 struct qcom_smd_channel
*cmd_channel
;
33 static int btqcomsmd_recv(struct hci_dev
*hdev
, unsigned int type
,
34 const void *data
, size_t count
)
38 /* Use GFP_ATOMIC as we're in IRQ context */
39 skb
= bt_skb_alloc(count
, GFP_ATOMIC
);
45 hci_skb_pkt_type(skb
) = type
;
46 memcpy(skb_put(skb
, count
), data
, count
);
48 return hci_recv_frame(hdev
, skb
);
51 static int btqcomsmd_acl_callback(struct qcom_smd_channel
*channel
,
52 const void *data
, size_t count
)
54 struct btqcomsmd
*btq
= qcom_smd_get_drvdata(channel
);
56 btq
->hdev
->stat
.byte_rx
+= count
;
57 return btqcomsmd_recv(btq
->hdev
, HCI_ACLDATA_PKT
, data
, count
);
60 static int btqcomsmd_cmd_callback(struct qcom_smd_channel
*channel
,
61 const void *data
, size_t count
)
63 struct btqcomsmd
*btq
= qcom_smd_get_drvdata(channel
);
65 return btqcomsmd_recv(btq
->hdev
, HCI_EVENT_PKT
, data
, count
);
68 static int btqcomsmd_send(struct hci_dev
*hdev
, struct sk_buff
*skb
)
70 struct btqcomsmd
*btq
= hci_get_drvdata(hdev
);
73 switch (hci_skb_pkt_type(skb
)) {
75 ret
= qcom_smd_send(btq
->acl_channel
, skb
->data
, skb
->len
);
77 hdev
->stat
.byte_tx
+= skb
->len
;
80 ret
= qcom_smd_send(btq
->cmd_channel
, skb
->data
, skb
->len
);
93 static int btqcomsmd_open(struct hci_dev
*hdev
)
98 static int btqcomsmd_close(struct hci_dev
*hdev
)
103 static int btqcomsmd_probe(struct platform_device
*pdev
)
105 struct btqcomsmd
*btq
;
106 struct hci_dev
*hdev
;
110 btq
= devm_kzalloc(&pdev
->dev
, sizeof(*btq
), GFP_KERNEL
);
114 wcnss
= dev_get_drvdata(pdev
->dev
.parent
);
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
);
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
);
126 qcom_smd_set_drvdata(btq
->acl_channel
, btq
);
127 qcom_smd_set_drvdata(btq
->cmd_channel
, btq
);
129 hdev
= hci_alloc_dev();
133 hci_set_drvdata(hdev
, btq
);
135 SET_HCIDEV_DEV(hdev
, &pdev
->dev
);
138 hdev
->open
= btqcomsmd_open
;
139 hdev
->close
= btqcomsmd_close
;
140 hdev
->send
= btqcomsmd_send
;
141 hdev
->set_bdaddr
= qca_set_bdaddr_rome
;
143 ret
= hci_register_dev(hdev
);
149 platform_set_drvdata(pdev
, btq
);
154 static int btqcomsmd_remove(struct platform_device
*pdev
)
156 struct btqcomsmd
*btq
= platform_get_drvdata(pdev
);
158 hci_unregister_dev(btq
->hdev
);
159 hci_free_dev(btq
->hdev
);
164 static const struct of_device_id btqcomsmd_of_match
[] = {
165 { .compatible
= "qcom,wcnss-bt", },
169 static struct platform_driver btqcomsmd_driver
= {
170 .probe
= btqcomsmd_probe
,
171 .remove
= btqcomsmd_remove
,
174 .of_match_table
= btqcomsmd_of_match
,
178 module_platform_driver(btqcomsmd_driver
);
180 MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
181 MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
182 MODULE_LICENSE("GPL v2");