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