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