Commit | Line | Data |
---|---|---|
2be7d22f VK |
1 | /* |
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
2be7d22f VK |
17 | #include <linux/moduleparam.h> |
18 | #include <linux/if_arp.h> | |
108d1eb6 | 19 | #include <linux/etherdevice.h> |
2be7d22f VK |
20 | |
21 | #include "wil6210.h" | |
b4490f42 | 22 | #include "txrx.h" |
2be7d22f | 23 | |
ed6f9dc6 VK |
24 | static bool no_fw_recovery; |
25 | module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); | |
26 | MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery"); | |
27 | ||
2be7d22f VK |
28 | /* |
29 | * Due to a hardware issue, | |
30 | * one has to read/write to/from NIC in 32-bit chunks; | |
31 | * regular memcpy_fromio and siblings will | |
32 | * not work on 64-bit platform - it uses 64-bit transactions | |
33 | * | |
34 | * Force 32-bit transactions to enable NIC on 64-bit platforms | |
35 | * | |
36 | * To avoid byte swap on big endian host, __raw_{read|write}l | |
37 | * should be used - {read|write}l would swap bytes to provide | |
38 | * little endian on PCI value in host endianness. | |
39 | */ | |
40 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | |
41 | size_t count) | |
42 | { | |
43 | u32 *d = dst; | |
44 | const volatile u32 __iomem *s = src; | |
45 | ||
46 | /* size_t is unsigned, if (count%4 != 0) it will wrap */ | |
47 | for (count += 4; count > 4; count -= 4) | |
48 | *d++ = __raw_readl(s++); | |
49 | } | |
50 | ||
51 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | |
52 | size_t count) | |
53 | { | |
54 | volatile u32 __iomem *d = dst; | |
55 | const u32 *s = src; | |
56 | ||
57 | for (count += 4; count > 4; count -= 4) | |
58 | __raw_writel(*s++, d++); | |
59 | } | |
60 | ||
91886b0b VK |
61 | static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) |
62 | { | |
63 | uint i; | |
64 | struct wil_sta_info *sta = &wil->sta[cid]; | |
4d55a0a1 | 65 | |
e58c9f70 | 66 | sta->data_port_open = false; |
4d55a0a1 VK |
67 | if (sta->status != wil_sta_unused) { |
68 | wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING); | |
69 | sta->status = wil_sta_unused; | |
70 | } | |
71 | ||
91886b0b VK |
72 | for (i = 0; i < WIL_STA_TID_NUM; i++) { |
73 | struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; | |
74 | sta->tid_rx[i] = NULL; | |
75 | wil_tid_ampdu_rx_free(wil, r); | |
76 | } | |
77 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { | |
78 | if (wil->vring2cid_tid[i][0] == cid) | |
79 | wil_vring_fini_tx(wil, i); | |
80 | } | |
81 | memset(&sta->stats, 0, sizeof(sta->stats)); | |
82 | } | |
83 | ||
3b3a0162 | 84 | static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) |
2be7d22f | 85 | { |
91886b0b | 86 | int cid = -ENOENT; |
2be7d22f | 87 | struct net_device *ndev = wil_to_ndev(wil); |
91886b0b VK |
88 | struct wireless_dev *wdev = wil->wdev; |
89 | ||
90 | might_sleep(); | |
91 | if (bssid) { | |
92 | cid = wil_find_cid(wil, bssid); | |
93 | wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); | |
94 | } else { | |
95 | wil_dbg_misc(wil, "%s(all)\n", __func__); | |
b4490f42 VK |
96 | } |
97 | ||
91886b0b VK |
98 | if (cid >= 0) /* disconnect 1 peer */ |
99 | wil_disconnect_cid(wil, cid); | |
100 | else /* disconnect all */ | |
101 | for (cid = 0; cid < WIL6210_MAX_CID; cid++) | |
102 | wil_disconnect_cid(wil, cid); | |
103 | ||
104 | /* link state */ | |
105 | switch (wdev->iftype) { | |
106 | case NL80211_IFTYPE_STATION: | |
107 | case NL80211_IFTYPE_P2P_CLIENT: | |
108 | wil_link_off(wil); | |
109 | if (test_bit(wil_status_fwconnected, &wil->status)) { | |
110 | clear_bit(wil_status_fwconnected, &wil->status); | |
111 | cfg80211_disconnected(ndev, | |
112 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
113 | NULL, 0, GFP_KERNEL); | |
114 | } else if (test_bit(wil_status_fwconnecting, &wil->status)) { | |
115 | cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, | |
116 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
117 | GFP_KERNEL); | |
118 | } | |
119 | clear_bit(wil_status_fwconnecting, &wil->status); | |
91886b0b VK |
120 | break; |
121 | default: | |
122 | /* AP-like interface and monitor: | |
123 | * never scan, always connected | |
124 | */ | |
125 | if (bssid) | |
126 | cfg80211_del_sta(ndev, bssid, GFP_KERNEL); | |
127 | break; | |
2be7d22f | 128 | } |
2be7d22f VK |
129 | } |
130 | ||
131 | static void wil_disconnect_worker(struct work_struct *work) | |
132 | { | |
133 | struct wil6210_priv *wil = container_of(work, | |
134 | struct wil6210_priv, disconnect_worker); | |
135 | ||
097638a0 | 136 | mutex_lock(&wil->mutex); |
2be7d22f | 137 | _wil6210_disconnect(wil, NULL); |
097638a0 | 138 | mutex_unlock(&wil->mutex); |
2be7d22f VK |
139 | } |
140 | ||
141 | static void wil_connect_timer_fn(ulong x) | |
142 | { | |
143 | struct wil6210_priv *wil = (void *)x; | |
144 | ||
7743882d | 145 | wil_dbg_misc(wil, "Connect timeout\n"); |
2be7d22f VK |
146 | |
147 | /* reschedule to thread context - disconnect won't | |
148 | * run from atomic context | |
149 | */ | |
150 | schedule_work(&wil->disconnect_worker); | |
151 | } | |
152 | ||
047e5d74 VK |
153 | static void wil_scan_timer_fn(ulong x) |
154 | { | |
155 | struct wil6210_priv *wil = (void *)x; | |
156 | ||
157 | clear_bit(wil_status_fwready, &wil->status); | |
158 | wil_err(wil, "Scan timeout detected, start fw error recovery\n"); | |
159 | schedule_work(&wil->fw_error_worker); | |
160 | } | |
161 | ||
ed6f9dc6 VK |
162 | static void wil_fw_error_worker(struct work_struct *work) |
163 | { | |
164 | struct wil6210_priv *wil = container_of(work, | |
165 | struct wil6210_priv, fw_error_worker); | |
166 | struct wireless_dev *wdev = wil->wdev; | |
167 | ||
168 | wil_dbg_misc(wil, "fw error worker\n"); | |
169 | ||
170 | if (no_fw_recovery) | |
171 | return; | |
172 | ||
fc219eed VK |
173 | /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO |
174 | * passed since last recovery attempt | |
175 | */ | |
176 | if (time_is_after_jiffies(wil->last_fw_recovery + | |
177 | WIL6210_FW_RECOVERY_TO)) | |
178 | wil->recovery_count++; | |
179 | else | |
180 | wil->recovery_count = 1; /* fw was alive for a long time */ | |
181 | ||
182 | if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { | |
183 | wil_err(wil, "too many recovery attempts (%d), giving up\n", | |
184 | wil->recovery_count); | |
185 | return; | |
186 | } | |
187 | ||
188 | wil->last_fw_recovery = jiffies; | |
189 | ||
9c3bde56 | 190 | mutex_lock(&wil->mutex); |
ed6f9dc6 VK |
191 | switch (wdev->iftype) { |
192 | case NL80211_IFTYPE_STATION: | |
193 | case NL80211_IFTYPE_P2P_CLIENT: | |
194 | case NL80211_IFTYPE_MONITOR: | |
fc219eed VK |
195 | wil_info(wil, "fw error recovery started (try %d)...\n", |
196 | wil->recovery_count); | |
ed6f9dc6 VK |
197 | wil_reset(wil); |
198 | ||
199 | /* need to re-allocate Rx ring after reset */ | |
200 | wil_rx_init(wil); | |
201 | break; | |
202 | case NL80211_IFTYPE_AP: | |
203 | case NL80211_IFTYPE_P2P_GO: | |
204 | /* recovery in these modes is done by upper layers */ | |
205 | break; | |
206 | default: | |
207 | break; | |
208 | } | |
9c3bde56 | 209 | mutex_unlock(&wil->mutex); |
ed6f9dc6 VK |
210 | } |
211 | ||
9a177384 VK |
212 | static int wil_find_free_vring(struct wil6210_priv *wil) |
213 | { | |
214 | int i; | |
215 | for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { | |
216 | if (!wil->vring_tx[i].va) | |
217 | return i; | |
218 | } | |
219 | return -EINVAL; | |
220 | } | |
221 | ||
d81079f1 VK |
222 | static void wil_connect_worker(struct work_struct *work) |
223 | { | |
224 | int rc; | |
225 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | |
226 | connect_worker); | |
227 | int cid = wil->pending_connect_cid; | |
9a177384 | 228 | int ringid = wil_find_free_vring(wil); |
d81079f1 VK |
229 | |
230 | if (cid < 0) { | |
231 | wil_err(wil, "No connection pending\n"); | |
232 | return; | |
233 | } | |
234 | ||
235 | wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); | |
236 | ||
9a177384 | 237 | rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0); |
d81079f1 | 238 | wil->pending_connect_cid = -1; |
3df2cd36 VK |
239 | if (rc == 0) { |
240 | wil->sta[cid].status = wil_sta_connected; | |
d81079f1 | 241 | wil_link_on(wil); |
3df2cd36 VK |
242 | } else { |
243 | wil->sta[cid].status = wil_sta_unused; | |
244 | } | |
d81079f1 VK |
245 | } |
246 | ||
2be7d22f VK |
247 | int wil_priv_init(struct wil6210_priv *wil) |
248 | { | |
7743882d | 249 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f | 250 | |
3df2cd36 VK |
251 | memset(wil->sta, 0, sizeof(wil->sta)); |
252 | ||
2be7d22f VK |
253 | mutex_init(&wil->mutex); |
254 | mutex_init(&wil->wmi_mutex); | |
255 | ||
256 | init_completion(&wil->wmi_ready); | |
257 | ||
258 | wil->pending_connect_cid = -1; | |
259 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); | |
047e5d74 | 260 | setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); |
2be7d22f | 261 | |
d81079f1 | 262 | INIT_WORK(&wil->connect_worker, wil_connect_worker); |
2be7d22f VK |
263 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); |
264 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | |
ed6f9dc6 | 265 | INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); |
2be7d22f VK |
266 | |
267 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | |
268 | spin_lock_init(&wil->wmi_ev_lock); | |
269 | ||
270 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); | |
271 | if (!wil->wmi_wq) | |
272 | return -EAGAIN; | |
273 | ||
274 | wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); | |
275 | if (!wil->wmi_wq_conn) { | |
276 | destroy_workqueue(wil->wmi_wq); | |
277 | return -EAGAIN; | |
278 | } | |
279 | ||
fc219eed VK |
280 | wil->last_fw_recovery = jiffies; |
281 | ||
2be7d22f VK |
282 | return 0; |
283 | } | |
284 | ||
3b3a0162 | 285 | void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) |
2be7d22f VK |
286 | { |
287 | del_timer_sync(&wil->connect_timer); | |
288 | _wil6210_disconnect(wil, bssid); | |
289 | } | |
290 | ||
291 | void wil_priv_deinit(struct wil6210_priv *wil) | |
292 | { | |
047e5d74 | 293 | del_timer_sync(&wil->scan_timer); |
2be7d22f | 294 | cancel_work_sync(&wil->disconnect_worker); |
ed6f9dc6 | 295 | cancel_work_sync(&wil->fw_error_worker); |
097638a0 | 296 | mutex_lock(&wil->mutex); |
2be7d22f | 297 | wil6210_disconnect(wil, NULL); |
097638a0 | 298 | mutex_unlock(&wil->mutex); |
2be7d22f VK |
299 | wmi_event_flush(wil); |
300 | destroy_workqueue(wil->wmi_wq_conn); | |
301 | destroy_workqueue(wil->wmi_wq); | |
302 | } | |
303 | ||
304 | static void wil_target_reset(struct wil6210_priv *wil) | |
305 | { | |
98a65b59 | 306 | int delay = 0; |
d28bcc30 | 307 | u32 hw_state; |
36b10a72 VK |
308 | u32 rev_id; |
309 | ||
7743882d | 310 | wil_dbg_misc(wil, "Resetting...\n"); |
2be7d22f | 311 | |
36b10a72 VK |
312 | /* register read */ |
313 | #define R(a) ioread32(wil->csr + HOSTADDR(a)) | |
2be7d22f VK |
314 | /* register write */ |
315 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) | |
316 | /* register set = read, OR, write */ | |
972072aa VK |
317 | #define S(a, v) W(a, R(a) | v) |
318 | /* register clear = read, AND with inverted, write */ | |
319 | #define C(a, v) W(a, R(a) & ~v) | |
2be7d22f | 320 | |
17123991 | 321 | wil->hw_version = R(RGF_USER_FW_REV_ID); |
36b10a72 | 322 | rev_id = wil->hw_version & 0xff; |
2be7d22f VK |
323 | /* hpal_perst_from_pad_src_n_mask */ |
324 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); | |
325 | /* car_perst_rst_src_n_mask */ | |
326 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); | |
260e6951 | 327 | wmb(); /* order is important here */ |
2be7d22f VK |
328 | |
329 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ | |
330 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ | |
260e6951 | 331 | wmb(); /* order is important here */ |
2be7d22f | 332 | |
2be7d22f VK |
333 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); |
334 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | |
335 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); | |
336 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); | |
260e6951 | 337 | wmb(); /* order is important here */ |
2be7d22f | 338 | |
2be7d22f VK |
339 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); |
340 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | |
341 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | |
342 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | |
260e6951 | 343 | wmb(); /* order is important here */ |
2be7d22f VK |
344 | |
345 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); | |
36b10a72 VK |
346 | if (rev_id == 1) { |
347 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | |
348 | } else { | |
17123991 | 349 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); |
36b10a72 VK |
350 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); |
351 | } | |
2be7d22f | 352 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); |
260e6951 | 353 | wmb(); /* order is important here */ |
2be7d22f | 354 | |
d28bcc30 | 355 | /* wait until device ready */ |
36b10a72 VK |
356 | do { |
357 | msleep(1); | |
d28bcc30 | 358 | hw_state = R(RGF_USER_HW_MACHINE_STATE); |
98a65b59 | 359 | if (delay++ > 100) { |
d28bcc30 VK |
360 | wil_err(wil, "Reset not completed, hw_state 0x%08x\n", |
361 | hw_state); | |
36b10a72 VK |
362 | return; |
363 | } | |
d28bcc30 | 364 | } while (hw_state != HW_MACHINE_BOOT_DONE); |
36b10a72 VK |
365 | |
366 | if (rev_id == 2) | |
17123991 | 367 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); |
36b10a72 | 368 | |
972072aa | 369 | C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); |
260e6951 | 370 | wmb(); /* order is important here */ |
972072aa | 371 | |
98a65b59 | 372 | wil_dbg_misc(wil, "Reset completed in %d ms\n", delay); |
2be7d22f | 373 | |
36b10a72 | 374 | #undef R |
2be7d22f VK |
375 | #undef W |
376 | #undef S | |
972072aa | 377 | #undef C |
2be7d22f VK |
378 | } |
379 | ||
380 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) | |
381 | { | |
382 | le32_to_cpus(&r->base); | |
383 | le16_to_cpus(&r->entry_size); | |
384 | le16_to_cpus(&r->size); | |
385 | le32_to_cpus(&r->tail); | |
386 | le32_to_cpus(&r->head); | |
387 | } | |
388 | ||
389 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) | |
390 | { | |
391 | ulong to = msecs_to_jiffies(1000); | |
392 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | |
393 | if (0 == left) { | |
394 | wil_err(wil, "Firmware not ready\n"); | |
395 | return -ETIME; | |
396 | } else { | |
15e23124 VK |
397 | wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", |
398 | jiffies_to_msecs(to-left), wil->hw_version); | |
2be7d22f VK |
399 | } |
400 | return 0; | |
401 | } | |
402 | ||
403 | /* | |
404 | * We reset all the structures, and we reset the UMAC. | |
405 | * After calling this routine, you're expected to reload | |
406 | * the firmware. | |
407 | */ | |
408 | int wil_reset(struct wil6210_priv *wil) | |
409 | { | |
410 | int rc; | |
411 | ||
097638a0 VK |
412 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
413 | ||
414 | cancel_work_sync(&wil->disconnect_worker); | |
415 | wil6210_disconnect(wil, NULL); | |
416 | ||
0fef1818 VK |
417 | wil->status = 0; /* prevent NAPI from being scheduled */ |
418 | if (test_bit(wil_status_napi_en, &wil->status)) { | |
419 | napi_synchronize(&wil->napi_rx); | |
0fef1818 VK |
420 | } |
421 | ||
ed6f9dc6 VK |
422 | if (wil->scan_request) { |
423 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", | |
424 | wil->scan_request); | |
047e5d74 | 425 | del_timer_sync(&wil->scan_timer); |
ed6f9dc6 VK |
426 | cfg80211_scan_done(wil->scan_request, true); |
427 | wil->scan_request = NULL; | |
428 | } | |
429 | ||
e08b5906 | 430 | wil6210_disable_irq(wil); |
e08b5906 | 431 | |
2be7d22f VK |
432 | wmi_event_flush(wil); |
433 | ||
2be7d22f | 434 | flush_workqueue(wil->wmi_wq_conn); |
e08b5906 | 435 | flush_workqueue(wil->wmi_wq); |
2be7d22f VK |
436 | |
437 | /* TODO: put MAC in reset */ | |
438 | wil_target_reset(wil); | |
439 | ||
8bf6adb9 VK |
440 | wil_rx_fini(wil); |
441 | ||
2be7d22f VK |
442 | /* init after reset */ |
443 | wil->pending_connect_cid = -1; | |
16735d02 | 444 | reinit_completion(&wil->wmi_ready); |
2be7d22f | 445 | |
2be7d22f VK |
446 | /* TODO: release MAC reset */ |
447 | wil6210_enable_irq(wil); | |
448 | ||
449 | /* we just started MAC, wait for FW ready */ | |
450 | rc = wil_wait_for_fw_ready(wil); | |
451 | ||
452 | return rc; | |
453 | } | |
454 | ||
ed6f9dc6 VK |
455 | void wil_fw_error_recovery(struct wil6210_priv *wil) |
456 | { | |
457 | wil_dbg_misc(wil, "starting fw error recovery\n"); | |
458 | schedule_work(&wil->fw_error_worker); | |
459 | } | |
2be7d22f VK |
460 | |
461 | void wil_link_on(struct wil6210_priv *wil) | |
462 | { | |
463 | struct net_device *ndev = wil_to_ndev(wil); | |
464 | ||
7743882d | 465 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
466 | |
467 | netif_carrier_on(ndev); | |
468 | netif_tx_wake_all_queues(ndev); | |
469 | } | |
470 | ||
471 | void wil_link_off(struct wil6210_priv *wil) | |
472 | { | |
473 | struct net_device *ndev = wil_to_ndev(wil); | |
474 | ||
7743882d | 475 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
476 | |
477 | netif_tx_stop_all_queues(ndev); | |
478 | netif_carrier_off(ndev); | |
479 | } | |
480 | ||
481 | static int __wil_up(struct wil6210_priv *wil) | |
482 | { | |
483 | struct net_device *ndev = wil_to_ndev(wil); | |
484 | struct wireless_dev *wdev = wil->wdev; | |
2be7d22f | 485 | int rc; |
2be7d22f | 486 | |
097638a0 VK |
487 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
488 | ||
2be7d22f VK |
489 | rc = wil_reset(wil); |
490 | if (rc) | |
491 | return rc; | |
492 | ||
e31b2562 VK |
493 | /* Rx VRING. After MAC and beacon */ |
494 | rc = wil_rx_init(wil); | |
495 | if (rc) | |
496 | return rc; | |
497 | ||
2be7d22f VK |
498 | switch (wdev->iftype) { |
499 | case NL80211_IFTYPE_STATION: | |
7743882d | 500 | wil_dbg_misc(wil, "type: STATION\n"); |
2be7d22f VK |
501 | ndev->type = ARPHRD_ETHER; |
502 | break; | |
503 | case NL80211_IFTYPE_AP: | |
7743882d | 504 | wil_dbg_misc(wil, "type: AP\n"); |
2be7d22f VK |
505 | ndev->type = ARPHRD_ETHER; |
506 | break; | |
507 | case NL80211_IFTYPE_P2P_CLIENT: | |
7743882d | 508 | wil_dbg_misc(wil, "type: P2P_CLIENT\n"); |
2be7d22f VK |
509 | ndev->type = ARPHRD_ETHER; |
510 | break; | |
511 | case NL80211_IFTYPE_P2P_GO: | |
7743882d | 512 | wil_dbg_misc(wil, "type: P2P_GO\n"); |
2be7d22f VK |
513 | ndev->type = ARPHRD_ETHER; |
514 | break; | |
515 | case NL80211_IFTYPE_MONITOR: | |
7743882d | 516 | wil_dbg_misc(wil, "type: Monitor\n"); |
2be7d22f VK |
517 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; |
518 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | |
519 | break; | |
520 | default: | |
521 | return -EOPNOTSUPP; | |
522 | } | |
523 | ||
2be7d22f VK |
524 | /* MAC address - pre-requisite for other commands */ |
525 | wmi_set_mac_address(wil, ndev->dev_addr); | |
526 | ||
2be7d22f | 527 | |
e0287c4a VK |
528 | napi_enable(&wil->napi_rx); |
529 | napi_enable(&wil->napi_tx); | |
0fef1818 | 530 | set_bit(wil_status_napi_en, &wil->status); |
e0287c4a | 531 | |
2be7d22f VK |
532 | return 0; |
533 | } | |
534 | ||
535 | int wil_up(struct wil6210_priv *wil) | |
536 | { | |
537 | int rc; | |
538 | ||
539 | mutex_lock(&wil->mutex); | |
540 | rc = __wil_up(wil); | |
541 | mutex_unlock(&wil->mutex); | |
542 | ||
543 | return rc; | |
544 | } | |
545 | ||
546 | static int __wil_down(struct wil6210_priv *wil) | |
547 | { | |
097638a0 VK |
548 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
549 | ||
0fef1818 | 550 | clear_bit(wil_status_napi_en, &wil->status); |
e0287c4a VK |
551 | napi_disable(&wil->napi_rx); |
552 | napi_disable(&wil->napi_tx); | |
553 | ||
2be7d22f | 554 | if (wil->scan_request) { |
2a91d7d0 VK |
555 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", |
556 | wil->scan_request); | |
047e5d74 | 557 | del_timer_sync(&wil->scan_timer); |
2be7d22f VK |
558 | cfg80211_scan_done(wil->scan_request, true); |
559 | wil->scan_request = NULL; | |
560 | } | |
561 | ||
562 | wil6210_disconnect(wil, NULL); | |
563 | wil_rx_fini(wil); | |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
568 | int wil_down(struct wil6210_priv *wil) | |
569 | { | |
570 | int rc; | |
571 | ||
572 | mutex_lock(&wil->mutex); | |
573 | rc = __wil_down(wil); | |
574 | mutex_unlock(&wil->mutex); | |
575 | ||
576 | return rc; | |
577 | } | |
3df2cd36 VK |
578 | |
579 | int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) | |
580 | { | |
581 | int i; | |
582 | int rc = -ENOENT; | |
583 | ||
584 | for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { | |
585 | if ((wil->sta[i].status != wil_sta_unused) && | |
108d1eb6 | 586 | ether_addr_equal(wil->sta[i].addr, mac)) { |
3df2cd36 VK |
587 | rc = i; |
588 | break; | |
589 | } | |
590 | } | |
591 | ||
592 | return rc; | |
593 | } |