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 MODULE_DEVICE_TABLE(of, btqcomsmd_of_match); 169 170 static struct platform_driver btqcomsmd_driver = { 171 .probe = btqcomsmd_probe, 172 .remove = btqcomsmd_remove, 173 .driver = { 174 .name = "btqcomsmd", 175 .of_match_table = btqcomsmd_of_match, 176 }, 177 }; 178 179 module_platform_driver(btqcomsmd_driver); 180 181 MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 182 MODULE_DESCRIPTION("Qualcomm SMD HCI driver"); 183 MODULE_LICENSE("GPL v2"); 184