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