diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/main.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/main.c | 234 |
1 files changed, 210 insertions, 24 deletions
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index fd30cddd5882..95f4efe9ef37 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c | |||
@@ -16,8 +16,14 @@ | |||
16 | 16 | ||
17 | #include <linux/moduleparam.h> | 17 | #include <linux/moduleparam.h> |
18 | #include <linux/if_arp.h> | 18 | #include <linux/if_arp.h> |
19 | #include <linux/etherdevice.h> | ||
19 | 20 | ||
20 | #include "wil6210.h" | 21 | #include "wil6210.h" |
22 | #include "txrx.h" | ||
23 | |||
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"); | ||
21 | 27 | ||
22 | /* | 28 | /* |
23 | * Due to a hardware issue, | 29 | * Due to a hardware issue, |
@@ -52,29 +58,74 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | |||
52 | __raw_writel(*s++, d++); | 58 | __raw_writel(*s++, d++); |
53 | } | 59 | } |
54 | 60 | ||
55 | static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | 61 | static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) |
56 | { | 62 | { |
57 | uint i; | 63 | uint i; |
58 | struct net_device *ndev = wil_to_ndev(wil); | 64 | struct wil_sta_info *sta = &wil->sta[cid]; |
59 | 65 | ||
60 | wil_dbg_misc(wil, "%s()\n", __func__); | 66 | sta->data_port_open = false; |
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 | } | ||
61 | 71 | ||
62 | wil_link_off(wil); | 72 | for (i = 0; i < WIL_STA_TID_NUM; i++) { |
63 | if (test_bit(wil_status_fwconnected, &wil->status)) { | 73 | struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; |
64 | clear_bit(wil_status_fwconnected, &wil->status); | 74 | sta->tid_rx[i] = NULL; |
65 | cfg80211_disconnected(ndev, | 75 | wil_tid_ampdu_rx_free(wil, r); |
66 | WLAN_STATUS_UNSPECIFIED_FAILURE, | 76 | } |
67 | NULL, 0, GFP_KERNEL); | 77 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { |
68 | } else if (test_bit(wil_status_fwconnecting, &wil->status)) { | 78 | if (wil->vring2cid_tid[i][0] == cid) |
69 | cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, | 79 | wil_vring_fini_tx(wil, i); |
70 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
71 | GFP_KERNEL); | ||
72 | } | 80 | } |
73 | clear_bit(wil_status_fwconnecting, &wil->status); | 81 | memset(&sta->stats, 0, sizeof(sta->stats)); |
74 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) | 82 | } |
75 | wil_vring_fini_tx(wil, i); | ||
76 | 83 | ||
77 | clear_bit(wil_status_dontscan, &wil->status); | 84 | static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) |
85 | { | ||
86 | int cid = -ENOENT; | ||
87 | struct net_device *ndev = wil_to_ndev(wil); | ||
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__); | ||
96 | } | ||
97 | |||
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); | ||
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; | ||
128 | } | ||
78 | } | 129 | } |
79 | 130 | ||
80 | static void wil_disconnect_worker(struct work_struct *work) | 131 | static void wil_disconnect_worker(struct work_struct *work) |
@@ -82,7 +133,9 @@ static void wil_disconnect_worker(struct work_struct *work) | |||
82 | struct wil6210_priv *wil = container_of(work, | 133 | struct wil6210_priv *wil = container_of(work, |
83 | struct wil6210_priv, disconnect_worker); | 134 | struct wil6210_priv, disconnect_worker); |
84 | 135 | ||
136 | mutex_lock(&wil->mutex); | ||
85 | _wil6210_disconnect(wil, NULL); | 137 | _wil6210_disconnect(wil, NULL); |
138 | mutex_unlock(&wil->mutex); | ||
86 | } | 139 | } |
87 | 140 | ||
88 | static void wil_connect_timer_fn(ulong x) | 141 | static void wil_connect_timer_fn(ulong x) |
@@ -97,12 +150,55 @@ static void wil_connect_timer_fn(ulong x) | |||
97 | schedule_work(&wil->disconnect_worker); | 150 | schedule_work(&wil->disconnect_worker); |
98 | } | 151 | } |
99 | 152 | ||
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 | |||
164 | mutex_lock(&wil->mutex); | ||
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 | } | ||
182 | mutex_unlock(&wil->mutex); | ||
183 | } | ||
184 | |||
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 | |||
100 | static void wil_connect_worker(struct work_struct *work) | 195 | static void wil_connect_worker(struct work_struct *work) |
101 | { | 196 | { |
102 | int rc; | 197 | int rc; |
103 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | 198 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, |
104 | connect_worker); | 199 | connect_worker); |
105 | int cid = wil->pending_connect_cid; | 200 | int cid = wil->pending_connect_cid; |
201 | int ringid = wil_find_free_vring(wil); | ||
106 | 202 | ||
107 | if (cid < 0) { | 203 | if (cid < 0) { |
108 | wil_err(wil, "No connection pending\n"); | 204 | wil_err(wil, "No connection pending\n"); |
@@ -111,16 +207,22 @@ static void wil_connect_worker(struct work_struct *work) | |||
111 | 207 | ||
112 | wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); | 208 | wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); |
113 | 209 | ||
114 | rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0); | 210 | rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0); |
115 | wil->pending_connect_cid = -1; | 211 | wil->pending_connect_cid = -1; |
116 | if (rc == 0) | 212 | if (rc == 0) { |
213 | wil->sta[cid].status = wil_sta_connected; | ||
117 | wil_link_on(wil); | 214 | wil_link_on(wil); |
215 | } else { | ||
216 | wil->sta[cid].status = wil_sta_unused; | ||
217 | } | ||
118 | } | 218 | } |
119 | 219 | ||
120 | int wil_priv_init(struct wil6210_priv *wil) | 220 | int wil_priv_init(struct wil6210_priv *wil) |
121 | { | 221 | { |
122 | wil_dbg_misc(wil, "%s()\n", __func__); | 222 | wil_dbg_misc(wil, "%s()\n", __func__); |
123 | 223 | ||
224 | memset(wil->sta, 0, sizeof(wil->sta)); | ||
225 | |||
124 | mutex_init(&wil->mutex); | 226 | mutex_init(&wil->mutex); |
125 | mutex_init(&wil->wmi_mutex); | 227 | mutex_init(&wil->wmi_mutex); |
126 | 228 | ||
@@ -132,6 +234,7 @@ int wil_priv_init(struct wil6210_priv *wil) | |||
132 | INIT_WORK(&wil->connect_worker, wil_connect_worker); | 234 | INIT_WORK(&wil->connect_worker, wil_connect_worker); |
133 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); | 235 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); |
134 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | 236 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); |
237 | INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); | ||
135 | 238 | ||
136 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | 239 | INIT_LIST_HEAD(&wil->pending_wmi_ev); |
137 | spin_lock_init(&wil->wmi_ev_lock); | 240 | spin_lock_init(&wil->wmi_ev_lock); |
@@ -158,7 +261,10 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | |||
158 | void wil_priv_deinit(struct wil6210_priv *wil) | 261 | void wil_priv_deinit(struct wil6210_priv *wil) |
159 | { | 262 | { |
160 | cancel_work_sync(&wil->disconnect_worker); | 263 | cancel_work_sync(&wil->disconnect_worker); |
264 | cancel_work_sync(&wil->fw_error_worker); | ||
265 | mutex_lock(&wil->mutex); | ||
161 | wil6210_disconnect(wil, NULL); | 266 | wil6210_disconnect(wil, NULL); |
267 | mutex_unlock(&wil->mutex); | ||
162 | wmi_event_flush(wil); | 268 | wmi_event_flush(wil); |
163 | destroy_workqueue(wil->wmi_wq_conn); | 269 | destroy_workqueue(wil->wmi_wq_conn); |
164 | destroy_workqueue(wil->wmi_wq); | 270 | destroy_workqueue(wil->wmi_wq); |
@@ -166,40 +272,78 @@ void wil_priv_deinit(struct wil6210_priv *wil) | |||
166 | 272 | ||
167 | static void wil_target_reset(struct wil6210_priv *wil) | 273 | static void wil_target_reset(struct wil6210_priv *wil) |
168 | { | 274 | { |
275 | int delay = 0; | ||
276 | u32 hw_state; | ||
277 | u32 rev_id; | ||
278 | |||
169 | wil_dbg_misc(wil, "Resetting...\n"); | 279 | wil_dbg_misc(wil, "Resetting...\n"); |
170 | 280 | ||
281 | /* register read */ | ||
282 | #define R(a) ioread32(wil->csr + HOSTADDR(a)) | ||
171 | /* register write */ | 283 | /* register write */ |
172 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) | 284 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) |
173 | /* register set = read, OR, write */ | 285 | /* register set = read, OR, write */ |
174 | #define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ | 286 | #define S(a, v) W(a, R(a) | v) |
175 | wil->csr + HOSTADDR(a)) | 287 | /* register clear = read, AND with inverted, write */ |
288 | #define C(a, v) W(a, R(a) & ~v) | ||
176 | 289 | ||
290 | wil->hw_version = R(RGF_USER_FW_REV_ID); | ||
291 | rev_id = wil->hw_version & 0xff; | ||
177 | /* hpal_perst_from_pad_src_n_mask */ | 292 | /* hpal_perst_from_pad_src_n_mask */ |
178 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); | 293 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); |
179 | /* car_perst_rst_src_n_mask */ | 294 | /* car_perst_rst_src_n_mask */ |
180 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); | 295 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); |
296 | wmb(); /* order is important here */ | ||
181 | 297 | ||
182 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ | 298 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ |
183 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ | 299 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ |
300 | wmb(); /* order is important here */ | ||
184 | 301 | ||
185 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); | 302 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); |
186 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | 303 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); |
187 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); | 304 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); |
188 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); | 305 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); |
306 | wmb(); /* order is important here */ | ||
189 | 307 | ||
190 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); | 308 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); |
191 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | 309 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); |
192 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | 310 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); |
193 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | 311 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); |
312 | wmb(); /* order is important here */ | ||
194 | 313 | ||
195 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); | 314 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); |
196 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | 315 | if (rev_id == 1) { |
316 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | ||
317 | } else { | ||
318 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); | ||
319 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); | ||
320 | } | ||
197 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | 321 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); |
322 | wmb(); /* order is important here */ | ||
323 | |||
324 | /* wait until device ready */ | ||
325 | do { | ||
326 | msleep(1); | ||
327 | hw_state = R(RGF_USER_HW_MACHINE_STATE); | ||
328 | if (delay++ > 100) { | ||
329 | wil_err(wil, "Reset not completed, hw_state 0x%08x\n", | ||
330 | hw_state); | ||
331 | return; | ||
332 | } | ||
333 | } while (hw_state != HW_MACHINE_BOOT_DONE); | ||
334 | |||
335 | if (rev_id == 2) | ||
336 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); | ||
337 | |||
338 | C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); | ||
339 | wmb(); /* order is important here */ | ||
198 | 340 | ||
199 | wil_dbg_misc(wil, "Reset completed\n"); | 341 | wil_dbg_misc(wil, "Reset completed in %d ms\n", delay); |
200 | 342 | ||
343 | #undef R | ||
201 | #undef W | 344 | #undef W |
202 | #undef S | 345 | #undef S |
346 | #undef C | ||
203 | } | 347 | } |
204 | 348 | ||
205 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) | 349 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) |
@@ -234,11 +378,24 @@ int wil_reset(struct wil6210_priv *wil) | |||
234 | { | 378 | { |
235 | int rc; | 379 | int rc; |
236 | 380 | ||
381 | WARN_ON(!mutex_is_locked(&wil->mutex)); | ||
382 | |||
237 | cancel_work_sync(&wil->disconnect_worker); | 383 | cancel_work_sync(&wil->disconnect_worker); |
238 | wil6210_disconnect(wil, NULL); | 384 | wil6210_disconnect(wil, NULL); |
239 | 385 | ||
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); | ||
389 | } | ||
390 | |||
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 | |||
240 | wil6210_disable_irq(wil); | 398 | wil6210_disable_irq(wil); |
241 | wil->status = 0; | ||
242 | 399 | ||
243 | wmi_event_flush(wil); | 400 | wmi_event_flush(wil); |
244 | 401 | ||
@@ -248,6 +405,8 @@ int wil_reset(struct wil6210_priv *wil) | |||
248 | /* TODO: put MAC in reset */ | 405 | /* TODO: put MAC in reset */ |
249 | wil_target_reset(wil); | 406 | wil_target_reset(wil); |
250 | 407 | ||
408 | wil_rx_fini(wil); | ||
409 | |||
251 | /* init after reset */ | 410 | /* init after reset */ |
252 | wil->pending_connect_cid = -1; | 411 | wil->pending_connect_cid = -1; |
253 | reinit_completion(&wil->wmi_ready); | 412 | reinit_completion(&wil->wmi_ready); |
@@ -261,6 +420,11 @@ int wil_reset(struct wil6210_priv *wil) | |||
261 | return rc; | 420 | return rc; |
262 | } | 421 | } |
263 | 422 | ||
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 | } | ||
264 | 428 | ||
265 | void wil_link_on(struct wil6210_priv *wil) | 429 | void wil_link_on(struct wil6210_priv *wil) |
266 | { | 430 | { |
@@ -288,6 +452,8 @@ static int __wil_up(struct wil6210_priv *wil) | |||
288 | struct wireless_dev *wdev = wil->wdev; | 452 | struct wireless_dev *wdev = wil->wdev; |
289 | int rc; | 453 | int rc; |
290 | 454 | ||
455 | WARN_ON(!mutex_is_locked(&wil->mutex)); | ||
456 | |||
291 | rc = wil_reset(wil); | 457 | rc = wil_reset(wil); |
292 | if (rc) | 458 | if (rc) |
293 | return rc; | 459 | return rc; |
@@ -329,6 +495,7 @@ static int __wil_up(struct wil6210_priv *wil) | |||
329 | 495 | ||
330 | napi_enable(&wil->napi_rx); | 496 | napi_enable(&wil->napi_rx); |
331 | napi_enable(&wil->napi_tx); | 497 | napi_enable(&wil->napi_tx); |
498 | set_bit(wil_status_napi_en, &wil->status); | ||
332 | 499 | ||
333 | return 0; | 500 | return 0; |
334 | } | 501 | } |
@@ -346,6 +513,9 @@ int wil_up(struct wil6210_priv *wil) | |||
346 | 513 | ||
347 | static int __wil_down(struct wil6210_priv *wil) | 514 | static int __wil_down(struct wil6210_priv *wil) |
348 | { | 515 | { |
516 | WARN_ON(!mutex_is_locked(&wil->mutex)); | ||
517 | |||
518 | clear_bit(wil_status_napi_en, &wil->status); | ||
349 | napi_disable(&wil->napi_rx); | 519 | napi_disable(&wil->napi_rx); |
350 | napi_disable(&wil->napi_tx); | 520 | napi_disable(&wil->napi_tx); |
351 | 521 | ||
@@ -370,3 +540,19 @@ int wil_down(struct wil6210_priv *wil) | |||
370 | 540 | ||
371 | return rc; | 541 | return rc; |
372 | } | 542 | } |
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) && | ||
551 | ether_addr_equal(wil->sta[i].addr, mac)) { | ||
552 | rc = i; | ||
553 | break; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | return rc; | ||
558 | } | ||