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, len: 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(p: pkt) & 0x0fff; |
95 | |
96 | skb = dev_alloc_skb(length: pkt_len); |
97 | if (!skb) |
98 | return -ENOMEM; |
99 | |
100 | memcpy(skb->data, pkt + RSI_FRAME_DESC_SIZE, pkt_len); |
101 | skb_put(skb, len: 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(size: 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, data: 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(objp: 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(objp: 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" ); |
197 | |