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/netdevice.h> | |
d43c36dc | 41 | #include <linux/sched.h> |
bb9f8692 ZY |
42 | #include <linux/ieee80211.h> |
43 | #include <linux/wireless.h> | |
5a0e3ad6 | 44 | #include <linux/slab.h> |
6eb07caf | 45 | #include <linux/moduleparam.h> |
bb9f8692 ZY |
46 | |
47 | #include "iwm.h" | |
48 | #include "debug.h" | |
49 | #include "bus.h" | |
50 | #include "umac.h" | |
51 | #include "commands.h" | |
52 | #include "hal.h" | |
53 | #include "fw.h" | |
54 | #include "rx.h" | |
55 | ||
56 | static struct iwm_conf def_iwm_conf = { | |
57 | ||
58 | .sdio_ior_timeout = 5000, | |
d04bd628 SO |
59 | .calib_map = BIT(CALIB_CFG_DC_IDX) | |
60 | BIT(CALIB_CFG_LO_IDX) | | |
61 | BIT(CALIB_CFG_TX_IQ_IDX) | | |
62 | BIT(CALIB_CFG_RX_IQ_IDX) | | |
63 | BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD), | |
64 | .expected_calib_map = BIT(PHY_CALIBRATE_DC_CMD) | | |
bb9f8692 ZY |
65 | BIT(PHY_CALIBRATE_LO_CMD) | |
66 | BIT(PHY_CALIBRATE_TX_IQ_CMD) | | |
67 | BIT(PHY_CALIBRATE_RX_IQ_CMD) | | |
68 | BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD), | |
e85498b2 SO |
69 | .ct_kill_entry = 110, |
70 | .ct_kill_exit = 110, | |
bb9f8692 ZY |
71 | .reset_on_fatal_err = 1, |
72 | .auto_connect = 1, | |
bb9f8692 ZY |
73 | .enable_qos = 1, |
74 | .mode = UMAC_MODE_BSS, | |
75 | ||
76 | /* UMAC configuration */ | |
77 | .power_index = 0, | |
78 | .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, | |
79 | .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, | |
80 | .cts_to_self = 0, | |
81 | ||
82 | .assoc_timeout = 2, | |
83 | .roam_timeout = 10, | |
0bed08de SO |
84 | .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G | |
85 | WIRELESS_MODE_11N, | |
bb9f8692 ZY |
86 | |
87 | /* IBSS */ | |
88 | .ibss_band = UMAC_BAND_2GHZ, | |
89 | .ibss_channel = 1, | |
90 | ||
91 | .mac_addr = {0x00, 0x02, 0xb3, 0x01, 0x02, 0x03}, | |
92 | }; | |
93 | ||
eb939922 | 94 | static bool modparam_reset; |
bb9f8692 ZY |
95 | module_param_named(reset, modparam_reset, bool, 0644); |
96 | MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])"); | |
97 | ||
eb939922 | 98 | static bool modparam_wimax_enable = true; |
159bcfeb SO |
99 | module_param_named(wimax_enable, modparam_wimax_enable, bool, 0644); |
100 | MODULE_PARM_DESC(wimax_enable, "Enable wimax core (default 1 [wimax enabled])"); | |
101 | ||
bb9f8692 ZY |
102 | int iwm_mode_to_nl80211_iftype(int mode) |
103 | { | |
104 | switch (mode) { | |
105 | case UMAC_MODE_BSS: | |
106 | return NL80211_IFTYPE_STATION; | |
107 | case UMAC_MODE_IBSS: | |
108 | return NL80211_IFTYPE_ADHOC; | |
109 | default: | |
110 | return NL80211_IFTYPE_UNSPECIFIED; | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static void iwm_statistics_request(struct work_struct *work) | |
117 | { | |
118 | struct iwm_priv *iwm = | |
119 | container_of(work, struct iwm_priv, stats_request.work); | |
120 | ||
121 | iwm_send_umac_stats_req(iwm, 0); | |
122 | } | |
123 | ||
c7436273 ZY |
124 | static void iwm_disconnect_work(struct work_struct *work) |
125 | { | |
126 | struct iwm_priv *iwm = | |
127 | container_of(work, struct iwm_priv, disconnect.work); | |
128 | ||
129 | if (iwm->umac_profile_active) | |
130 | iwm_invalidate_mlme_profile(iwm); | |
131 | ||
132 | clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status); | |
3db1cd5c | 133 | iwm->umac_profile_active = false; |
c7436273 ZY |
134 | memset(iwm->bssid, 0, ETH_ALEN); |
135 | iwm->channel = 0; | |
136 | ||
137 | iwm_link_off(iwm); | |
138 | ||
139 | wake_up_interruptible(&iwm->mlme_queue); | |
140 | ||
141 | cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL); | |
142 | } | |
143 | ||
e85498b2 SO |
144 | static void iwm_ct_kill_work(struct work_struct *work) |
145 | { | |
146 | struct iwm_priv *iwm = | |
147 | container_of(work, struct iwm_priv, ct_kill_delay.work); | |
148 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | |
149 | ||
150 | IWM_INFO(iwm, "CT kill delay timeout\n"); | |
151 | ||
152 | wiphy_rfkill_set_hw_state(wiphy, false); | |
153 | } | |
154 | ||
31452420 ZY |
155 | static int __iwm_up(struct iwm_priv *iwm); |
156 | static int __iwm_down(struct iwm_priv *iwm); | |
68810c5d | 157 | |
bb9f8692 ZY |
158 | static void iwm_reset_worker(struct work_struct *work) |
159 | { | |
160 | struct iwm_priv *iwm; | |
161 | struct iwm_umac_profile *profile = NULL; | |
162 | int uninitialized_var(ret), retry = 0; | |
163 | ||
164 | iwm = container_of(work, struct iwm_priv, reset_worker); | |
165 | ||
68810c5d ZY |
166 | /* |
167 | * XXX: The iwm->mutex is introduced purely for this reset work, | |
168 | * because the other users for iwm_up and iwm_down are only netdev | |
169 | * ndo_open and ndo_stop which are already protected by rtnl. | |
170 | * Please remove iwm->mutex together if iwm_reset_worker() is not | |
171 | * required in the future. | |
172 | */ | |
173 | if (!mutex_trylock(&iwm->mutex)) { | |
174 | IWM_WARN(iwm, "We are in the middle of interface bringing " | |
175 | "UP/DOWN. Skip driver resetting.\n"); | |
176 | return; | |
177 | } | |
178 | ||
bb9f8692 ZY |
179 | if (iwm->umac_profile_active) { |
180 | profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL); | |
181 | if (profile) | |
182 | memcpy(profile, iwm->umac_profile, sizeof(*profile)); | |
183 | else | |
184 | IWM_ERR(iwm, "Couldn't alloc memory for profile\n"); | |
185 | } | |
186 | ||
68810c5d | 187 | __iwm_down(iwm); |
bb9f8692 ZY |
188 | |
189 | while (retry++ < 3) { | |
68810c5d | 190 | ret = __iwm_up(iwm); |
bb9f8692 ZY |
191 | if (!ret) |
192 | break; | |
193 | ||
194 | schedule_timeout_uninterruptible(10 * HZ); | |
195 | } | |
196 | ||
197 | if (ret) { | |
198 | IWM_WARN(iwm, "iwm_up() failed: %d\n", ret); | |
199 | ||
200 | kfree(profile); | |
68810c5d | 201 | goto out; |
bb9f8692 ZY |
202 | } |
203 | ||
204 | if (profile) { | |
205 | IWM_DBG_MLME(iwm, DBG, "Resend UMAC profile\n"); | |
206 | memcpy(iwm->umac_profile, profile, sizeof(*profile)); | |
207 | iwm_send_mlme_profile(iwm); | |
208 | kfree(profile); | |
d210176e SO |
209 | } else |
210 | clear_bit(IWM_STATUS_RESETTING, &iwm->status); | |
68810c5d ZY |
211 | |
212 | out: | |
213 | mutex_unlock(&iwm->mutex); | |
bb9f8692 ZY |
214 | } |
215 | ||
9829e1b5 SO |
216 | static void iwm_auth_retry_worker(struct work_struct *work) |
217 | { | |
218 | struct iwm_priv *iwm; | |
219 | int i, ret; | |
220 | ||
221 | iwm = container_of(work, struct iwm_priv, auth_retry_worker); | |
222 | if (iwm->umac_profile_active) { | |
223 | ret = iwm_invalidate_mlme_profile(iwm); | |
224 | if (ret < 0) | |
225 | return; | |
226 | } | |
227 | ||
228 | iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; | |
229 | ||
230 | ret = iwm_send_mlme_profile(iwm); | |
231 | if (ret < 0) | |
232 | return; | |
233 | ||
234 | for (i = 0; i < IWM_NUM_KEYS; i++) | |
235 | if (iwm->keys[i].key_len) | |
236 | iwm_set_key(iwm, 0, &iwm->keys[i]); | |
237 | ||
238 | iwm_set_tx_key(iwm, iwm->default_key); | |
239 | } | |
240 | ||
241 | ||
242 | ||
bb9f8692 ZY |
243 | static void iwm_watchdog(unsigned long data) |
244 | { | |
245 | struct iwm_priv *iwm = (struct iwm_priv *)data; | |
246 | ||
247 | IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n"); | |
248 | ||
249 | if (modparam_reset) | |
d210176e | 250 | iwm_resetting(iwm); |
bb9f8692 ZY |
251 | } |
252 | ||
253 | int iwm_priv_init(struct iwm_priv *iwm) | |
254 | { | |
a7af530d | 255 | int i, j; |
bb9f8692 ZY |
256 | char name[32]; |
257 | ||
258 | iwm->status = 0; | |
259 | INIT_LIST_HEAD(&iwm->pending_notif); | |
260 | init_waitqueue_head(&iwm->notif_queue); | |
261 | init_waitqueue_head(&iwm->nonwifi_queue); | |
a70742f1 | 262 | init_waitqueue_head(&iwm->wifi_ntfy_queue); |
bb9f8692 ZY |
263 | init_waitqueue_head(&iwm->mlme_queue); |
264 | memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf)); | |
265 | spin_lock_init(&iwm->tx_credit.lock); | |
266 | INIT_LIST_HEAD(&iwm->wifi_pending_cmd); | |
267 | INIT_LIST_HEAD(&iwm->nonwifi_pending_cmd); | |
268 | iwm->wifi_seq_num = UMAC_WIFI_SEQ_NUM_BASE; | |
269 | iwm->nonwifi_seq_num = UMAC_NONWIFI_SEQ_NUM_BASE; | |
270 | spin_lock_init(&iwm->cmd_lock); | |
271 | iwm->scan_id = 1; | |
272 | INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request); | |
c7436273 | 273 | INIT_DELAYED_WORK(&iwm->disconnect, iwm_disconnect_work); |
e85498b2 | 274 | INIT_DELAYED_WORK(&iwm->ct_kill_delay, iwm_ct_kill_work); |
bb9f8692 | 275 | INIT_WORK(&iwm->reset_worker, iwm_reset_worker); |
9829e1b5 | 276 | INIT_WORK(&iwm->auth_retry_worker, iwm_auth_retry_worker); |
bb9f8692 ZY |
277 | INIT_LIST_HEAD(&iwm->bss_list); |
278 | ||
279 | skb_queue_head_init(&iwm->rx_list); | |
280 | INIT_LIST_HEAD(&iwm->rx_tickets); | |
c03c6aef ZY |
281 | spin_lock_init(&iwm->ticket_lock); |
282 | for (i = 0; i < IWM_RX_ID_HASH; i++) { | |
bb9f8692 | 283 | INIT_LIST_HEAD(&iwm->rx_packets[i]); |
c03c6aef ZY |
284 | spin_lock_init(&iwm->packet_lock[i]); |
285 | } | |
bb9f8692 ZY |
286 | |
287 | INIT_WORK(&iwm->rx_worker, iwm_rx_worker); | |
288 | ||
289 | iwm->rx_wq = create_singlethread_workqueue(KBUILD_MODNAME "_rx"); | |
290 | if (!iwm->rx_wq) | |
291 | return -EAGAIN; | |
292 | ||
293 | for (i = 0; i < IWM_TX_QUEUES; i++) { | |
294 | INIT_WORK(&iwm->txq[i].worker, iwm_tx_worker); | |
295 | snprintf(name, 32, KBUILD_MODNAME "_tx_%d", i); | |
296 | iwm->txq[i].id = i; | |
297 | iwm->txq[i].wq = create_singlethread_workqueue(name); | |
298 | if (!iwm->txq[i].wq) | |
299 | return -EAGAIN; | |
300 | ||
301 | skb_queue_head_init(&iwm->txq[i].queue); | |
a7af530d SO |
302 | skb_queue_head_init(&iwm->txq[i].stopped_queue); |
303 | spin_lock_init(&iwm->txq[i].lock); | |
bb9f8692 ZY |
304 | } |
305 | ||
306 | for (i = 0; i < IWM_NUM_KEYS; i++) | |
307 | memset(&iwm->keys[i], 0, sizeof(struct iwm_key)); | |
308 | ||
13e0fe70 | 309 | iwm->default_key = -1; |
bb9f8692 | 310 | |
a7af530d SO |
311 | for (i = 0; i < IWM_STA_TABLE_NUM; i++) |
312 | for (j = 0; j < IWM_UMAC_TID_NR; j++) { | |
313 | mutex_init(&iwm->sta_table[i].tid_info[j].mutex); | |
314 | iwm->sta_table[i].tid_info[j].stopped = false; | |
315 | } | |
316 | ||
bb9f8692 ZY |
317 | init_timer(&iwm->watchdog); |
318 | iwm->watchdog.function = iwm_watchdog; | |
319 | iwm->watchdog.data = (unsigned long)iwm; | |
68810c5d | 320 | mutex_init(&iwm->mutex); |
bb9f8692 | 321 | |
04e715cd SO |
322 | iwm->last_fw_err = kzalloc(sizeof(struct iwm_fw_error_hdr), |
323 | GFP_KERNEL); | |
324 | if (iwm->last_fw_err == NULL) | |
325 | return -ENOMEM; | |
326 | ||
bb9f8692 ZY |
327 | return 0; |
328 | } | |
329 | ||
8d96e796 ZY |
330 | void iwm_priv_deinit(struct iwm_priv *iwm) |
331 | { | |
332 | int i; | |
333 | ||
334 | for (i = 0; i < IWM_TX_QUEUES; i++) | |
335 | destroy_workqueue(iwm->txq[i].wq); | |
336 | ||
337 | destroy_workqueue(iwm->rx_wq); | |
04e715cd | 338 | kfree(iwm->last_fw_err); |
8d96e796 ZY |
339 | } |
340 | ||
bb9f8692 ZY |
341 | /* |
342 | * We reset all the structures, and we reset the UMAC. | |
343 | * After calling this routine, you're expected to reload | |
344 | * the firmware. | |
345 | */ | |
346 | void iwm_reset(struct iwm_priv *iwm) | |
347 | { | |
348 | struct iwm_notif *notif, *next; | |
349 | ||
350 | if (test_bit(IWM_STATUS_READY, &iwm->status)) | |
351 | iwm_target_reset(iwm); | |
352 | ||
d210176e SO |
353 | if (test_bit(IWM_STATUS_RESETTING, &iwm->status)) { |
354 | iwm->status = 0; | |
355 | set_bit(IWM_STATUS_RESETTING, &iwm->status); | |
356 | } else | |
357 | iwm->status = 0; | |
bb9f8692 ZY |
358 | iwm->scan_id = 1; |
359 | ||
360 | list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) { | |
361 | list_del(¬if->pending); | |
362 | kfree(notif->buf); | |
363 | kfree(notif); | |
364 | } | |
365 | ||
366 | iwm_cmd_flush(iwm); | |
367 | ||
368 | flush_workqueue(iwm->rx_wq); | |
369 | ||
370 | iwm_link_off(iwm); | |
371 | } | |
372 | ||
d210176e SO |
373 | void iwm_resetting(struct iwm_priv *iwm) |
374 | { | |
375 | set_bit(IWM_STATUS_RESETTING, &iwm->status); | |
376 | ||
377 | schedule_work(&iwm->reset_worker); | |
378 | } | |
379 | ||
bb9f8692 ZY |
380 | /* |
381 | * Notification code: | |
382 | * | |
383 | * We're faced with the following issue: Any host command can | |
384 | * have an answer or not, and if there's an answer to expect, | |
385 | * it can be treated synchronously or asynchronously. | |
386 | * To work around the synchronous answer case, we implemented | |
387 | * our notification mechanism. | |
388 | * When a code path needs to wait for a command response | |
389 | * synchronously, it calls notif_handle(), which waits for the | |
390 | * right notification to show up, and then process it. Before | |
391 | * starting to wait, it registered as a waiter for this specific | |
392 | * answer (by toggling a bit in on of the handler_map), so that | |
393 | * the rx code knows that it needs to send a notification to the | |
394 | * waiting processes. It does so by calling iwm_notif_send(), | |
395 | * which adds the notification to the pending notifications list, | |
396 | * and then wakes the waiting processes up. | |
397 | */ | |
398 | int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd, | |
399 | u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size) | |
400 | { | |
401 | struct iwm_notif *notif; | |
402 | ||
403 | notif = kzalloc(sizeof(struct iwm_notif), GFP_KERNEL); | |
404 | if (!notif) { | |
405 | IWM_ERR(iwm, "Couldn't alloc memory for notification\n"); | |
406 | return -ENOMEM; | |
407 | } | |
408 | ||
409 | INIT_LIST_HEAD(¬if->pending); | |
410 | notif->cmd = cmd; | |
411 | notif->cmd_id = cmd_id; | |
412 | notif->src = source; | |
413 | notif->buf = kzalloc(buf_size, GFP_KERNEL); | |
414 | if (!notif->buf) { | |
415 | IWM_ERR(iwm, "Couldn't alloc notification buffer\n"); | |
416 | kfree(notif); | |
417 | return -ENOMEM; | |
418 | } | |
419 | notif->buf_size = buf_size; | |
420 | memcpy(notif->buf, buf, buf_size); | |
421 | list_add_tail(¬if->pending, &iwm->pending_notif); | |
422 | ||
423 | wake_up_interruptible(&iwm->notif_queue); | |
424 | ||
425 | return 0; | |
426 | } | |
427 | ||
428 | static struct iwm_notif *iwm_notif_find(struct iwm_priv *iwm, u32 cmd, | |
429 | u8 source) | |
430 | { | |
04d1c227 | 431 | struct iwm_notif *notif; |
bb9f8692 | 432 | |
04d1c227 | 433 | list_for_each_entry(notif, &iwm->pending_notif, pending) { |
bb9f8692 ZY |
434 | if ((notif->cmd_id == cmd) && (notif->src == source)) { |
435 | list_del(¬if->pending); | |
436 | return notif; | |
437 | } | |
438 | } | |
439 | ||
440 | return NULL; | |
441 | } | |
442 | ||
443 | static struct iwm_notif *iwm_notif_wait(struct iwm_priv *iwm, u32 cmd, | |
444 | u8 source, long timeout) | |
445 | { | |
446 | int ret; | |
447 | struct iwm_notif *notif; | |
448 | unsigned long *map = NULL; | |
449 | ||
450 | switch (source) { | |
451 | case IWM_SRC_LMAC: | |
452 | map = &iwm->lmac_handler_map[0]; | |
453 | break; | |
454 | case IWM_SRC_UMAC: | |
455 | map = &iwm->umac_handler_map[0]; | |
456 | break; | |
457 | case IWM_SRC_UDMA: | |
458 | map = &iwm->udma_handler_map[0]; | |
459 | break; | |
460 | } | |
461 | ||
462 | set_bit(cmd, map); | |
463 | ||
464 | ret = wait_event_interruptible_timeout(iwm->notif_queue, | |
465 | ((notif = iwm_notif_find(iwm, cmd, source)) != NULL), | |
466 | timeout); | |
467 | clear_bit(cmd, map); | |
468 | ||
469 | if (!ret) | |
470 | return NULL; | |
471 | ||
472 | return notif; | |
473 | } | |
474 | ||
475 | int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout) | |
476 | { | |
477 | int ret; | |
478 | struct iwm_notif *notif; | |
479 | ||
480 | notif = iwm_notif_wait(iwm, cmd, source, timeout); | |
481 | if (!notif) | |
482 | return -ETIME; | |
483 | ||
484 | ret = iwm_rx_handle_resp(iwm, notif->buf, notif->buf_size, notif->cmd); | |
485 | kfree(notif->buf); | |
486 | kfree(notif); | |
487 | ||
488 | return ret; | |
489 | } | |
490 | ||
491 | static int iwm_config_boot_params(struct iwm_priv *iwm) | |
492 | { | |
493 | struct iwm_udma_nonwifi_cmd target_cmd; | |
494 | int ret; | |
495 | ||
496 | /* check Wimax is off and config debug monitor */ | |
159bcfeb | 497 | if (!modparam_wimax_enable) { |
bb9f8692 ZY |
498 | u32 data1 = 0x1f; |
499 | u32 addr1 = 0x606BE258; | |
500 | ||
501 | u32 data2_set = 0x0; | |
502 | u32 data2_clr = 0x1; | |
503 | u32 addr2 = 0x606BE100; | |
504 | ||
505 | u32 data3 = 0x1; | |
506 | u32 addr3 = 0x606BEC00; | |
507 | ||
508 | target_cmd.resp = 0; | |
509 | target_cmd.handle_by_hw = 0; | |
510 | target_cmd.eop = 1; | |
511 | ||
512 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; | |
513 | target_cmd.addr = cpu_to_le32(addr1); | |
514 | target_cmd.op1_sz = cpu_to_le32(sizeof(u32)); | |
515 | target_cmd.op2 = 0; | |
516 | ||
517 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1); | |
518 | if (ret < 0) { | |
519 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | |
520 | return ret; | |
521 | } | |
522 | ||
523 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE; | |
524 | target_cmd.addr = cpu_to_le32(addr2); | |
525 | target_cmd.op1_sz = cpu_to_le32(data2_set); | |
526 | target_cmd.op2 = cpu_to_le32(data2_clr); | |
527 | ||
528 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1); | |
529 | if (ret < 0) { | |
530 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | |
531 | return ret; | |
532 | } | |
533 | ||
534 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; | |
535 | target_cmd.addr = cpu_to_le32(addr3); | |
536 | target_cmd.op1_sz = cpu_to_le32(sizeof(u32)); | |
537 | target_cmd.op2 = 0; | |
538 | ||
539 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data3); | |
540 | if (ret < 0) { | |
541 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | |
542 | return ret; | |
543 | } | |
544 | } | |
545 | ||
546 | return 0; | |
547 | } | |
548 | ||
549 | void iwm_init_default_profile(struct iwm_priv *iwm, | |
550 | struct iwm_umac_profile *profile) | |
551 | { | |
552 | memset(profile, 0, sizeof(struct iwm_umac_profile)); | |
553 | ||
554 | profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN; | |
555 | profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE; | |
556 | profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE; | |
557 | profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE; | |
558 | ||
559 | if (iwm->conf.enable_qos) | |
560 | profile->flags |= cpu_to_le16(UMAC_PROFILE_QOS_ALLOWED); | |
561 | ||
562 | profile->wireless_mode = iwm->conf.wireless_mode; | |
563 | profile->mode = cpu_to_le32(iwm->conf.mode); | |
564 | ||
565 | profile->ibss.atim = 0; | |
566 | profile->ibss.beacon_interval = 100; | |
567 | profile->ibss.join_only = 0; | |
568 | profile->ibss.band = iwm->conf.ibss_band; | |
569 | profile->ibss.channel = iwm->conf.ibss_channel; | |
570 | } | |
571 | ||
572 | void iwm_link_on(struct iwm_priv *iwm) | |
573 | { | |
574 | netif_carrier_on(iwm_to_ndev(iwm)); | |
575 | netif_tx_wake_all_queues(iwm_to_ndev(iwm)); | |
576 | ||
577 | iwm_send_umac_stats_req(iwm, 0); | |
578 | } | |
579 | ||
580 | void iwm_link_off(struct iwm_priv *iwm) | |
581 | { | |
582 | struct iw_statistics *wstats = &iwm->wstats; | |
583 | int i; | |
584 | ||
585 | netif_tx_stop_all_queues(iwm_to_ndev(iwm)); | |
586 | netif_carrier_off(iwm_to_ndev(iwm)); | |
587 | ||
588 | for (i = 0; i < IWM_TX_QUEUES; i++) { | |
589 | skb_queue_purge(&iwm->txq[i].queue); | |
a7af530d | 590 | skb_queue_purge(&iwm->txq[i].stopped_queue); |
bb9f8692 ZY |
591 | |
592 | iwm->txq[i].concat_count = 0; | |
593 | iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf; | |
594 | ||
595 | flush_workqueue(iwm->txq[i].wq); | |
596 | } | |
597 | ||
598 | iwm_rx_free(iwm); | |
599 | ||
68810c5d | 600 | cancel_delayed_work_sync(&iwm->stats_request); |
bb9f8692 ZY |
601 | memset(wstats, 0, sizeof(struct iw_statistics)); |
602 | wstats->qual.updated = IW_QUAL_ALL_INVALID; | |
603 | ||
b68518fc ZY |
604 | kfree(iwm->req_ie); |
605 | iwm->req_ie = NULL; | |
606 | iwm->req_ie_len = 0; | |
607 | kfree(iwm->resp_ie); | |
608 | iwm->resp_ie = NULL; | |
609 | iwm->resp_ie_len = 0; | |
610 | ||
bb9f8692 ZY |
611 | del_timer_sync(&iwm->watchdog); |
612 | } | |
613 | ||
614 | static void iwm_bss_list_clean(struct iwm_priv *iwm) | |
615 | { | |
616 | struct iwm_bss_info *bss, *next; | |
617 | ||
618 | list_for_each_entry_safe(bss, next, &iwm->bss_list, node) { | |
619 | list_del(&bss->node); | |
620 | kfree(bss->bss); | |
621 | kfree(bss); | |
622 | } | |
623 | } | |
624 | ||
625 | static int iwm_channels_init(struct iwm_priv *iwm) | |
626 | { | |
627 | int ret; | |
628 | ||
bb9f8692 ZY |
629 | ret = iwm_send_umac_channel_list(iwm); |
630 | if (ret) { | |
631 | IWM_ERR(iwm, "Send channel list failed\n"); | |
632 | return ret; | |
633 | } | |
634 | ||
635 | ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST, | |
636 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | |
637 | if (ret) { | |
638 | IWM_ERR(iwm, "Didn't get a channel list notification\n"); | |
639 | return ret; | |
640 | } | |
641 | ||
642 | return 0; | |
643 | } | |
644 | ||
31452420 | 645 | static int __iwm_up(struct iwm_priv *iwm) |
bb9f8692 ZY |
646 | { |
647 | int ret; | |
648 | struct iwm_notif *notif_reboot, *notif_ack = NULL; | |
5dc53163 | 649 | struct wiphy *wiphy = iwm_to_wiphy(iwm); |
0bed08de | 650 | u32 wireless_mode; |
bb9f8692 ZY |
651 | |
652 | ret = iwm_bus_enable(iwm); | |
653 | if (ret) { | |
654 | IWM_ERR(iwm, "Couldn't enable function\n"); | |
655 | return ret; | |
656 | } | |
657 | ||
658 | iwm_rx_setup_handlers(iwm); | |
659 | ||
660 | /* Wait for initial BARKER_REBOOT from hardware */ | |
661 | notif_reboot = iwm_notif_wait(iwm, IWM_BARKER_REBOOT_NOTIFICATION, | |
662 | IWM_SRC_UDMA, 2 * HZ); | |
663 | if (!notif_reboot) { | |
664 | IWM_ERR(iwm, "Wait for REBOOT_BARKER timeout\n"); | |
665 | goto err_disable; | |
666 | } | |
667 | ||
668 | /* We send the barker back */ | |
669 | ret = iwm_bus_send_chunk(iwm, notif_reboot->buf, 16); | |
670 | if (ret) { | |
671 | IWM_ERR(iwm, "REBOOT barker response failed\n"); | |
672 | kfree(notif_reboot); | |
673 | goto err_disable; | |
674 | } | |
675 | ||
676 | kfree(notif_reboot->buf); | |
677 | kfree(notif_reboot); | |
678 | ||
679 | /* Wait for ACK_BARKER from hardware */ | |
680 | notif_ack = iwm_notif_wait(iwm, IWM_ACK_BARKER_NOTIFICATION, | |
681 | IWM_SRC_UDMA, 2 * HZ); | |
682 | if (!notif_ack) { | |
683 | IWM_ERR(iwm, "Wait for ACK_BARKER timeout\n"); | |
684 | goto err_disable; | |
685 | } | |
686 | ||
687 | kfree(notif_ack->buf); | |
688 | kfree(notif_ack); | |
689 | ||
690 | /* We start to config static boot parameters */ | |
691 | ret = iwm_config_boot_params(iwm); | |
692 | if (ret) { | |
693 | IWM_ERR(iwm, "Config boot parameters failed\n"); | |
694 | goto err_disable; | |
695 | } | |
696 | ||
697 | ret = iwm_read_mac(iwm, iwm_to_ndev(iwm)->dev_addr); | |
698 | if (ret) { | |
699 | IWM_ERR(iwm, "MAC reading failed\n"); | |
700 | goto err_disable; | |
701 | } | |
5b367378 JL |
702 | memcpy(iwm_to_ndev(iwm)->perm_addr, iwm_to_ndev(iwm)->dev_addr, |
703 | ETH_ALEN); | |
bb9f8692 ZY |
704 | |
705 | /* We can load the FWs */ | |
706 | ret = iwm_load_fw(iwm); | |
707 | if (ret) { | |
708 | IWM_ERR(iwm, "FW loading failed\n"); | |
709 | goto err_disable; | |
710 | } | |
711 | ||
902b6667 SO |
712 | ret = iwm_eeprom_fat_channels(iwm); |
713 | if (ret) { | |
714 | IWM_ERR(iwm, "Couldnt read HT channels EEPROM entries\n"); | |
715 | goto err_fw; | |
716 | } | |
717 | ||
0bed08de SO |
718 | /* |
719 | * Read our SKU capabilities. | |
2351178c ZY |
720 | * If it's valid, we AND the configured wireless mode with the |
721 | * device EEPROM value as the current profile wireless mode. | |
0bed08de SO |
722 | */ |
723 | wireless_mode = iwm_eeprom_wireless_mode(iwm); | |
724 | if (wireless_mode) { | |
2351178c | 725 | iwm->conf.wireless_mode &= wireless_mode; |
0bed08de SO |
726 | if (iwm->umac_profile) |
727 | iwm->umac_profile->wireless_mode = | |
728 | iwm->conf.wireless_mode; | |
729 | } else | |
730 | IWM_ERR(iwm, "Wrong SKU capabilities: 0x%x\n", | |
731 | *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP))); | |
732 | ||
5dc53163 SO |
733 | snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "L%s_U%s", |
734 | iwm->lmac_version, iwm->umac_version); | |
735 | ||
bb9f8692 ZY |
736 | /* We configure the UMAC and enable the wifi module */ |
737 | ret = iwm_send_umac_config(iwm, | |
738 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) | | |
739 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_LINK_EN) | | |
740 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_MLME_EN)); | |
741 | if (ret) { | |
742 | IWM_ERR(iwm, "UMAC config failed\n"); | |
743 | goto err_fw; | |
744 | } | |
745 | ||
746 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS, | |
747 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | |
748 | if (ret) { | |
749 | IWM_ERR(iwm, "Didn't get a wifi core status notification\n"); | |
750 | goto err_fw; | |
751 | } | |
752 | ||
753 | if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN | | |
754 | UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) { | |
755 | IWM_DBG_BOOT(iwm, DBG, "Not all cores enabled:0x%x\n", | |
756 | iwm->core_enabled); | |
757 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS, | |
758 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | |
759 | if (ret) { | |
760 | IWM_ERR(iwm, "Didn't get a core status notification\n"); | |
761 | goto err_fw; | |
762 | } | |
763 | ||
764 | if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN | | |
765 | UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) { | |
766 | IWM_ERR(iwm, "Not all cores enabled: 0x%x\n", | |
767 | iwm->core_enabled); | |
768 | goto err_fw; | |
769 | } else { | |
770 | IWM_INFO(iwm, "All cores enabled\n"); | |
771 | } | |
772 | } | |
773 | ||
bb9f8692 ZY |
774 | ret = iwm_channels_init(iwm); |
775 | if (ret < 0) { | |
776 | IWM_ERR(iwm, "Couldn't init channels\n"); | |
35497164 | 777 | goto err_fw; |
bb9f8692 ZY |
778 | } |
779 | ||
780 | /* Set the READY bit to indicate interface is brought up successfully */ | |
781 | set_bit(IWM_STATUS_READY, &iwm->status); | |
782 | ||
783 | return 0; | |
784 | ||
bb9f8692 ZY |
785 | err_fw: |
786 | iwm_eeprom_exit(iwm); | |
787 | ||
788 | err_disable: | |
789 | ret = iwm_bus_disable(iwm); | |
790 | if (ret < 0) | |
791 | IWM_ERR(iwm, "Couldn't disable function\n"); | |
792 | ||
793 | return -EIO; | |
794 | } | |
795 | ||
68810c5d ZY |
796 | int iwm_up(struct iwm_priv *iwm) |
797 | { | |
798 | int ret; | |
799 | ||
800 | mutex_lock(&iwm->mutex); | |
801 | ret = __iwm_up(iwm); | |
802 | mutex_unlock(&iwm->mutex); | |
803 | ||
804 | return ret; | |
805 | } | |
806 | ||
31452420 | 807 | static int __iwm_down(struct iwm_priv *iwm) |
bb9f8692 ZY |
808 | { |
809 | int ret; | |
810 | ||
811 | /* The interface is already down */ | |
812 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | |
813 | return 0; | |
814 | ||
815 | if (iwm->scan_request) { | |
816 | cfg80211_scan_done(iwm->scan_request, true); | |
817 | iwm->scan_request = NULL; | |
818 | } | |
819 | ||
820 | clear_bit(IWM_STATUS_READY, &iwm->status); | |
821 | ||
822 | iwm_eeprom_exit(iwm); | |
bb9f8692 | 823 | iwm_bss_list_clean(iwm); |
35497164 SO |
824 | iwm_init_default_profile(iwm, iwm->umac_profile); |
825 | iwm->umac_profile_active = false; | |
13e0fe70 | 826 | iwm->default_key = -1; |
bb9f8692 ZY |
827 | iwm->core_enabled = 0; |
828 | ||
829 | ret = iwm_bus_disable(iwm); | |
830 | if (ret < 0) { | |
831 | IWM_ERR(iwm, "Couldn't disable function\n"); | |
832 | return ret; | |
833 | } | |
834 | ||
835 | return 0; | |
836 | } | |
68810c5d ZY |
837 | |
838 | int iwm_down(struct iwm_priv *iwm) | |
839 | { | |
840 | int ret; | |
841 | ||
842 | mutex_lock(&iwm->mutex); | |
843 | ret = __iwm_down(iwm); | |
844 | mutex_unlock(&iwm->mutex); | |
845 | ||
846 | return ret; | |
847 | } |