Commit | Line | Data |
---|---|---|
bb9f8692 ZY |
1 | /* |
2 | * Intel Wireless Multicomm 3200 WiFi driver | |
3 | * | |
4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * | |
10 | * * Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * * Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in | |
14 | * the documentation and/or other materials provided with the | |
15 | * distribution. | |
16 | * * Neither the name of Intel Corporation nor the names of its | |
17 | * contributors may be used to endorse or promote products derived | |
18 | * from this software without specific prior written permission. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | * | |
32 | * | |
33 | * Intel Corporation <ilw@linux.intel.com> | |
34 | * Samuel Ortiz <samuel.ortiz@intel.com> | |
35 | * Zhu Yi <yi.zhu@intel.com> | |
36 | * | |
37 | */ | |
38 | ||
39 | #include <linux/kernel.h> | |
40 | #include <linux/wireless.h> | |
41 | #include <linux/etherdevice.h> | |
42 | #include <linux/ieee80211.h> | |
d43c36dc | 43 | #include <linux/sched.h> |
bb9f8692 ZY |
44 | |
45 | #include "iwm.h" | |
46 | #include "bus.h" | |
47 | #include "hal.h" | |
48 | #include "umac.h" | |
49 | #include "commands.h" | |
50 | #include "debug.h" | |
51 | ||
52 | static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm, | |
53 | u8 lmac_cmd_id, | |
54 | const void *lmac_payload, | |
55 | u16 lmac_payload_size, | |
56 | u8 resp) | |
57 | { | |
58 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_LMAC_INIT; | |
59 | struct iwm_umac_cmd umac_cmd; | |
60 | struct iwm_lmac_cmd lmac_cmd; | |
61 | ||
62 | lmac_cmd.id = lmac_cmd_id; | |
63 | ||
64 | umac_cmd.id = UMAC_CMD_OPCODE_WIFI_PASS_THROUGH; | |
65 | umac_cmd.resp = resp; | |
66 | ||
67 | return iwm_hal_send_host_cmd(iwm, &udma_cmd, &umac_cmd, &lmac_cmd, | |
68 | lmac_payload, lmac_payload_size); | |
69 | } | |
70 | ||
71 | int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, | |
72 | bool resp) | |
73 | { | |
a70742f1 | 74 | struct iwm_umac_wifi_if *hdr = (struct iwm_umac_wifi_if *)payload; |
bb9f8692 ZY |
75 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; |
76 | struct iwm_umac_cmd umac_cmd; | |
a70742f1 SO |
77 | int ret; |
78 | u8 oid = hdr->oid; | |
bb9f8692 | 79 | |
56e3f085 SO |
80 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) { |
81 | IWM_ERR(iwm, "Interface is not ready yet"); | |
82 | return -EAGAIN; | |
83 | } | |
84 | ||
bb9f8692 ZY |
85 | umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER; |
86 | umac_cmd.resp = resp; | |
87 | ||
a70742f1 SO |
88 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, |
89 | payload, payload_size); | |
90 | ||
91 | if (resp) { | |
92 | ret = wait_event_interruptible_timeout(iwm->wifi_ntfy_queue, | |
93 | test_and_clear_bit(oid, &iwm->wifi_ntfy[0]), | |
94 | 3 * HZ); | |
95 | ||
b6c32171 | 96 | return ret ? 0 : -EBUSY; |
a70742f1 SO |
97 | } |
98 | ||
99 | return ret; | |
bb9f8692 ZY |
100 | } |
101 | ||
102 | static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = | |
103 | { | |
104 | {4, 3, 0, COEX_UNASSOC_IDLE_FLAGS}, | |
105 | {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, | |
106 | {4, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, | |
107 | {4, 3, 0, COEX_CALIBRATION_FLAGS}, | |
108 | {4, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, | |
109 | {4, 3, 0, COEX_CONNECTION_ESTAB_FLAGS}, | |
110 | {4, 3, 0, COEX_ASSOCIATED_IDLE_FLAGS}, | |
111 | {4, 3, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, | |
112 | {4, 3, 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, | |
113 | {4, 3, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, | |
114 | {6, 3, 0, COEX_XOR_RF_ON_FLAGS}, | |
115 | {4, 3, 0, COEX_RF_OFF_FLAGS}, | |
116 | {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, | |
117 | {4, 3, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, | |
118 | {4, 3, 0, COEX_RSRVD1_FLAGS}, | |
119 | {4, 3, 0, COEX_RSRVD2_FLAGS} | |
120 | }; | |
121 | ||
122 | static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] = | |
123 | { | |
124 | {1, 1, 0, COEX_UNASSOC_IDLE_FLAGS}, | |
125 | {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, | |
126 | {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, | |
127 | {5, 5, 0, COEX_CALIBRATION_FLAGS}, | |
191506ec | 128 | {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, |
bb9f8692 ZY |
129 | {5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS}, |
130 | {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS}, | |
131 | {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, | |
132 | {4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, | |
133 | {4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, | |
134 | {1, 1, 0, COEX_RF_ON_FLAGS}, | |
135 | {1, 1, 0, COEX_RF_OFF_FLAGS}, | |
136 | {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, | |
137 | {5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, | |
138 | {1, 1, 0, COEX_RSRVD1_FLAGS}, | |
139 | {1, 1, 0, COEX_RSRVD2_FLAGS} | |
140 | }; | |
141 | ||
142 | int iwm_send_prio_table(struct iwm_priv *iwm) | |
143 | { | |
144 | struct iwm_coex_prio_table_cmd coex_table_cmd; | |
145 | u32 coex_enabled, mode_enabled; | |
146 | ||
147 | memset(&coex_table_cmd, 0, sizeof(struct iwm_coex_prio_table_cmd)); | |
148 | ||
149 | coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK; | |
150 | ||
151 | switch (iwm->conf.coexist_mode) { | |
152 | case COEX_MODE_XOR: | |
153 | case COEX_MODE_CM: | |
154 | coex_enabled = 1; | |
155 | break; | |
156 | default: | |
157 | coex_enabled = 0; | |
158 | break; | |
159 | } | |
160 | ||
161 | switch (iwm->conf.mode) { | |
162 | case UMAC_MODE_BSS: | |
163 | case UMAC_MODE_IBSS: | |
164 | mode_enabled = 1; | |
165 | break; | |
166 | default: | |
167 | mode_enabled = 0; | |
168 | break; | |
169 | } | |
170 | ||
171 | if (coex_enabled && mode_enabled) { | |
172 | coex_table_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK | | |
173 | COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK | | |
174 | COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK; | |
175 | ||
176 | switch (iwm->conf.coexist_mode) { | |
177 | case COEX_MODE_XOR: | |
178 | memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl, | |
179 | sizeof(iwm_sta_xor_prio_tbl)); | |
180 | break; | |
181 | case COEX_MODE_CM: | |
182 | memcpy(coex_table_cmd.sta_prio, iwm_sta_cm_prio_tbl, | |
183 | sizeof(iwm_sta_cm_prio_tbl)); | |
184 | break; | |
185 | default: | |
186 | IWM_ERR(iwm, "Invalid coex_mode 0x%x\n", | |
187 | iwm->conf.coexist_mode); | |
188 | break; | |
189 | } | |
190 | } else | |
191 | IWM_WARN(iwm, "coexistense disabled\n"); | |
192 | ||
193 | return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD, | |
194 | &coex_table_cmd, | |
195 | sizeof(struct iwm_coex_prio_table_cmd), 1); | |
196 | } | |
197 | ||
198 | int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested) | |
199 | { | |
200 | struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd; | |
201 | ||
202 | memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd)); | |
203 | ||
204 | cal_cfg_cmd.ucode_cfg.init.enable = cpu_to_le32(calib_requested); | |
205 | cal_cfg_cmd.ucode_cfg.init.start = cpu_to_le32(calib_requested); | |
206 | cal_cfg_cmd.ucode_cfg.init.send_res = cpu_to_le32(calib_requested); | |
207 | cal_cfg_cmd.ucode_cfg.flags = | |
208 | cpu_to_le32(CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK); | |
209 | ||
210 | return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd, | |
211 | sizeof(struct iwm_lmac_cal_cfg_cmd), 1); | |
212 | } | |
213 | ||
214 | int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested) | |
215 | { | |
216 | struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd; | |
217 | ||
218 | memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd)); | |
219 | ||
220 | cal_cfg_cmd.ucode_cfg.periodic.enable = cpu_to_le32(calib_requested); | |
221 | cal_cfg_cmd.ucode_cfg.periodic.start = cpu_to_le32(calib_requested); | |
222 | ||
223 | return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd, | |
224 | sizeof(struct iwm_lmac_cal_cfg_cmd), 0); | |
225 | } | |
226 | ||
227 | int iwm_store_rxiq_calib_result(struct iwm_priv *iwm) | |
228 | { | |
229 | struct iwm_calib_rxiq *rxiq; | |
230 | u8 *eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ); | |
231 | int grplen = sizeof(struct iwm_calib_rxiq_group); | |
232 | ||
233 | rxiq = kzalloc(sizeof(struct iwm_calib_rxiq), GFP_KERNEL); | |
234 | if (!rxiq) { | |
235 | IWM_ERR(iwm, "Couldn't alloc memory for RX IQ\n"); | |
236 | return -ENOMEM; | |
237 | } | |
238 | ||
239 | eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ); | |
240 | if (IS_ERR(eeprom_rxiq)) { | |
241 | IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n"); | |
9f9857bb | 242 | kfree(rxiq); |
bb9f8692 ZY |
243 | return PTR_ERR(eeprom_rxiq); |
244 | } | |
245 | ||
246 | iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].buf = (u8 *)rxiq; | |
247 | iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].size = sizeof(*rxiq); | |
248 | ||
249 | rxiq->hdr.opcode = SHILOH_PHY_CALIBRATE_RX_IQ_CMD; | |
250 | rxiq->hdr.first_grp = 0; | |
251 | rxiq->hdr.grp_num = 1; | |
252 | rxiq->hdr.all_data_valid = 1; | |
253 | ||
254 | memcpy(&rxiq->group[0], eeprom_rxiq, 4 * grplen); | |
255 | memcpy(&rxiq->group[4], eeprom_rxiq + 6 * grplen, grplen); | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | int iwm_send_calib_results(struct iwm_priv *iwm) | |
261 | { | |
262 | int i, ret = 0; | |
263 | ||
264 | for (i = PHY_CALIBRATE_OPCODES_NUM; i < CALIBRATION_CMD_NUM; i++) { | |
265 | if (test_bit(i - PHY_CALIBRATE_OPCODES_NUM, | |
266 | &iwm->calib_done_map)) { | |
267 | IWM_DBG_CMD(iwm, DBG, | |
268 | "Send calibration %d result\n", i); | |
269 | ret |= iwm_send_lmac_ptrough_cmd(iwm, | |
270 | REPLY_PHY_CALIBRATION_CMD, | |
271 | iwm->calib_res[i].buf, | |
272 | iwm->calib_res[i].size, 0); | |
273 | ||
274 | kfree(iwm->calib_res[i].buf); | |
275 | iwm->calib_res[i].buf = NULL; | |
276 | iwm->calib_res[i].size = 0; | |
277 | } | |
278 | } | |
279 | ||
280 | return ret; | |
281 | } | |
282 | ||
e85498b2 SO |
283 | int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit) |
284 | { | |
285 | struct iwm_ct_kill_cfg_cmd cmd; | |
286 | ||
287 | cmd.entry_threshold = entry; | |
288 | cmd.exit_threshold = exit; | |
289 | ||
290 | return iwm_send_lmac_ptrough_cmd(iwm, REPLY_CT_KILL_CONFIG_CMD, &cmd, | |
291 | sizeof(struct iwm_ct_kill_cfg_cmd), 0); | |
292 | } | |
293 | ||
bb9f8692 ZY |
294 | int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp) |
295 | { | |
296 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | |
297 | struct iwm_umac_cmd umac_cmd; | |
298 | struct iwm_umac_cmd_reset reset; | |
299 | ||
300 | reset.flags = reset_flags; | |
301 | ||
302 | umac_cmd.id = UMAC_CMD_OPCODE_RESET; | |
303 | umac_cmd.resp = resp; | |
304 | ||
305 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &reset, | |
306 | sizeof(struct iwm_umac_cmd_reset)); | |
307 | } | |
308 | ||
309 | int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value) | |
310 | { | |
311 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | |
312 | struct iwm_umac_cmd umac_cmd; | |
313 | struct iwm_umac_cmd_set_param_fix param; | |
314 | ||
315 | if ((tbl != UMAC_PARAM_TBL_CFG_FIX) && | |
316 | (tbl != UMAC_PARAM_TBL_FA_CFG_FIX)) | |
317 | return -EINVAL; | |
318 | ||
319 | umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_FIX; | |
320 | umac_cmd.resp = 0; | |
321 | ||
322 | param.tbl = cpu_to_le16(tbl); | |
323 | param.key = cpu_to_le16(key); | |
324 | param.value = cpu_to_le32(value); | |
325 | ||
326 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ¶m, | |
327 | sizeof(struct iwm_umac_cmd_set_param_fix)); | |
328 | } | |
329 | ||
330 | int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key, | |
331 | void *payload, u16 payload_size) | |
332 | { | |
333 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | |
334 | struct iwm_umac_cmd umac_cmd; | |
335 | struct iwm_umac_cmd_set_param_var *param_hdr; | |
336 | u8 *param; | |
337 | int ret; | |
338 | ||
339 | param = kzalloc(payload_size + | |
340 | sizeof(struct iwm_umac_cmd_set_param_var), GFP_KERNEL); | |
341 | if (!param) { | |
342 | IWM_ERR(iwm, "Couldn't allocate param\n"); | |
343 | return -ENOMEM; | |
344 | } | |
345 | ||
346 | param_hdr = (struct iwm_umac_cmd_set_param_var *)param; | |
347 | ||
348 | umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_VAR; | |
349 | umac_cmd.resp = 0; | |
350 | ||
351 | param_hdr->tbl = cpu_to_le16(UMAC_PARAM_TBL_CFG_VAR); | |
352 | param_hdr->key = cpu_to_le16(key); | |
353 | param_hdr->len = cpu_to_le16(payload_size); | |
354 | memcpy(param + sizeof(struct iwm_umac_cmd_set_param_var), | |
355 | payload, payload_size); | |
356 | ||
357 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, param, | |
358 | sizeof(struct iwm_umac_cmd_set_param_var) + | |
359 | payload_size); | |
360 | kfree(param); | |
361 | ||
362 | return ret; | |
363 | } | |
364 | ||
191506ec | 365 | int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags) |
bb9f8692 ZY |
366 | { |
367 | int ret; | |
368 | ||
369 | /* Use UMAC default values */ | |
370 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
371 | CFG_POWER_INDEX, iwm->conf.power_index); | |
372 | if (ret < 0) | |
373 | return ret; | |
374 | ||
375 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX, | |
376 | CFG_FRAG_THRESHOLD, | |
377 | iwm->conf.frag_threshold); | |
378 | if (ret < 0) | |
379 | return ret; | |
380 | ||
381 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
382 | CFG_RTS_THRESHOLD, | |
383 | iwm->conf.rts_threshold); | |
384 | if (ret < 0) | |
385 | return ret; | |
386 | ||
387 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
388 | CFG_CTS_TO_SELF, iwm->conf.cts_to_self); | |
389 | if (ret < 0) | |
390 | return ret; | |
391 | ||
191506ec ZY |
392 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, |
393 | CFG_WIRELESS_MODE, | |
394 | iwm->conf.wireless_mode); | |
395 | if (ret < 0) | |
396 | return ret; | |
397 | ||
bb9f8692 ZY |
398 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, |
399 | CFG_COEX_MODE, iwm->conf.coexist_mode); | |
400 | if (ret < 0) | |
401 | return ret; | |
402 | ||
403 | /* | |
404 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
405 | CFG_ASSOCIATION_TIMEOUT, | |
406 | iwm->conf.assoc_timeout); | |
407 | if (ret < 0) | |
408 | return ret; | |
409 | ||
410 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
411 | CFG_ROAM_TIMEOUT, | |
412 | iwm->conf.roam_timeout); | |
413 | if (ret < 0) | |
414 | return ret; | |
415 | ||
416 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
417 | CFG_WIRELESS_MODE, | |
418 | WIRELESS_MODE_11A | WIRELESS_MODE_11G); | |
419 | if (ret < 0) | |
420 | return ret; | |
421 | */ | |
422 | ||
423 | ret = iwm_umac_set_config_var(iwm, CFG_NET_ADDR, | |
424 | iwm_to_ndev(iwm)->dev_addr, ETH_ALEN); | |
425 | if (ret < 0) | |
426 | return ret; | |
427 | ||
428 | /* UMAC PM static configurations */ | |
429 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
430 | CFG_PM_LEGACY_RX_TIMEOUT, 0x12C); | |
431 | if (ret < 0) | |
432 | return ret; | |
433 | ||
434 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
435 | CFG_PM_LEGACY_TX_TIMEOUT, 0x15E); | |
436 | if (ret < 0) | |
437 | return ret; | |
438 | ||
439 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
191506ec | 440 | CFG_PM_CTRL_FLAGS, 0x1); |
bb9f8692 ZY |
441 | if (ret < 0) |
442 | return ret; | |
443 | ||
444 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | |
445 | CFG_PM_KEEP_ALIVE_IN_BEACONS, 0x80); | |
446 | if (ret < 0) | |
447 | return ret; | |
448 | ||
449 | /* reset UMAC */ | |
450 | ret = iwm_send_umac_reset(iwm, reset_flags, 1); | |
451 | if (ret < 0) | |
452 | return ret; | |
453 | ||
454 | ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC, | |
455 | WAIT_NOTIF_TIMEOUT); | |
456 | if (ret) { | |
457 | IWM_ERR(iwm, "Wait for UMAC RESET timeout\n"); | |
458 | return ret; | |
459 | } | |
460 | ||
461 | return ret; | |
462 | } | |
463 | ||
464 | int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id) | |
465 | { | |
466 | struct iwm_udma_wifi_cmd udma_cmd; | |
467 | struct iwm_umac_cmd umac_cmd; | |
468 | struct iwm_tx_info *tx_info = skb_to_tx_info(skb); | |
469 | ||
470 | udma_cmd.eop = 1; /* always set eop for non-concatenated Tx */ | |
471 | udma_cmd.credit_group = pool_id; | |
472 | udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid; | |
473 | udma_cmd.lmac_offset = 0; | |
474 | ||
475 | umac_cmd.id = REPLY_TX; | |
476 | umac_cmd.color = tx_info->color; | |
477 | umac_cmd.resp = 0; | |
478 | ||
479 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, | |
480 | skb->data, skb->len); | |
481 | } | |
482 | ||
483 | static int iwm_target_read(struct iwm_priv *iwm, __le32 address, | |
484 | u8 *response, u32 resp_size) | |
485 | { | |
486 | struct iwm_udma_nonwifi_cmd target_cmd; | |
487 | struct iwm_nonwifi_cmd *cmd; | |
488 | u16 seq_num; | |
489 | int ret = 0; | |
490 | ||
491 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ; | |
492 | target_cmd.addr = address; | |
493 | target_cmd.op1_sz = cpu_to_le32(resp_size); | |
494 | target_cmd.op2 = 0; | |
495 | target_cmd.handle_by_hw = 0; | |
496 | target_cmd.resp = 1; | |
497 | target_cmd.eop = 1; | |
498 | ||
499 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); | |
b6c32171 | 500 | if (ret < 0) { |
bb9f8692 | 501 | IWM_ERR(iwm, "Couldn't send READ command\n"); |
b6c32171 ZY |
502 | return ret; |
503 | } | |
bb9f8692 ZY |
504 | |
505 | /* When succeding, the send_target routine returns the seq number */ | |
506 | seq_num = ret; | |
507 | ||
508 | ret = wait_event_interruptible_timeout(iwm->nonwifi_queue, | |
509 | (cmd = iwm_get_pending_nonwifi_cmd(iwm, seq_num, | |
510 | UMAC_HDI_OUT_OPCODE_READ)) != NULL, | |
511 | 2 * HZ); | |
512 | ||
513 | if (!ret) { | |
514 | IWM_ERR(iwm, "Didn't receive a target READ answer\n"); | |
515 | return ret; | |
516 | } | |
517 | ||
518 | memcpy(response, cmd->buf.hdr + sizeof(struct iwm_udma_in_hdr), | |
519 | resp_size); | |
520 | ||
521 | kfree(cmd); | |
522 | ||
b6c32171 | 523 | return 0; |
bb9f8692 ZY |
524 | } |
525 | ||
526 | int iwm_read_mac(struct iwm_priv *iwm, u8 *mac) | |
527 | { | |
528 | int ret; | |
529 | u8 mac_align[ALIGN(ETH_ALEN, 8)]; | |
530 | ||
531 | ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR), | |
532 | mac_align, sizeof(mac_align)); | |
b6c32171 | 533 | if (ret) |
bb9f8692 ZY |
534 | return ret; |
535 | ||
536 | if (is_valid_ether_addr(mac_align)) | |
537 | memcpy(mac, mac_align, ETH_ALEN); | |
538 | else { | |
539 | IWM_ERR(iwm, "Invalid EEPROM MAC\n"); | |
540 | memcpy(mac, iwm->conf.mac_addr, ETH_ALEN); | |
541 | get_random_bytes(&mac[3], 3); | |
542 | } | |
543 | ||
544 | return 0; | |
545 | } | |
546 | ||
bb9f8692 ZY |
547 | static int iwm_check_profile(struct iwm_priv *iwm) |
548 | { | |
549 | if (!iwm->umac_profile_active) | |
550 | return -EAGAIN; | |
551 | ||
552 | if (iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 && | |
553 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104 && | |
554 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_TKIP && | |
555 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_CCMP) { | |
556 | IWM_ERR(iwm, "Wrong unicast cipher: 0x%x\n", | |
557 | iwm->umac_profile->sec.ucast_cipher); | |
558 | return -EAGAIN; | |
559 | } | |
560 | ||
561 | if (iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_40 && | |
562 | iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_104 && | |
563 | iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_TKIP && | |
564 | iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_CCMP) { | |
565 | IWM_ERR(iwm, "Wrong multicast cipher: 0x%x\n", | |
566 | iwm->umac_profile->sec.mcast_cipher); | |
567 | return -EAGAIN; | |
568 | } | |
569 | ||
570 | if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 || | |
571 | iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) && | |
572 | (iwm->umac_profile->sec.ucast_cipher != | |
573 | iwm->umac_profile->sec.mcast_cipher)) { | |
574 | IWM_ERR(iwm, "Unicast and multicast ciphers differ for WEP\n"); | |
575 | } | |
576 | ||
577 | return 0; | |
578 | } | |
579 | ||
847c1e13 ZY |
580 | int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx) |
581 | { | |
582 | struct iwm_umac_tx_key_id tx_key_id; | |
583 | int ret; | |
584 | ||
585 | ret = iwm_check_profile(iwm); | |
586 | if (ret < 0) | |
587 | return ret; | |
588 | ||
589 | /* UMAC only allows to set default key for WEP and auth type is | |
590 | * NOT 802.1X or RSNA. */ | |
591 | if ((iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 && | |
592 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104) || | |
593 | iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_8021X || | |
594 | iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_RSNA_PSK) | |
595 | return 0; | |
596 | ||
597 | tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID; | |
598 | tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) - | |
599 | sizeof(struct iwm_umac_wifi_if)); | |
600 | ||
601 | tx_key_id.key_idx = key_idx; | |
602 | ||
603 | return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1); | |
604 | } | |
605 | ||
13e0fe70 | 606 | int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key) |
bb9f8692 | 607 | { |
13e0fe70 | 608 | int ret = 0; |
bb9f8692 ZY |
609 | u8 cmd[64], *sta_addr, *key_data, key_len; |
610 | s8 key_idx; | |
611 | u16 cmd_size = 0; | |
612 | struct iwm_umac_key_hdr *key_hdr = &key->hdr; | |
613 | struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd; | |
614 | struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd; | |
615 | struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd; | |
616 | struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd; | |
617 | ||
bb9f8692 ZY |
618 | if (!remove) { |
619 | ret = iwm_check_profile(iwm); | |
620 | if (ret < 0) | |
621 | return ret; | |
622 | } | |
623 | ||
624 | sta_addr = key->hdr.mac; | |
625 | key_data = key->key; | |
626 | key_len = key->key_len; | |
627 | key_idx = key->hdr.key_idx; | |
628 | ||
629 | if (!remove) { | |
beda278d ZY |
630 | u8 auth_type = iwm->umac_profile->sec.auth_type; |
631 | ||
13e0fe70 | 632 | IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx); |
bb9f8692 ZY |
633 | IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len); |
634 | IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n", | |
635 | key_hdr->mac, key_hdr->key_idx, key_hdr->multicast); | |
636 | ||
637 | IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n", | |
638 | iwm->umac_profile->sec.mcast_cipher, | |
639 | iwm->umac_profile->sec.ucast_cipher); | |
640 | IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n", | |
641 | iwm->umac_profile->sec.auth_type, | |
642 | iwm->umac_profile->sec.flags); | |
643 | ||
13e0fe70 SO |
644 | switch (key->cipher) { |
645 | case WLAN_CIPHER_SUITE_WEP40: | |
bb9f8692 ZY |
646 | wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY; |
647 | wep40->hdr.buf_size = | |
648 | cpu_to_le16(sizeof(struct iwm_umac_key_wep40) - | |
649 | sizeof(struct iwm_umac_wifi_if)); | |
650 | ||
651 | memcpy(&wep40->key_hdr, key_hdr, | |
652 | sizeof(struct iwm_umac_key_hdr)); | |
653 | memcpy(wep40->key, key_data, key_len); | |
beda278d ZY |
654 | wep40->static_key = |
655 | !!((auth_type != UMAC_AUTH_TYPE_8021X) && | |
656 | (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); | |
bb9f8692 ZY |
657 | |
658 | cmd_size = sizeof(struct iwm_umac_key_wep40); | |
659 | break; | |
660 | ||
13e0fe70 | 661 | case WLAN_CIPHER_SUITE_WEP104: |
bb9f8692 ZY |
662 | wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY; |
663 | wep104->hdr.buf_size = | |
664 | cpu_to_le16(sizeof(struct iwm_umac_key_wep104) - | |
665 | sizeof(struct iwm_umac_wifi_if)); | |
666 | ||
667 | memcpy(&wep104->key_hdr, key_hdr, | |
668 | sizeof(struct iwm_umac_key_hdr)); | |
669 | memcpy(wep104->key, key_data, key_len); | |
beda278d ZY |
670 | wep104->static_key = |
671 | !!((auth_type != UMAC_AUTH_TYPE_8021X) && | |
672 | (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); | |
bb9f8692 ZY |
673 | |
674 | cmd_size = sizeof(struct iwm_umac_key_wep104); | |
675 | break; | |
676 | ||
13e0fe70 | 677 | case WLAN_CIPHER_SUITE_CCMP: |
bb9f8692 ZY |
678 | key_hdr->key_idx++; |
679 | ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY; | |
680 | ccmp->hdr.buf_size = | |
681 | cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) - | |
682 | sizeof(struct iwm_umac_wifi_if)); | |
683 | ||
684 | memcpy(&ccmp->key_hdr, key_hdr, | |
685 | sizeof(struct iwm_umac_key_hdr)); | |
686 | ||
687 | memcpy(ccmp->key, key_data, key_len); | |
688 | ||
13e0fe70 SO |
689 | if (key->seq_len) |
690 | memcpy(ccmp->iv_count, key->seq, key->seq_len); | |
bb9f8692 ZY |
691 | |
692 | cmd_size = sizeof(struct iwm_umac_key_ccmp); | |
693 | break; | |
694 | ||
13e0fe70 | 695 | case WLAN_CIPHER_SUITE_TKIP: |
bb9f8692 ZY |
696 | key_hdr->key_idx++; |
697 | tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY; | |
698 | tkip->hdr.buf_size = | |
699 | cpu_to_le16(sizeof(struct iwm_umac_key_tkip) - | |
700 | sizeof(struct iwm_umac_wifi_if)); | |
701 | ||
702 | memcpy(&tkip->key_hdr, key_hdr, | |
703 | sizeof(struct iwm_umac_key_hdr)); | |
704 | ||
705 | memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE); | |
706 | memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE, | |
707 | IWM_TKIP_MIC_SIZE); | |
708 | memcpy(tkip->mic_rx_key, | |
709 | key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE, | |
710 | IWM_TKIP_MIC_SIZE); | |
711 | ||
13e0fe70 SO |
712 | if (key->seq_len) |
713 | memcpy(ccmp->iv_count, key->seq, key->seq_len); | |
bb9f8692 ZY |
714 | |
715 | cmd_size = sizeof(struct iwm_umac_key_tkip); | |
716 | break; | |
717 | ||
718 | default: | |
719 | return -ENOTSUPP; | |
720 | } | |
721 | ||
13e0fe70 SO |
722 | if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) || |
723 | (key->cipher == WLAN_CIPHER_SUITE_CCMP)) | |
bb9f8692 ZY |
724 | /* |
725 | * UGLY_UGLY_UGLY | |
726 | * Copied HACK from the MWG driver. | |
727 | * Without it, the key is set before the second | |
728 | * EAPOL frame is sent, and the latter is thus | |
729 | * encrypted. | |
730 | */ | |
731 | schedule_timeout_interruptible(usecs_to_jiffies(300)); | |
732 | ||
733 | ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1); | |
bb9f8692 ZY |
734 | } else { |
735 | struct iwm_umac_key_remove key_remove; | |
736 | ||
13e0fe70 SO |
737 | IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx); |
738 | ||
bb9f8692 ZY |
739 | key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY; |
740 | key_remove.hdr.buf_size = | |
741 | cpu_to_le16(sizeof(struct iwm_umac_key_remove) - | |
742 | sizeof(struct iwm_umac_wifi_if)); | |
743 | memcpy(&key_remove.key_hdr, key_hdr, | |
744 | sizeof(struct iwm_umac_key_hdr)); | |
745 | ||
746 | ret = iwm_send_wifi_if_cmd(iwm, &key_remove, | |
747 | sizeof(struct iwm_umac_key_remove), | |
748 | 1); | |
b6c32171 | 749 | if (ret) |
bb9f8692 ZY |
750 | return ret; |
751 | ||
13e0fe70 | 752 | iwm->keys[key_idx].key_len = 0; |
bb9f8692 ZY |
753 | } |
754 | ||
bb9f8692 ZY |
755 | return ret; |
756 | } | |
757 | ||
758 | ||
759 | int iwm_send_mlme_profile(struct iwm_priv *iwm) | |
760 | { | |
6e5db0a8 | 761 | int ret; |
bb9f8692 ZY |
762 | struct iwm_umac_profile profile; |
763 | ||
764 | memcpy(&profile, iwm->umac_profile, sizeof(profile)); | |
765 | ||
766 | profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE; | |
767 | profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) - | |
768 | sizeof(struct iwm_umac_wifi_if)); | |
769 | ||
770 | ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1); | |
b6c32171 | 771 | if (ret) { |
bb9f8692 ZY |
772 | IWM_ERR(iwm, "Send profile command failed\n"); |
773 | return ret; | |
774 | } | |
775 | ||
de15fd31 | 776 | set_bit(IWM_STATUS_SME_CONNECTING, &iwm->status); |
bb9f8692 ZY |
777 | return 0; |
778 | } | |
779 | ||
780 | int iwm_invalidate_mlme_profile(struct iwm_priv *iwm) | |
781 | { | |
bb9f8692 | 782 | struct iwm_umac_invalidate_profile invalid; |
a70742f1 | 783 | int ret; |
bb9f8692 ZY |
784 | |
785 | invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE; | |
786 | invalid.hdr.buf_size = | |
787 | cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) - | |
788 | sizeof(struct iwm_umac_wifi_if)); | |
789 | ||
790 | invalid.reason = WLAN_REASON_UNSPECIFIED; | |
791 | ||
792 | ret = iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1); | |
b6c32171 | 793 | if (ret) |
bb9f8692 ZY |
794 | return ret; |
795 | ||
796 | ret = wait_event_interruptible_timeout(iwm->mlme_queue, | |
9829e1b5 | 797 | (iwm->umac_profile_active == 0), 5 * HZ); |
bb9f8692 | 798 | |
b6c32171 | 799 | return ret ? 0 : -EBUSY; |
bb9f8692 ZY |
800 | } |
801 | ||
88e6195a SO |
802 | int iwm_tx_power_trigger(struct iwm_priv *iwm) |
803 | { | |
804 | struct iwm_umac_pwr_trigger pwr_trigger; | |
805 | ||
806 | pwr_trigger.hdr.oid = UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER; | |
807 | pwr_trigger.hdr.buf_size = | |
808 | cpu_to_le16(sizeof(struct iwm_umac_pwr_trigger) - | |
809 | sizeof(struct iwm_umac_wifi_if)); | |
810 | ||
811 | ||
812 | return iwm_send_wifi_if_cmd(iwm, &pwr_trigger, sizeof(pwr_trigger), 1); | |
813 | } | |
814 | ||
bb9f8692 ZY |
815 | int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags) |
816 | { | |
817 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | |
818 | struct iwm_umac_cmd umac_cmd; | |
819 | struct iwm_umac_cmd_stats_req stats_req; | |
820 | ||
821 | stats_req.flags = cpu_to_le32(flags); | |
822 | ||
823 | umac_cmd.id = UMAC_CMD_OPCODE_STATISTIC_REQUEST; | |
824 | umac_cmd.resp = 0; | |
825 | ||
826 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stats_req, | |
827 | sizeof(struct iwm_umac_cmd_stats_req)); | |
828 | } | |
829 | ||
830 | int iwm_send_umac_channel_list(struct iwm_priv *iwm) | |
831 | { | |
832 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | |
833 | struct iwm_umac_cmd umac_cmd; | |
834 | struct iwm_umac_cmd_get_channel_list *ch_list; | |
835 | int size = sizeof(struct iwm_umac_cmd_get_channel_list) + | |
836 | sizeof(struct iwm_umac_channel_info) * 4; | |
837 | int ret; | |
838 | ||
839 | ch_list = kzalloc(size, GFP_KERNEL); | |
840 | if (!ch_list) { | |
841 | IWM_ERR(iwm, "Couldn't allocate channel list cmd\n"); | |
842 | return -ENOMEM; | |
843 | } | |
844 | ||
845 | ch_list->ch[0].band = UMAC_BAND_2GHZ; | |
846 | ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ; | |
847 | ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID; | |
848 | ||
849 | ch_list->ch[1].band = UMAC_BAND_5GHZ; | |
850 | ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ; | |
851 | ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID; | |
852 | ||
853 | ch_list->ch[2].band = UMAC_BAND_2GHZ; | |
854 | ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ; | |
855 | ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; | |
856 | ||
857 | ch_list->ch[3].band = UMAC_BAND_5GHZ; | |
858 | ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ; | |
859 | ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; | |
860 | ||
861 | ch_list->count = cpu_to_le16(4); | |
862 | ||
863 | umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST; | |
864 | umac_cmd.resp = 1; | |
865 | ||
866 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size); | |
867 | ||
868 | kfree(ch_list); | |
869 | ||
870 | return ret; | |
871 | } | |
872 | ||
873 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, | |
874 | int ssid_num) | |
875 | { | |
876 | struct iwm_umac_cmd_scan_request req; | |
877 | int i, ret; | |
878 | ||
879 | memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request)); | |
880 | ||
881 | req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST; | |
882 | req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request) | |
883 | - sizeof(struct iwm_umac_wifi_if)); | |
884 | req.type = UMAC_WIFI_IF_SCAN_TYPE_USER; | |
885 | req.timeout = 2; | |
886 | req.seq_num = iwm->scan_id; | |
887 | req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX); | |
888 | ||
889 | for (i = 0; i < req.ssid_num; i++) { | |
890 | memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len); | |
891 | req.ssids[i].ssid_len = ssids[i].ssid_len; | |
892 | } | |
893 | ||
894 | ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0); | |
b6c32171 | 895 | if (ret) { |
bb9f8692 ZY |
896 | IWM_ERR(iwm, "Couldn't send scan request\n"); |
897 | return ret; | |
898 | } | |
899 | ||
900 | iwm->scan_id = iwm->scan_id++ % IWM_SCAN_ID_MAX; | |
901 | ||
902 | return 0; | |
903 | } | |
904 | ||
905 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len) | |
906 | { | |
907 | struct cfg80211_ssid one_ssid; | |
908 | ||
909 | if (test_and_set_bit(IWM_STATUS_SCANNING, &iwm->status)) | |
910 | return 0; | |
911 | ||
912 | one_ssid.ssid_len = min(ssid_len, IEEE80211_MAX_SSID_LEN); | |
913 | memcpy(&one_ssid.ssid, ssid, one_ssid.ssid_len); | |
914 | ||
915 | return iwm_scan_ssids(iwm, &one_ssid, 1); | |
916 | } | |
917 | ||
918 | int iwm_target_reset(struct iwm_priv *iwm) | |
919 | { | |
920 | struct iwm_udma_nonwifi_cmd target_cmd; | |
921 | ||
922 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_REBOOT; | |
923 | target_cmd.addr = 0; | |
924 | target_cmd.op1_sz = 0; | |
925 | target_cmd.op2 = 0; | |
926 | target_cmd.handle_by_hw = 0; | |
927 | target_cmd.resp = 0; | |
928 | target_cmd.eop = 1; | |
929 | ||
930 | return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); | |
931 | } | |
a7af530d SO |
932 | |
933 | int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, | |
934 | struct iwm_umac_notif_stop_resume_tx *ntf) | |
935 | { | |
936 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | |
937 | struct iwm_umac_cmd umac_cmd; | |
938 | struct iwm_umac_cmd_stop_resume_tx stp_res_cmd; | |
939 | struct iwm_sta_info *sta_info; | |
940 | u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id); | |
941 | int i; | |
942 | ||
943 | sta_info = &iwm->sta_table[sta_id]; | |
944 | if (!sta_info->valid) { | |
945 | IWM_ERR(iwm, "Invalid STA: %d\n", sta_id); | |
946 | return -EINVAL; | |
947 | } | |
948 | ||
949 | umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX; | |
950 | umac_cmd.resp = 0; | |
951 | ||
952 | stp_res_cmd.flags = ntf->flags; | |
953 | stp_res_cmd.sta_id = ntf->sta_id; | |
954 | stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk; | |
955 | for (i = 0; i < IWM_UMAC_TID_NR; i++) | |
956 | stp_res_cmd.last_seq_num[i] = | |
957 | sta_info->tid_info[i].last_seq_num; | |
958 | ||
959 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd, | |
960 | sizeof(struct iwm_umac_cmd_stop_resume_tx)); | |
961 | ||
962 | } | |
9bf22f2c SO |
963 | |
964 | int iwm_send_pmkid_update(struct iwm_priv *iwm, | |
965 | struct cfg80211_pmksa *pmksa, u32 command) | |
966 | { | |
967 | struct iwm_umac_pmkid_update update; | |
968 | int ret; | |
969 | ||
970 | memset(&update, 0, sizeof(struct iwm_umac_pmkid_update)); | |
971 | ||
972 | update.command = cpu_to_le32(command); | |
973 | memcpy(&update.bssid, pmksa->bssid, ETH_ALEN); | |
974 | memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN); | |
975 | ||
976 | ret = iwm_send_wifi_if_cmd(iwm, &update, | |
977 | sizeof(struct iwm_umac_pmkid_update), 0); | |
978 | if (ret) { | |
979 | IWM_ERR(iwm, "PMKID update command failed\n"); | |
980 | return ret; | |
981 | } | |
982 | ||
983 | return 0; | |
984 | } |