]>
Commit | Line | Data |
---|---|---|
e8b168e8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
774e9b2e MM |
2 | /* |
3 | * Espressif Systems Wireless LAN device driver | |
4 | * | |
d7215282 | 5 | * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD |
774e9b2e | 6 | * |
774e9b2e | 7 | */ |
057d3956 | 8 | #include "utils.h" |
774e9b2e MM |
9 | #include "esp.h" |
10 | #include "esp_api.h" | |
11 | #include "esp_cfg80211.h" | |
12 | #include "esp_cmd.h" | |
aad2ae2e | 13 | #include "esp_kernel_port.h" |
774e9b2e MM |
14 | |
15 | /** | |
16 | * @brief WiFi PHY rate encodings | |
17 | * | |
18 | */ | |
19 | typedef enum { | |
5a47b073 MM |
20 | WIFI_PHY_RATE_1M_L = 0x00, /**< 1 Mbps with long preamble */ |
21 | WIFI_PHY_RATE_2M_L = 0x01, /**< 2 Mbps with long preamble */ | |
22 | WIFI_PHY_RATE_5M_L = 0x02, /**< 5.5 Mbps with long preamble */ | |
23 | WIFI_PHY_RATE_11M_L = 0x03, /**< 11 Mbps with long preamble */ | |
24 | WIFI_PHY_RATE_2M_S = 0x05, /**< 2 Mbps with short preamble */ | |
25 | WIFI_PHY_RATE_5M_S = 0x06, /**< 5.5 Mbps with short preamble */ | |
26 | WIFI_PHY_RATE_11M_S = 0x07, /**< 11 Mbps with short preamble */ | |
27 | WIFI_PHY_RATE_48M = 0x08, /**< 48 Mbps */ | |
28 | WIFI_PHY_RATE_24M = 0x09, /**< 24 Mbps */ | |
29 | WIFI_PHY_RATE_12M = 0x0A, /**< 12 Mbps */ | |
30 | WIFI_PHY_RATE_6M = 0x0B, /**< 6 Mbps */ | |
31 | WIFI_PHY_RATE_54M = 0x0C, /**< 54 Mbps */ | |
32 | WIFI_PHY_RATE_36M = 0x0D, /**< 36 Mbps */ | |
33 | WIFI_PHY_RATE_18M = 0x0E, /**< 18 Mbps */ | |
34 | WIFI_PHY_RATE_9M = 0x0F, /**< 9 Mbps */ | |
35 | WIFI_PHY_RATE_MCS0_LGI = 0x10, /**< MCS0 with long GI, 6.5 Mbps for 20MHz, 13.5 Mbps for 40MHz */ | |
36 | WIFI_PHY_RATE_MCS1_LGI = 0x11, /**< MCS1 with long GI, 13 Mbps for 20MHz, 27 Mbps for 40MHz */ | |
37 | WIFI_PHY_RATE_MCS2_LGI = 0x12, /**< MCS2 with long GI, 19.5 Mbps for 20MHz, 40.5 Mbps for 40MHz */ | |
38 | WIFI_PHY_RATE_MCS3_LGI = 0x13, /**< MCS3 with long GI, 26 Mbps for 20MHz, 54 Mbps for 40MHz */ | |
39 | WIFI_PHY_RATE_MCS4_LGI = 0x14, /**< MCS4 with long GI, 39 Mbps for 20MHz, 81 Mbps for 40MHz */ | |
40 | WIFI_PHY_RATE_MCS5_LGI = 0x15, /**< MCS5 with long GI, 52 Mbps for 20MHz, 108 Mbps for 40MHz */ | |
41 | WIFI_PHY_RATE_MCS6_LGI = 0x16, /**< MCS6 with long GI, 58.5 Mbps for 20MHz, 121.5 Mbps for 40MHz */ | |
42 | WIFI_PHY_RATE_MCS7_LGI = 0x17, /**< MCS7 with long GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz */ | |
43 | WIFI_PHY_RATE_MCS0_SGI = 0x18, /**< MCS0 with short GI, 7.2 Mbps for 20MHz, 15 Mbps for 40MHz */ | |
44 | WIFI_PHY_RATE_MCS1_SGI = 0x19, /**< MCS1 with short GI, 14.4 Mbps for 20MHz, 30 Mbps for 40MHz */ | |
45 | WIFI_PHY_RATE_MCS2_SGI = 0x1A, /**< MCS2 with short GI, 21.7 Mbps for 20MHz, 45 Mbps for 40MHz */ | |
46 | WIFI_PHY_RATE_MCS3_SGI = 0x1B, /**< MCS3 with short GI, 28.9 Mbps for 20MHz, 60 Mbps for 40MHz */ | |
47 | WIFI_PHY_RATE_MCS4_SGI = 0x1C, /**< MCS4 with short GI, 43.3 Mbps for 20MHz, 90 Mbps for 40MHz */ | |
48 | WIFI_PHY_RATE_MCS5_SGI = 0x1D, /**< MCS5 with short GI, 57.8 Mbps for 20MHz, 120 Mbps for 40MHz */ | |
49 | WIFI_PHY_RATE_MCS6_SGI = 0x1E, /**< MCS6 with short GI, 65 Mbps for 20MHz, 135 Mbps for 40MHz */ | |
50 | WIFI_PHY_RATE_MCS7_SGI = 0x1F, /**< MCS7 with short GI, 72.2 Mbps for 20MHz, 150 Mbps for 40MHz */ | |
51 | WIFI_PHY_RATE_LORA_250K = 0x29, /**< 250 Kbps */ | |
52 | WIFI_PHY_RATE_LORA_500K = 0x2A, /**< 500 Kbps */ | |
53 | WIFI_PHY_RATE_MAX, | |
774e9b2e MM |
54 | } wifi_phy_rate_t; |
55 | ||
56 | /* Supported rates to be advertised to the cfg80211 */ | |
57 | static struct ieee80211_rate esp_rates[] = { | |
5a47b073 MM |
58 | {.bitrate = 10, .hw_value = WIFI_PHY_RATE_1M_L, }, |
59 | {.bitrate = 20, .hw_value = WIFI_PHY_RATE_2M_L, }, | |
60 | {.bitrate = 55, .hw_value = WIFI_PHY_RATE_5M_L, .hw_value_short = WIFI_PHY_RATE_5M_S}, | |
61 | {.bitrate = 110, .hw_value = WIFI_PHY_RATE_11M_L, .hw_value_short = WIFI_PHY_RATE_11M_S}, | |
62 | {.bitrate = 60, .hw_value = WIFI_PHY_RATE_6M, }, | |
63 | {.bitrate = 90, .hw_value = WIFI_PHY_RATE_9M, }, | |
64 | {.bitrate = 120, .hw_value = WIFI_PHY_RATE_12M, }, | |
65 | {.bitrate = 180, .hw_value = WIFI_PHY_RATE_18M, }, | |
66 | {.bitrate = 240, .hw_value = WIFI_PHY_RATE_24M, }, | |
67 | {.bitrate = 360, .hw_value = WIFI_PHY_RATE_36M, }, | |
68 | {.bitrate = 480, .hw_value = WIFI_PHY_RATE_48M, }, | |
69 | {.bitrate = 540, .hw_value = WIFI_PHY_RATE_54M, }, | |
774e9b2e MM |
70 | }; |
71 | ||
72 | ||
73 | /* Channel definitions to be advertised to cfg80211 */ | |
74 | static struct ieee80211_channel esp_channels_2ghz[] = { | |
5a47b073 MM |
75 | {.center_freq = 2412, .hw_value = 1, }, |
76 | {.center_freq = 2417, .hw_value = 2, }, | |
77 | {.center_freq = 2422, .hw_value = 3, }, | |
78 | {.center_freq = 2427, .hw_value = 4, }, | |
79 | {.center_freq = 2432, .hw_value = 5, }, | |
80 | {.center_freq = 2437, .hw_value = 6, }, | |
81 | {.center_freq = 2442, .hw_value = 7, }, | |
82 | {.center_freq = 2447, .hw_value = 8, }, | |
83 | {.center_freq = 2452, .hw_value = 9, }, | |
84 | {.center_freq = 2457, .hw_value = 10, }, | |
85 | {.center_freq = 2462, .hw_value = 11, }, | |
86 | {.center_freq = 2467, .hw_value = 12, }, | |
87 | {.center_freq = 2472, .hw_value = 13, }, | |
88 | {.center_freq = 2484, .hw_value = 14, }, | |
774e9b2e MM |
89 | }; |
90 | ||
91 | static struct ieee80211_supported_band esp_wifi_bands = { | |
5a47b073 MM |
92 | .channels = esp_channels_2ghz, |
93 | .n_channels = ARRAY_SIZE(esp_channels_2ghz), | |
94 | .bitrates = esp_rates, | |
95 | .n_bitrates = ARRAY_SIZE(esp_rates), | |
774e9b2e MM |
96 | }; |
97 | ||
98 | /* Supported crypto cipher suits to be advertised to cfg80211 */ | |
99 | static const u32 esp_cipher_suites[] = { | |
5a47b073 MM |
100 | WLAN_CIPHER_SUITE_WEP40, |
101 | WLAN_CIPHER_SUITE_WEP104, | |
102 | WLAN_CIPHER_SUITE_TKIP, | |
103 | WLAN_CIPHER_SUITE_CCMP, | |
104 | WLAN_CIPHER_SUITE_SMS4, | |
105 | WLAN_CIPHER_SUITE_AES_CMAC, | |
774e9b2e MM |
106 | }; |
107 | ||
3fef9acf | 108 | static const struct wiphy_wowlan_support esp_wowlan_support = { |
5a47b073 MM |
109 | .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT, |
110 | .n_patterns = 0, | |
111 | .pattern_max_len = 0, | |
112 | .pattern_min_len = 0, | |
113 | .max_pkt_offset = 0, | |
3fef9acf MM |
114 | }; |
115 | ||
7406fe82 | 116 | static int esp_inetaddr_event(struct notifier_block *nb, |
5a47b073 | 117 | unsigned long event, void *data) |
7406fe82 | 118 | { |
5a47b073 MM |
119 | struct in_ifaddr *ifa = data; |
120 | struct net_device *netdev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL; | |
7406fe82 MM |
121 | struct esp_wifi_device *priv = netdev_priv(netdev); |
122 | ||
6f8fee57 | 123 | /*esp_info("------- IP event -------: %d\n", priv->if_type);*/ |
5a47b073 MM |
124 | |
125 | if (!strstr(netdev->name, "espsta")) { | |
126 | return 0; | |
127 | } | |
128 | ||
129 | switch (event) { | |
130 | ||
131 | case NETDEV_UP: | |
132 | if (priv && (priv->if_type == ESP_STA_IF)) { | |
133 | cmd_set_ip_address(priv, ifa->ifa_local); | |
a3829bf3 | 134 | esp_info("NETDEV_UP interface %s ip changed to %pi4\n", |
6f8fee57 | 135 | netdev->name, &ifa->ifa_local); |
5a47b073 MM |
136 | } |
137 | break; | |
138 | ||
139 | case NETDEV_DOWN: | |
6f8fee57 | 140 | esp_info("Interface Down: %d\n", priv->if_type); |
5a47b073 MM |
141 | if (priv && (priv->if_type == ESP_STA_IF)) |
142 | cmd_set_ip_address(priv, 0); | |
143 | break; | |
144 | } | |
145 | ||
146 | return 0; | |
7406fe82 MM |
147 | } |
148 | ||
774e9b2e | 149 | struct wireless_dev *esp_cfg80211_add_iface(struct wiphy *wiphy, |
5a47b073 MM |
150 | const char *name, |
151 | unsigned char name_assign_type, | |
152 | enum nl80211_iftype type, | |
153 | struct vif_params *params) | |
774e9b2e | 154 | { |
bd131325 | 155 | struct esp_device *esp_dev = NULL; |
774e9b2e MM |
156 | /* struct wireless_dev *wdev = NULL;*/ |
157 | struct net_device *ndev; | |
158 | struct esp_wifi_device *esp_wdev; | |
159 | uint8_t esp_nw_if_num = 0; | |
160 | ||
bd131325 | 161 | if (!wiphy || !name) { |
6f8fee57 | 162 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
163 | return NULL; |
164 | } | |
165 | ||
166 | esp_dev = wiphy_priv(wiphy); | |
167 | ||
168 | if (!esp_dev || !esp_dev->adapter) { | |
6f8fee57 | 169 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
170 | return NULL; |
171 | } | |
172 | ||
a3829bf3 | 173 | if (type == NL80211_IFTYPE_STATION) { |
774e9b2e | 174 | esp_nw_if_num = ESP_STA_NW_IF; |
a3829bf3 | 175 | } else if (type == NL80211_IFTYPE_AP) { |
774e9b2e MM |
176 | esp_nw_if_num = ESP_AP_NW_IF; |
177 | } else { | |
6f8fee57 KG |
178 | esp_info("%u network type[%u] is not supported\n", |
179 | __LINE__, type); | |
774e9b2e MM |
180 | return NULL; |
181 | } | |
182 | ||
aad2ae2e | 183 | ndev = ALLOC_NETDEV(sizeof(struct esp_wifi_device), name, name_assign_type, |
774e9b2e MM |
184 | ether_setup); |
185 | ||
186 | if (!ndev) | |
187 | return ERR_PTR(-ENOMEM); | |
188 | ||
225e14eb | 189 | set_bit(ESP_DRIVER_ACTIVE, &esp_dev->adapter->state_flags); |
774e9b2e MM |
190 | esp_wdev = netdev_priv(ndev); |
191 | ||
192 | ndev->ieee80211_ptr = &esp_wdev->wdev; | |
193 | esp_wdev->wdev.wiphy = wiphy; | |
194 | esp_wdev->esp_dev = esp_dev; | |
195 | esp_wdev->ndev = ndev; | |
196 | esp_wdev->adapter = esp_dev->adapter; | |
197 | esp_wdev->adapter->priv[esp_nw_if_num] = esp_wdev; | |
6f8fee57 | 198 | /*esp_info("Updated priv[%u] to %px\n", |
774e9b2e MM |
199 | * esp_nw_if_num, esp_wdev->adapter->priv[esp_nw_if_num]);*/ |
200 | dev_net_set(ndev, wiphy_net(wiphy)); | |
201 | SET_NETDEV_DEV(ndev, wiphy_dev(esp_wdev->wdev.wiphy)); | |
202 | esp_wdev->wdev.netdev = ndev; | |
203 | esp_wdev->wdev.iftype = type; | |
204 | ||
205 | init_waitqueue_head(&esp_wdev->wait_for_scan_completion); | |
206 | esp_wdev->stop_data = 1; | |
207 | esp_wdev->port_open = 0; | |
208 | ||
209 | if (cmd_init_interface(esp_wdev)) | |
210 | goto free_and_return; | |
211 | ||
212 | if (cmd_get_mac(esp_wdev)) | |
213 | goto free_and_return; | |
214 | ||
1294f8ac | 215 | eth_hw_addr_set(ndev, esp_wdev->mac_address); |
774e9b2e MM |
216 | |
217 | esp_init_priv(ndev); | |
218 | ||
219 | if (register_netdevice(ndev)) | |
220 | goto free_and_return; | |
221 | ||
222 | ||
223 | set_bit(ESP_NETWORK_UP, &esp_wdev->priv_flags); | |
225e14eb YM |
224 | clear_bit(ESP_CLEANUP_IN_PROGRESS, &esp_dev->adapter->state_flags); |
225 | ||
7406fe82 MM |
226 | esp_wdev->nb.notifier_call = esp_inetaddr_event; |
227 | register_inetaddr_notifier(&esp_wdev->nb); | |
228 | ||
774e9b2e MM |
229 | return &esp_wdev->wdev; |
230 | ||
231 | free_and_return: | |
225e14eb | 232 | clear_bit(ESP_DRIVER_ACTIVE, &esp_wdev->adapter->state_flags); |
774e9b2e MM |
233 | dev_net_set(ndev, NULL); |
234 | free_netdev(ndev); | |
235 | esp_wdev->ndev = NULL; | |
236 | esp_wdev->wdev.netdev = NULL; | |
237 | ndev = NULL; | |
238 | return NULL; | |
239 | } | |
240 | ||
241 | #if 0 | |
242 | static int esp_cfg80211_del_iface(struct wiphy *wiphy, | |
243 | struct wireless_dev *wdev) | |
244 | { | |
245 | return 0; | |
246 | } | |
247 | ||
248 | static int esp_cfg80211_change_iface(struct wiphy *wiphy, | |
249 | struct net_device *ndev, | |
250 | enum nl80211_iftype type, | |
251 | struct vif_params *params) | |
252 | { | |
253 | return 0; | |
254 | } | |
255 | #endif | |
256 | ||
257 | static int esp_cfg80211_scan(struct wiphy *wiphy, | |
bd131325 | 258 | struct cfg80211_scan_request *request) |
774e9b2e | 259 | { |
bd131325 YM |
260 | |
261 | struct net_device *ndev = NULL; | |
262 | struct esp_wifi_device *priv = NULL; | |
bd131325 | 263 | if (!wiphy || !request || !request->wdev || !request->wdev->netdev) { |
6f8fee57 | 264 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
265 | return -EINVAL; |
266 | } | |
267 | ||
268 | ndev = request->wdev->netdev; | |
269 | priv = netdev_priv(ndev); | |
774e9b2e | 270 | |
e58fd496 | 271 | esp_dbg("\n"); |
774e9b2e | 272 | if (!priv) { |
6f8fee57 | 273 | esp_err("Empty priv\n"); |
774e9b2e MM |
274 | return -EINVAL; |
275 | } | |
276 | ||
277 | return cmd_scan_request(priv, request); | |
278 | } | |
279 | ||
bd131325 | 280 | #if 0 |
774e9b2e MM |
281 | static int esp_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, |
282 | struct cfg80211_connect_params *sme) | |
283 | { | |
284 | struct esp_wifi_device *priv = netdev_priv(dev); | |
285 | ||
e58fd496 | 286 | esp_dbg("\n"); |
774e9b2e | 287 | if (!priv) { |
6f8fee57 | 288 | esp_err("Empty priv\n"); |
774e9b2e MM |
289 | return -EINVAL; |
290 | } | |
291 | ||
292 | return cmd_connect_request(priv, sme); | |
293 | } | |
bd131325 | 294 | #endif |
774e9b2e | 295 | |
aad2ae2e | 296 | static ESP_MGMT_TX_PROTOTYPE() |
774e9b2e MM |
297 | { |
298 | return 0; | |
299 | } | |
300 | ||
7426e123 | 301 | static int esp_cfg80211_set_default_key(struct wiphy *wiphy, |
1294f8ac KG |
302 | struct net_device *dev, INT_LINK_ID |
303 | u8 key_index, bool unicast, bool multicast) | |
774e9b2e | 304 | { |
bd131325 | 305 | struct esp_wifi_device *priv = NULL; |
774e9b2e | 306 | |
bd131325 | 307 | if (!wiphy || !dev) { |
6f8fee57 | 308 | esp_err("%u invalid params\n", __LINE__); |
bd131325 YM |
309 | return -EINVAL; |
310 | } | |
311 | ||
312 | priv = netdev_priv(dev); | |
774e9b2e | 313 | if (!priv) { |
6f8fee57 | 314 | esp_err("Empty priv"); |
774e9b2e MM |
315 | return -EINVAL; |
316 | } | |
317 | ||
318 | return cmd_set_default_key(priv, key_index); | |
319 | } | |
320 | ||
7426e123 | 321 | static int esp_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, |
1294f8ac KG |
322 | INT_LINK_ID u8 key_index, bool pairwise, |
323 | const u8 *mac_addr) | |
774e9b2e | 324 | { |
1c991ada | 325 | return 0; |
774e9b2e MM |
326 | } |
327 | ||
7426e123 | 328 | static int esp_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, |
1294f8ac KG |
329 | INT_LINK_ID u8 key_index, bool pairwise, |
330 | const u8 *mac_addr, struct key_params *params) | |
774e9b2e | 331 | { |
bd131325 YM |
332 | struct esp_wifi_device *priv = NULL; |
333 | ||
334 | if (!wiphy || !dev || !params) { | |
6f8fee57 | 335 | esp_err("%u invalid params\n", __LINE__); |
bd131325 YM |
336 | return -EINVAL; |
337 | } | |
774e9b2e | 338 | |
bd131325 | 339 | priv = netdev_priv(dev); |
774e9b2e | 340 | if (!priv) { |
6f8fee57 | 341 | esp_err("Empty priv\n"); |
774e9b2e MM |
342 | return -EINVAL; |
343 | } | |
e58fd496 | 344 | esp_dbg("\n"); |
774e9b2e | 345 | |
7426e123 | 346 | if (params->key_len == 0) { |
1294f8ac | 347 | return esp_cfg80211_del_key(wiphy, dev, ZERO_LINK_ID key_index, pairwise, mac_addr); |
7426e123 | 348 | } |
774e9b2e MM |
349 | return cmd_add_key(priv, key_index, pairwise, mac_addr, params); |
350 | } | |
351 | ||
352 | static int esp_cfg80211_disconnect(struct wiphy *wiphy, | |
353 | struct net_device *dev, u16 reason_code) | |
354 | { | |
bd131325 YM |
355 | struct esp_wifi_device *priv = NULL; |
356 | ||
357 | if (!wiphy || !dev) { | |
6f8fee57 | 358 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
359 | return -EINVAL; |
360 | } | |
361 | ||
362 | priv = netdev_priv(dev); | |
774e9b2e MM |
363 | |
364 | if (!priv) { | |
6f8fee57 | 365 | esp_err("empty priv\n"); |
774e9b2e MM |
366 | return 0; |
367 | } | |
e58fd496 | 368 | esp_dbg("\n"); |
774e9b2e MM |
369 | |
370 | return cmd_disconnect_request(priv, reason_code); | |
371 | } | |
372 | ||
7b6ed49e MM |
373 | static int esp_cfg80211_authenticate(struct wiphy *wiphy, struct net_device *dev, |
374 | struct cfg80211_auth_request *req) | |
375 | { | |
bd131325 YM |
376 | struct esp_wifi_device *priv = NULL; |
377 | ||
378 | if (!wiphy || !dev || !req) { | |
6f8fee57 | 379 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
380 | return -EINVAL; |
381 | } | |
382 | ||
e58fd496 | 383 | esp_dbg("\n"); |
7b6ed49e | 384 | |
bd131325 YM |
385 | priv = netdev_priv(dev); |
386 | ||
7b6ed49e | 387 | if (!priv) { |
6f8fee57 | 388 | esp_err("Empty priv\n"); |
7b6ed49e MM |
389 | return 0; |
390 | } | |
391 | ||
392 | return cmd_auth_request(priv, req); | |
393 | } | |
394 | ||
395 | ||
396 | static int esp_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev, | |
397 | struct cfg80211_assoc_request *req) | |
398 | { | |
bd131325 YM |
399 | struct esp_wifi_device *priv = NULL; |
400 | ||
401 | if (!wiphy || !dev || !req) { | |
6f8fee57 | 402 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
403 | return -EINVAL; |
404 | } | |
405 | ||
406 | priv = netdev_priv(dev); | |
7b6ed49e | 407 | |
e58fd496 | 408 | esp_dbg("\n"); |
bd131325 | 409 | |
7b6ed49e | 410 | if (!priv) { |
6f8fee57 | 411 | esp_err("Empty priv\n"); |
7b6ed49e MM |
412 | return 0; |
413 | } | |
414 | ||
415 | return cmd_assoc_request(priv, req); | |
416 | } | |
417 | ||
418 | static int esp_cfg80211_deauth(struct wiphy *wiphy, struct net_device *dev, | |
419 | struct cfg80211_deauth_request *req) | |
420 | { | |
bd131325 YM |
421 | struct esp_wifi_device *priv = NULL; |
422 | ||
423 | if (!wiphy || !dev || !req) { | |
6f8fee57 | 424 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
425 | return -EINVAL; |
426 | } | |
7b6ed49e | 427 | |
e58fd496 | 428 | esp_dbg("\n"); |
bd131325 | 429 | priv = netdev_priv(dev); |
7b6ed49e MM |
430 | |
431 | if (!priv) { | |
6f8fee57 | 432 | esp_err("Empty priv\n"); |
7b6ed49e MM |
433 | return 0; |
434 | } | |
435 | ||
436 | return cmd_disconnect_request(priv, req->reason_code); | |
437 | } | |
438 | ||
439 | static int esp_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, | |
440 | struct cfg80211_disassoc_request *req) | |
441 | { | |
bd131325 YM |
442 | struct esp_wifi_device *priv = NULL; |
443 | ||
444 | if (!wiphy || !dev || !req) { | |
6f8fee57 | 445 | esp_info("%u invalid input\n", __LINE__); |
bd131325 YM |
446 | return -EINVAL; |
447 | } | |
7b6ed49e | 448 | |
e58fd496 | 449 | esp_dbg("\n"); |
bd131325 | 450 | priv = netdev_priv(dev); |
7b6ed49e MM |
451 | |
452 | if (!priv) { | |
6f8fee57 | 453 | esp_err("Empty priv\n"); |
7b6ed49e MM |
454 | return 0; |
455 | } | |
bd131325 | 456 | |
7b6ed49e MM |
457 | return cmd_disconnect_request(priv, req->reason_code); |
458 | } | |
459 | ||
3fef9acf | 460 | static int esp_cfg80211_suspend(struct wiphy *wiphy, |
5a47b073 | 461 | struct cfg80211_wowlan *wowlan) |
3fef9acf | 462 | { |
e58fd496 | 463 | /*esp_dbg("\n");*/ |
3fef9acf MM |
464 | return 0; |
465 | } | |
466 | ||
467 | static int esp_cfg80211_resume(struct wiphy *wiphy) | |
468 | { | |
e58fd496 | 469 | /*esp_dbg("\n");*/ |
3fef9acf MM |
470 | return 0; |
471 | } | |
472 | ||
473 | static void esp_cfg80211_set_wakeup(struct wiphy *wiphy, | |
5a47b073 | 474 | bool enabled) |
3fef9acf | 475 | { |
e58fd496 | 476 | /*esp_dbg("\n");*/ |
3fef9acf MM |
477 | } |
478 | ||
98f8ece9 KG |
479 | /* TODO get MAX_TX_POWER_MBM from Firmware for future chips */ |
480 | #define MAX_TX_POWER_MBM (20 * 100) | |
9726a3d0 KG |
481 | static bool is_txpwr_valid(int mbm) |
482 | { | |
98f8ece9 | 483 | if (mbm > MAX_TX_POWER_MBM) |
9726a3d0 KG |
484 | return false; |
485 | ||
486 | return true; | |
487 | } | |
488 | ||
489 | static int mbm_to_esp_pwr(int mbm) | |
490 | { | |
491 | return ((mbm * 4) / 100); | |
492 | } | |
493 | ||
494 | static int esp_pwr_to_dbm(int power) | |
495 | { | |
496 | return ((power / 4)); | |
497 | } | |
498 | ||
499 | static int esp_cfg80211_set_tx_power(struct wiphy *wiphy, | |
500 | struct wireless_dev *wdev, | |
501 | enum nl80211_tx_power_setting type, int mbm) | |
502 | { | |
503 | struct esp_adapter *adapter = esp_get_adapter(); | |
504 | struct esp_wifi_device *priv = NULL; | |
505 | ||
506 | if (!wiphy || !adapter) { | |
507 | esp_info("%u invalid input %p %p \n", __LINE__, wiphy, wdev); | |
508 | return -EINVAL; | |
509 | } | |
510 | ||
511 | esp_dbg("\n"); | |
512 | ||
513 | priv = adapter->priv[0]; | |
514 | if (!priv) { | |
515 | esp_err("Empty priv\n"); | |
516 | return 0; | |
517 | } | |
518 | ||
519 | switch (type) { | |
520 | case NL80211_TX_POWER_AUTOMATIC: | |
521 | priv->tx_pwr_type = NL80211_TX_POWER_AUTOMATIC; | |
98f8ece9 | 522 | priv->tx_pwr = mbm_to_esp_pwr(MAX_TX_POWER_MBM); |
9726a3d0 KG |
523 | break; |
524 | case NL80211_TX_POWER_LIMITED: | |
525 | if (!is_txpwr_valid(mbm)) { | |
526 | esp_warn("mbm:%d not support\n", mbm); | |
527 | return -EOPNOTSUPP; | |
528 | } | |
529 | priv->tx_pwr_type = NL80211_TX_POWER_LIMITED; | |
530 | priv->tx_pwr = mbm_to_esp_pwr(mbm); | |
531 | break; | |
532 | case NL80211_TX_POWER_FIXED: | |
533 | return -EOPNOTSUPP; | |
534 | break; | |
535 | default: | |
536 | esp_warn("unknown type:%d\n", type); | |
537 | } | |
538 | ||
539 | return cmd_set_tx_power(priv, priv->tx_pwr); | |
540 | } | |
541 | ||
542 | static int esp_cfg80211_get_tx_power(struct wiphy *wiphy, | |
543 | struct wireless_dev *wdev, | |
544 | int *dbm) | |
545 | { | |
546 | struct esp_wifi_device *priv = NULL; | |
547 | ||
548 | if (!wiphy || !wdev || !dbm || !wdev->netdev) { | |
549 | esp_info("%u invalid input\n", __LINE__); | |
550 | return -EINVAL; | |
551 | } | |
552 | ||
553 | esp_dbg("\n"); | |
554 | priv = netdev_priv(wdev->netdev); | |
555 | ||
556 | if (!priv) { | |
557 | esp_err("Empty priv\n"); | |
558 | return -EINVAL; | |
559 | } | |
560 | ||
561 | *dbm = esp_pwr_to_dbm(priv->tx_pwr); | |
562 | ||
563 | return 0; | |
564 | } | |
565 | ||
774e9b2e MM |
566 | static struct cfg80211_ops esp_cfg80211_ops = { |
567 | #if 0 | |
568 | .add_virtual_intf = esp_cfg80211_add_iface, | |
569 | .del_virtual_intf = esp_cfg80211_del_iface, | |
570 | .change_virtual_intf = esp_cfg80211_change_iface, | |
571 | #endif | |
572 | .scan = esp_cfg80211_scan, | |
bd131325 | 573 | /*.connect = esp_cfg80211_connect,*/ |
774e9b2e MM |
574 | .disconnect = esp_cfg80211_disconnect, |
575 | .add_key = esp_cfg80211_add_key, | |
576 | .del_key = esp_cfg80211_del_key, | |
577 | .set_default_key = esp_cfg80211_set_default_key, | |
578 | .mgmt_tx = esp_cfg80211_mgmt_tx, | |
7b6ed49e MM |
579 | .auth = esp_cfg80211_authenticate, |
580 | .deauth = esp_cfg80211_deauth, | |
581 | .disassoc = esp_cfg80211_disassoc, | |
582 | .assoc = esp_cfg80211_associate, | |
3fef9acf MM |
583 | .suspend = esp_cfg80211_suspend, |
584 | .resume = esp_cfg80211_resume, | |
585 | .set_wakeup = esp_cfg80211_set_wakeup, | |
9726a3d0 KG |
586 | .set_tx_power = esp_cfg80211_set_tx_power, |
587 | .get_tx_power = esp_cfg80211_get_tx_power, | |
774e9b2e MM |
588 | }; |
589 | ||
f7ac1df4 KG |
590 | static void esp_reg_notifier(struct wiphy *wiphy, |
591 | struct regulatory_request *request) | |
592 | { | |
593 | struct esp_wifi_device *priv = NULL; | |
594 | struct esp_device *esp_dev = NULL; | |
012db46c | 595 | struct esp_adapter *adapter = esp_get_adapter(); |
f7ac1df4 KG |
596 | |
597 | if (!wiphy || !request) { | |
598 | esp_info("%u invalid input\n", __LINE__); | |
599 | return; | |
600 | } | |
601 | ||
366297f9 KG |
602 | if (!test_bit(ESP_INIT_DONE, &adapter->state_flags)) { |
603 | esp_info("Driver init is ongoing\n"); | |
604 | return; | |
605 | } | |
606 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &adapter->state_flags)) { | |
607 | esp_info("Driver cleanup is ongoing\n"); | |
608 | return; | |
609 | } | |
610 | ||
f7ac1df4 KG |
611 | esp_dev = wiphy_priv(wiphy); |
612 | ||
613 | if (!esp_dev || !esp_dev->adapter) { | |
614 | esp_info("%u esp_dev not initialized yet \n", __LINE__); | |
615 | return; | |
616 | } | |
012db46c | 617 | |
f7ac1df4 KG |
618 | priv = esp_dev->adapter->priv[0]; |
619 | ||
620 | if (!priv) { | |
621 | esp_info("%u esp_wifi_device not initialized yet \n", __LINE__); | |
622 | return; | |
623 | } | |
624 | esp_info("cfg80211 regulatory domain callback for %c%c, current=%c%c\n", | |
625 | request->alpha2[0], request->alpha2[1], priv->country_code[0], priv->country_code[1]); | |
626 | ||
627 | switch (request->initiator) { | |
628 | case NL80211_REGDOM_SET_BY_DRIVER: | |
629 | case NL80211_REGDOM_SET_BY_CORE: | |
630 | case NL80211_REGDOM_SET_BY_USER: | |
631 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | |
632 | break; | |
633 | default: | |
634 | esp_dbg("unknown regdom initiator: %d\n", request->initiator); | |
635 | return; | |
636 | } | |
637 | ||
638 | /* Don't send same regdom info to firmware */ | |
639 | if (strncmp(request->alpha2, priv->country_code, strlen(request->alpha2))) { | |
640 | strlcpy(priv->country_code, request->alpha2, MAX_COUNTRY_LEN); | |
641 | cmd_set_reg_domain(priv); | |
642 | } | |
643 | } | |
644 | ||
774e9b2e MM |
645 | int esp_cfg80211_register(struct esp_adapter *adapter) |
646 | { | |
647 | struct wiphy *wiphy; | |
648 | struct esp_device *esp_dev; | |
649 | int ret = 0; | |
650 | ||
651 | wiphy = wiphy_new(&esp_cfg80211_ops, sizeof(struct esp_device)); | |
652 | ||
653 | if (!wiphy) { | |
6f8fee57 | 654 | esp_err("Failed to create wiphy\n"); |
774e9b2e MM |
655 | return -EFAULT; |
656 | } | |
657 | ||
658 | adapter->wiphy = wiphy; | |
659 | ||
660 | esp_dev = wiphy_priv(wiphy); | |
661 | esp_dev->wiphy = wiphy; | |
662 | esp_dev->adapter = adapter; | |
663 | ||
664 | esp_dev->dev = adapter->dev; | |
665 | ||
666 | set_wiphy_dev(wiphy, esp_dev->dev); | |
667 | ||
668 | wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); | |
669 | wiphy->bands[NL80211_BAND_2GHZ] = &esp_wifi_bands; | |
670 | ||
671 | /* Initialize cipher suits */ | |
672 | wiphy->cipher_suites = esp_cipher_suites; | |
673 | wiphy->n_cipher_suites = ARRAY_SIZE(esp_cipher_suites); | |
674 | ||
675 | /* TODO: check and finalize the numbers */ | |
676 | wiphy->max_scan_ssids = 10; | |
677 | /* wiphy->max_match_sets = 10;*/ | |
678 | wiphy->max_scan_ie_len = 1000; | |
679 | wiphy->max_sched_scan_ssids = 10; | |
680 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | |
cb55e27a | 681 | #ifdef CONFIG_PM |
3fef9acf | 682 | wiphy->wowlan = &esp_wowlan_support; |
cb55e27a | 683 | #endif |
774e9b2e | 684 | |
7b6ed49e MM |
685 | /* Advertise SAE support */ |
686 | wiphy->features |= NL80211_FEATURE_SAE; | |
687 | ||
f7ac1df4 KG |
688 | wiphy->reg_notifier = esp_reg_notifier; |
689 | ||
774e9b2e MM |
690 | ret = wiphy_register(wiphy); |
691 | ||
692 | return ret; | |
693 | } | |
694 | ||
225e14eb YM |
695 | int esp_mark_disconnect(struct esp_wifi_device *priv, uint16_t reason, |
696 | uint8_t locally_disconnect) | |
697 | { | |
698 | if (priv && priv->ndev) | |
aad2ae2e YM |
699 | if (priv->ndev->reg_state == NETREG_REGISTERED) |
700 | CFG80211_DISCONNECTED(priv->ndev, reason, NULL, 0, locally_disconnect, | |
701 | GFP_KERNEL); | |
225e14eb YM |
702 | return 0; |
703 | } | |
704 | ||
705 | int esp_mark_scan_done_and_disconnect(struct esp_wifi_device *priv, | |
706 | uint8_t locally_disconnect) | |
707 | { | |
225e14eb YM |
708 | |
709 | if (!priv) | |
710 | return -EINVAL; | |
711 | ||
0af9c02e | 712 | ESP_MARK_SCAN_DONE(priv, true); |
225e14eb YM |
713 | |
714 | ESP_CANCEL_SCHED_SCAN(); | |
715 | ||
aad2ae2e | 716 | esp_mark_disconnect(priv, 0, locally_disconnect); |
225e14eb | 717 | |
225e14eb YM |
718 | return 0; |
719 | } |