Commit | Line | Data |
---|---|---|
903ac63f | 1 | // SPDX-License-Identifier: GPL-2.0 |
e61c7a1c AS |
2 | /* |
3 | * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. | |
4 | * All rights reserved. | |
5 | */ | |
6 | ||
c5c77ba1 | 7 | #include <linux/irq.h> |
c5c77ba1 JK |
8 | #include <linux/kthread.h> |
9 | #include <linux/firmware.h> | |
c5c77ba1 | 10 | #include <linux/netdevice.h> |
c5c77ba1 | 11 | #include <linux/inetdevice.h> |
880e404e AS |
12 | |
13 | #include "wilc_wfi_cfgoperations.h" | |
4ad36601 | 14 | #include "wilc_wlan_cfg.h" |
c5c77ba1 | 15 | |
6b0b7d86 AS |
16 | #define WILC_MULTICAST_TABLE_SIZE 8 |
17 | ||
c5c77ba1 JK |
18 | static irqreturn_t isr_uh_routine(int irq, void *user_data) |
19 | { | |
d5c89442 | 20 | struct net_device *dev = user_data; |
6bcba96e AS |
21 | struct wilc_vif *vif = netdev_priv(dev); |
22 | struct wilc *wilc = vif->wilc; | |
c5c77ba1 | 23 | |
3948362d | 24 | if (wilc->close) { |
5ac24427 | 25 | netdev_err(dev, "Can't handle UH interrupt\n"); |
c5c77ba1 | 26 | return IRQ_HANDLED; |
c5c77ba1 | 27 | } |
c5c77ba1 | 28 | return IRQ_WAKE_THREAD; |
c5c77ba1 | 29 | } |
c5c77ba1 | 30 | |
1608c403 | 31 | static irqreturn_t isr_bh_routine(int irq, void *userdata) |
c5c77ba1 | 32 | { |
d5c89442 | 33 | struct net_device *dev = userdata; |
6bcba96e AS |
34 | struct wilc_vif *vif = netdev_priv(userdata); |
35 | struct wilc *wilc = vif->wilc; | |
2e7933d0 | 36 | |
2e7933d0 | 37 | if (wilc->close) { |
5ac24427 | 38 | netdev_err(dev, "Can't handle BH interrupt\n"); |
c5c77ba1 | 39 | return IRQ_HANDLED; |
c5c77ba1 JK |
40 | } |
41 | ||
50b929e0 | 42 | wilc_handle_isr(wilc); |
c5c77ba1 | 43 | |
c5c77ba1 | 44 | return IRQ_HANDLED; |
c5c77ba1 | 45 | } |
c5c77ba1 | 46 | |
2c1d05d1 | 47 | static int init_irq(struct net_device *dev) |
c5c77ba1 JK |
48 | { |
49 | int ret = 0; | |
6bcba96e AS |
50 | struct wilc_vif *vif = netdev_priv(dev); |
51 | struct wilc *wl = vif->wilc; | |
c5c77ba1 | 52 | |
367b9559 AS |
53 | ret = gpiod_direction_input(wl->gpio_irq); |
54 | if (ret) { | |
5ac24427 | 55 | netdev_err(dev, "could not obtain gpio for WILC_INTR\n"); |
367b9559 | 56 | return ret; |
c5c77ba1 JK |
57 | } |
58 | ||
367b9559 AS |
59 | wl->dev_irq_num = gpiod_to_irq(wl->gpio_irq); |
60 | ||
61 | ret = request_threaded_irq(wl->dev_irq_num, isr_uh_routine, | |
62 | isr_bh_routine, | |
9a4c56be | 63 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
367b9559 AS |
64 | "WILC_IRQ", dev); |
65 | if (ret < 0) | |
66 | netdev_err(dev, "Failed to request IRQ\n"); | |
67 | else | |
68 | netdev_dbg(dev, "IRQ request succeeded IRQ-NUM= %d\n", | |
69 | wl->dev_irq_num); | |
c5c77ba1 JK |
70 | |
71 | return ret; | |
72 | } | |
c5c77ba1 | 73 | |
ec5cc750 | 74 | static void deinit_irq(struct net_device *dev) |
c5c77ba1 | 75 | { |
6bcba96e AS |
76 | struct wilc_vif *vif = netdev_priv(dev); |
77 | struct wilc *wilc = vif->wilc; | |
ec5cc750 | 78 | |
1cce2000 | 79 | /* Deinitialize IRQ */ |
367b9559 | 80 | if (wilc->dev_irq_num) |
ec5cc750 | 81 | free_irq(wilc->dev_irq_num, wilc); |
c5c77ba1 JK |
82 | } |
83 | ||
c7ceac21 | 84 | void wilc_mac_indicate(struct wilc *wilc) |
c5c77ba1 | 85 | { |
de11f709 | 86 | s8 status; |
c5c77ba1 | 87 | |
acceb12a | 88 | wilc_wlan_cfg_get_val(wilc, WID_STATUS, &status, 1); |
8c4574d6 | 89 | if (wilc->mac_status == WILC_MAC_STATUS_INIT) { |
c7ceac21 AS |
90 | wilc->mac_status = status; |
91 | complete(&wilc->sync_event); | |
92 | } else { | |
93 | wilc->mac_status = status; | |
c5c77ba1 | 94 | } |
c5c77ba1 JK |
95 | } |
96 | ||
1608c403 | 97 | static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header) |
c5c77ba1 | 98 | { |
660786ea | 99 | u8 *bssid, *bssid1; |
c5c77ba1 | 100 | int i = 0; |
9bc061e8 | 101 | struct net_device *ndev = NULL; |
c5c77ba1 | 102 | |
d239222e | 103 | bssid = mac_header + 10; |
660786ea | 104 | bssid1 = mac_header + 4; |
c5c77ba1 | 105 | |
9bc061e8 | 106 | mutex_lock(&wilc->vif_mutex); |
ba615f1e | 107 | for (i = 0; i < wilc->vif_num; i++) { |
d0e41eff | 108 | if (wilc->vif[i]->mode == WILC_STATION_MODE) |
fa633941 | 109 | if (ether_addr_equal_unaligned(bssid, |
9bc061e8 AS |
110 | wilc->vif[i]->bssid)) { |
111 | ndev = wilc->vif[i]->ndev; | |
112 | goto out; | |
113 | } | |
d0e41eff | 114 | if (wilc->vif[i]->mode == WILC_AP_MODE) |
fa633941 | 115 | if (ether_addr_equal_unaligned(bssid1, |
9bc061e8 AS |
116 | wilc->vif[i]->bssid)) { |
117 | ndev = wilc->vif[i]->ndev; | |
118 | goto out; | |
119 | } | |
ba615f1e | 120 | } |
9bc061e8 AS |
121 | out: |
122 | mutex_unlock(&wilc->vif_mutex); | |
123 | return ndev; | |
c5c77ba1 JK |
124 | } |
125 | ||
080de249 | 126 | void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode) |
c5c77ba1 | 127 | { |
735bb39c | 128 | struct wilc_vif *vif = netdev_priv(wilc_netdev); |
472791a9 | 129 | |
4e90d5f3 AS |
130 | if (bssid) |
131 | ether_addr_copy(vif->bssid, bssid); | |
132 | else | |
133 | eth_zero_addr(vif->bssid); | |
134 | ||
735bb39c | 135 | vif->mode = mode; |
c5c77ba1 JK |
136 | } |
137 | ||
562ed3f1 | 138 | int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) |
c5c77ba1 | 139 | { |
51e825f7 | 140 | u8 i = 0; |
51e825f7 | 141 | u8 ret_val = 0; |
c5c77ba1 | 142 | |
562ed3f1 | 143 | for (i = 0; i < wilc->vif_num; i++) |
4c00f705 | 144 | if (!is_zero_ether_addr(wilc->vif[i]->bssid)) |
c5c77ba1 | 145 | ret_val++; |
8259a53e | 146 | |
c5c77ba1 JK |
147 | return ret_val; |
148 | } | |
149 | ||
c6eab5e6 | 150 | static int wilc_txq_task(void *vp) |
c5c77ba1 | 151 | { |
8a88dd46 IS |
152 | int ret; |
153 | u32 txq_count; | |
9bc061e8 | 154 | struct wilc *wl = vp; |
88687584 | 155 | |
11a54b3f | 156 | complete(&wl->txq_thread_started); |
c5c77ba1 | 157 | while (1) { |
b27a6d5e | 158 | wait_for_completion(&wl->txq_event); |
c5c77ba1 | 159 | |
88687584 | 160 | if (wl->close) { |
11a54b3f | 161 | complete(&wl->txq_thread_started); |
c5c77ba1 JK |
162 | |
163 | while (!kthread_should_stop()) | |
164 | schedule(); | |
c5c77ba1 JK |
165 | break; |
166 | } | |
c5c77ba1 | 167 | do { |
9bc061e8 | 168 | ret = wilc_wlan_handle_txq(wl, &txq_count); |
98b89847 | 169 | if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) { |
9bc061e8 AS |
170 | int i; |
171 | struct wilc_vif *ifc; | |
172 | ||
173 | mutex_lock(&wl->vif_mutex); | |
174 | for (i = 0; i < wl->vif_num; i++) { | |
175 | ifc = wl->vif[i]; | |
176 | if (ifc->mac_opened && ifc->ndev) | |
177 | netif_wake_queue(ifc->ndev); | |
178 | } | |
179 | mutex_unlock(&wl->vif_mutex); | |
c5c77ba1 | 180 | } |
68b4f745 | 181 | } while (ret == -ENOBUFS && !wl->close); |
c5c77ba1 JK |
182 | } |
183 | return 0; | |
184 | } | |
185 | ||
ead6cb05 | 186 | static int wilc_wlan_get_firmware(struct net_device *dev) |
c5c77ba1 | 187 | { |
6bcba96e AS |
188 | struct wilc_vif *vif = netdev_priv(dev); |
189 | struct wilc *wilc = vif->wilc; | |
14823bf2 | 190 | int chip_id, ret = 0; |
c5c77ba1 JK |
191 | const struct firmware *wilc_firmware; |
192 | char *firmware; | |
193 | ||
65c3f000 | 194 | chip_id = wilc_get_chipid(wilc, false); |
14823bf2 GL |
195 | |
196 | if (chip_id < 0x1003a0) | |
197 | firmware = FIRMWARE_1002; | |
198 | else | |
199 | firmware = FIRMWARE_1003; | |
200 | ||
201 | netdev_info(dev, "loading firmware %s\n", firmware); | |
c5c77ba1 | 202 | |
b03314e2 | 203 | if (request_firmware(&wilc_firmware, firmware, wilc->dev) != 0) { |
b9811891 | 204 | netdev_err(dev, "%s - firmware not available\n", firmware); |
c5c77ba1 | 205 | ret = -1; |
5fe678b1 | 206 | goto fail; |
c5c77ba1 | 207 | } |
65c8adcf | 208 | wilc->firmware = wilc_firmware; |
c5c77ba1 | 209 | |
5fe678b1 | 210 | fail: |
c5c77ba1 JK |
211 | |
212 | return ret; | |
c5c77ba1 JK |
213 | } |
214 | ||
c6eab5e6 | 215 | static int wilc_start_firmware(struct net_device *dev) |
c5c77ba1 | 216 | { |
6bcba96e AS |
217 | struct wilc_vif *vif = netdev_priv(dev); |
218 | struct wilc *wilc = vif->wilc; | |
c5c77ba1 | 219 | int ret = 0; |
9bf3d727 | 220 | |
562ed3f1 | 221 | ret = wilc_wlan_start(wilc); |
1408603c | 222 | if (ret < 0) |
0aeea1ad | 223 | return ret; |
c5c77ba1 | 224 | |
04247e7d | 225 | if (!wait_for_completion_timeout(&wilc->sync_event, |
36190caa | 226 | msecs_to_jiffies(5000))) |
04247e7d | 227 | return -ETIME; |
c5c77ba1 | 228 | |
0aeea1ad | 229 | return 0; |
c5c77ba1 | 230 | } |
a40b22c5 | 231 | |
562ed3f1 | 232 | static int wilc1000_firmware_download(struct net_device *dev) |
c5c77ba1 | 233 | { |
6bcba96e AS |
234 | struct wilc_vif *vif = netdev_priv(dev); |
235 | struct wilc *wilc = vif->wilc; | |
c5c77ba1 JK |
236 | int ret = 0; |
237 | ||
ed760b67 | 238 | if (!wilc->firmware) { |
5ac24427 | 239 | netdev_err(dev, "Firmware buffer is NULL\n"); |
14b1821d | 240 | return -ENOBUFS; |
c5c77ba1 | 241 | } |
1408603c | 242 | |
562ed3f1 | 243 | ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data, |
ed760b67 | 244 | wilc->firmware->size); |
fc4b95d6 | 245 | if (ret < 0) |
14b1821d | 246 | return ret; |
c5c77ba1 | 247 | |
ed760b67 | 248 | release_firmware(wilc->firmware); |
6f72ed75 | 249 | wilc->firmware = NULL; |
c5c77ba1 | 250 | |
5ac24427 | 251 | netdev_dbg(dev, "Download Succeeded\n"); |
c5c77ba1 | 252 | |
14b1821d | 253 | return 0; |
c5c77ba1 JK |
254 | } |
255 | ||
c6eab5e6 | 256 | static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif) |
c5c77ba1 | 257 | { |
9bc061e8 | 258 | struct wilc_priv *priv = &vif->priv; |
0fa66c71 | 259 | struct host_if_drv *hif_drv; |
9a4b45fb AS |
260 | u8 b; |
261 | u16 hw; | |
262 | u32 w; | |
c5c77ba1 | 263 | |
5ac24427 | 264 | netdev_dbg(dev, "Start configuring Firmware\n"); |
48b28df9 | 265 | hif_drv = (struct host_if_drv *)priv->hif_drv; |
5ac24427 | 266 | netdev_dbg(dev, "Host = %p\n", hif_drv); |
c5c77ba1 | 267 | |
9a4b45fb AS |
268 | w = vif->iftype; |
269 | cpu_to_le32s(&w); | |
270 | if (!wilc_wlan_cfg_set(vif, 1, WID_SET_OPERATION_MODE, (u8 *)&w, 4, | |
271 | 0, 0)) | |
5fe678b1 | 272 | goto fail; |
c5c77ba1 | 273 | |
9a4b45fb AS |
274 | b = WILC_FW_BSS_TYPE_INFRA; |
275 | if (!wilc_wlan_cfg_set(vif, 0, WID_BSS_TYPE, &b, 1, 0, 0)) | |
5fe678b1 | 276 | goto fail; |
c5c77ba1 | 277 | |
9a4b45fb AS |
278 | b = WILC_FW_TX_RATE_AUTO; |
279 | if (!wilc_wlan_cfg_set(vif, 0, WID_CURRENT_TX_RATE, &b, 1, 0, 0)) | |
5fe678b1 | 280 | goto fail; |
c5c77ba1 | 281 | |
9a4b45fb AS |
282 | b = WILC_FW_OPER_MODE_G_MIXED_11B_2; |
283 | if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0, 0)) | |
5fe678b1 | 284 | goto fail; |
c5c77ba1 | 285 | |
9a4b45fb AS |
286 | b = WILC_FW_PREAMBLE_SHORT; |
287 | if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0)) | |
5fe678b1 | 288 | goto fail; |
c5c77ba1 | 289 | |
9a4b45fb AS |
290 | b = WILC_FW_11N_PROT_AUTO; |
291 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_PROT_MECH, &b, 1, 0, 0)) | |
5fe678b1 | 292 | goto fail; |
c5c77ba1 | 293 | |
9a4b45fb AS |
294 | b = WILC_FW_ACTIVE_SCAN; |
295 | if (!wilc_wlan_cfg_set(vif, 0, WID_SCAN_TYPE, &b, 1, 0, 0)) | |
5fe678b1 | 296 | goto fail; |
c5c77ba1 | 297 | |
9a4b45fb AS |
298 | b = WILC_FW_SITE_SURVEY_OFF; |
299 | if (!wilc_wlan_cfg_set(vif, 0, WID_SITE_SURVEY, &b, 1, 0, 0)) | |
5fe678b1 | 300 | goto fail; |
c5c77ba1 | 301 | |
9a4b45fb AS |
302 | hw = 0xffff; |
303 | cpu_to_le16s(&hw); | |
304 | if (!wilc_wlan_cfg_set(vif, 0, WID_RTS_THRESHOLD, (u8 *)&hw, 2, 0, 0)) | |
5fe678b1 | 305 | goto fail; |
c5c77ba1 | 306 | |
9a4b45fb AS |
307 | hw = 2346; |
308 | cpu_to_le16s(&hw); | |
309 | if (!wilc_wlan_cfg_set(vif, 0, WID_FRAG_THRESHOLD, (u8 *)&hw, 2, 0, 0)) | |
5fe678b1 | 310 | goto fail; |
c5c77ba1 | 311 | |
9a4b45fb AS |
312 | b = 0; |
313 | if (!wilc_wlan_cfg_set(vif, 0, WID_BCAST_SSID, &b, 1, 0, 0)) | |
5fe678b1 | 314 | goto fail; |
c5c77ba1 | 315 | |
9a4b45fb AS |
316 | b = 1; |
317 | if (!wilc_wlan_cfg_set(vif, 0, WID_QOS_ENABLE, &b, 1, 0, 0)) | |
5fe678b1 | 318 | goto fail; |
c5c77ba1 | 319 | |
9a4b45fb AS |
320 | b = WILC_FW_NO_POWERSAVE; |
321 | if (!wilc_wlan_cfg_set(vif, 0, WID_POWER_MANAGEMENT, &b, 1, 0, 0)) | |
5fe678b1 | 322 | goto fail; |
c5c77ba1 | 323 | |
9a4b45fb AS |
324 | b = WILC_FW_SEC_NO; |
325 | if (!wilc_wlan_cfg_set(vif, 0, WID_11I_MODE, &b, 1, 0, 0)) | |
5fe678b1 | 326 | goto fail; |
c5c77ba1 | 327 | |
9a4b45fb AS |
328 | b = WILC_FW_AUTH_OPEN_SYSTEM; |
329 | if (!wilc_wlan_cfg_set(vif, 0, WID_AUTH_TYPE, &b, 1, 0, 0)) | |
5fe678b1 | 330 | goto fail; |
c5c77ba1 | 331 | |
9a4b45fb AS |
332 | b = 3; |
333 | if (!wilc_wlan_cfg_set(vif, 0, WID_LISTEN_INTERVAL, &b, 1, 0, 0)) | |
5fe678b1 | 334 | goto fail; |
c5c77ba1 | 335 | |
9a4b45fb AS |
336 | b = 3; |
337 | if (!wilc_wlan_cfg_set(vif, 0, WID_DTIM_PERIOD, &b, 1, 0, 0)) | |
5fe678b1 | 338 | goto fail; |
c5c77ba1 | 339 | |
9a4b45fb AS |
340 | b = WILC_FW_ACK_POLICY_NORMAL; |
341 | if (!wilc_wlan_cfg_set(vif, 0, WID_ACK_POLICY, &b, 1, 0, 0)) | |
5fe678b1 | 342 | goto fail; |
c5c77ba1 | 343 | |
9a4b45fb AS |
344 | b = 0; |
345 | if (!wilc_wlan_cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, &b, 1, | |
89758e13 | 346 | 0, 0)) |
5fe678b1 | 347 | goto fail; |
c5c77ba1 | 348 | |
9a4b45fb AS |
349 | b = 48; |
350 | if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, &b, 1, 0, 0)) | |
5fe678b1 | 351 | goto fail; |
c5c77ba1 | 352 | |
9a4b45fb AS |
353 | b = 28; |
354 | if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, &b, 1, 0, 0)) | |
5fe678b1 | 355 | goto fail; |
c5c77ba1 | 356 | |
9a4b45fb AS |
357 | hw = 100; |
358 | cpu_to_le16s(&hw); | |
359 | if (!wilc_wlan_cfg_set(vif, 0, WID_BEACON_INTERVAL, (u8 *)&hw, 2, 0, 0)) | |
5fe678b1 | 360 | goto fail; |
c5c77ba1 | 361 | |
9a4b45fb AS |
362 | b = WILC_FW_REKEY_POLICY_DISABLE; |
363 | if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_POLICY, &b, 1, 0, 0)) | |
5fe678b1 | 364 | goto fail; |
c5c77ba1 | 365 | |
9a4b45fb AS |
366 | w = 84600; |
367 | cpu_to_le32s(&w); | |
368 | if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PERIOD, (u8 *)&w, 4, 0, 0)) | |
5fe678b1 | 369 | goto fail; |
c5c77ba1 | 370 | |
9a4b45fb AS |
371 | w = 500; |
372 | cpu_to_le32s(&w); | |
373 | if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, (u8 *)&w, 4, 0, | |
89758e13 | 374 | 0)) |
5fe678b1 | 375 | goto fail; |
c5c77ba1 | 376 | |
9a4b45fb AS |
377 | b = 1; |
378 | if (!wilc_wlan_cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, &b, 1, 0, | |
89758e13 | 379 | 0)) |
5fe678b1 | 380 | goto fail; |
c5c77ba1 | 381 | |
9a4b45fb AS |
382 | b = WILC_FW_ERP_PROT_SELF_CTS; |
383 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, &b, 1, 0, 0)) | |
5fe678b1 | 384 | goto fail; |
c5c77ba1 | 385 | |
9a4b45fb AS |
386 | b = 1; |
387 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ENABLE, &b, 1, 0, 0)) | |
5fe678b1 | 388 | goto fail; |
c5c77ba1 | 389 | |
9a4b45fb AS |
390 | b = WILC_FW_11N_OP_MODE_HT_MIXED; |
391 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OPERATING_MODE, &b, 1, 0, 0)) | |
5fe678b1 | 392 | goto fail; |
c5c77ba1 | 393 | |
9a4b45fb AS |
394 | b = 1; |
395 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, &b, 1, 0, 0)) | |
5fe678b1 | 396 | goto fail; |
c5c77ba1 | 397 | |
9a4b45fb AS |
398 | b = WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT; |
399 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, &b, 1, | |
89758e13 | 400 | 0, 0)) |
5fe678b1 | 401 | goto fail; |
c5c77ba1 | 402 | |
9a4b45fb AS |
403 | b = WILC_FW_HT_PROT_RTS_CTS_NONHT; |
404 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, &b, 1, 0, 0)) | |
5fe678b1 | 405 | goto fail; |
c5c77ba1 | 406 | |
9a4b45fb AS |
407 | b = 0; |
408 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, &b, 1, 0, | |
89758e13 | 409 | 0)) |
5fe678b1 | 410 | goto fail; |
c5c77ba1 | 411 | |
9a4b45fb AS |
412 | b = 7; |
413 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0, 0)) | |
5fe678b1 | 414 | goto fail; |
c5c77ba1 | 415 | |
9a4b45fb AS |
416 | b = 1; |
417 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1, | |
89758e13 | 418 | 1, 1)) |
5fe678b1 | 419 | goto fail; |
c5c77ba1 JK |
420 | |
421 | return 0; | |
422 | ||
5fe678b1 | 423 | fail: |
c5c77ba1 JK |
424 | return -1; |
425 | } | |
426 | ||
d579112b | 427 | static void wlan_deinit_locks(struct net_device *dev) |
fec2dbfe | 428 | { |
6bcba96e AS |
429 | struct wilc_vif *vif = netdev_priv(dev); |
430 | struct wilc *wilc = vif->wilc; | |
fec2dbfe AS |
431 | |
432 | mutex_destroy(&wilc->hif_cs); | |
433 | mutex_destroy(&wilc->rxq_cs); | |
6dea3302 | 434 | mutex_destroy(&wilc->cfg_cmd_lock); |
fec2dbfe | 435 | mutex_destroy(&wilc->txq_add_to_head_cs); |
9bc061e8 | 436 | mutex_destroy(&wilc->vif_mutex); |
fec2dbfe AS |
437 | } |
438 | ||
439 | static void wlan_deinitialize_threads(struct net_device *dev) | |
440 | { | |
6bcba96e AS |
441 | struct wilc_vif *vif = netdev_priv(dev); |
442 | struct wilc *wl = vif->wilc; | |
fec2dbfe AS |
443 | |
444 | wl->close = 1; | |
445 | ||
446 | complete(&wl->txq_event); | |
447 | ||
448 | if (wl->txq_thread) { | |
449 | kthread_stop(wl->txq_thread); | |
450 | wl->txq_thread = NULL; | |
451 | } | |
452 | } | |
453 | ||
ead6cb05 | 454 | static void wilc_wlan_deinitialize(struct net_device *dev) |
c5c77ba1 | 455 | { |
6bcba96e AS |
456 | struct wilc_vif *vif = netdev_priv(dev); |
457 | struct wilc *wl = vif->wilc; | |
53dc0cfe GL |
458 | |
459 | if (!wl) { | |
460 | netdev_err(dev, "wl is NULL\n"); | |
461 | return; | |
462 | } | |
463 | ||
74cffafb | 464 | if (wl->initialized) { |
53dc0cfe | 465 | netdev_info(dev, "Deinitializing wilc1000...\n"); |
c5c77ba1 | 466 | |
5547c1f0 | 467 | if (!wl->dev_irq_num && |
af9ae09a | 468 | wl->hif_func->disable_interrupt) { |
c4d139cb | 469 | mutex_lock(&wl->hif_cs); |
af9ae09a | 470 | wl->hif_func->disable_interrupt(wl); |
c4d139cb AB |
471 | mutex_unlock(&wl->hif_cs); |
472 | } | |
1646ff6c | 473 | complete(&wl->txq_event); |
c5c77ba1 | 474 | |
32dd51bc | 475 | wlan_deinitialize_threads(dev); |
ec5cc750 | 476 | deinit_irq(dev); |
c5c77ba1 | 477 | |
0d1b57c1 | 478 | wilc_wlan_stop(wl, vif); |
2de7cbec | 479 | wilc_wlan_cleanup(dev); |
7c67c053 | 480 | wlan_deinit_locks(dev); |
c5c77ba1 | 481 | |
53dc0cfe | 482 | wl->initialized = false; |
c5c77ba1 | 483 | |
5ac24427 | 484 | netdev_dbg(dev, "wilc1000 deinitialization Done\n"); |
c5c77ba1 | 485 | } else { |
5ac24427 | 486 | netdev_dbg(dev, "wilc1000 is not initialized\n"); |
c5c77ba1 | 487 | } |
c5c77ba1 JK |
488 | } |
489 | ||
1608c403 | 490 | static int wlan_initialize_threads(struct net_device *dev) |
c5c77ba1 | 491 | { |
6bcba96e AS |
492 | struct wilc_vif *vif = netdev_priv(dev); |
493 | struct wilc *wilc = vif->wilc; | |
75a94665 | 494 | |
9bc061e8 | 495 | wilc->txq_thread = kthread_run(wilc_txq_task, (void *)wilc, |
387fbf00 | 496 | "K_TXQ_TASK"); |
b3e6916d | 497 | if (IS_ERR(wilc->txq_thread)) { |
5ac24427 | 498 | netdev_err(dev, "couldn't create TXQ thread\n"); |
6bc72c5a | 499 | wilc->close = 0; |
b3e6916d | 500 | return PTR_ERR(wilc->txq_thread); |
c5c77ba1 | 501 | } |
11a54b3f | 502 | wait_for_completion(&wilc->txq_thread_started); |
c5c77ba1 JK |
503 | |
504 | return 0; | |
c5c77ba1 JK |
505 | } |
506 | ||
ead6cb05 | 507 | static int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif) |
c5c77ba1 | 508 | { |
c5c77ba1 | 509 | int ret = 0; |
a4cac481 | 510 | struct wilc *wl = vif->wilc; |
c5c77ba1 | 511 | |
0fa683b6 | 512 | if (!wl->initialized) { |
8c4574d6 | 513 | wl->mac_status = WILC_MAC_STATUS_INIT; |
0fa683b6 | 514 | wl->close = 0; |
c5c77ba1 | 515 | |
4bd7baf0 | 516 | ret = wilc_wlan_init(dev); |
9bc061e8 AS |
517 | if (ret < 0) |
518 | return -EIO; | |
c5c77ba1 | 519 | |
75a94665 | 520 | ret = wlan_initialize_threads(dev); |
b46d6882 | 521 | if (ret < 0) { |
b46d6882 | 522 | ret = -EIO; |
8eb658f1 | 523 | goto fail_wilc_wlan; |
b46d6882 TC |
524 | } |
525 | ||
6419f818 AS |
526 | if (wl->gpio_irq && init_irq(dev)) { |
527 | ret = -EIO; | |
528 | goto fail_threads; | |
529 | } | |
530 | ||
5547c1f0 | 531 | if (!wl->dev_irq_num && |
af9ae09a GL |
532 | wl->hif_func->enable_interrupt && |
533 | wl->hif_func->enable_interrupt(wl)) { | |
c5c77ba1 | 534 | ret = -EIO; |
8eb658f1 | 535 | goto fail_irq_init; |
c5c77ba1 | 536 | } |
c5c77ba1 | 537 | |
0e1af73d | 538 | if (wilc_wlan_get_firmware(dev)) { |
c5c77ba1 | 539 | ret = -EIO; |
8eb658f1 | 540 | goto fail_irq_enable; |
c5c77ba1 JK |
541 | } |
542 | ||
562ed3f1 | 543 | ret = wilc1000_firmware_download(dev); |
c5c77ba1 | 544 | if (ret < 0) { |
c5c77ba1 | 545 | ret = -EIO; |
8eb658f1 | 546 | goto fail_irq_enable; |
c5c77ba1 JK |
547 | } |
548 | ||
c6eab5e6 | 549 | ret = wilc_start_firmware(dev); |
c5c77ba1 | 550 | if (ret < 0) { |
c5c77ba1 | 551 | ret = -EIO; |
8eb658f1 | 552 | goto fail_irq_enable; |
c5c77ba1 JK |
553 | } |
554 | ||
79df6a49 | 555 | if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) { |
c5c77ba1 | 556 | int size; |
5ac24427 | 557 | char firmware_ver[20]; |
8dfaafd6 | 558 | |
acceb12a | 559 | size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION, |
5ac24427 LK |
560 | firmware_ver, |
561 | sizeof(firmware_ver)); | |
562 | firmware_ver[size] = '\0'; | |
563 | netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver); | |
c5c77ba1 | 564 | } |
c6eab5e6 | 565 | ret = wilc_init_fw_config(dev, vif); |
c5c77ba1 JK |
566 | |
567 | if (ret < 0) { | |
5ac24427 | 568 | netdev_err(dev, "Failed to configure firmware\n"); |
c5c77ba1 | 569 | ret = -EIO; |
8eb658f1 | 570 | goto fail_fw_start; |
c5c77ba1 | 571 | } |
0fa683b6 | 572 | wl->initialized = true; |
98b89847 | 573 | return 0; |
c5c77ba1 | 574 | |
8eb658f1 | 575 | fail_fw_start: |
0d1b57c1 | 576 | wilc_wlan_stop(wl, vif); |
c5c77ba1 | 577 | |
8eb658f1 | 578 | fail_irq_enable: |
5547c1f0 | 579 | if (!wl->dev_irq_num && |
af9ae09a GL |
580 | wl->hif_func->disable_interrupt) |
581 | wl->hif_func->disable_interrupt(wl); | |
8eb658f1 | 582 | fail_irq_init: |
c4d139cb AB |
583 | if (wl->dev_irq_num) |
584 | deinit_irq(dev); | |
6419f818 | 585 | fail_threads: |
32dd51bc | 586 | wlan_deinitialize_threads(dev); |
8eb658f1 | 587 | fail_wilc_wlan: |
2de7cbec | 588 | wilc_wlan_cleanup(dev); |
073cbb6c | 589 | netdev_err(dev, "WLAN initialization FAILED\n"); |
c5c77ba1 | 590 | } else { |
5ac24427 | 591 | netdev_dbg(dev, "wilc1000 already initialized\n"); |
c5c77ba1 JK |
592 | } |
593 | return ret; | |
594 | } | |
595 | ||
1608c403 | 596 | static int mac_init_fn(struct net_device *ndev) |
c5c77ba1 | 597 | { |
98b89847 LK |
598 | netif_start_queue(ndev); |
599 | netif_stop_queue(ndev); | |
c5c77ba1 JK |
600 | |
601 | return 0; | |
602 | } | |
c5c77ba1 | 603 | |
68a30a63 | 604 | static int wilc_mac_open(struct net_device *ndev) |
c5c77ba1 | 605 | { |
6bcba96e AS |
606 | struct wilc_vif *vif = netdev_priv(ndev); |
607 | struct wilc *wl = vif->wilc; | |
f41cf246 | 608 | struct wilc_priv *priv = wdev_priv(vif->ndev->ieee80211_ptr); |
c5c77ba1 | 609 | unsigned char mac_add[ETH_ALEN] = {0}; |
c5c77ba1 | 610 | int ret = 0; |
c5c77ba1 | 611 | |
40095ad9 | 612 | if (!wl || !wl->dev) { |
90fd4cc5 | 613 | netdev_err(ndev, "device not ready\n"); |
7d05652c CG |
614 | return -ENODEV; |
615 | } | |
b03314e2 | 616 | |
5ac24427 | 617 | netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev); |
c5c77ba1 | 618 | |
dd4b6a83 | 619 | ret = wilc_init_host_int(ndev); |
1408603c | 620 | if (ret < 0) |
c5c77ba1 | 621 | return ret; |
c5c77ba1 | 622 | |
81a7dc3d | 623 | ret = wilc_wlan_initialize(ndev, vif); |
c5c77ba1 | 624 | if (ret < 0) { |
a9a16823 | 625 | wilc_deinit_host_int(ndev); |
c5c77ba1 JK |
626 | return ret; |
627 | } | |
628 | ||
4b1b8b08 AA |
629 | wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), vif->iftype, |
630 | vif->idx); | |
76c01fdd HE |
631 | wilc_get_mac_address(vif, mac_add); |
632 | netdev_dbg(ndev, "Mac address: %pM\n", mac_add); | |
39cf54fc | 633 | ether_addr_copy(ndev->dev_addr, mac_add); |
c5c77ba1 JK |
634 | |
635 | if (!is_valid_ether_addr(ndev->dev_addr)) { | |
5ac24427 | 636 | netdev_err(ndev, "Wrong MAC address\n"); |
339d244a | 637 | wilc_deinit_host_int(ndev); |
81a7dc3d | 638 | wilc_wlan_deinitialize(ndev); |
339d244a | 639 | return -EINVAL; |
c5c77ba1 JK |
640 | } |
641 | ||
1006b5c7 GL |
642 | wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy, |
643 | vif->ndev->ieee80211_ptr, | |
340a84ff | 644 | vif->frame_reg[0].type, |
89febb21 | 645 | vif->frame_reg[0].reg); |
1006b5c7 GL |
646 | wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy, |
647 | vif->ndev->ieee80211_ptr, | |
340a84ff | 648 | vif->frame_reg[1].type, |
89febb21 | 649 | vif->frame_reg[1].reg); |
c5c77ba1 | 650 | netif_wake_queue(ndev); |
f3c1366e | 651 | wl->open_ifcs++; |
f41cf246 | 652 | priv->p2p.local_random = 0x01; |
a4cac481 | 653 | vif->mac_opened = 1; |
c5c77ba1 | 654 | return 0; |
c5c77ba1 | 655 | } |
c5c77ba1 | 656 | |
1608c403 | 657 | static struct net_device_stats *mac_stats(struct net_device *dev) |
c5c77ba1 | 658 | { |
40095ad9 | 659 | struct wilc_vif *vif = netdev_priv(dev); |
c5c77ba1 | 660 | |
a4cac481 | 661 | return &vif->netstats; |
c5c77ba1 JK |
662 | } |
663 | ||
c5c77ba1 JK |
664 | static void wilc_set_multicast_list(struct net_device *dev) |
665 | { | |
c5c77ba1 | 666 | struct netdev_hw_addr *ha; |
6bcba96e | 667 | struct wilc_vif *vif = netdev_priv(dev); |
ba853fe6 | 668 | int i; |
e624c58c | 669 | u8 *mc_list; |
ba853fe6 | 670 | u8 *cur_mc; |
8dfaafd6 | 671 | |
1408603c | 672 | if (dev->flags & IFF_PROMISC) |
c5c77ba1 | 673 | return; |
c5c77ba1 | 674 | |
7bf0242a AS |
675 | if (dev->flags & IFF_ALLMULTI || |
676 | dev->mc.count > WILC_MULTICAST_TABLE_SIZE) { | |
a0c6a32e | 677 | wilc_setup_multicast_filter(vif, 0, 0, NULL); |
c5c77ba1 JK |
678 | return; |
679 | } | |
680 | ||
7bf0242a | 681 | if (dev->mc.count == 0) { |
a0c6a32e | 682 | wilc_setup_multicast_filter(vif, 1, 0, NULL); |
c5c77ba1 JK |
683 | return; |
684 | } | |
685 | ||
ae26aa84 | 686 | mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC); |
e624c58c AS |
687 | if (!mc_list) |
688 | return; | |
689 | ||
ba853fe6 AS |
690 | cur_mc = mc_list; |
691 | i = 0; | |
c8537e6d | 692 | netdev_for_each_mc_addr(ha, dev) { |
ba853fe6 AS |
693 | memcpy(cur_mc, ha->addr, ETH_ALEN); |
694 | netdev_dbg(dev, "Entry[%d]: %pM\n", i, cur_mc); | |
695 | i++; | |
696 | cur_mc += ETH_ALEN; | |
c5c77ba1 JK |
697 | } |
698 | ||
a0c6a32e | 699 | if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list)) |
e624c58c | 700 | kfree(mc_list); |
c5c77ba1 JK |
701 | } |
702 | ||
c6eab5e6 | 703 | static void wilc_tx_complete(void *priv, int status) |
c5c77ba1 | 704 | { |
d5c89442 | 705 | struct tx_complete_data *pv_data = priv; |
8dfaafd6 | 706 | |
c5c77ba1 | 707 | dev_kfree_skb(pv_data->skb); |
a18dd630 | 708 | kfree(pv_data); |
c5c77ba1 JK |
709 | } |
710 | ||
ad891807 | 711 | netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) |
c5c77ba1 | 712 | { |
6bcba96e AS |
713 | struct wilc_vif *vif = netdev_priv(ndev); |
714 | struct wilc *wilc = vif->wilc; | |
c5c77ba1 | 715 | struct tx_complete_data *tx_data = NULL; |
44ec3b75 | 716 | int queue_count; |
c5c77ba1 | 717 | |
c5c77ba1 | 718 | if (skb->dev != ndev) { |
5ac24427 | 719 | netdev_err(ndev, "Packet not destined to this device\n"); |
c5c77ba1 JK |
720 | return 0; |
721 | } | |
722 | ||
b38e9030 | 723 | tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC); |
3a147c07 | 724 | if (!tx_data) { |
c5c77ba1 JK |
725 | dev_kfree_skb(skb); |
726 | netif_wake_queue(ndev); | |
727 | return 0; | |
728 | } | |
729 | ||
730 | tx_data->buff = skb->data; | |
731 | tx_data->size = skb->len; | |
732 | tx_data->skb = skb; | |
733 | ||
a4cac481 GL |
734 | vif->netstats.tx_packets++; |
735 | vif->netstats.tx_bytes += tx_data->size; | |
44ec3b75 LK |
736 | queue_count = wilc_wlan_txq_add_net_pkt(ndev, (void *)tx_data, |
737 | tx_data->buff, tx_data->size, | |
c6eab5e6 | 738 | wilc_tx_complete); |
c5c77ba1 | 739 | |
44ec3b75 | 740 | if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) { |
9bc061e8 AS |
741 | int i; |
742 | ||
743 | mutex_lock(&wilc->vif_mutex); | |
744 | for (i = 0; i < wilc->vif_num; i++) { | |
745 | if (wilc->vif[i]->mac_opened) | |
746 | netif_stop_queue(wilc->vif[i]->ndev); | |
747 | } | |
748 | mutex_unlock(&wilc->vif_mutex); | |
c5c77ba1 JK |
749 | } |
750 | ||
751 | return 0; | |
752 | } | |
753 | ||
68a30a63 | 754 | static int wilc_mac_close(struct net_device *ndev) |
c5c77ba1 | 755 | { |
6bcba96e | 756 | struct wilc_vif *vif = netdev_priv(ndev); |
440592df | 757 | struct wilc *wl = vif->wilc; |
c5c77ba1 | 758 | |
5ac24427 | 759 | netdev_dbg(ndev, "Mac close\n"); |
c5c77ba1 | 760 | |
7bf0242a | 761 | if (wl->open_ifcs > 0) |
ca64ad6e | 762 | wl->open_ifcs--; |
1408603c | 763 | else |
c5c77ba1 | 764 | return 0; |
c5c77ba1 | 765 | |
1006b5c7 GL |
766 | if (vif->ndev) { |
767 | netif_stop_queue(vif->ndev); | |
c5c77ba1 | 768 | |
1006b5c7 | 769 | wilc_deinit_host_int(vif->ndev); |
c5c77ba1 JK |
770 | } |
771 | ||
ca64ad6e | 772 | if (wl->open_ifcs == 0) { |
5ac24427 | 773 | netdev_dbg(ndev, "Deinitializing wilc1000\n"); |
ca64ad6e | 774 | wl->close = 1; |
81a7dc3d | 775 | wilc_wlan_deinitialize(ndev); |
c5c77ba1 JK |
776 | } |
777 | ||
a4cac481 | 778 | vif->mac_opened = 0; |
c5c77ba1 JK |
779 | |
780 | return 0; | |
781 | } | |
782 | ||
55e311d8 AS |
783 | void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, |
784 | u32 pkt_offset) | |
c5c77ba1 | 785 | { |
c5c77ba1 JK |
786 | unsigned int frame_len = 0; |
787 | int stats; | |
788 | unsigned char *buff_to_send = NULL; | |
789 | struct sk_buff *skb; | |
c5c77ba1 | 790 | struct net_device *wilc_netdev; |
a4cac481 | 791 | struct wilc_vif *vif; |
c5c77ba1 | 792 | |
0953a2e3 LK |
793 | if (!wilc) |
794 | return; | |
795 | ||
7e725b47 | 796 | wilc_netdev = get_if_handler(wilc, buff); |
3a147c07 | 797 | if (!wilc_netdev) |
c5c77ba1 JK |
798 | return; |
799 | ||
800 | buff += pkt_offset; | |
a4cac481 | 801 | vif = netdev_priv(wilc_netdev); |
c5c77ba1 JK |
802 | |
803 | if (size > 0) { | |
c5c77ba1 JK |
804 | frame_len = size; |
805 | buff_to_send = buff; | |
806 | ||
c5c77ba1 | 807 | skb = dev_alloc_skb(frame_len); |
1408603c | 808 | if (!skb) |
c5c77ba1 | 809 | return; |
1408603c | 810 | |
c5c77ba1 JK |
811 | skb->dev = wilc_netdev; |
812 | ||
59ae1d12 | 813 | skb_put_data(skb, buff_to_send, frame_len); |
c5c77ba1 | 814 | |
c5c77ba1 | 815 | skb->protocol = eth_type_trans(skb, wilc_netdev); |
a4cac481 GL |
816 | vif->netstats.rx_packets++; |
817 | vif->netstats.rx_bytes += frame_len; | |
c5c77ba1 JK |
818 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
819 | stats = netif_rx(skb); | |
5ac24427 | 820 | netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); |
c5c77ba1 | 821 | } |
c5c77ba1 JK |
822 | } |
823 | ||
44b4709c | 824 | void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size) |
c5c77ba1 JK |
825 | { |
826 | int i = 0; | |
a4cac481 | 827 | struct wilc_vif *vif; |
c5c77ba1 | 828 | |
9bc061e8 | 829 | mutex_lock(&wilc->vif_mutex); |
11f4b2ee | 830 | for (i = 0; i < wilc->vif_num; i++) { |
9bc061e8 AS |
831 | u16 type = le16_to_cpup((__le16 *)buff); |
832 | ||
1f435d2e | 833 | vif = netdev_priv(wilc->vif[i]->ndev); |
9bc061e8 AS |
834 | if ((type == vif->frame_reg[0].type && vif->frame_reg[0].reg) || |
835 | (type == vif->frame_reg[1].type && vif->frame_reg[1].reg)) { | |
836 | wilc_wfi_p2p_rx(vif, buff, size); | |
837 | break; | |
838 | } | |
839 | ||
a4cac481 | 840 | if (vif->monitor_flag) { |
58871300 | 841 | wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size); |
9bc061e8 | 842 | break; |
c5c77ba1 JK |
843 | } |
844 | } | |
9bc061e8 | 845 | mutex_unlock(&wilc->vif_mutex); |
c5c77ba1 JK |
846 | } |
847 | ||
f2bf97cb AS |
848 | static const struct net_device_ops wilc_netdev_ops = { |
849 | .ndo_init = mac_init_fn, | |
850 | .ndo_open = wilc_mac_open, | |
851 | .ndo_stop = wilc_mac_close, | |
852 | .ndo_start_xmit = wilc_mac_xmit, | |
853 | .ndo_get_stats = mac_stats, | |
854 | .ndo_set_rx_mode = wilc_set_multicast_list, | |
855 | }; | |
856 | ||
857c7b00 | 857 | void wilc_netdev_cleanup(struct wilc *wilc) |
4875c499 | 858 | { |
735bb39c | 859 | int i; |
4875c499 | 860 | |
b4a01d8f CIK |
861 | if (!wilc) |
862 | return; | |
863 | ||
b4a01d8f | 864 | if (wilc->firmware) { |
90b984c8 | 865 | release_firmware(wilc->firmware); |
3f626cf4 LK |
866 | wilc->firmware = NULL; |
867 | } | |
4875c499 | 868 | |
9bc061e8 AS |
869 | for (i = 0; i < wilc->vif_num; i++) { |
870 | if (wilc->vif[i] && wilc->vif[i]->ndev) | |
1f435d2e | 871 | unregister_netdev(wilc->vif[i]->ndev); |
4875c499 TC |
872 | } |
873 | ||
9bc061e8 | 874 | wilc_wfi_deinit_mon_interface(wilc, false); |
b3ee105c AS |
875 | flush_workqueue(wilc->hif_workqueue); |
876 | destroy_workqueue(wilc->hif_workqueue); | |
acceb12a | 877 | wilc_wlan_cfg_deinit(wilc); |
97eaff57 | 878 | kfree(wilc->bus_data); |
9bc061e8 AS |
879 | wiphy_unregister(wilc->wiphy); |
880 | wiphy_free(wilc->wiphy); | |
4875c499 | 881 | } |
f45b8934 | 882 | EXPORT_SYMBOL_GPL(wilc_netdev_cleanup); |
4875c499 | 883 | |
9bc061e8 AS |
884 | struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, |
885 | int vif_type, enum nl80211_iftype type, | |
886 | bool rtnl_locked) | |
c5c77ba1 | 887 | { |
c5c77ba1 | 888 | struct net_device *ndev; |
9bc061e8 AS |
889 | struct wilc_vif *vif; |
890 | int ret; | |
026e14ae | 891 | |
9bc061e8 AS |
892 | ndev = alloc_etherdev(sizeof(*vif)); |
893 | if (!ndev) | |
894 | return ERR_PTR(-ENOMEM); | |
c5c77ba1 | 895 | |
9bc061e8 AS |
896 | vif = netdev_priv(ndev); |
897 | ndev->ieee80211_ptr = &vif->priv.wdev; | |
898 | strcpy(ndev->name, name); | |
899 | vif->wilc = wl; | |
900 | vif->ndev = ndev; | |
901 | ndev->ml_priv = vif; | |
c5c77ba1 | 902 | |
9bc061e8 | 903 | ndev->netdev_ops = &wilc_netdev_ops; |
c5c77ba1 | 904 | |
9bc061e8 | 905 | SET_NETDEV_DEV(ndev, wiphy_dev(wl->wiphy)); |
a53b0b13 | 906 | |
9bc061e8 AS |
907 | vif->priv.wdev.wiphy = wl->wiphy; |
908 | vif->priv.wdev.netdev = ndev; | |
909 | vif->priv.wdev.iftype = type; | |
910 | vif->priv.dev = ndev; | |
026e14ae | 911 | |
9bc061e8 AS |
912 | if (rtnl_locked) |
913 | ret = register_netdevice(ndev); | |
914 | else | |
fe747f0f | 915 | ret = register_netdev(ndev); |
c5c77ba1 | 916 | |
9bc061e8 AS |
917 | if (ret) { |
918 | free_netdev(ndev); | |
919 | return ERR_PTR(-EFAULT); | |
c5c77ba1 JK |
920 | } |
921 | ||
9bc061e8 AS |
922 | ndev->needs_free_netdev = true; |
923 | vif->iftype = vif_type; | |
924 | vif->wilc->vif[wl->vif_num] = vif; | |
9bc061e8 AS |
925 | vif->idx = wl->vif_num; |
926 | wl->vif_num += 1; | |
927 | vif->mac_opened = 0; | |
928 | return vif; | |
c5c77ba1 | 929 | } |
f45b8934 AB |
930 | |
931 | MODULE_LICENSE("GPL"); |