Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.11-rc2 183 lines 4.3 kB view raw
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 26struct btqcomsmd { 27 struct hci_dev *hdev; 28 29 struct qcom_smd_channel *acl_channel; 30 struct qcom_smd_channel *cmd_channel; 31}; 32 33static 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 51static 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 60static 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 68static 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 93static int btqcomsmd_open(struct hci_dev *hdev) 94{ 95 return 0; 96} 97 98static int btqcomsmd_close(struct hci_dev *hdev) 99{ 100 return 0; 101} 102 103static 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 154static 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 164static const struct of_device_id btqcomsmd_of_match[] = { 165 { .compatible = "qcom,wcnss-bt", }, 166 { }, 167}; 168MODULE_DEVICE_TABLE(of, btqcomsmd_of_match); 169 170static 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 179module_platform_driver(btqcomsmd_driver); 180 181MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 182MODULE_DESCRIPTION("Qualcomm SMD HCI driver"); 183MODULE_LICENSE("GPL v2");