]>
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_cmd.h" |
10 | #include "esp_api.h" | |
11 | #include "esp_wpa_utils.h" | |
225e14eb YM |
12 | #include "esp.h" |
13 | #include "esp_cfg80211.h" | |
aad2ae2e | 14 | #include "esp_kernel_port.h" |
774e9b2e | 15 | |
a3829bf3 | 16 | #define PRINT_HEXDUMP(STR, ARG, ARG_LEN, level) \ |
774e9b2e MM |
17 | print_hex_dump(KERN_INFO, STR, DUMP_PREFIX_ADDRESS, 16, 1, ARG, ARG_LEN, 1); |
18 | ||
19 | #define COMMAND_RESPONSE_TIMEOUT (5 * HZ) | |
20 | u8 ap_bssid[MAC_ADDR_LEN]; | |
21 | ||
a3829bf3 | 22 | int internal_scan_request(struct esp_wifi_device *priv, char *ssid, |
774e9b2e MM |
23 | uint8_t channel, uint8_t is_blocking); |
24 | ||
25 | struct beacon_probe_fixed_params { | |
26 | __le64 timestamp; | |
27 | __le16 beacon_interval; | |
28 | __le16 cap_info; | |
29 | } __packed; | |
30 | ||
a3829bf3 | 31 | static struct command_node *get_free_cmd_node(struct esp_adapter *adapter) |
774e9b2e MM |
32 | { |
33 | struct command_node *cmd_node; | |
34 | ||
35 | spin_lock_bh(&adapter->cmd_free_queue_lock); | |
36 | ||
37 | if (list_empty(&adapter->cmd_free_queue)) { | |
38 | spin_unlock_bh(&adapter->cmd_free_queue_lock); | |
6f8fee57 | 39 | esp_err("No free cmd node found\n"); |
774e9b2e MM |
40 | return NULL; |
41 | } | |
42 | cmd_node = list_first_entry(&adapter->cmd_free_queue, | |
43 | struct command_node, list); | |
44 | list_del(&cmd_node->list); | |
45 | spin_unlock_bh(&adapter->cmd_free_queue_lock); | |
46 | ||
47 | cmd_node->cmd_skb = esp_alloc_skb(ESP_SIZE_OF_CMD_NODE); | |
48 | if (!cmd_node->cmd_skb) { | |
6f8fee57 | 49 | esp_err("No free cmd node skb found\n"); |
774e9b2e MM |
50 | } |
51 | ||
52 | return cmd_node; | |
53 | } | |
54 | ||
a3829bf3 | 55 | static inline void reset_cmd_node(struct esp_adapter *adapter, struct command_node *cmd_node) |
774e9b2e MM |
56 | { |
57 | cmd_node->cmd_code = 0; | |
58 | ||
225e14eb | 59 | spin_lock_bh(&adapter->cmd_lock); |
774e9b2e MM |
60 | if (cmd_node->resp_skb) { |
61 | dev_kfree_skb_any(cmd_node->resp_skb); | |
62 | cmd_node->resp_skb = NULL; | |
63 | } | |
225e14eb | 64 | spin_unlock_bh(&adapter->cmd_lock); |
774e9b2e MM |
65 | } |
66 | ||
67 | static void queue_cmd_node(struct esp_adapter *adapter, | |
a3829bf3 | 68 | struct command_node *cmd_node, u8 flag_high_prio) |
774e9b2e MM |
69 | { |
70 | spin_lock_bh(&adapter->cmd_pending_queue_lock); | |
71 | ||
72 | if (flag_high_prio) | |
225e14eb | 73 | list_add_rcu(&cmd_node->list, &adapter->cmd_pending_queue); |
774e9b2e | 74 | else |
225e14eb | 75 | list_add_tail_rcu(&cmd_node->list, &adapter->cmd_pending_queue); |
774e9b2e MM |
76 | |
77 | spin_unlock_bh(&adapter->cmd_pending_queue_lock); | |
78 | } | |
79 | ||
9726a3d0 | 80 | static int decode_mac_addr(struct esp_wifi_device *priv, |
774e9b2e MM |
81 | struct command_node *cmd_node) |
82 | { | |
83 | int ret = 0; | |
84 | struct cmd_config_mac_address *header; | |
85 | ||
86 | if (!priv || !cmd_node || | |
87 | !cmd_node->resp_skb || | |
88 | !cmd_node->resp_skb->data) { | |
6f8fee57 | 89 | esp_info("Invalid arg\n"); |
774e9b2e MM |
90 | return -1; |
91 | } | |
92 | ||
93 | header = (struct cmd_config_mac_address *) (cmd_node->resp_skb->data); | |
94 | ||
95 | if (header->header.cmd_status != CMD_RESPONSE_SUCCESS) { | |
6f8fee57 | 96 | esp_info("Command failed\n"); |
774e9b2e MM |
97 | ret = -1; |
98 | } | |
99 | ||
100 | if (priv) | |
101 | memcpy(priv->mac_address, header->mac_addr, MAC_ADDR_LEN); | |
102 | else | |
6f8fee57 | 103 | esp_err("priv not updated\n"); |
774e9b2e MM |
104 | |
105 | return ret; | |
106 | } | |
107 | ||
9726a3d0 KG |
108 | static int decode_tx_power(struct esp_wifi_device *priv, |
109 | struct command_node *cmd_node) | |
110 | { | |
111 | int ret = 0; | |
112 | struct cmd_set_get_val *header; | |
113 | ||
114 | if (!priv || !cmd_node || | |
115 | !cmd_node->resp_skb || | |
116 | !cmd_node->resp_skb->data) { | |
117 | esp_info("Invalid arg\n"); | |
118 | return -1; | |
119 | } | |
120 | ||
121 | header = (struct cmd_set_get_val *) (cmd_node->resp_skb->data); | |
122 | ||
123 | if (header->header.cmd_status != CMD_RESPONSE_SUCCESS) { | |
124 | esp_info("Command failed\n"); | |
125 | ret = -1; | |
126 | } | |
127 | ||
128 | if (priv) | |
129 | priv->tx_pwr = header->value; | |
130 | else | |
131 | esp_err("priv not updated\n"); | |
132 | ||
133 | return ret; | |
134 | } | |
774e9b2e MM |
135 | |
136 | static int decode_common_resp(struct command_node *cmd_node) | |
137 | { | |
138 | int ret = 0; | |
139 | struct command_header *cmd; | |
140 | ||
141 | ||
142 | if (!cmd_node || !cmd_node->resp_skb || !cmd_node->resp_skb->data) { | |
225e14eb | 143 | |
a3829bf3 | 144 | esp_info("Failed. cmd_node:%p\n", cmd_node); |
225e14eb YM |
145 | |
146 | if (cmd_node) | |
a3829bf3 | 147 | esp_info("code: %u resp_skb:%p\n", |
6f8fee57 | 148 | cmd_node->cmd_code, cmd_node->resp_skb); |
225e14eb | 149 | |
774e9b2e MM |
150 | return -1; |
151 | } | |
152 | ||
153 | cmd = (struct command_header *) (cmd_node->resp_skb->data); | |
154 | ||
155 | if (cmd->cmd_status != CMD_RESPONSE_SUCCESS) { | |
6f8fee57 | 156 | esp_info("[0x%x] Command failed\n", cmd_node->cmd_code); |
774e9b2e MM |
157 | ret = -1; |
158 | } | |
159 | ||
160 | return ret; | |
161 | } | |
162 | ||
163 | static void recycle_cmd_node(struct esp_adapter *adapter, | |
a3829bf3 | 164 | struct command_node *cmd_node) |
774e9b2e MM |
165 | { |
166 | if (!adapter || !cmd_node) | |
167 | return; | |
168 | ||
225e14eb | 169 | reset_cmd_node(adapter, cmd_node); |
774e9b2e MM |
170 | |
171 | spin_lock_bh(&adapter->cmd_free_queue_lock); | |
172 | list_add_tail(&cmd_node->list, &adapter->cmd_free_queue); | |
173 | spin_unlock_bh(&adapter->cmd_free_queue_lock); | |
174 | } | |
175 | ||
176 | ||
177 | static int wait_and_decode_cmd_resp(struct esp_wifi_device *priv, | |
178 | struct command_node *cmd_node) | |
179 | { | |
180 | struct esp_adapter *adapter = NULL; | |
181 | int ret = 0; | |
182 | ||
183 | if (!priv || !priv->adapter || !cmd_node) { | |
6f8fee57 | 184 | esp_info("Invalid params\n"); |
774e9b2e MM |
185 | if (priv->adapter) { |
186 | adapter = priv->adapter; | |
187 | if (adapter && cmd_node) | |
188 | recycle_cmd_node(adapter, cmd_node); | |
189 | } | |
190 | return -EINVAL; | |
191 | } | |
192 | ||
193 | adapter = priv->adapter; | |
194 | ||
195 | /* wait for command response */ | |
196 | ret = wait_event_interruptible_timeout(adapter->wait_for_cmd_resp, | |
225e14eb YM |
197 | adapter->cmd_resp == cmd_node->cmd_code, COMMAND_RESPONSE_TIMEOUT); |
198 | ||
199 | if (!test_bit(ESP_DRIVER_ACTIVE, &adapter->state_flags)) | |
200 | return 0; | |
774e9b2e MM |
201 | |
202 | if (ret == 0) { | |
6f8fee57 | 203 | esp_err("Command[%u] timed out\n", cmd_node->cmd_code); |
774e9b2e MM |
204 | ret = -EINVAL; |
205 | } else { | |
6f8fee57 | 206 | /*esp_dbg("Resp for command [%u]\n", cmd_node->cmd_code);*/ |
774e9b2e MM |
207 | ret = 0; |
208 | } | |
209 | ||
210 | spin_lock_bh(&adapter->cmd_lock); | |
211 | adapter->cur_cmd = NULL; | |
212 | adapter->cmd_resp = 0; | |
213 | spin_unlock_bh(&adapter->cmd_lock); | |
214 | ||
215 | switch (cmd_node->cmd_code) { | |
216 | ||
217 | case CMD_SCAN_REQUEST: | |
218 | if (ret == 0) | |
219 | ret = decode_common_resp(cmd_node); | |
220 | ||
225e14eb | 221 | if (ret) |
0af9c02e | 222 | ESP_MARK_SCAN_DONE(priv, false); |
774e9b2e MM |
223 | break; |
224 | ||
225 | case CMD_INIT_INTERFACE: | |
226 | case CMD_DEINIT_INTERFACE: | |
7b6ed49e MM |
227 | case CMD_STA_AUTH: |
228 | case CMD_STA_ASSOC: | |
774e9b2e MM |
229 | case CMD_STA_CONNECT: |
230 | case CMD_STA_DISCONNECT: | |
231 | case CMD_ADD_KEY: | |
232 | case CMD_DEL_KEY: | |
233 | case CMD_SET_DEFAULT_KEY: | |
7406fe82 MM |
234 | case CMD_SET_IP_ADDR: |
235 | case CMD_SET_MCAST_MAC_ADDR: | |
f7ac1df4 KG |
236 | case CMD_GET_REG_DOMAIN: |
237 | case CMD_SET_REG_DOMAIN: | |
774e9b2e MM |
238 | /* intentional fallthrough */ |
239 | if (ret == 0) | |
240 | ret = decode_common_resp(cmd_node); | |
241 | break; | |
242 | ||
243 | case CMD_GET_MAC: | |
98f8ece9 | 244 | case CMD_SET_MAC: |
774e9b2e | 245 | if (ret == 0) |
9726a3d0 KG |
246 | ret = decode_mac_addr(priv, cmd_node); |
247 | break; | |
248 | ||
249 | case CMD_GET_TXPOWER: | |
250 | case CMD_SET_TXPOWER: | |
251 | if (ret == 0) | |
252 | ret = decode_tx_power(priv, cmd_node); | |
774e9b2e MM |
253 | break; |
254 | ||
255 | default: | |
6f8fee57 | 256 | esp_info("Resp for [0x%x] ignored\n", cmd_node->cmd_code); |
774e9b2e MM |
257 | ret = -EINVAL; |
258 | break; | |
259 | } | |
260 | ||
261 | recycle_cmd_node(adapter, cmd_node); | |
262 | return ret; | |
263 | } | |
264 | ||
265 | static void free_esp_cmd_pool(struct esp_adapter *adapter) | |
266 | { | |
267 | int i; | |
a3829bf3 | 268 | struct command_node *cmd_pool = NULL; |
774e9b2e MM |
269 | |
270 | if (!adapter || !adapter->cmd_pool) | |
271 | return; | |
272 | ||
273 | cmd_pool = adapter->cmd_pool; | |
274 | ||
a3829bf3 | 275 | for (i = 0; i < ESP_NUM_OF_CMD_NODES; i++) { |
774e9b2e | 276 | |
225e14eb | 277 | spin_lock_bh(&adapter->cmd_lock); |
774e9b2e MM |
278 | if (cmd_pool[i].resp_skb) { |
279 | dev_kfree_skb_any(cmd_pool[i].resp_skb); | |
280 | cmd_pool[i].resp_skb = NULL; | |
281 | } | |
225e14eb | 282 | spin_unlock_bh(&adapter->cmd_lock); |
774e9b2e MM |
283 | } |
284 | ||
285 | kfree(adapter->cmd_pool); | |
286 | adapter->cmd_pool = NULL; | |
287 | } | |
288 | ||
289 | static int alloc_esp_cmd_pool(struct esp_adapter *adapter) | |
290 | { | |
291 | u16 i; | |
292 | ||
a3829bf3 | 293 | struct command_node *cmd_pool = kcalloc(ESP_NUM_OF_CMD_NODES, |
774e9b2e MM |
294 | sizeof(struct command_node), GFP_KERNEL); |
295 | ||
a3829bf3 | 296 | if (!cmd_pool) |
774e9b2e MM |
297 | return -ENOMEM; |
298 | ||
299 | adapter->cmd_pool = cmd_pool; | |
300 | ||
a3829bf3 | 301 | for (i = 0; i < ESP_NUM_OF_CMD_NODES; i++) { |
774e9b2e MM |
302 | |
303 | cmd_pool[i].cmd_skb = NULL; | |
304 | cmd_pool[i].resp_skb = NULL; | |
305 | recycle_cmd_node(adapter, &cmd_pool[i]); | |
306 | } | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
774e9b2e MM |
311 | static void esp_cmd_work(struct work_struct *work) |
312 | { | |
313 | int ret; | |
314 | struct command_node *cmd_node = NULL; | |
a3829bf3 | 315 | struct esp_adapter *adapter = NULL; |
fadb81aa | 316 | struct esp_payload_header *payload_header = NULL; |
774e9b2e MM |
317 | |
318 | adapter = esp_get_adapter(); | |
319 | ||
320 | if (!adapter) | |
321 | return; | |
322 | ||
225e14eb YM |
323 | if (!test_bit(ESP_DRIVER_ACTIVE, &adapter->state_flags)) |
324 | return; | |
325 | ||
326 | synchronize_rcu(); | |
327 | spin_lock_bh(&adapter->cmd_lock); | |
774e9b2e | 328 | if (adapter->cur_cmd) { |
225e14eb | 329 | /* Busy in another command */ |
6f8fee57 | 330 | /*esp_dbg("Busy in another cmd execution\n");*/ |
225e14eb | 331 | spin_unlock_bh(&adapter->cmd_lock); |
774e9b2e MM |
332 | return; |
333 | } | |
334 | ||
335 | spin_lock_bh(&adapter->cmd_pending_queue_lock); | |
336 | ||
337 | if (list_empty(&adapter->cmd_pending_queue)) { | |
225e14eb | 338 | /* No command to process */ |
6f8fee57 | 339 | /*esp_dbg("No more command in queue.\n");*/ |
774e9b2e | 340 | spin_unlock_bh(&adapter->cmd_pending_queue_lock); |
225e14eb | 341 | spin_unlock_bh(&adapter->cmd_lock); |
774e9b2e MM |
342 | return; |
343 | } | |
344 | ||
345 | cmd_node = list_first_entry(&adapter->cmd_pending_queue, | |
346 | struct command_node, list); | |
a3829bf3 | 347 | if (!cmd_node) { |
6f8fee57 | 348 | esp_dbg("cmd node NULL\n"); |
774e9b2e | 349 | spin_unlock_bh(&adapter->cmd_pending_queue_lock); |
225e14eb | 350 | spin_unlock_bh(&adapter->cmd_lock); |
774e9b2e MM |
351 | return; |
352 | } | |
6f8fee57 | 353 | /*esp_dbg("Processing Command [0x%X]\n", cmd_node->cmd_code);*/ |
225e14eb | 354 | |
774e9b2e MM |
355 | list_del(&cmd_node->list); |
356 | ||
a3829bf3 KG |
357 | if (!cmd_node->cmd_skb) { |
358 | esp_dbg("cmd_node->cmd_skb NULL\n"); | |
774e9b2e | 359 | spin_unlock_bh(&adapter->cmd_pending_queue_lock); |
225e14eb | 360 | spin_unlock_bh(&adapter->cmd_lock); |
774e9b2e MM |
361 | return; |
362 | } | |
363 | ||
225e14eb | 364 | /* Set as current cmd */ |
774e9b2e | 365 | adapter->cur_cmd = cmd_node; |
225e14eb | 366 | |
774e9b2e | 367 | adapter->cmd_resp = 0; |
774e9b2e | 368 | |
fadb81aa | 369 | payload_header = (struct esp_payload_header *)cmd_node->cmd_skb->data; |
bf3d6cb6 SR |
370 | if (adapter->capabilities & ESP_CHECKSUM_ENABLED) |
371 | payload_header->checksum = cpu_to_le16(compute_checksum(cmd_node->cmd_skb->data, | |
372 | payload_header->len+payload_header->offset)); | |
774e9b2e MM |
373 | |
374 | ret = esp_send_packet(adapter, cmd_node->cmd_skb); | |
225e14eb | 375 | spin_unlock_bh(&adapter->cmd_lock); |
774e9b2e MM |
376 | |
377 | if (ret) { | |
a3829bf3 | 378 | esp_err("Failed to send command [0x%X]\n", cmd_node->cmd_code); |
774e9b2e | 379 | adapter->cur_cmd = NULL; |
225e14eb | 380 | spin_unlock_bh(&adapter->cmd_pending_queue_lock); |
774e9b2e MM |
381 | recycle_cmd_node(adapter, cmd_node); |
382 | return; | |
383 | } | |
225e14eb YM |
384 | |
385 | if (!list_empty(&adapter->cmd_pending_queue)) { | |
6f8fee57 | 386 | /*esp_dbg("Ym2: Pending cmds, queue work again\n");*/ |
225e14eb YM |
387 | spin_unlock_bh(&adapter->cmd_pending_queue_lock); |
388 | queue_work(adapter->cmd_wq, &adapter->cmd_work); | |
389 | return; | |
390 | } | |
391 | spin_unlock_bh(&adapter->cmd_pending_queue_lock); | |
774e9b2e MM |
392 | } |
393 | ||
394 | static int create_cmd_wq(struct esp_adapter *adapter) | |
395 | { | |
396 | adapter->cmd_wq = alloc_workqueue("ESP_CMD_WORK_QUEUE", 0, 0); | |
397 | ||
398 | RET_ON_FAIL(!adapter->cmd_wq); | |
399 | ||
400 | INIT_WORK(&adapter->cmd_work, esp_cmd_work); | |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | static void destroy_cmd_wq(struct esp_adapter *adapter) | |
406 | { | |
407 | if (adapter->cmd_wq) { | |
408 | flush_scheduled_work(); | |
409 | destroy_workqueue(adapter->cmd_wq); | |
410 | adapter->cmd_wq = NULL; | |
411 | } | |
412 | } | |
413 | ||
a3829bf3 | 414 | struct command_node *prepare_command_request(struct esp_adapter *adapter, u8 cmd_code, u16 len) |
774e9b2e MM |
415 | { |
416 | struct command_header *cmd; | |
417 | struct esp_payload_header *payload_header; | |
418 | struct command_node *node = NULL; | |
419 | ||
420 | if (!adapter) { | |
6f8fee57 | 421 | esp_info("%u null adapter\n", __LINE__); |
774e9b2e MM |
422 | return NULL; |
423 | } | |
424 | ||
425 | if (!cmd_code || cmd_code >= CMD_MAX) { | |
6f8fee57 | 426 | esp_err("unsupported command code\n"); |
774e9b2e MM |
427 | return NULL; |
428 | } | |
429 | ||
430 | node = get_free_cmd_node(adapter); | |
431 | ||
432 | if (!node || !node->cmd_skb) { | |
6f8fee57 | 433 | esp_err("Failed to get new free cmd node\n"); |
774e9b2e MM |
434 | return NULL; |
435 | } | |
436 | ||
437 | node->cmd_code = cmd_code; | |
438 | ||
439 | len += sizeof(struct esp_payload_header); | |
440 | ||
aad2ae2e | 441 | payload_header = (struct esp_payload_header *)skb_put(node->cmd_skb, len); |
774e9b2e MM |
442 | memset(payload_header, 0, len); |
443 | ||
444 | payload_header->if_type = ESP_STA_IF; | |
445 | payload_header->len = cpu_to_le16(len - sizeof(struct esp_payload_header)); | |
446 | payload_header->offset = cpu_to_le16(sizeof(struct esp_payload_header)); | |
447 | payload_header->packet_type = PACKET_TYPE_COMMAND_REQUEST; | |
448 | ||
449 | cmd = (struct command_header *) (node->cmd_skb->data + payload_header->offset); | |
450 | cmd->cmd_code = cmd_code; | |
451 | ||
452 | /* payload_header->checksum = cpu_to_le16(compute_checksum(skb->data, len));*/ | |
774e9b2e MM |
453 | return node; |
454 | } | |
455 | ||
225e14eb | 456 | int process_cmd_resp(struct esp_adapter *adapter, struct sk_buff *skb) |
774e9b2e MM |
457 | { |
458 | if (!skb || !adapter) { | |
6f8fee57 | 459 | esp_err("CMD resp: invalid!\n"); |
774e9b2e MM |
460 | |
461 | if (skb) | |
462 | dev_kfree_skb_any(skb); | |
463 | ||
464 | return -1; | |
465 | } | |
466 | ||
467 | if (!adapter->cur_cmd) { | |
6f8fee57 | 468 | esp_err("Command response not expected\n"); |
774e9b2e MM |
469 | dev_kfree_skb_any(skb); |
470 | return -1; | |
471 | } | |
472 | ||
473 | spin_lock_bh(&adapter->cmd_lock); | |
474 | adapter->cur_cmd->resp_skb = skb; | |
225e14eb | 475 | adapter->cmd_resp = adapter->cur_cmd->cmd_code; |
774e9b2e MM |
476 | spin_unlock_bh(&adapter->cmd_lock); |
477 | ||
774e9b2e MM |
478 | |
479 | wake_up_interruptible(&adapter->wait_for_cmd_resp); | |
480 | queue_work(adapter->cmd_wq, &adapter->cmd_work); | |
481 | ||
482 | return 0; | |
483 | } | |
484 | ||
485 | static void process_scan_result_event(struct esp_wifi_device *priv, | |
486 | struct scan_event *scan_evt) | |
487 | { | |
225e14eb YM |
488 | struct cfg80211_bss *bss = NULL; |
489 | struct beacon_probe_fixed_params *fixed_params = NULL; | |
490 | struct ieee80211_channel *chan = NULL; | |
491 | u8 *ie_buf = NULL; | |
774e9b2e MM |
492 | u64 timestamp; |
493 | u16 beacon_interval; | |
494 | u16 cap_info; | |
774e9b2e MM |
495 | u32 ie_len; |
496 | int freq; | |
b1372379 | 497 | int frame_type = CFG80211_BSS_FTYPE_UNKNOWN; /* int type for older compatibilty */ |
774e9b2e MM |
498 | |
499 | if (!priv || !scan_evt) { | |
6f8fee57 | 500 | esp_err("Invalid arguments\n"); |
774e9b2e MM |
501 | return; |
502 | } | |
503 | ||
225e14eb YM |
504 | /*if (!priv->scan_in_progress) { |
505 | return; | |
506 | }*/ | |
507 | ||
774e9b2e MM |
508 | /* End of scan; notify cfg80211 */ |
509 | if (scan_evt->header.status == 0) { | |
774e9b2e | 510 | |
0af9c02e | 511 | ESP_MARK_SCAN_DONE(priv, false); |
774e9b2e MM |
512 | if (priv->waiting_for_scan_done) { |
513 | priv->waiting_for_scan_done = false; | |
514 | wake_up_interruptible(&priv->wait_for_scan_completion); | |
515 | } | |
516 | return; | |
517 | } | |
518 | ||
519 | ie_buf = (u8 *) scan_evt->frame; | |
520 | ie_len = le16_to_cpu(scan_evt->frame_len); | |
521 | ||
522 | fixed_params = (struct beacon_probe_fixed_params *) ie_buf; | |
523 | ||
524 | timestamp = le64_to_cpu(fixed_params->timestamp); | |
525 | beacon_interval = le16_to_cpu(fixed_params->beacon_interval); | |
526 | cap_info = le16_to_cpu(fixed_params->cap_info); | |
527 | ||
528 | freq = ieee80211_channel_to_frequency(scan_evt->channel, NL80211_BAND_2GHZ); | |
529 | chan = ieee80211_get_channel(priv->adapter->wiphy, freq); | |
530 | ||
531 | ie_buf += sizeof(struct beacon_probe_fixed_params); | |
532 | ie_len -= sizeof(struct beacon_probe_fixed_params); | |
533 | ||
b1372379 SS |
534 | if ((scan_evt->frame_type << 4) == IEEE80211_STYPE_BEACON) { |
535 | frame_type = CFG80211_BSS_FTYPE_BEACON; | |
536 | } else if ((scan_evt->frame_type << 4) == IEEE80211_STYPE_PROBE_RESP) { | |
537 | frame_type = CFG80211_BSS_FTYPE_PRESP; | |
538 | } | |
539 | ||
774e9b2e | 540 | if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { |
aad2ae2e | 541 | bss = CFG80211_INFORM_BSS(priv->adapter->wiphy, chan, |
b1372379 | 542 | frame_type, scan_evt->bssid, timestamp, |
774e9b2e | 543 | cap_info, beacon_interval, ie_buf, ie_len, |
c13c46a9 | 544 | (le32_to_cpu(scan_evt->rssi) * 100), GFP_ATOMIC); |
774e9b2e MM |
545 | |
546 | if (bss) | |
547 | cfg80211_put_bss(priv->adapter->wiphy, bss); | |
548 | } else { | |
6f8fee57 | 549 | esp_info("Scan report: Skip invalid or disabled channel\n"); |
774e9b2e MM |
550 | } |
551 | } | |
552 | ||
a3829bf3 | 553 | static void process_auth_event(struct esp_wifi_device *priv, |
7b6ed49e MM |
554 | struct auth_event *event) |
555 | { | |
556 | if (!priv || !event) { | |
6f8fee57 | 557 | esp_err("Invalid arguments\n"); |
7b6ed49e MM |
558 | return; |
559 | } | |
560 | ||
561 | #if 0 | |
562 | print_hex_dump(KERN_INFO, "Auth frame: ", DUMP_PREFIX_ADDRESS, 16, 1, | |
563 | event->frame, event->frame_len, 1); | |
564 | #endif | |
565 | ||
566 | cfg80211_rx_mlme_mgmt(priv->ndev, event->frame, event->frame_len); | |
567 | ||
568 | } | |
569 | ||
774e9b2e MM |
570 | static void process_disconnect_event(struct esp_wifi_device *priv, |
571 | struct disconnect_event *event) | |
572 | { | |
573 | if (!priv || !event) { | |
6f8fee57 | 574 | esp_err("Invalid arguments\n"); |
774e9b2e MM |
575 | return; |
576 | } | |
577 | ||
6f8fee57 | 578 | esp_info("Disconnect event for ssid %s [reason:%d]\n", |
225e14eb | 579 | event->ssid, event->reason); |
774e9b2e | 580 | |
aad2ae2e | 581 | esp_mark_disconnect(priv, event->reason, true); |
774e9b2e MM |
582 | } |
583 | ||
bd131325 YM |
584 | static void process_assoc_event(struct esp_wifi_device *priv, |
585 | struct assoc_event *event) | |
774e9b2e MM |
586 | { |
587 | u8 mac[6]; | |
588 | ||
589 | if (!priv || !event) { | |
6f8fee57 | 590 | esp_err("Invalid arguments\n"); |
774e9b2e MM |
591 | return; |
592 | } | |
593 | ||
6f8fee57 | 594 | esp_info("Connection status: %d\n", event->header.status); |
774e9b2e | 595 | |
774e9b2e MM |
596 | memcpy(mac, event->bssid, MAC_ADDR_LEN); |
597 | ||
1294f8ac | 598 | CFG80211_RX_ASSOC_RESP(priv->ndev, priv->bss, event->frame, event->frame_len, |
7b6ed49e MM |
599 | 0, priv->assoc_req_ie, priv->assoc_req_ie_len); |
600 | ||
601 | #if 0 | |
602 | if (priv->bss) { | |
603 | cfg80211_connect_bss(priv->ndev, mac, priv->bss, NULL, 0, NULL, 0, | |
604 | 0, GFP_KERNEL, NL80211_TIMEOUT_UNSPECIFIED); | |
605 | } else { | |
606 | cfg80211_connect_result(priv->ndev, mac, NULL, 0, NULL, 0, | |
607 | 0, GFP_KERNEL); | |
608 | } | |
609 | #endif | |
774e9b2e MM |
610 | |
611 | esp_port_open(priv); | |
612 | } | |
613 | ||
225e14eb | 614 | int process_cmd_event(struct esp_wifi_device *priv, struct sk_buff *skb) |
774e9b2e MM |
615 | { |
616 | struct event_header *header; | |
617 | ||
618 | if (!skb || !priv) { | |
6f8fee57 | 619 | esp_err("CMD evnt: invalid!\n"); |
774e9b2e MM |
620 | return -1; |
621 | } | |
622 | ||
623 | header = (struct event_header *) (skb->data); | |
624 | ||
625 | switch (header->event_code) { | |
626 | ||
627 | case EVENT_SCAN_RESULT: | |
628 | process_scan_result_event(priv, | |
629 | (struct scan_event *)(skb->data)); | |
630 | break; | |
631 | ||
bd131325 YM |
632 | case EVENT_ASSOC_RX: |
633 | process_assoc_event(priv, | |
634 | (struct assoc_event *)(skb->data)); | |
774e9b2e MM |
635 | break; |
636 | ||
637 | case EVENT_STA_DISCONNECT: | |
638 | process_disconnect_event(priv, | |
639 | (struct disconnect_event *)(skb->data)); | |
640 | break; | |
641 | ||
7b6ed49e MM |
642 | case EVENT_AUTH_RX: |
643 | process_auth_event(priv, (struct auth_event *)(skb->data)); | |
644 | break; | |
645 | ||
774e9b2e | 646 | default: |
6f8fee57 KG |
647 | esp_info("%u unhandled event[%u]\n", |
648 | __LINE__, header->event_code); | |
774e9b2e MM |
649 | break; |
650 | } | |
651 | ||
652 | return 0; | |
653 | } | |
654 | ||
7406fe82 MM |
655 | int cmd_set_mcast_mac_list(struct esp_wifi_device *priv, struct multicast_list *list) |
656 | { | |
657 | struct command_node *cmd_node = NULL; | |
658 | struct cmd_set_mcast_mac_addr *cmd_mcast_mac_list; | |
7406fe82 MM |
659 | |
660 | if (!priv || !priv->adapter) { | |
6f8fee57 | 661 | esp_err("Invalid argument\n"); |
7406fe82 MM |
662 | return -EINVAL; |
663 | } | |
664 | ||
665 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) | |
666 | return 0; | |
667 | ||
5a47b073 MM |
668 | cmd_node = prepare_command_request(priv->adapter, CMD_SET_MCAST_MAC_ADDR, |
669 | sizeof(struct cmd_set_mcast_mac_addr)); | |
7406fe82 | 670 | |
5a47b073 | 671 | if (!cmd_node) { |
6f8fee57 | 672 | esp_err("Failed to get command node\n"); |
5a47b073 MM |
673 | return -ENOMEM; |
674 | } | |
7406fe82 | 675 | |
5a47b073 MM |
676 | cmd_mcast_mac_list = (struct cmd_set_mcast_mac_addr *) |
677 | (cmd_node->cmd_skb->data + sizeof(struct esp_payload_header)); | |
7406fe82 | 678 | |
5a47b073 MM |
679 | cmd_mcast_mac_list->count = list->addr_count; |
680 | memcpy(cmd_mcast_mac_list->mcast_addr, list->mcast_addr, | |
681 | sizeof(cmd_mcast_mac_list->mcast_addr)); | |
7406fe82 | 682 | |
5a47b073 MM |
683 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); |
684 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
7406fe82 | 685 | |
5a47b073 | 686 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); |
7406fe82 MM |
687 | return 0; |
688 | } | |
689 | ||
690 | int cmd_set_ip_address(struct esp_wifi_device *priv, u32 ip) | |
691 | { | |
692 | struct command_node *cmd_node = NULL; | |
693 | struct cmd_set_ip_addr *cmd_set_ip; | |
694 | ||
7406fe82 | 695 | if (!priv || !priv->adapter) { |
6f8fee57 | 696 | esp_err("Invalid argument\n"); |
7406fe82 MM |
697 | return -EINVAL; |
698 | } | |
699 | ||
700 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) | |
701 | return 0; | |
702 | ||
703 | cmd_node = prepare_command_request(priv->adapter, CMD_SET_IP_ADDR, | |
704 | sizeof(struct cmd_set_ip_addr)); | |
705 | ||
706 | if (!cmd_node) { | |
6f8fee57 | 707 | esp_err("Failed to get command node\n"); |
7406fe82 MM |
708 | return -ENOMEM; |
709 | } | |
710 | ||
711 | cmd_set_ip = (struct cmd_set_ip_addr *) | |
712 | (cmd_node->cmd_skb->data + sizeof(struct esp_payload_header)); | |
713 | ||
714 | cmd_set_ip->ip = cpu_to_le32(ip); | |
715 | ||
716 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
717 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
718 | ||
719 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
720 | ||
721 | return 0; | |
722 | } | |
723 | ||
774e9b2e MM |
724 | int cmd_disconnect_request(struct esp_wifi_device *priv, u16 reason_code) |
725 | { | |
726 | struct command_node *cmd_node = NULL; | |
727 | struct cmd_sta_disconnect *cmd_disconnect; | |
728 | ||
729 | if (!priv || !priv->adapter) { | |
6f8fee57 | 730 | esp_err("Invalid argument\n"); |
774e9b2e MM |
731 | return -EINVAL; |
732 | } | |
733 | ||
734 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) | |
735 | return 0; | |
736 | ||
737 | cmd_node = prepare_command_request(priv->adapter, CMD_STA_DISCONNECT, | |
738 | sizeof(struct cmd_sta_disconnect)); | |
739 | ||
740 | if (!cmd_node) { | |
6f8fee57 | 741 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
742 | return -ENOMEM; |
743 | } | |
744 | ||
745 | cmd_disconnect = (struct cmd_sta_disconnect *) | |
746 | (cmd_node->cmd_skb->data + sizeof(struct esp_payload_header)); | |
747 | ||
748 | cmd_disconnect->reason_code = reason_code; | |
749 | ||
750 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
751 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
752 | ||
753 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
754 | ||
755 | return 0; | |
756 | } | |
757 | ||
bd131325 | 758 | #if 0 |
774e9b2e MM |
759 | int cmd_connect_request(struct esp_wifi_device *priv, |
760 | struct cfg80211_connect_params *params) | |
761 | { | |
762 | u16 cmd_len; | |
763 | struct command_node *cmd_node = NULL; | |
764 | struct cmd_sta_connect *cmd; | |
765 | struct ieee80211_channel *chan; | |
766 | struct cfg80211_bss *bss; | |
767 | struct esp_adapter *adapter = NULL; | |
774e9b2e MM |
768 | u8 retry = 2; |
769 | ||
770 | if (!priv || !params || !priv->adapter) { | |
6f8fee57 | 771 | esp_err("Invalid argument\n"); |
774e9b2e MM |
772 | return -EINVAL; |
773 | } | |
774 | ||
775 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 776 | esp_err("%u cleanup in progress, return failure", __LINE__); |
774e9b2e MM |
777 | return -EFAULT; |
778 | } | |
779 | ||
780 | adapter = priv->adapter; | |
781 | ||
782 | cmd_len = sizeof(struct cmd_sta_connect) + params->ie_len; | |
783 | ||
784 | cmd_node = prepare_command_request(adapter, CMD_STA_CONNECT, cmd_len); | |
785 | if (!cmd_node) { | |
6f8fee57 | 786 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
787 | return -ENOMEM; |
788 | } | |
789 | cmd = (struct cmd_sta_connect *) (cmd_node->cmd_skb->data + sizeof(struct esp_payload_header)); | |
790 | ||
791 | if (params->ssid_len) | |
792 | memcpy(cmd->ssid, params->ssid, MAX_SSID_LEN); | |
793 | else | |
6f8fee57 | 794 | esp_err("No ssid\n"); |
774e9b2e MM |
795 | |
796 | if (params->bssid) { | |
797 | memcpy(ap_bssid, params->bssid, MAC_ADDR_LEN); | |
798 | memcpy(cmd->bssid, params->bssid, MAC_ADDR_LEN); | |
799 | } | |
800 | ||
801 | if (params->channel) { | |
802 | chan = params->channel; | |
803 | cmd->channel = chan->hw_value; | |
804 | } | |
805 | ||
806 | if (params->ie_len) { | |
807 | cmd->assoc_ie_len = cpu_to_le16(params->ie_len); | |
808 | memcpy(cmd->assoc_ie, params->ie, params->ie_len); | |
809 | } | |
810 | ||
811 | if (params->privacy) | |
812 | cmd->is_auth_open = 0; | |
813 | else | |
814 | cmd->is_auth_open = 1; | |
815 | ||
e58fd496 | 816 | esp_info("Connection request: %s "MACSTR" %d\n", |
817 | cmd->ssid, MAC2STR(params->bssid), cmd->channel); | |
774e9b2e MM |
818 | |
819 | do { | |
820 | bss = cfg80211_get_bss(adapter->wiphy, params->channel, params->bssid, | |
821 | params->ssid, params->ssid_len, IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); | |
822 | ||
823 | if (bss) { | |
824 | break; | |
825 | } else { | |
6f8fee57 | 826 | esp_info("No BSS in the list.. scanning..\n"); |
774e9b2e MM |
827 | internal_scan_request(priv, cmd->ssid, cmd->channel, true); |
828 | } | |
829 | ||
830 | retry--; | |
831 | } while (retry); | |
832 | ||
833 | if (retry) { | |
834 | queue_cmd_node(adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
835 | queue_work(adapter->cmd_wq, &adapter->cmd_work); | |
836 | ||
837 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
838 | } else { | |
6f8fee57 | 839 | esp_info("Failed to find %s\n", cmd->ssid); |
774e9b2e MM |
840 | return -EFAULT; |
841 | } | |
842 | ||
843 | return 0; | |
844 | } | |
bd131325 | 845 | #endif |
774e9b2e | 846 | |
7b6ed49e MM |
847 | |
848 | int cmd_assoc_request(struct esp_wifi_device *priv, | |
849 | struct cfg80211_assoc_request *req) | |
850 | { | |
851 | struct command_node *cmd_node = NULL; | |
852 | struct cmd_sta_assoc *cmd; | |
853 | struct cfg80211_bss *bss; | |
854 | struct esp_adapter *adapter = NULL; | |
855 | u16 cmd_len; | |
856 | ||
857 | if (!priv || !req || !req->bss || !priv->adapter) { | |
6f8fee57 | 858 | esp_err("Invalid argument\n"); |
7b6ed49e MM |
859 | return -EINVAL; |
860 | } | |
861 | ||
862 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 863 | esp_err("%u cleanup in progress, return failure", __LINE__); |
7b6ed49e MM |
864 | return -EFAULT; |
865 | } | |
866 | ||
867 | bss = req->bss; | |
868 | adapter = priv->adapter; | |
869 | ||
870 | cmd_len = sizeof(struct cmd_sta_assoc) + req->ie_len; | |
871 | ||
872 | cmd_node = prepare_command_request(adapter, CMD_STA_ASSOC, cmd_len); | |
873 | ||
874 | if (!cmd_node) { | |
6f8fee57 | 875 | esp_err("Failed to get command node\n"); |
7b6ed49e MM |
876 | return -ENOMEM; |
877 | } | |
878 | ||
879 | cmd = (struct cmd_sta_assoc *) (cmd_node->cmd_skb->data + sizeof(struct esp_payload_header)); | |
880 | ||
881 | cmd->assoc_ie_len = req->ie_len; | |
882 | memcpy(cmd->assoc_ie, req->ie, req->ie_len); | |
883 | ||
884 | /* Make a copy of assoc req IEs */ | |
bd131325 | 885 | if (priv->assoc_req_ie) { |
7b6ed49e | 886 | kfree(priv->assoc_req_ie); |
bd131325 YM |
887 | priv->assoc_req_ie = NULL; |
888 | } | |
7b6ed49e MM |
889 | |
890 | priv->assoc_req_ie = kmemdup(req->ie, req->ie_len, GFP_ATOMIC); | |
891 | ||
892 | if (!priv->assoc_req_ie) { | |
a3829bf3 | 893 | esp_err("Failed to allocate buffer for assoc request IEs\n"); |
7b6ed49e MM |
894 | return -ENOMEM; |
895 | } | |
896 | ||
897 | priv->assoc_req_ie_len = req->ie_len; | |
898 | ||
e58fd496 | 899 | esp_info("Association request: "MACSTR" %d %d\n", |
900 | MAC2STR(bss->bssid), bss->channel->hw_value, cmd->assoc_ie_len); | |
7b6ed49e MM |
901 | |
902 | queue_cmd_node(adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
903 | queue_work(adapter->cmd_wq, &adapter->cmd_work); | |
904 | ||
905 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
906 | ||
907 | return 0; | |
908 | } | |
909 | ||
910 | int cmd_auth_request(struct esp_wifi_device *priv, | |
911 | struct cfg80211_auth_request *req) | |
912 | { | |
913 | struct command_node *cmd_node = NULL; | |
914 | struct cmd_sta_auth *cmd; | |
bd131325 YM |
915 | struct cfg80211_bss *bss; |
916 | /*struct cfg80211_bss *bss1;*/ | |
7b6ed49e MM |
917 | struct esp_adapter *adapter = NULL; |
918 | u16 cmd_len; | |
bd131325 | 919 | /* u8 retry = 2; */ |
7b6ed49e MM |
920 | |
921 | if (!priv || !req || !req->bss || !priv->adapter) { | |
6f8fee57 | 922 | esp_err("Invalid argument\n"); |
7b6ed49e MM |
923 | return -EINVAL; |
924 | } | |
925 | ||
926 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 927 | esp_err(":%u cleanup in progress, return failure", __LINE__); |
7b6ed49e MM |
928 | return -EFAULT; |
929 | } | |
930 | ||
931 | bss = req->bss; | |
932 | ||
933 | priv->bss = req->bss; | |
934 | ||
935 | adapter = priv->adapter; | |
936 | ||
937 | cmd_len = sizeof(struct cmd_sta_auth) + req->auth_data_len; | |
938 | ||
939 | cmd_node = prepare_command_request(adapter, CMD_STA_AUTH, cmd_len); | |
940 | ||
941 | if (!cmd_node) { | |
6f8fee57 | 942 | esp_err("Failed to get command node\n"); |
7b6ed49e MM |
943 | return -ENOMEM; |
944 | } | |
945 | cmd = (struct cmd_sta_auth *) (cmd_node->cmd_skb->data + sizeof(struct esp_payload_header)); | |
946 | ||
947 | memcpy(cmd->bssid, bss->bssid, MAC_ADDR_LEN); | |
948 | cmd->channel = bss->channel->hw_value; | |
949 | cmd->auth_type = req->auth_type; | |
950 | cmd->auth_data_len = req->auth_data_len; | |
951 | memcpy(cmd->auth_data, req->auth_data, req->auth_data_len); | |
952 | ||
5d6c5617 KG |
953 | if (req->key_len) { |
954 | memcpy(cmd->key, req->key, req->key_len); | |
955 | cmd->key_len = req->key_len; | |
956 | } | |
e58fd496 | 957 | esp_info("Authentication request: "MACSTR" %d %d %d %d\n", |
958 | MAC2STR(cmd->bssid), cmd->channel, cmd->auth_type, cmd->auth_data_len, | |
7b6ed49e MM |
959 | (u32) req->ie_len); |
960 | #if 0 | |
961 | do { | |
962 | bss1 = cfg80211_get_bss(adapter->wiphy, bss->channel, bss->bssid, | |
963 | NULL, 0, IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); | |
964 | ||
965 | if (bss1) { | |
966 | break; | |
967 | } else { | |
6f8fee57 | 968 | esp_info("No BSS in the list.. scanning..\n"); |
7b6ed49e MM |
969 | internal_scan_request(priv, cmd->ssid, cmd->channel, true); |
970 | } | |
971 | ||
972 | retry--; | |
973 | } while (retry); | |
974 | #endif | |
975 | queue_cmd_node(adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
976 | queue_work(adapter->cmd_wq, &adapter->cmd_work); | |
977 | ||
978 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
979 | ||
980 | return 0; | |
981 | } | |
982 | ||
774e9b2e MM |
983 | int cmd_set_default_key(struct esp_wifi_device *priv, u8 key_index) |
984 | { | |
985 | u16 cmd_len; | |
986 | struct command_node *cmd_node = NULL; | |
987 | struct cmd_key_operation *cmd; | |
a3829bf3 | 988 | struct wifi_sec_key *key = NULL; |
774e9b2e MM |
989 | |
990 | if (!priv || !priv->adapter) { | |
6f8fee57 | 991 | esp_err("Invalid argument\n"); |
774e9b2e MM |
992 | return -EINVAL; |
993 | } | |
994 | ||
995 | #if 0 | |
996 | if (key_index > ESP_MAX_KEY_INDEX) { | |
6f8fee57 | 997 | esp_err("invalid key index[%u] > max[%u]\n", |
774e9b2e MM |
998 | key_index, ESP_MAX_KEY_INDEX); |
999 | return -EINVAL; | |
1000 | } | |
1001 | #endif | |
1002 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 1003 | esp_err(":%u cleanup in progress, return", __LINE__); |
774e9b2e MM |
1004 | return 0; |
1005 | } | |
1006 | ||
774e9b2e MM |
1007 | cmd_len = sizeof(struct cmd_key_operation); |
1008 | ||
1009 | /* get new cmd node */ | |
1010 | cmd_node = prepare_command_request(priv->adapter, CMD_SET_DEFAULT_KEY, cmd_len); | |
1011 | if (!cmd_node) { | |
6f8fee57 | 1012 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1013 | return -ENOMEM; |
1014 | } | |
1015 | ||
1016 | /* cmd specific update */ | |
1017 | cmd = (struct cmd_key_operation *) (cmd_node->cmd_skb->data + | |
1018 | sizeof(struct esp_payload_header)); | |
1019 | key = &cmd->key; | |
1020 | ||
1021 | key->index = key_index; | |
1022 | key->set_cur = 1; | |
1023 | ||
1024 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1025 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1026 | ||
1027 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1028 | ||
1029 | return 0; | |
1030 | } | |
1031 | ||
1032 | int cmd_del_key(struct esp_wifi_device *priv, u8 key_index, bool pairwise, | |
1033 | const u8 *mac_addr) | |
1034 | { | |
1035 | u16 cmd_len; | |
1036 | struct command_node *cmd_node = NULL; | |
1037 | struct cmd_key_operation *cmd; | |
a3829bf3 | 1038 | struct wifi_sec_key *key = NULL; |
774e9b2e MM |
1039 | const u8 *mac = NULL; |
1040 | const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | |
1041 | ||
1042 | if (!priv || !priv->adapter) { | |
6f8fee57 | 1043 | esp_err("Invalid argument\n"); |
774e9b2e MM |
1044 | return -EINVAL; |
1045 | } | |
1046 | ||
1047 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) | |
1048 | return 0; | |
1049 | ||
1050 | #if 0 | |
1051 | if (key_index > ESP_MAX_KEY_INDEX) { | |
6f8fee57 | 1052 | esp_err("invalid key index[%u] > max[%u]\n", |
774e9b2e MM |
1053 | key_index, ESP_MAX_KEY_INDEX); |
1054 | return -EINVAL; | |
1055 | } | |
1056 | #endif | |
1057 | ||
1058 | mac = pairwise ? mac_addr : bc_mac; | |
774e9b2e MM |
1059 | |
1060 | cmd_len = sizeof(struct cmd_key_operation); | |
1061 | ||
1062 | /* get new cmd node */ | |
1063 | cmd_node = prepare_command_request(priv->adapter, CMD_DEL_KEY, cmd_len); | |
1064 | if (!cmd_node) { | |
6f8fee57 | 1065 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1066 | return -ENOMEM; |
1067 | } | |
1068 | ||
1069 | /* cmd specific update */ | |
1070 | cmd = (struct cmd_key_operation *) (cmd_node->cmd_skb->data + | |
1071 | sizeof(struct esp_payload_header)); | |
1072 | key = &cmd->key; | |
1073 | ||
1074 | if (mac && !is_multicast_ether_addr(mac)) | |
1075 | memcpy((char *)&key->mac_addr, (void *)mac, MAC_ADDR_LEN); | |
1076 | ||
1077 | key->index = key_index; | |
1078 | key->del = 1; | |
1079 | ||
1080 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1081 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1082 | ||
1083 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1084 | ||
1085 | return 0; | |
1086 | } | |
1087 | ||
1088 | int cmd_add_key(struct esp_wifi_device *priv, u8 key_index, bool pairwise, | |
1089 | const u8 *mac_addr, struct key_params *params) | |
1090 | { | |
1091 | u16 cmd_len; | |
1092 | struct command_node *cmd_node = NULL; | |
1093 | struct cmd_key_operation *cmd; | |
a3829bf3 | 1094 | struct wifi_sec_key *key = NULL; |
774e9b2e MM |
1095 | const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
1096 | const u8 *mac = NULL; | |
1097 | ||
1098 | #if 0 | |
e58fd496 | 1099 | esp_info("%u key_idx: %u pairwise: %u params->key_len: %u\nparams->seq_len:%u params->mode: 0x%x\nparams->cipher: 0x%x\n", |
6f8fee57 | 1100 | __LINE__, |
774e9b2e MM |
1101 | key_index, pairwise, params->key_len, params->seq_len, params->mode, params->cipher); |
1102 | #endif | |
1103 | if (!priv || !priv->adapter || !params || | |
1104 | !params->key || !params->key_len || !params->seq_len) { | |
6f8fee57 | 1105 | esp_err("Invalid argument\n"); |
774e9b2e MM |
1106 | return -EINVAL; |
1107 | } | |
1108 | ||
1109 | #if 0 | |
1110 | if (key_index > ESP_MAX_KEY_INDEX) { | |
6f8fee57 | 1111 | esp_err("invalid key index[%u] > max[%u]\n", |
774e9b2e MM |
1112 | key_index, ESP_MAX_KEY_INDEX); |
1113 | return -EINVAL; | |
1114 | } | |
1115 | #endif | |
1116 | ||
1117 | if (params->key_len > sizeof(key->data)) { | |
6f8fee57 | 1118 | esp_err("Too long key length (%u)\n", params->key_len); |
774e9b2e MM |
1119 | return -EINVAL; |
1120 | } | |
1121 | ||
1122 | if (params->seq_len > sizeof(key->seq)) { | |
6f8fee57 | 1123 | esp_err("Too long key seq length (%u)\n", params->seq_len); |
774e9b2e MM |
1124 | return -EINVAL; |
1125 | } | |
1126 | ||
1127 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 1128 | esp_err("%u cleanup in progress, return failure", __LINE__); |
774e9b2e MM |
1129 | return -EFAULT; |
1130 | } | |
1131 | ||
1132 | mac = pairwise ? mac_addr : bc_mac; | |
1133 | if (mac) { | |
5a47b073 | 1134 | #if 0 |
774e9b2e MM |
1135 | print_hex_dump(KERN_INFO, "mac: ", DUMP_PREFIX_ADDRESS, 16, 1, |
1136 | mac, MAC_ADDR_LEN, 1); | |
5a47b073 | 1137 | #endif |
774e9b2e MM |
1138 | } |
1139 | ||
1140 | cmd_len = sizeof(struct cmd_key_operation); | |
1141 | ||
1142 | cmd_node = prepare_command_request(priv->adapter, CMD_ADD_KEY, cmd_len); | |
1143 | if (!cmd_node) { | |
6f8fee57 | 1144 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1145 | return -ENOMEM; |
1146 | } | |
1147 | ||
1148 | cmd = (struct cmd_key_operation *) (cmd_node->cmd_skb->data + | |
1149 | sizeof(struct esp_payload_header)); | |
1150 | key = &cmd->key; | |
1151 | ||
1152 | if (mac && !is_multicast_ether_addr(mac)) | |
1153 | memcpy((char *)&key->mac_addr, (void *)mac, MAC_ADDR_LEN); | |
1154 | ||
1155 | key->index = key_index; | |
1156 | ||
1157 | key->len = params->key_len; | |
1158 | if (params->key && key->len) | |
1159 | memcpy(key->data, params->key, key->len); | |
1160 | ||
1161 | key->seq_len = params->seq_len; | |
1162 | if (params->seq && key->seq_len) | |
1163 | memcpy(key->seq, params->seq, key->seq_len); | |
1164 | ||
774e9b2e MM |
1165 | key->algo = wpa_cipher_to_alg(params->cipher); |
1166 | #if 0 | |
1167 | if (key->algo == WIFI_WPA_ALG_NONE) { | |
e58fd496 | 1168 | esp_info("CIPHER NONE does not use pairwise keys\n"); |
774e9b2e MM |
1169 | return 0; |
1170 | } | |
1171 | #endif | |
1172 | ||
99fc102b SS |
1173 | /* Supplicant swaps tx/rx Mic keys whereas esp needs it normal format */ |
1174 | if (key->algo == WIFI_WPA_ALG_TKIP && !key->index) { | |
1175 | u8 buf[8]; | |
1176 | memcpy(buf, &key->data[16], 8); | |
1177 | memcpy(&key->data[16], &key->data[24], 8); | |
1178 | memcpy(&key->data[24], buf, 8); | |
1179 | memset(buf, 0, 8); | |
1180 | } | |
1181 | ||
774e9b2e | 1182 | #if 0 |
a3829bf3 | 1183 | esp_err("%u algo: %u idx: %u seq_len: %u len:%u\n", __LINE__, |
9ea97217 | 1184 | key->algo, key->index, key->seq_len, key->len); |
774e9b2e MM |
1185 | PRINT_HEXDUMP("mac", key->mac_addr, 6, ESP_LOG_INFO); |
1186 | PRINT_HEXDUMP("seq", key->seq, key->seq_len, ESP_LOG_INFO); | |
1187 | PRINT_HEXDUMP("key_data", key->data, key->len, ESP_LOG_INFO); | |
1188 | #endif | |
1189 | ||
1190 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1191 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1192 | ||
1193 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1194 | ||
1195 | return 0; | |
1196 | } | |
1197 | ||
1198 | int cmd_init_interface(struct esp_wifi_device *priv) | |
1199 | { | |
1200 | u16 cmd_len; | |
1201 | struct command_node *cmd_node = NULL; | |
1202 | ||
1203 | if (!priv || !priv->adapter) { | |
6f8fee57 | 1204 | esp_err("Invalid argument\n"); |
774e9b2e MM |
1205 | return -EINVAL; |
1206 | } | |
1207 | ||
1208 | cmd_len = sizeof(struct command_header); | |
1209 | ||
1210 | cmd_node = prepare_command_request(priv->adapter, CMD_INIT_INTERFACE, cmd_len); | |
1211 | ||
1212 | if (!cmd_node) { | |
6f8fee57 | 1213 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1214 | return -ENOMEM; |
1215 | } | |
1216 | ||
1217 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1218 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1219 | ||
1220 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1221 | ||
1222 | return 0; | |
1223 | } | |
1224 | ||
1225 | int cmd_deinit_interface(struct esp_wifi_device *priv) | |
1226 | { | |
1227 | u16 cmd_len; | |
1228 | struct command_node *cmd_node = NULL; | |
1229 | ||
1230 | if (!priv || !priv->adapter) | |
1231 | return -EINVAL; | |
1232 | ||
1233 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 1234 | esp_err("%u cleanup in progress, return\n", __LINE__); |
774e9b2e MM |
1235 | return 0; |
1236 | } | |
1237 | ||
1238 | cmd_len = sizeof(struct command_header); | |
1239 | ||
1240 | cmd_node = prepare_command_request(priv->adapter, CMD_DEINIT_INTERFACE, cmd_len); | |
1241 | ||
1242 | if (!cmd_node) { | |
6f8fee57 | 1243 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1244 | return -ENOMEM; |
1245 | } | |
1246 | ||
1247 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1248 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1249 | ||
1250 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1251 | ||
1252 | return 0; | |
1253 | } | |
1254 | ||
a3829bf3 | 1255 | int internal_scan_request(struct esp_wifi_device *priv, char *ssid, |
774e9b2e MM |
1256 | uint8_t channel, uint8_t is_blocking) |
1257 | { | |
1258 | int ret = 0; | |
1259 | u16 cmd_len; | |
1260 | struct command_node *cmd_node = NULL; | |
1261 | struct scan_request *scan_req; | |
1262 | ||
1263 | if (!priv || !priv->adapter) { | |
6f8fee57 | 1264 | esp_err("Invalid argument\n"); |
774e9b2e MM |
1265 | return -EINVAL; |
1266 | } | |
1267 | ||
1268 | if (priv->scan_in_progress) { | |
6f8fee57 | 1269 | esp_err("Scan in progress.. return\n"); |
774e9b2e MM |
1270 | return -EBUSY; |
1271 | } | |
1272 | ||
1273 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 1274 | esp_err("%u cleanup in progress, return", __LINE__); |
059da5be | 1275 | return -EBUSY; |
774e9b2e MM |
1276 | } |
1277 | ||
1278 | cmd_len = sizeof(struct scan_request); | |
1279 | ||
1280 | cmd_node = prepare_command_request(priv->adapter, CMD_SCAN_REQUEST, cmd_len); | |
1281 | ||
1282 | if (!cmd_node) { | |
6f8fee57 | 1283 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1284 | return -ENOMEM; |
1285 | } | |
1286 | ||
1287 | scan_req = (struct scan_request *) (cmd_node->cmd_skb->data + | |
1288 | sizeof(struct esp_payload_header)); | |
1289 | ||
1290 | if (ssid) { | |
1291 | memcpy(scan_req->ssid, ssid, MAX_SSID_LEN); | |
1292 | } | |
1293 | ||
1294 | scan_req->channel = channel; | |
1295 | ||
1296 | priv->scan_in_progress = true; | |
1297 | ||
1298 | if (is_blocking) | |
1299 | priv->waiting_for_scan_done = true; | |
1300 | ||
1301 | /* Enqueue command */ | |
1302 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1303 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1304 | ||
1305 | ret = wait_and_decode_cmd_resp(priv, cmd_node); | |
1306 | ||
1307 | if (!ret && is_blocking) { | |
1308 | /* Wait for scan done */ | |
1309 | wait_event_interruptible_timeout(priv->wait_for_scan_completion, | |
1310 | priv->waiting_for_scan_done != true, COMMAND_RESPONSE_TIMEOUT); | |
1311 | } | |
1312 | ||
1313 | return ret; | |
1314 | } | |
1315 | ||
1316 | int cmd_scan_request(struct esp_wifi_device *priv, struct cfg80211_scan_request *request) | |
1317 | { | |
1318 | u16 cmd_len; | |
1319 | struct command_node *cmd_node = NULL; | |
1320 | struct scan_request *scan_req; | |
1321 | ||
1322 | if (!priv || !priv->adapter || !request) { | |
6f8fee57 | 1323 | esp_err("Invalid argument\n"); |
774e9b2e MM |
1324 | return -EINVAL; |
1325 | } | |
1326 | ||
1327 | if (test_bit(ESP_CLEANUP_IN_PROGRESS, &priv->adapter->state_flags)) { | |
6f8fee57 | 1328 | esp_err("%u cleanup in progress, return", __LINE__); |
059da5be | 1329 | return -EBUSY; |
774e9b2e MM |
1330 | } |
1331 | ||
774e9b2e | 1332 | if (priv->scan_in_progress || priv->request) { |
6f8fee57 | 1333 | esp_err("Scan in progress.. return\n"); |
774e9b2e MM |
1334 | return -EBUSY; |
1335 | } | |
1336 | ||
1337 | cmd_len = sizeof(struct scan_request); | |
1338 | ||
1339 | cmd_node = prepare_command_request(priv->adapter, CMD_SCAN_REQUEST, cmd_len); | |
1340 | ||
1341 | if (!cmd_node) { | |
6f8fee57 | 1342 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1343 | return -ENOMEM; |
1344 | } | |
1345 | ||
1346 | scan_req = (struct scan_request *) (cmd_node->cmd_skb->data + | |
1347 | sizeof(struct esp_payload_header)); | |
1348 | ||
1349 | /* TODO: Handle case of multiple SSIDs or channels */ | |
a3829bf3 | 1350 | if (request->ssids && request->ssids[0].ssid_len) { |
774e9b2e MM |
1351 | memcpy(scan_req->ssid, request->ssids[0].ssid, MAX_SSID_LEN); |
1352 | } | |
1353 | ||
1354 | #if 0 | |
1355 | if (request->n_channels) { | |
1356 | chan = request->channels[0]; | |
1357 | scan_req->channel = chan->hw_value; | |
1358 | } | |
1359 | #endif | |
1360 | ||
aad2ae2e | 1361 | #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 8, 0) |
774e9b2e | 1362 | scan_req->duration = request->duration; |
aad2ae2e YM |
1363 | #endif |
1364 | #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 7, 0) | |
774e9b2e | 1365 | memcpy(scan_req->bssid, request->bssid, MAC_ADDR_LEN); |
aad2ae2e | 1366 | #endif |
774e9b2e MM |
1367 | |
1368 | priv->scan_in_progress = true; | |
1369 | priv->request = request; | |
1370 | ||
1371 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1372 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1373 | ||
1374 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1375 | ||
1376 | return 0; | |
1377 | } | |
1378 | ||
1379 | ||
1380 | int cmd_get_mac(struct esp_wifi_device *priv) | |
1381 | { | |
1382 | u16 cmd_len; | |
1383 | struct command_node *cmd_node = NULL; | |
1384 | ||
1385 | if (!priv || !priv->adapter) { | |
6f8fee57 | 1386 | esp_err("Invalid argument\n"); |
774e9b2e MM |
1387 | return -EINVAL; |
1388 | } | |
1389 | ||
1390 | cmd_len = sizeof(struct command_header); | |
1391 | ||
1392 | cmd_node = prepare_command_request(priv->adapter, CMD_GET_MAC, cmd_len); | |
1393 | ||
1394 | if (!cmd_node) { | |
6f8fee57 | 1395 | esp_err("Failed to get command node\n"); |
774e9b2e MM |
1396 | return -ENOMEM; |
1397 | } | |
1398 | ||
1399 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1400 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1401 | ||
1402 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1403 | ||
1404 | return 0; | |
1405 | } | |
1406 | ||
98f8ece9 KG |
1407 | int cmd_set_mac(struct esp_wifi_device *priv, uint8_t *mac_addr) |
1408 | { | |
1409 | u16 cmd_len; | |
1410 | struct command_node *cmd_node = NULL; | |
1411 | struct cmd_config_mac_address *cmd;; | |
1412 | ||
1413 | if (!priv || !priv->adapter) { | |
1414 | esp_err("Invalid argument\n"); | |
1415 | return -EINVAL; | |
1416 | } | |
1417 | ||
1418 | cmd_len = sizeof(struct cmd_config_mac_address); | |
1419 | ||
1420 | cmd_node = prepare_command_request(priv->adapter, CMD_SET_MAC, cmd_len); | |
1421 | ||
1422 | if (!cmd_node) { | |
1423 | esp_err("Failed to get command node\n"); | |
1424 | return -ENOMEM; | |
1425 | } | |
1426 | ||
1427 | cmd = (struct cmd_config_mac_address *) (cmd_node->cmd_skb->data + | |
1428 | sizeof(struct esp_payload_header)); | |
1429 | ||
1430 | memcpy(cmd->mac_addr, mac_addr, MAC_ADDR_LEN); | |
1431 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1432 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1433 | ||
1434 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1435 | ||
1436 | return 0; | |
1437 | } | |
774e9b2e | 1438 | |
225e14eb | 1439 | int esp_commands_teardown(struct esp_adapter *adapter) |
774e9b2e MM |
1440 | { |
1441 | #define MAX_DEINIT_RETRY 5 | |
1442 | uint8_t iface_idx = 0; | |
225e14eb | 1443 | struct esp_wifi_device *priv = NULL; |
774e9b2e | 1444 | |
a3829bf3 KG |
1445 | if (!adapter) { |
1446 | return -EINVAL; | |
774e9b2e MM |
1447 | } |
1448 | ||
1449 | set_bit(ESP_CLEANUP_IN_PROGRESS, &adapter->state_flags); | |
1450 | ||
1451 | if (!test_bit(ESP_CMD_INIT_DONE, &adapter->state_flags)) | |
1452 | return 0; | |
1453 | ||
774e9b2e | 1454 | |
a3829bf3 | 1455 | for (iface_idx = 0; iface_idx < ESP_MAX_INTERFACE; iface_idx++) { |
774e9b2e | 1456 | |
225e14eb | 1457 | priv = adapter->priv[iface_idx]; |
774e9b2e | 1458 | |
225e14eb YM |
1459 | if (!priv) |
1460 | continue; | |
774e9b2e | 1461 | |
225e14eb YM |
1462 | esp_mark_scan_done_and_disconnect(priv, false); |
1463 | ||
1464 | esp_port_close(priv); | |
1465 | } | |
774e9b2e | 1466 | |
a3829bf3 KG |
1467 | destroy_cmd_wq(adapter); |
1468 | free_esp_cmd_pool(adapter); | |
774e9b2e | 1469 | |
a3829bf3 | 1470 | return 0; |
774e9b2e MM |
1471 | } |
1472 | ||
225e14eb | 1473 | int esp_commands_setup(struct esp_adapter *adapter) |
774e9b2e MM |
1474 | { |
1475 | if (!adapter) { | |
6f8fee57 | 1476 | esp_err("no adapter\n"); |
774e9b2e MM |
1477 | return -EINVAL; |
1478 | } | |
1479 | ||
1480 | init_waitqueue_head(&adapter->wait_for_cmd_resp); | |
1481 | ||
1482 | spin_lock_init(&adapter->cmd_lock); | |
1483 | ||
1484 | INIT_LIST_HEAD(&adapter->cmd_pending_queue); | |
1485 | INIT_LIST_HEAD(&adapter->cmd_free_queue); | |
1486 | ||
1487 | spin_lock_init(&adapter->cmd_pending_queue_lock); | |
1488 | spin_lock_init(&adapter->cmd_free_queue_lock); | |
1489 | ||
1490 | RET_ON_FAIL(create_cmd_wq(adapter)); | |
1491 | ||
1492 | RET_ON_FAIL(alloc_esp_cmd_pool(adapter)); | |
1493 | ||
1494 | set_bit(ESP_CMD_INIT_DONE, &adapter->state_flags); | |
1495 | return 0; | |
1496 | } | |
1497 | ||
9726a3d0 KG |
1498 | int cmd_set_tx_power(struct esp_wifi_device *priv, int power) |
1499 | { | |
1500 | u16 cmd_len; | |
1501 | struct command_node *cmd_node = NULL; | |
1502 | struct cmd_set_get_val *val;; | |
1503 | ||
1504 | if (!priv || !priv->adapter) { | |
1505 | esp_err("Invalid argument\n"); | |
1506 | return -EINVAL; | |
1507 | } | |
1508 | ||
1509 | cmd_len = sizeof(struct cmd_set_get_val); | |
1510 | ||
1511 | cmd_node = prepare_command_request(priv->adapter, CMD_SET_TXPOWER, cmd_len); | |
1512 | ||
1513 | if (!cmd_node) { | |
1514 | esp_err("Failed to get command node\n"); | |
1515 | return -ENOMEM; | |
1516 | } | |
1517 | ||
1518 | val = (struct cmd_set_get_val *) (cmd_node->cmd_skb->data + | |
1519 | sizeof(struct esp_payload_header)); | |
1520 | ||
1521 | val->value = power; | |
1522 | ||
1523 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1524 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1525 | ||
1526 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1527 | ||
1528 | return 0; | |
1529 | } | |
1530 | ||
1531 | int cmd_get_tx_power(struct esp_wifi_device *priv) | |
1532 | { | |
1533 | u16 cmd_len; | |
1534 | struct command_node *cmd_node = NULL; | |
1535 | ||
1536 | if (!priv || !priv->adapter) { | |
1537 | esp_err("Invalid argument\n"); | |
1538 | return -EINVAL; | |
1539 | } | |
1540 | ||
1541 | cmd_len = sizeof(struct command_header) + sizeof(int32_t); | |
1542 | ||
1543 | cmd_node = prepare_command_request(priv->adapter, CMD_GET_TXPOWER, cmd_len); | |
1544 | ||
1545 | if (!cmd_node) { | |
1546 | esp_err("Failed to get command node\n"); | |
1547 | return -ENOMEM; | |
1548 | } | |
1549 | ||
1550 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1551 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1552 | ||
1553 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1554 | ||
1555 | return 0; | |
1556 | } | |
f7ac1df4 KG |
1557 | |
1558 | int cmd_get_reg_domain(struct esp_wifi_device *priv) | |
1559 | { | |
1560 | u16 cmd_len; | |
1561 | struct command_node *cmd_node = NULL; | |
1562 | ||
1563 | if (!priv || !priv->adapter) { | |
1564 | esp_err("Invalid argument\n"); | |
1565 | return -EINVAL; | |
1566 | } | |
1567 | ||
1568 | cmd_len = sizeof(struct cmd_reg_domain); | |
1569 | ||
1570 | cmd_node = prepare_command_request(priv->adapter, CMD_GET_REG_DOMAIN, cmd_len); | |
1571 | ||
1572 | if (!cmd_node) { | |
1573 | esp_err("Failed to get command node\n"); | |
1574 | return -ENOMEM; | |
1575 | } | |
1576 | ||
1577 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1578 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1579 | ||
1580 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1581 | ||
1582 | return 0; | |
1583 | } | |
1584 | ||
1585 | int cmd_set_reg_domain(struct esp_wifi_device *priv) | |
1586 | { | |
1587 | u16 cmd_len; | |
1588 | struct command_node *cmd_node = NULL; | |
1589 | struct cmd_reg_domain *cmd; | |
1590 | ||
1591 | if (!priv || !priv->adapter) { | |
1592 | esp_err("Invalid argument\n"); | |
1593 | return -EINVAL; | |
1594 | } | |
1595 | ||
1596 | cmd_len = sizeof(struct cmd_reg_domain); | |
1597 | ||
1598 | cmd_node = prepare_command_request(priv->adapter, CMD_SET_REG_DOMAIN, cmd_len); | |
1599 | ||
1600 | if (!cmd_node) { | |
1601 | esp_err("Failed to get command node\n"); | |
1602 | return -ENOMEM; | |
1603 | } | |
1604 | ||
1605 | cmd = (struct cmd_reg_domain *) (cmd_node->cmd_skb->data + sizeof(struct esp_payload_header)); | |
1606 | ||
1607 | strlcpy(cmd->country_code, priv->country_code, MAX_COUNTRY_LEN); | |
1608 | ||
1609 | queue_cmd_node(priv->adapter, cmd_node, ESP_CMD_DFLT_PRIO); | |
1610 | queue_work(priv->adapter->cmd_wq, &priv->adapter->cmd_work); | |
1611 | ||
1612 | RET_ON_FAIL(wait_and_decode_cmd_resp(priv, cmd_node)); | |
1613 | ||
1614 | return 0; | |
1615 | } |