]>
Commit | Line | Data |
---|---|---|
e8b168e8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
71d37aa1 MM |
2 | /* |
3 | * Espressif Systems Wireless LAN device driver | |
4 | * | |
d7215282 | 5 | * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD |
71d37aa1 | 6 | * |
71d37aa1 | 7 | */ |
057d3956 | 8 | #include "utils.h" |
71d37aa1 | 9 | #include "esp_api.h" |
4c494b4b | 10 | #include "esp_kernel_port.h" |
71d37aa1 | 11 | |
d718e190 YM |
12 | #define INVALID_HDEV_BUS (0xff) |
13 | ||
c6a5eef9 MM |
14 | void esp_hci_update_tx_counter(struct hci_dev *hdev, u8 pkt_type, size_t len) |
15 | { | |
a3829bf3 KG |
16 | if (!hdev) |
17 | return; | |
18 | if (pkt_type == HCI_COMMAND_PKT) { | |
19 | hdev->stat.cmd_tx++; | |
20 | } else if (pkt_type == HCI_ACLDATA_PKT) { | |
21 | hdev->stat.acl_tx++; | |
22 | } else if (pkt_type == HCI_SCODATA_PKT) { | |
23 | hdev->stat.sco_tx++; | |
c6a5eef9 | 24 | } |
a3829bf3 KG |
25 | |
26 | hdev->stat.byte_tx += len; | |
c6a5eef9 MM |
27 | } |
28 | ||
29 | void esp_hci_update_rx_counter(struct hci_dev *hdev, u8 pkt_type, size_t len) | |
30 | { | |
a3829bf3 KG |
31 | if (!hdev) |
32 | return; | |
33 | ||
34 | if (pkt_type == HCI_EVENT_PKT) { | |
35 | hdev->stat.evt_rx++; | |
36 | } else if (pkt_type == HCI_ACLDATA_PKT) { | |
37 | hdev->stat.acl_rx++; | |
38 | } else if (pkt_type == HCI_SCODATA_PKT) { | |
39 | hdev->stat.sco_rx++; | |
c6a5eef9 | 40 | } |
a3829bf3 KG |
41 | |
42 | hdev->stat.byte_rx += len; | |
c6a5eef9 MM |
43 | } |
44 | ||
71d37aa1 MM |
45 | static int esp_bt_open(struct hci_dev *hdev) |
46 | { | |
47 | return 0; | |
48 | } | |
49 | ||
50 | static int esp_bt_close(struct hci_dev *hdev) | |
51 | { | |
52 | return 0; | |
53 | } | |
54 | ||
55 | static int esp_bt_flush(struct hci_dev *hdev) | |
56 | { | |
57 | return 0; | |
58 | } | |
59 | ||
4c494b4b | 60 | static ESP_BT_SEND_FRAME_PROTOTYPE() |
71d37aa1 MM |
61 | { |
62 | struct esp_payload_header *hdr; | |
63 | size_t total_len, len = skb->len; | |
64 | int ret = 0; | |
85c5e78a | 65 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)) |
a3829bf3 | 66 | struct hci_dev *hdev = (struct hci_dev *)(skb->dev); |
85c5e78a | 67 | #endif |
71d37aa1 MM |
68 | struct esp_adapter *adapter = hci_get_drvdata(hdev); |
69 | struct sk_buff *new_skb; | |
5bd53032 MM |
70 | u8 pad_len = 0, realloc_skb = 0; |
71 | u8 *pos = NULL; | |
72 | u8 pkt_type; | |
71d37aa1 MM |
73 | |
74 | if (!adapter) { | |
6f8fee57 | 75 | esp_err("Invalid args"); |
71d37aa1 MM |
76 | return -EINVAL; |
77 | } | |
d53a8a4a | 78 | //print_hex_dump(KERN_INFO, "bt_tx: ", DUMP_PREFIX_ADDRESS, 16, 1, skb->data, len, 1 ); |
71d37aa1 | 79 | |
5bd53032 MM |
80 | /* Create space for payload header */ |
81 | pad_len = sizeof(struct esp_payload_header); | |
71d37aa1 MM |
82 | total_len = len + sizeof(struct esp_payload_header); |
83 | ||
5bd53032 MM |
84 | /* Align buffer len */ |
85 | pad_len += SKB_DATA_ADDR_ALIGNMENT - (total_len % SKB_DATA_ADDR_ALIGNMENT); | |
71d37aa1 | 86 | |
5bd53032 MM |
87 | pkt_type = hci_skb_pkt_type(skb); |
88 | ||
89 | if (skb_headroom(skb) < pad_len) { | |
90 | /* Headroom is not sufficient */ | |
91 | realloc_skb = 1; | |
92 | } | |
93 | ||
94 | if (realloc_skb || !IS_ALIGNED((unsigned long) skb->data, SKB_DATA_ADDR_ALIGNMENT)) { | |
95 | /* Realloc SKB */ | |
96 | if (skb_linearize(skb)) { | |
97 | hdev->stat.err_tx++; | |
98 | return -EINVAL; | |
99 | } | |
100 | ||
101 | new_skb = esp_alloc_skb(skb->len + pad_len); | |
102 | ||
103 | if (!new_skb) { | |
6f8fee57 | 104 | esp_err("Failed to allocate SKB"); |
c6a5eef9 | 105 | hdev->stat.err_tx++; |
71d37aa1 MM |
106 | return -ENOMEM; |
107 | } | |
108 | ||
5bd53032 MM |
109 | pos = new_skb->data; |
110 | ||
111 | pos += pad_len; | |
112 | ||
113 | /* Populate new SKB */ | |
114 | skb_copy_from_linear_data(skb, pos, skb->len); | |
115 | skb_put(new_skb, skb->len); | |
71d37aa1 | 116 | |
5bd53032 MM |
117 | /* Replace old SKB */ |
118 | dev_kfree_skb_any(skb); | |
71d37aa1 | 119 | skb = new_skb; |
5bd53032 MM |
120 | } else { |
121 | /* Realloc is not needed, Make space for interface header */ | |
122 | skb_push(skb, pad_len); | |
71d37aa1 MM |
123 | } |
124 | ||
71d37aa1 MM |
125 | hdr = (struct esp_payload_header *) skb->data; |
126 | ||
a3829bf3 | 127 | memset(hdr, 0, sizeof(struct esp_payload_header)); |
71d37aa1 MM |
128 | |
129 | hdr->if_type = ESP_HCI_IF; | |
130 | hdr->if_num = 0; | |
131 | hdr->len = cpu_to_le16(len); | |
5bd53032 MM |
132 | hdr->offset = cpu_to_le16(pad_len); |
133 | pos = skb->data; | |
134 | ||
135 | /* set HCI packet type */ | |
136 | *(pos + pad_len - 1) = pkt_type; | |
71d37aa1 | 137 | |
bf3d6cb6 SR |
138 | if (adapter->capabilities & ESP_CHECKSUM_ENABLED) |
139 | hdr->checksum = cpu_to_le16(compute_checksum(skb->data, (len + pad_len))); | |
54f5d2e4 | 140 | |
03e9f879 | 141 | ret = esp_send_packet(adapter, skb); |
71d37aa1 | 142 | |
c6a5eef9 MM |
143 | if (ret) { |
144 | hdev->stat.err_tx++; | |
5bd53032 | 145 | return ret; |
c6a5eef9 MM |
146 | } else { |
147 | esp_hci_update_tx_counter(hdev, hdr->hci_pkt_type, skb->len); | |
148 | } | |
149 | ||
71d37aa1 MM |
150 | return 0; |
151 | } | |
152 | ||
85c5e78a | 153 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) |
71d37aa1 MM |
154 | static int esp_bt_setup(struct hci_dev *hdev) |
155 | { | |
156 | return 0; | |
157 | } | |
85c5e78a | 158 | #endif |
71d37aa1 | 159 | |
85c5e78a | 160 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) |
71d37aa1 MM |
161 | static int esp_bt_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) |
162 | { | |
163 | return 0; | |
164 | } | |
85c5e78a | 165 | #endif |
71d37aa1 MM |
166 | |
167 | int esp_deinit_bt(struct esp_adapter *adapter) | |
168 | { | |
169 | struct hci_dev *hdev = NULL; | |
170 | ||
062be972 MM |
171 | if (!adapter || !adapter->hcidev) |
172 | return 0; | |
173 | ||
71d37aa1 MM |
174 | hdev = adapter->hcidev; |
175 | ||
176 | hci_unregister_dev(hdev); | |
177 | hci_free_dev(hdev); | |
178 | ||
179 | adapter->hcidev = NULL; | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | int esp_init_bt(struct esp_adapter *adapter) | |
185 | { | |
186 | int ret = 0; | |
187 | struct hci_dev *hdev = NULL; | |
188 | ||
189 | if (!adapter) { | |
190 | return -EINVAL; | |
191 | } | |
192 | ||
193 | if (adapter->hcidev) { | |
194 | return -EEXIST; | |
195 | } | |
196 | ||
197 | hdev = hci_alloc_dev(); | |
198 | ||
199 | if (!hdev) { | |
200 | BT_ERR("Can not allocate HCI device"); | |
201 | return -ENOMEM; | |
202 | } | |
203 | ||
204 | adapter->hcidev = hdev; | |
205 | hci_set_drvdata(hdev, adapter); | |
206 | ||
d718e190 YM |
207 | hdev->bus = INVALID_HDEV_BUS; |
208 | ||
062be972 MM |
209 | if (adapter->if_type == ESP_IF_TYPE_SDIO) |
210 | hdev->bus = HCI_SDIO; | |
85c5e78a | 211 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) |
062be972 MM |
212 | else if (adapter->if_type == ESP_IF_TYPE_SPI) |
213 | hdev->bus = HCI_SPI; | |
85c5e78a | 214 | #endif |
d718e190 YM |
215 | |
216 | if (hdev->bus == INVALID_HDEV_BUS) { | |
217 | ||
218 | if (adapter->if_type == ESP_IF_TYPE_SDIO) { | |
6f8fee57 | 219 | esp_err("Kernel version does not support HCI over SDIO BUS\n"); |
d718e190 | 220 | } else if (adapter->if_type == ESP_IF_TYPE_SPI) { |
6f8fee57 | 221 | esp_err("Kernel version does not support HCI over SPI BUS\n"); |
d718e190 | 222 | } else { |
a3829bf3 | 223 | esp_err("HCI over expected BUS[%u] is not supported\n", adapter->if_type); |
d718e190 YM |
224 | } |
225 | hci_free_dev(hdev); | |
226 | adapter->hcidev = NULL; | |
227 | return -EINVAL; | |
228 | } | |
062be972 | 229 | |
71d37aa1 MM |
230 | hdev->open = esp_bt_open; |
231 | hdev->close = esp_bt_close; | |
232 | hdev->flush = esp_bt_flush; | |
233 | hdev->send = esp_bt_send_frame; | |
85c5e78a | 234 | |
235 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) | |
71d37aa1 | 236 | hdev->setup = esp_bt_setup; |
85c5e78a | 237 | #endif |
238 | ||
239 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) | |
71d37aa1 | 240 | hdev->set_bdaddr = esp_bt_set_bdaddr; |
85c5e78a | 241 | #endif |
71d37aa1 MM |
242 | |
243 | hdev->dev_type = HCI_PRIMARY; | |
244 | ||
245 | ret = hci_register_dev(hdev); | |
246 | if (ret < 0) { | |
247 | BT_ERR("Can not register HCI device"); | |
248 | hci_free_dev(hdev); | |
249 | return -ENOMEM; | |
250 | } | |
251 | ||
252 | return 0; | |
253 | } |