linux/drivers/bluetooth/btqcomsmd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016, Linaro Ltd.
   4 * Copyright (c) 2015, Sony Mobile Communications Inc.
   5 */
   6
   7#include <linux/module.h>
   8#include <linux/slab.h>
   9#include <linux/rpmsg.h>
  10#include <linux/of.h>
  11
  12#include <linux/soc/qcom/wcnss_ctrl.h>
  13#include <linux/platform_device.h>
  14
  15#include <net/bluetooth/bluetooth.h>
  16#include <net/bluetooth/hci_core.h>
  17
  18#include "btqca.h"
  19
  20struct btqcomsmd {
  21        struct hci_dev *hdev;
  22
  23        struct rpmsg_endpoint *acl_channel;
  24        struct rpmsg_endpoint *cmd_channel;
  25};
  26
  27static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type,
  28                           const void *data, size_t count)
  29{
  30        struct sk_buff *skb;
  31
  32        /* Use GFP_ATOMIC as we're in IRQ context */
  33        skb = bt_skb_alloc(count, GFP_ATOMIC);
  34        if (!skb) {
  35                hdev->stat.err_rx++;
  36                return -ENOMEM;
  37        }
  38
  39        hci_skb_pkt_type(skb) = type;
  40        skb_put_data(skb, data, count);
  41
  42        return hci_recv_frame(hdev, skb);
  43}
  44
  45static int btqcomsmd_acl_callback(struct rpmsg_device *rpdev, void *data,
  46                                  int count, void *priv, u32 addr)
  47{
  48        struct btqcomsmd *btq = priv;
  49
  50        btq->hdev->stat.byte_rx += count;
  51        return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
  52}
  53
  54static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
  55                                  int count, void *priv, u32 addr)
  56{
  57        struct btqcomsmd *btq = priv;
  58
  59        btq->hdev->stat.byte_rx += count;
  60        return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
  61}
  62
  63static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
  64{
  65        struct btqcomsmd *btq = hci_get_drvdata(hdev);
  66        int ret;
  67
  68        switch (hci_skb_pkt_type(skb)) {
  69        case HCI_ACLDATA_PKT:
  70                ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
  71                if (ret) {
  72                        hdev->stat.err_tx++;
  73                        break;
  74                }
  75                hdev->stat.acl_tx++;
  76                hdev->stat.byte_tx += skb->len;
  77                break;
  78        case HCI_COMMAND_PKT:
  79                ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
  80                if (ret) {
  81                        hdev->stat.err_tx++;
  82                        break;
  83                }
  84                hdev->stat.cmd_tx++;
  85                hdev->stat.byte_tx += skb->len;
  86                break;
  87        default:
  88                ret = -EILSEQ;
  89                break;
  90        }
  91
  92        if (!ret)
  93                kfree_skb(skb);
  94
  95        return ret;
  96}
  97
  98static int btqcomsmd_open(struct hci_dev *hdev)
  99{
 100        return 0;
 101}
 102
 103static int btqcomsmd_close(struct hci_dev *hdev)
 104{
 105        return 0;
 106}
 107
 108static int btqcomsmd_setup(struct hci_dev *hdev)
 109{
 110        struct sk_buff *skb;
 111
 112        skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
 113        if (IS_ERR(skb))
 114                return PTR_ERR(skb);
 115        kfree_skb(skb);
 116
 117        /* Devices do not have persistent storage for BD address. Retrieve
 118         * it from the firmware node property.
 119         */
 120        set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
 121
 122        return 0;
 123}
 124
 125static int btqcomsmd_probe(struct platform_device *pdev)
 126{
 127        struct btqcomsmd *btq;
 128        struct hci_dev *hdev;
 129        void *wcnss;
 130        int ret;
 131
 132        btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
 133        if (!btq)
 134                return -ENOMEM;
 135
 136        wcnss = dev_get_drvdata(pdev->dev.parent);
 137
 138        btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
 139                                                   btqcomsmd_acl_callback, btq);
 140        if (IS_ERR(btq->acl_channel))
 141                return PTR_ERR(btq->acl_channel);
 142
 143        btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
 144                                                   btqcomsmd_cmd_callback, btq);
 145        if (IS_ERR(btq->cmd_channel)) {
 146                ret = PTR_ERR(btq->cmd_channel);
 147                goto destroy_acl_channel;
 148        }
 149
 150        hdev = hci_alloc_dev();
 151        if (!hdev) {
 152                ret = -ENOMEM;
 153                goto destroy_cmd_channel;
 154        }
 155
 156        hci_set_drvdata(hdev, btq);
 157        btq->hdev = hdev;
 158        SET_HCIDEV_DEV(hdev, &pdev->dev);
 159
 160        hdev->bus = HCI_SMD;
 161        hdev->open = btqcomsmd_open;
 162        hdev->close = btqcomsmd_close;
 163        hdev->send = btqcomsmd_send;
 164        hdev->setup = btqcomsmd_setup;
 165        hdev->set_bdaddr = qca_set_bdaddr_rome;
 166
 167        ret = hci_register_dev(hdev);
 168        if (ret < 0)
 169                goto hci_free_dev;
 170
 171        platform_set_drvdata(pdev, btq);
 172
 173        return 0;
 174
 175hci_free_dev:
 176        hci_free_dev(hdev);
 177destroy_cmd_channel:
 178        rpmsg_destroy_ept(btq->cmd_channel);
 179destroy_acl_channel:
 180        rpmsg_destroy_ept(btq->acl_channel);
 181
 182        return ret;
 183}
 184
 185static int btqcomsmd_remove(struct platform_device *pdev)
 186{
 187        struct btqcomsmd *btq = platform_get_drvdata(pdev);
 188
 189        hci_unregister_dev(btq->hdev);
 190        hci_free_dev(btq->hdev);
 191
 192        rpmsg_destroy_ept(btq->cmd_channel);
 193        rpmsg_destroy_ept(btq->acl_channel);
 194
 195        return 0;
 196}
 197
 198static const struct of_device_id btqcomsmd_of_match[] = {
 199        { .compatible = "qcom,wcnss-bt", },
 200        { },
 201};
 202MODULE_DEVICE_TABLE(of, btqcomsmd_of_match);
 203
 204static struct platform_driver btqcomsmd_driver = {
 205        .probe = btqcomsmd_probe,
 206        .remove = btqcomsmd_remove,
 207        .driver  = {
 208                .name  = "btqcomsmd",
 209                .of_match_table = btqcomsmd_of_match,
 210        },
 211};
 212
 213module_platform_driver(btqcomsmd_driver);
 214
 215MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
 216MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
 217MODULE_LICENSE("GPL v2");
 218