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 | ||
2be7d22f VK |
84 | static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) |
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 | ||
ed6f9dc6 VK |
153 | static void wil_fw_error_worker(struct work_struct *work) |
154 | { | |
155 | struct wil6210_priv *wil = container_of(work, | |
156 | struct wil6210_priv, fw_error_worker); | |
157 | struct wireless_dev *wdev = wil->wdev; | |
158 | ||
159 | wil_dbg_misc(wil, "fw error worker\n"); | |
160 | ||
161 | if (no_fw_recovery) | |
162 | return; | |
163 | ||
9c3bde56 | 164 | mutex_lock(&wil->mutex); |
ed6f9dc6 VK |
165 | switch (wdev->iftype) { |
166 | case NL80211_IFTYPE_STATION: | |
167 | case NL80211_IFTYPE_P2P_CLIENT: | |
168 | case NL80211_IFTYPE_MONITOR: | |
169 | wil_info(wil, "fw error recovery started...\n"); | |
170 | wil_reset(wil); | |
171 | ||
172 | /* need to re-allocate Rx ring after reset */ | |
173 | wil_rx_init(wil); | |
174 | break; | |
175 | case NL80211_IFTYPE_AP: | |
176 | case NL80211_IFTYPE_P2P_GO: | |
177 | /* recovery in these modes is done by upper layers */ | |
178 | break; | |
179 | default: | |
180 | break; | |
181 | } | |
9c3bde56 | 182 | mutex_unlock(&wil->mutex); |
ed6f9dc6 VK |
183 | } |
184 | ||
9a177384 VK |
185 | static int wil_find_free_vring(struct wil6210_priv *wil) |
186 | { | |
187 | int i; | |
188 | for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { | |
189 | if (!wil->vring_tx[i].va) | |
190 | return i; | |
191 | } | |
192 | return -EINVAL; | |
193 | } | |
194 | ||
d81079f1 VK |
195 | static void wil_connect_worker(struct work_struct *work) |
196 | { | |
197 | int rc; | |
198 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | |
199 | connect_worker); | |
200 | int cid = wil->pending_connect_cid; | |
9a177384 | 201 | int ringid = wil_find_free_vring(wil); |
d81079f1 VK |
202 | |
203 | if (cid < 0) { | |
204 | wil_err(wil, "No connection pending\n"); | |
205 | return; | |
206 | } | |
207 | ||
208 | wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); | |
209 | ||
9a177384 | 210 | rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0); |
d81079f1 | 211 | wil->pending_connect_cid = -1; |
3df2cd36 VK |
212 | if (rc == 0) { |
213 | wil->sta[cid].status = wil_sta_connected; | |
d81079f1 | 214 | wil_link_on(wil); |
3df2cd36 VK |
215 | } else { |
216 | wil->sta[cid].status = wil_sta_unused; | |
217 | } | |
d81079f1 VK |
218 | } |
219 | ||
2be7d22f VK |
220 | int wil_priv_init(struct wil6210_priv *wil) |
221 | { | |
7743882d | 222 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f | 223 | |
3df2cd36 VK |
224 | memset(wil->sta, 0, sizeof(wil->sta)); |
225 | ||
2be7d22f VK |
226 | mutex_init(&wil->mutex); |
227 | mutex_init(&wil->wmi_mutex); | |
228 | ||
229 | init_completion(&wil->wmi_ready); | |
230 | ||
231 | wil->pending_connect_cid = -1; | |
232 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); | |
233 | ||
d81079f1 | 234 | INIT_WORK(&wil->connect_worker, wil_connect_worker); |
2be7d22f VK |
235 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); |
236 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | |
ed6f9dc6 | 237 | INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); |
2be7d22f VK |
238 | |
239 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | |
240 | spin_lock_init(&wil->wmi_ev_lock); | |
241 | ||
242 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); | |
243 | if (!wil->wmi_wq) | |
244 | return -EAGAIN; | |
245 | ||
246 | wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); | |
247 | if (!wil->wmi_wq_conn) { | |
248 | destroy_workqueue(wil->wmi_wq); | |
249 | return -EAGAIN; | |
250 | } | |
251 | ||
2be7d22f VK |
252 | return 0; |
253 | } | |
254 | ||
255 | void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | |
256 | { | |
257 | del_timer_sync(&wil->connect_timer); | |
258 | _wil6210_disconnect(wil, bssid); | |
259 | } | |
260 | ||
261 | void wil_priv_deinit(struct wil6210_priv *wil) | |
262 | { | |
263 | cancel_work_sync(&wil->disconnect_worker); | |
ed6f9dc6 | 264 | cancel_work_sync(&wil->fw_error_worker); |
097638a0 | 265 | mutex_lock(&wil->mutex); |
2be7d22f | 266 | wil6210_disconnect(wil, NULL); |
097638a0 | 267 | mutex_unlock(&wil->mutex); |
2be7d22f VK |
268 | wmi_event_flush(wil); |
269 | destroy_workqueue(wil->wmi_wq_conn); | |
270 | destroy_workqueue(wil->wmi_wq); | |
271 | } | |
272 | ||
273 | static void wil_target_reset(struct wil6210_priv *wil) | |
274 | { | |
98a65b59 | 275 | int delay = 0; |
d28bcc30 | 276 | u32 hw_state; |
36b10a72 VK |
277 | u32 rev_id; |
278 | ||
7743882d | 279 | wil_dbg_misc(wil, "Resetting...\n"); |
2be7d22f | 280 | |
36b10a72 VK |
281 | /* register read */ |
282 | #define R(a) ioread32(wil->csr + HOSTADDR(a)) | |
2be7d22f VK |
283 | /* register write */ |
284 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) | |
285 | /* register set = read, OR, write */ | |
972072aa VK |
286 | #define S(a, v) W(a, R(a) | v) |
287 | /* register clear = read, AND with inverted, write */ | |
288 | #define C(a, v) W(a, R(a) & ~v) | |
2be7d22f | 289 | |
17123991 | 290 | wil->hw_version = R(RGF_USER_FW_REV_ID); |
36b10a72 | 291 | rev_id = wil->hw_version & 0xff; |
2be7d22f VK |
292 | /* hpal_perst_from_pad_src_n_mask */ |
293 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); | |
294 | /* car_perst_rst_src_n_mask */ | |
295 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); | |
260e6951 | 296 | wmb(); /* order is important here */ |
2be7d22f VK |
297 | |
298 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ | |
299 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ | |
260e6951 | 300 | wmb(); /* order is important here */ |
2be7d22f | 301 | |
2be7d22f VK |
302 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); |
303 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | |
304 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); | |
305 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); | |
260e6951 | 306 | wmb(); /* order is important here */ |
2be7d22f | 307 | |
2be7d22f VK |
308 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); |
309 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | |
310 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | |
311 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | |
260e6951 | 312 | wmb(); /* order is important here */ |
2be7d22f VK |
313 | |
314 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); | |
36b10a72 VK |
315 | if (rev_id == 1) { |
316 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | |
317 | } else { | |
17123991 | 318 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); |
36b10a72 VK |
319 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); |
320 | } | |
2be7d22f | 321 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); |
260e6951 | 322 | wmb(); /* order is important here */ |
2be7d22f | 323 | |
d28bcc30 | 324 | /* wait until device ready */ |
36b10a72 VK |
325 | do { |
326 | msleep(1); | |
d28bcc30 | 327 | hw_state = R(RGF_USER_HW_MACHINE_STATE); |
98a65b59 | 328 | if (delay++ > 100) { |
d28bcc30 VK |
329 | wil_err(wil, "Reset not completed, hw_state 0x%08x\n", |
330 | hw_state); | |
36b10a72 VK |
331 | return; |
332 | } | |
d28bcc30 | 333 | } while (hw_state != HW_MACHINE_BOOT_DONE); |
36b10a72 VK |
334 | |
335 | if (rev_id == 2) | |
17123991 | 336 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); |
36b10a72 | 337 | |
972072aa | 338 | C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); |
260e6951 | 339 | wmb(); /* order is important here */ |
972072aa | 340 | |
98a65b59 | 341 | wil_dbg_misc(wil, "Reset completed in %d ms\n", delay); |
2be7d22f | 342 | |
36b10a72 | 343 | #undef R |
2be7d22f VK |
344 | #undef W |
345 | #undef S | |
972072aa | 346 | #undef C |
2be7d22f VK |
347 | } |
348 | ||
349 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) | |
350 | { | |
351 | le32_to_cpus(&r->base); | |
352 | le16_to_cpus(&r->entry_size); | |
353 | le16_to_cpus(&r->size); | |
354 | le32_to_cpus(&r->tail); | |
355 | le32_to_cpus(&r->head); | |
356 | } | |
357 | ||
358 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) | |
359 | { | |
360 | ulong to = msecs_to_jiffies(1000); | |
361 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | |
362 | if (0 == left) { | |
363 | wil_err(wil, "Firmware not ready\n"); | |
364 | return -ETIME; | |
365 | } else { | |
7743882d | 366 | wil_dbg_misc(wil, "FW ready after %d ms\n", |
2057ebb2 | 367 | jiffies_to_msecs(to-left)); |
2be7d22f VK |
368 | } |
369 | return 0; | |
370 | } | |
371 | ||
372 | /* | |
373 | * We reset all the structures, and we reset the UMAC. | |
374 | * After calling this routine, you're expected to reload | |
375 | * the firmware. | |
376 | */ | |
377 | int wil_reset(struct wil6210_priv *wil) | |
378 | { | |
379 | int rc; | |
380 | ||
097638a0 VK |
381 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
382 | ||
383 | cancel_work_sync(&wil->disconnect_worker); | |
384 | wil6210_disconnect(wil, NULL); | |
385 | ||
0fef1818 VK |
386 | wil->status = 0; /* prevent NAPI from being scheduled */ |
387 | if (test_bit(wil_status_napi_en, &wil->status)) { | |
388 | napi_synchronize(&wil->napi_rx); | |
0fef1818 VK |
389 | } |
390 | ||
ed6f9dc6 VK |
391 | if (wil->scan_request) { |
392 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", | |
393 | wil->scan_request); | |
394 | cfg80211_scan_done(wil->scan_request, true); | |
395 | wil->scan_request = NULL; | |
396 | } | |
397 | ||
e08b5906 | 398 | wil6210_disable_irq(wil); |
e08b5906 | 399 | |
2be7d22f VK |
400 | wmi_event_flush(wil); |
401 | ||
2be7d22f | 402 | flush_workqueue(wil->wmi_wq_conn); |
e08b5906 | 403 | flush_workqueue(wil->wmi_wq); |
2be7d22f VK |
404 | |
405 | /* TODO: put MAC in reset */ | |
406 | wil_target_reset(wil); | |
407 | ||
8bf6adb9 VK |
408 | wil_rx_fini(wil); |
409 | ||
2be7d22f VK |
410 | /* init after reset */ |
411 | wil->pending_connect_cid = -1; | |
16735d02 | 412 | reinit_completion(&wil->wmi_ready); |
2be7d22f | 413 | |
2be7d22f VK |
414 | /* TODO: release MAC reset */ |
415 | wil6210_enable_irq(wil); | |
416 | ||
417 | /* we just started MAC, wait for FW ready */ | |
418 | rc = wil_wait_for_fw_ready(wil); | |
419 | ||
420 | return rc; | |
421 | } | |
422 | ||
ed6f9dc6 VK |
423 | void wil_fw_error_recovery(struct wil6210_priv *wil) |
424 | { | |
425 | wil_dbg_misc(wil, "starting fw error recovery\n"); | |
426 | schedule_work(&wil->fw_error_worker); | |
427 | } | |
2be7d22f VK |
428 | |
429 | void wil_link_on(struct wil6210_priv *wil) | |
430 | { | |
431 | struct net_device *ndev = wil_to_ndev(wil); | |
432 | ||
7743882d | 433 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
434 | |
435 | netif_carrier_on(ndev); | |
436 | netif_tx_wake_all_queues(ndev); | |
437 | } | |
438 | ||
439 | void wil_link_off(struct wil6210_priv *wil) | |
440 | { | |
441 | struct net_device *ndev = wil_to_ndev(wil); | |
442 | ||
7743882d | 443 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
444 | |
445 | netif_tx_stop_all_queues(ndev); | |
446 | netif_carrier_off(ndev); | |
447 | } | |
448 | ||
449 | static int __wil_up(struct wil6210_priv *wil) | |
450 | { | |
451 | struct net_device *ndev = wil_to_ndev(wil); | |
452 | struct wireless_dev *wdev = wil->wdev; | |
2be7d22f | 453 | int rc; |
2be7d22f | 454 | |
097638a0 VK |
455 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
456 | ||
2be7d22f VK |
457 | rc = wil_reset(wil); |
458 | if (rc) | |
459 | return rc; | |
460 | ||
e31b2562 VK |
461 | /* Rx VRING. After MAC and beacon */ |
462 | rc = wil_rx_init(wil); | |
463 | if (rc) | |
464 | return rc; | |
465 | ||
2be7d22f VK |
466 | switch (wdev->iftype) { |
467 | case NL80211_IFTYPE_STATION: | |
7743882d | 468 | wil_dbg_misc(wil, "type: STATION\n"); |
2be7d22f VK |
469 | ndev->type = ARPHRD_ETHER; |
470 | break; | |
471 | case NL80211_IFTYPE_AP: | |
7743882d | 472 | wil_dbg_misc(wil, "type: AP\n"); |
2be7d22f VK |
473 | ndev->type = ARPHRD_ETHER; |
474 | break; | |
475 | case NL80211_IFTYPE_P2P_CLIENT: | |
7743882d | 476 | wil_dbg_misc(wil, "type: P2P_CLIENT\n"); |
2be7d22f VK |
477 | ndev->type = ARPHRD_ETHER; |
478 | break; | |
479 | case NL80211_IFTYPE_P2P_GO: | |
7743882d | 480 | wil_dbg_misc(wil, "type: P2P_GO\n"); |
2be7d22f VK |
481 | ndev->type = ARPHRD_ETHER; |
482 | break; | |
483 | case NL80211_IFTYPE_MONITOR: | |
7743882d | 484 | wil_dbg_misc(wil, "type: Monitor\n"); |
2be7d22f VK |
485 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; |
486 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | |
487 | break; | |
488 | default: | |
489 | return -EOPNOTSUPP; | |
490 | } | |
491 | ||
2be7d22f VK |
492 | /* MAC address - pre-requisite for other commands */ |
493 | wmi_set_mac_address(wil, ndev->dev_addr); | |
494 | ||
2be7d22f | 495 | |
e0287c4a VK |
496 | napi_enable(&wil->napi_rx); |
497 | napi_enable(&wil->napi_tx); | |
0fef1818 | 498 | set_bit(wil_status_napi_en, &wil->status); |
e0287c4a | 499 | |
2be7d22f VK |
500 | return 0; |
501 | } | |
502 | ||
503 | int wil_up(struct wil6210_priv *wil) | |
504 | { | |
505 | int rc; | |
506 | ||
507 | mutex_lock(&wil->mutex); | |
508 | rc = __wil_up(wil); | |
509 | mutex_unlock(&wil->mutex); | |
510 | ||
511 | return rc; | |
512 | } | |
513 | ||
514 | static int __wil_down(struct wil6210_priv *wil) | |
515 | { | |
097638a0 VK |
516 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
517 | ||
0fef1818 | 518 | clear_bit(wil_status_napi_en, &wil->status); |
e0287c4a VK |
519 | napi_disable(&wil->napi_rx); |
520 | napi_disable(&wil->napi_tx); | |
521 | ||
2be7d22f VK |
522 | if (wil->scan_request) { |
523 | cfg80211_scan_done(wil->scan_request, true); | |
524 | wil->scan_request = NULL; | |
525 | } | |
526 | ||
527 | wil6210_disconnect(wil, NULL); | |
528 | wil_rx_fini(wil); | |
529 | ||
530 | return 0; | |
531 | } | |
532 | ||
533 | int wil_down(struct wil6210_priv *wil) | |
534 | { | |
535 | int rc; | |
536 | ||
537 | mutex_lock(&wil->mutex); | |
538 | rc = __wil_down(wil); | |
539 | mutex_unlock(&wil->mutex); | |
540 | ||
541 | return rc; | |
542 | } | |
3df2cd36 VK |
543 | |
544 | int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) | |
545 | { | |
546 | int i; | |
547 | int rc = -ENOENT; | |
548 | ||
549 | for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { | |
550 | if ((wil->sta[i].status != wil_sta_unused) && | |
108d1eb6 | 551 | ether_addr_equal(wil->sta[i].addr, mac)) { |
3df2cd36 VK |
552 | rc = i; |
553 | break; | |
554 | } | |
555 | } | |
556 | ||
557 | return rc; | |
558 | } |