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