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