Linux 6.9-rc1
[linux-2.6-microblaze.git] / drivers / bluetooth / btrsi.c
1 /*
2  * Copyright (c) 2017 Redpine Signals Inc.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <net/bluetooth/bluetooth.h>
19 #include <net/bluetooth/hci_core.h>
20 #include <asm/unaligned.h>
21 #include <net/rsi_91x.h>
22
23 #define RSI_DMA_ALIGN   8
24 #define RSI_FRAME_DESC_SIZE     16
25 #define RSI_HEADROOM_FOR_BT_HAL (RSI_FRAME_DESC_SIZE + RSI_DMA_ALIGN)
26
27 struct rsi_hci_adapter {
28         void *priv;
29         struct rsi_proto_ops *proto_ops;
30         struct hci_dev *hdev;
31 };
32
33 static int rsi_hci_open(struct hci_dev *hdev)
34 {
35         return 0;
36 }
37
38 static int rsi_hci_close(struct hci_dev *hdev)
39 {
40         return 0;
41 }
42
43 static int rsi_hci_flush(struct hci_dev *hdev)
44 {
45         return 0;
46 }
47
48 static int rsi_hci_send_pkt(struct hci_dev *hdev, struct sk_buff *skb)
49 {
50         struct rsi_hci_adapter *h_adapter = hci_get_drvdata(hdev);
51         struct sk_buff *new_skb = NULL;
52
53         switch (hci_skb_pkt_type(skb)) {
54         case HCI_COMMAND_PKT:
55                 hdev->stat.cmd_tx++;
56                 break;
57         case HCI_ACLDATA_PKT:
58                 hdev->stat.acl_tx++;
59                 break;
60         case HCI_SCODATA_PKT:
61                 hdev->stat.sco_tx++;
62                 break;
63         }
64
65         if (skb_headroom(skb) < RSI_HEADROOM_FOR_BT_HAL) {
66                 /* Insufficient skb headroom - allocate a new skb */
67                 new_skb = skb_realloc_headroom(skb, RSI_HEADROOM_FOR_BT_HAL);
68                 if (unlikely(!new_skb))
69                         return -ENOMEM;
70                 bt_cb(new_skb)->pkt_type = hci_skb_pkt_type(skb);
71                 kfree_skb(skb);
72                 skb = new_skb;
73                 if (!IS_ALIGNED((unsigned long)skb->data, RSI_DMA_ALIGN)) {
74                         u8 *skb_data = skb->data;
75                         int skb_len = skb->len;
76
77                         skb_push(skb, RSI_DMA_ALIGN);
78                         skb_pull(skb, PTR_ALIGN(skb->data,
79                                                 RSI_DMA_ALIGN) - skb->data);
80                         memmove(skb->data, skb_data, skb_len);
81                         skb_trim(skb, skb_len);
82                 }
83         }
84
85         return h_adapter->proto_ops->coex_send_pkt(h_adapter->priv, skb,
86                                                    RSI_BT_Q);
87 }
88
89 static int rsi_hci_recv_pkt(void *priv, const u8 *pkt)
90 {
91         struct rsi_hci_adapter *h_adapter = priv;
92         struct hci_dev *hdev = h_adapter->hdev;
93         struct sk_buff *skb;
94         int pkt_len = get_unaligned_le16(pkt) & 0x0fff;
95
96         skb = dev_alloc_skb(pkt_len);
97         if (!skb)
98                 return -ENOMEM;
99
100         memcpy(skb->data, pkt + RSI_FRAME_DESC_SIZE, pkt_len);
101         skb_put(skb, pkt_len);
102         h_adapter->hdev->stat.byte_rx += skb->len;
103
104         hci_skb_pkt_type(skb) = pkt[14];
105
106         return hci_recv_frame(hdev, skb);
107 }
108
109 static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
110 {
111         struct rsi_hci_adapter *h_adapter = NULL;
112         struct hci_dev *hdev;
113         int err = 0;
114
115         h_adapter = kzalloc(sizeof(*h_adapter), GFP_KERNEL);
116         if (!h_adapter)
117                 return -ENOMEM;
118
119         h_adapter->priv = priv;
120         ops->set_bt_context(priv, h_adapter);
121         h_adapter->proto_ops = ops;
122
123         hdev = hci_alloc_dev();
124         if (!hdev) {
125                 BT_ERR("Failed to alloc HCI device");
126                 goto err;
127         }
128
129         h_adapter->hdev = hdev;
130
131         if (ops->get_host_intf(priv) == RSI_HOST_INTF_SDIO)
132                 hdev->bus = HCI_SDIO;
133         else
134                 hdev->bus = HCI_USB;
135
136         hci_set_drvdata(hdev, h_adapter);
137         hdev->dev_type = HCI_PRIMARY;
138         hdev->open = rsi_hci_open;
139         hdev->close = rsi_hci_close;
140         hdev->flush = rsi_hci_flush;
141         hdev->send = rsi_hci_send_pkt;
142
143         err = hci_register_dev(hdev);
144         if (err < 0) {
145                 BT_ERR("HCI registration failed with errcode %d", err);
146                 hci_free_dev(hdev);
147                 goto err;
148         }
149
150         return 0;
151 err:
152         h_adapter->hdev = NULL;
153         kfree(h_adapter);
154         return -EINVAL;
155 }
156
157 static void rsi_hci_detach(void *priv)
158 {
159         struct rsi_hci_adapter *h_adapter = priv;
160         struct hci_dev *hdev;
161
162         if (!h_adapter)
163                 return;
164
165         hdev = h_adapter->hdev;
166         if (hdev) {
167                 hci_unregister_dev(hdev);
168                 hci_free_dev(hdev);
169                 h_adapter->hdev = NULL;
170         }
171
172         kfree(h_adapter);
173 }
174
175 const struct rsi_mod_ops rsi_bt_ops = {
176         .attach = rsi_hci_attach,
177         .detach = rsi_hci_detach,
178         .recv_pkt = rsi_hci_recv_pkt,
179 };
180 EXPORT_SYMBOL(rsi_bt_ops);
181
182 static int rsi_91x_bt_module_init(void)
183 {
184         return 0;
185 }
186
187 static void rsi_91x_bt_module_exit(void)
188 {
189         return;
190 }
191
192 module_init(rsi_91x_bt_module_init);
193 module_exit(rsi_91x_bt_module_exit);
194 MODULE_AUTHOR("Redpine Signals Inc");
195 MODULE_DESCRIPTION("RSI BT driver");
196 MODULE_LICENSE("Dual BSD/GPL");