Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2016, Linaro Ltd.
0004  * Copyright (c) 2015, Sony Mobile Communications Inc.
0005  */
0006 
0007 #include <linux/module.h>
0008 #include <linux/slab.h>
0009 #include <linux/rpmsg.h>
0010 #include <linux/of.h>
0011 
0012 #include <linux/soc/qcom/wcnss_ctrl.h>
0013 #include <linux/platform_device.h>
0014 
0015 #include <net/bluetooth/bluetooth.h>
0016 #include <net/bluetooth/hci_core.h>
0017 
0018 #include "btqca.h"
0019 
0020 struct btqcomsmd {
0021     struct hci_dev *hdev;
0022 
0023     struct rpmsg_endpoint *acl_channel;
0024     struct rpmsg_endpoint *cmd_channel;
0025 };
0026 
0027 static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type,
0028                const void *data, size_t count)
0029 {
0030     struct sk_buff *skb;
0031 
0032     /* Use GFP_ATOMIC as we're in IRQ context */
0033     skb = bt_skb_alloc(count, GFP_ATOMIC);
0034     if (!skb) {
0035         hdev->stat.err_rx++;
0036         return -ENOMEM;
0037     }
0038 
0039     hci_skb_pkt_type(skb) = type;
0040     skb_put_data(skb, data, count);
0041 
0042     return hci_recv_frame(hdev, skb);
0043 }
0044 
0045 static int btqcomsmd_acl_callback(struct rpmsg_device *rpdev, void *data,
0046                   int count, void *priv, u32 addr)
0047 {
0048     struct btqcomsmd *btq = priv;
0049 
0050     btq->hdev->stat.byte_rx += count;
0051     return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
0052 }
0053 
0054 static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
0055                   int count, void *priv, u32 addr)
0056 {
0057     struct btqcomsmd *btq = priv;
0058 
0059     btq->hdev->stat.byte_rx += count;
0060     return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
0061 }
0062 
0063 static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
0064 {
0065     struct btqcomsmd *btq = hci_get_drvdata(hdev);
0066     int ret;
0067 
0068     switch (hci_skb_pkt_type(skb)) {
0069     case HCI_ACLDATA_PKT:
0070         ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
0071         if (ret) {
0072             hdev->stat.err_tx++;
0073             break;
0074         }
0075         hdev->stat.acl_tx++;
0076         hdev->stat.byte_tx += skb->len;
0077         break;
0078     case HCI_COMMAND_PKT:
0079         ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
0080         if (ret) {
0081             hdev->stat.err_tx++;
0082             break;
0083         }
0084         hdev->stat.cmd_tx++;
0085         hdev->stat.byte_tx += skb->len;
0086         break;
0087     default:
0088         ret = -EILSEQ;
0089         break;
0090     }
0091 
0092     if (!ret)
0093         kfree_skb(skb);
0094 
0095     return ret;
0096 }
0097 
0098 static int btqcomsmd_open(struct hci_dev *hdev)
0099 {
0100     return 0;
0101 }
0102 
0103 static int btqcomsmd_close(struct hci_dev *hdev)
0104 {
0105     return 0;
0106 }
0107 
0108 static int btqcomsmd_setup(struct hci_dev *hdev)
0109 {
0110     struct sk_buff *skb;
0111 
0112     skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
0113     if (IS_ERR(skb))
0114         return PTR_ERR(skb);
0115     kfree_skb(skb);
0116 
0117     /* Devices do not have persistent storage for BD address. Retrieve
0118      * it from the firmware node property.
0119      */
0120     set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
0121 
0122     return 0;
0123 }
0124 
0125 static int btqcomsmd_probe(struct platform_device *pdev)
0126 {
0127     struct btqcomsmd *btq;
0128     struct hci_dev *hdev;
0129     void *wcnss;
0130     int ret;
0131 
0132     btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
0133     if (!btq)
0134         return -ENOMEM;
0135 
0136     wcnss = dev_get_drvdata(pdev->dev.parent);
0137 
0138     btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
0139                            btqcomsmd_acl_callback, btq);
0140     if (IS_ERR(btq->acl_channel))
0141         return PTR_ERR(btq->acl_channel);
0142 
0143     btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
0144                            btqcomsmd_cmd_callback, btq);
0145     if (IS_ERR(btq->cmd_channel)) {
0146         ret = PTR_ERR(btq->cmd_channel);
0147         goto destroy_acl_channel;
0148     }
0149 
0150     hdev = hci_alloc_dev();
0151     if (!hdev) {
0152         ret = -ENOMEM;
0153         goto destroy_cmd_channel;
0154     }
0155 
0156     hci_set_drvdata(hdev, btq);
0157     btq->hdev = hdev;
0158     SET_HCIDEV_DEV(hdev, &pdev->dev);
0159 
0160     hdev->bus = HCI_SMD;
0161     hdev->open = btqcomsmd_open;
0162     hdev->close = btqcomsmd_close;
0163     hdev->send = btqcomsmd_send;
0164     hdev->setup = btqcomsmd_setup;
0165     hdev->set_bdaddr = qca_set_bdaddr_rome;
0166 
0167     ret = hci_register_dev(hdev);
0168     if (ret < 0)
0169         goto hci_free_dev;
0170 
0171     platform_set_drvdata(pdev, btq);
0172 
0173     return 0;
0174 
0175 hci_free_dev:
0176     hci_free_dev(hdev);
0177 destroy_cmd_channel:
0178     rpmsg_destroy_ept(btq->cmd_channel);
0179 destroy_acl_channel:
0180     rpmsg_destroy_ept(btq->acl_channel);
0181 
0182     return ret;
0183 }
0184 
0185 static int btqcomsmd_remove(struct platform_device *pdev)
0186 {
0187     struct btqcomsmd *btq = platform_get_drvdata(pdev);
0188 
0189     hci_unregister_dev(btq->hdev);
0190     hci_free_dev(btq->hdev);
0191 
0192     rpmsg_destroy_ept(btq->cmd_channel);
0193     rpmsg_destroy_ept(btq->acl_channel);
0194 
0195     return 0;
0196 }
0197 
0198 static const struct of_device_id btqcomsmd_of_match[] = {
0199     { .compatible = "qcom,wcnss-bt", },
0200     { },
0201 };
0202 MODULE_DEVICE_TABLE(of, btqcomsmd_of_match);
0203 
0204 static struct platform_driver btqcomsmd_driver = {
0205     .probe = btqcomsmd_probe,
0206     .remove = btqcomsmd_remove,
0207     .driver  = {
0208         .name  = "btqcomsmd",
0209         .of_match_table = btqcomsmd_of_match,
0210     },
0211 };
0212 
0213 module_platform_driver(btqcomsmd_driver);
0214 
0215 MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
0216 MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
0217 MODULE_LICENSE("GPL v2");