diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/net/wireless/iwmc3200wifi/commands.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi/commands.c')
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/commands.c | 1001 |
1 files changed, 1001 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c new file mode 100644 index 00000000000..50dee6a0a5c --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/commands.c | |||
@@ -0,0 +1,1001 @@ | |||
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> | ||
43 | #include <linux/sched.h> | ||
44 | #include <linux/slab.h> | ||
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 | { | ||
75 | struct iwm_umac_wifi_if *hdr = (struct iwm_umac_wifi_if *)payload; | ||
76 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
77 | struct iwm_umac_cmd umac_cmd; | ||
78 | int ret; | ||
79 | u8 oid = hdr->oid; | ||
80 | |||
81 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) { | ||
82 | IWM_ERR(iwm, "Interface is not ready yet"); | ||
83 | return -EAGAIN; | ||
84 | } | ||
85 | |||
86 | umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER; | ||
87 | umac_cmd.resp = resp; | ||
88 | |||
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 | |||
97 | return ret ? 0 : -EBUSY; | ||
98 | } | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
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 | |||
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}, | ||
130 | {4, 4, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, | ||
131 | {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, | ||
132 | {6, 6, 0, COEX_CALIBRATION_FLAGS}, | ||
133 | {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, | ||
134 | {6, 5, 0, COEX_CONNECTION_ESTAB_FLAGS}, | ||
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}, | ||
141 | {7, 7, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, | ||
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 | |||
156 | switch (modparam_wiwi) { | ||
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 | |||
181 | switch (modparam_wiwi) { | ||
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", | ||
192 | modparam_wiwi); | ||
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, | ||
200 | sizeof(struct iwm_coex_prio_table_cmd), 0); | ||
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"); | ||
247 | kfree(rxiq); | ||
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 | |||
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 | |||
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 | |||
370 | int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags) | ||
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 | |||
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 | |||
403 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
404 | CFG_COEX_MODE, modparam_wiwi); | ||
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, | ||
445 | CFG_PM_CTRL_FLAGS, 0x1); | ||
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); | ||
505 | if (ret < 0) { | ||
506 | IWM_ERR(iwm, "Couldn't send READ command\n"); | ||
507 | return ret; | ||
508 | } | ||
509 | |||
510 | /* When succeeding, 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 | |||
528 | return 0; | ||
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)); | ||
538 | if (ret) | ||
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 | |||
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 | |||
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 | |||
611 | int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key) | ||
612 | { | ||
613 | int ret = 0; | ||
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 | |||
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) { | ||
635 | u8 auth_type = iwm->umac_profile->sec.auth_type; | ||
636 | |||
637 | IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx); | ||
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 | |||
649 | switch (key->cipher) { | ||
650 | case WLAN_CIPHER_SUITE_WEP40: | ||
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); | ||
659 | wep40->static_key = | ||
660 | !!((auth_type != UMAC_AUTH_TYPE_8021X) && | ||
661 | (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); | ||
662 | |||
663 | cmd_size = sizeof(struct iwm_umac_key_wep40); | ||
664 | break; | ||
665 | |||
666 | case WLAN_CIPHER_SUITE_WEP104: | ||
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); | ||
675 | wep104->static_key = | ||
676 | !!((auth_type != UMAC_AUTH_TYPE_8021X) && | ||
677 | (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); | ||
678 | |||
679 | cmd_size = sizeof(struct iwm_umac_key_wep104); | ||
680 | break; | ||
681 | |||
682 | case WLAN_CIPHER_SUITE_CCMP: | ||
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 | |||
694 | if (key->seq_len) | ||
695 | memcpy(ccmp->iv_count, key->seq, key->seq_len); | ||
696 | |||
697 | cmd_size = sizeof(struct iwm_umac_key_ccmp); | ||
698 | break; | ||
699 | |||
700 | case WLAN_CIPHER_SUITE_TKIP: | ||
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 | |||
717 | if (key->seq_len) | ||
718 | memcpy(ccmp->iv_count, key->seq, key->seq_len); | ||
719 | |||
720 | cmd_size = sizeof(struct iwm_umac_key_tkip); | ||
721 | break; | ||
722 | |||
723 | default: | ||
724 | return -ENOTSUPP; | ||
725 | } | ||
726 | |||
727 | if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) || | ||
728 | (key->cipher == WLAN_CIPHER_SUITE_CCMP)) | ||
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); | ||
739 | } else { | ||
740 | struct iwm_umac_key_remove key_remove; | ||
741 | |||
742 | IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx); | ||
743 | |||
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); | ||
754 | if (ret) | ||
755 | return ret; | ||
756 | |||
757 | iwm->keys[key_idx].key_len = 0; | ||
758 | } | ||
759 | |||
760 | return ret; | ||
761 | } | ||
762 | |||
763 | |||
764 | int iwm_send_mlme_profile(struct iwm_priv *iwm) | ||
765 | { | ||
766 | int ret; | ||
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); | ||
776 | if (ret) { | ||
777 | IWM_ERR(iwm, "Send profile command failed\n"); | ||
778 | return ret; | ||
779 | } | ||
780 | |||
781 | set_bit(IWM_STATUS_SME_CONNECTING, &iwm->status); | ||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | int __iwm_invalidate_mlme_profile(struct iwm_priv *iwm) | ||
786 | { | ||
787 | struct iwm_umac_invalidate_profile invalid; | ||
788 | |||
789 | invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE; | ||
790 | invalid.hdr.buf_size = | ||
791 | cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) - | ||
792 | sizeof(struct iwm_umac_wifi_if)); | ||
793 | |||
794 | invalid.reason = WLAN_REASON_UNSPECIFIED; | ||
795 | |||
796 | return iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1); | ||
797 | } | ||
798 | |||
799 | int iwm_invalidate_mlme_profile(struct iwm_priv *iwm) | ||
800 | { | ||
801 | int ret; | ||
802 | |||
803 | ret = __iwm_invalidate_mlme_profile(iwm); | ||
804 | if (ret) | ||
805 | return ret; | ||
806 | |||
807 | ret = wait_event_interruptible_timeout(iwm->mlme_queue, | ||
808 | (iwm->umac_profile_active == 0), 5 * HZ); | ||
809 | |||
810 | return ret ? 0 : -EBUSY; | ||
811 | } | ||
812 | |||
813 | int iwm_tx_power_trigger(struct iwm_priv *iwm) | ||
814 | { | ||
815 | struct iwm_umac_pwr_trigger pwr_trigger; | ||
816 | |||
817 | pwr_trigger.hdr.oid = UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER; | ||
818 | pwr_trigger.hdr.buf_size = | ||
819 | cpu_to_le16(sizeof(struct iwm_umac_pwr_trigger) - | ||
820 | sizeof(struct iwm_umac_wifi_if)); | ||
821 | |||
822 | |||
823 | return iwm_send_wifi_if_cmd(iwm, &pwr_trigger, sizeof(pwr_trigger), 1); | ||
824 | } | ||
825 | |||
826 | int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags) | ||
827 | { | ||
828 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
829 | struct iwm_umac_cmd umac_cmd; | ||
830 | struct iwm_umac_cmd_stats_req stats_req; | ||
831 | |||
832 | stats_req.flags = cpu_to_le32(flags); | ||
833 | |||
834 | umac_cmd.id = UMAC_CMD_OPCODE_STATISTIC_REQUEST; | ||
835 | umac_cmd.resp = 0; | ||
836 | |||
837 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stats_req, | ||
838 | sizeof(struct iwm_umac_cmd_stats_req)); | ||
839 | } | ||
840 | |||
841 | int iwm_send_umac_channel_list(struct iwm_priv *iwm) | ||
842 | { | ||
843 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
844 | struct iwm_umac_cmd umac_cmd; | ||
845 | struct iwm_umac_cmd_get_channel_list *ch_list; | ||
846 | int size = sizeof(struct iwm_umac_cmd_get_channel_list) + | ||
847 | sizeof(struct iwm_umac_channel_info) * 4; | ||
848 | int ret; | ||
849 | |||
850 | ch_list = kzalloc(size, GFP_KERNEL); | ||
851 | if (!ch_list) { | ||
852 | IWM_ERR(iwm, "Couldn't allocate channel list cmd\n"); | ||
853 | return -ENOMEM; | ||
854 | } | ||
855 | |||
856 | ch_list->ch[0].band = UMAC_BAND_2GHZ; | ||
857 | ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
858 | ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID; | ||
859 | |||
860 | ch_list->ch[1].band = UMAC_BAND_5GHZ; | ||
861 | ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
862 | ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID; | ||
863 | |||
864 | ch_list->ch[2].band = UMAC_BAND_2GHZ; | ||
865 | ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
866 | ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; | ||
867 | |||
868 | ch_list->ch[3].band = UMAC_BAND_5GHZ; | ||
869 | ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
870 | ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; | ||
871 | |||
872 | ch_list->count = cpu_to_le16(4); | ||
873 | |||
874 | umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST; | ||
875 | umac_cmd.resp = 1; | ||
876 | |||
877 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size); | ||
878 | |||
879 | kfree(ch_list); | ||
880 | |||
881 | return ret; | ||
882 | } | ||
883 | |||
884 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, | ||
885 | int ssid_num) | ||
886 | { | ||
887 | struct iwm_umac_cmd_scan_request req; | ||
888 | int i, ret; | ||
889 | |||
890 | memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request)); | ||
891 | |||
892 | req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST; | ||
893 | req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request) | ||
894 | - sizeof(struct iwm_umac_wifi_if)); | ||
895 | req.type = UMAC_WIFI_IF_SCAN_TYPE_USER; | ||
896 | req.timeout = 2; | ||
897 | req.seq_num = iwm->scan_id; | ||
898 | req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX); | ||
899 | |||
900 | for (i = 0; i < req.ssid_num; i++) { | ||
901 | memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len); | ||
902 | req.ssids[i].ssid_len = ssids[i].ssid_len; | ||
903 | } | ||
904 | |||
905 | ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0); | ||
906 | if (ret) { | ||
907 | IWM_ERR(iwm, "Couldn't send scan request\n"); | ||
908 | return ret; | ||
909 | } | ||
910 | |||
911 | iwm->scan_id = (iwm->scan_id + 1) % IWM_SCAN_ID_MAX; | ||
912 | |||
913 | return 0; | ||
914 | } | ||
915 | |||
916 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len) | ||
917 | { | ||
918 | struct cfg80211_ssid one_ssid; | ||
919 | |||
920 | if (test_and_set_bit(IWM_STATUS_SCANNING, &iwm->status)) | ||
921 | return 0; | ||
922 | |||
923 | one_ssid.ssid_len = min(ssid_len, IEEE80211_MAX_SSID_LEN); | ||
924 | memcpy(&one_ssid.ssid, ssid, one_ssid.ssid_len); | ||
925 | |||
926 | return iwm_scan_ssids(iwm, &one_ssid, 1); | ||
927 | } | ||
928 | |||
929 | int iwm_target_reset(struct iwm_priv *iwm) | ||
930 | { | ||
931 | struct iwm_udma_nonwifi_cmd target_cmd; | ||
932 | |||
933 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_REBOOT; | ||
934 | target_cmd.addr = 0; | ||
935 | target_cmd.op1_sz = 0; | ||
936 | target_cmd.op2 = 0; | ||
937 | target_cmd.handle_by_hw = 0; | ||
938 | target_cmd.resp = 0; | ||
939 | target_cmd.eop = 1; | ||
940 | |||
941 | return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); | ||
942 | } | ||
943 | |||
944 | int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, | ||
945 | struct iwm_umac_notif_stop_resume_tx *ntf) | ||
946 | { | ||
947 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
948 | struct iwm_umac_cmd umac_cmd; | ||
949 | struct iwm_umac_cmd_stop_resume_tx stp_res_cmd; | ||
950 | struct iwm_sta_info *sta_info; | ||
951 | u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id); | ||
952 | int i; | ||
953 | |||
954 | sta_info = &iwm->sta_table[sta_id]; | ||
955 | if (!sta_info->valid) { | ||
956 | IWM_ERR(iwm, "Invalid STA: %d\n", sta_id); | ||
957 | return -EINVAL; | ||
958 | } | ||
959 | |||
960 | umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX; | ||
961 | umac_cmd.resp = 0; | ||
962 | |||
963 | stp_res_cmd.flags = ntf->flags; | ||
964 | stp_res_cmd.sta_id = ntf->sta_id; | ||
965 | stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk; | ||
966 | for (i = 0; i < IWM_UMAC_TID_NR; i++) | ||
967 | stp_res_cmd.last_seq_num[i] = | ||
968 | sta_info->tid_info[i].last_seq_num; | ||
969 | |||
970 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd, | ||
971 | sizeof(struct iwm_umac_cmd_stop_resume_tx)); | ||
972 | |||
973 | } | ||
974 | |||
975 | int iwm_send_pmkid_update(struct iwm_priv *iwm, | ||
976 | struct cfg80211_pmksa *pmksa, u32 command) | ||
977 | { | ||
978 | struct iwm_umac_pmkid_update update; | ||
979 | int ret; | ||
980 | |||
981 | memset(&update, 0, sizeof(struct iwm_umac_pmkid_update)); | ||
982 | |||
983 | update.hdr.oid = UMAC_WIFI_IF_CMD_PMKID_UPDATE; | ||
984 | update.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_pmkid_update) - | ||
985 | sizeof(struct iwm_umac_wifi_if)); | ||
986 | |||
987 | update.command = cpu_to_le32(command); | ||
988 | if (pmksa->bssid) | ||
989 | memcpy(&update.bssid, pmksa->bssid, ETH_ALEN); | ||
990 | if (pmksa->pmkid) | ||
991 | memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN); | ||
992 | |||
993 | ret = iwm_send_wifi_if_cmd(iwm, &update, | ||
994 | sizeof(struct iwm_umac_pmkid_update), 0); | ||
995 | if (ret) { | ||
996 | IWM_ERR(iwm, "PMKID update command failed\n"); | ||
997 | return ret; | ||
998 | } | ||
999 | |||
1000 | return 0; | ||
1001 | } | ||