Commit | Line | Data |
---|---|---|
c5c77ba1 | 1 | #include "wilc_wfi_cfgoperations.h" |
c5c77ba1 JK |
2 | #include "wilc_wlan_if.h" |
3 | #include "wilc_wlan.h" | |
c5c77ba1 JK |
4 | |
5 | #include <linux/slab.h> | |
6 | #include <linux/sched.h> | |
7 | #include <linux/delay.h> | |
8 | #include <linux/workqueue.h> | |
9 | #include <linux/interrupt.h> | |
10 | #include <linux/irq.h> | |
f1a99830 | 11 | #include <linux/gpio.h> |
c5c77ba1 JK |
12 | |
13 | #include <linux/kthread.h> | |
14 | #include <linux/firmware.h> | |
c5c77ba1 JK |
15 | |
16 | #include <linux/init.h> | |
17 | #include <linux/netdevice.h> | |
c5c77ba1 | 18 | #include <linux/inetdevice.h> |
c5c77ba1 JK |
19 | #include <linux/etherdevice.h> |
20 | #include <linux/module.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/skbuff.h> | |
334bed08 | 23 | #include <linux/mutex.h> |
c5c77ba1 | 24 | #include <linux/semaphore.h> |
11a54b3f | 25 | #include <linux/completion.h> |
c5c77ba1 | 26 | |
885cabcc LK |
27 | static int dev_state_ev_handler(struct notifier_block *this, |
28 | unsigned long event, void *ptr); | |
c5c77ba1 JK |
29 | |
30 | static struct notifier_block g_dev_notifier = { | |
31 | .notifier_call = dev_state_ev_handler | |
32 | }; | |
c5c77ba1 | 33 | |
7c67c053 | 34 | static int wlan_deinit_locks(struct net_device *dev); |
32dd51bc | 35 | static void wlan_deinitialize_threads(struct net_device *dev); |
c5c77ba1 | 36 | |
c5c77ba1 | 37 | static void linux_wlan_tx_complete(void *priv, int status); |
c5c77ba1 | 38 | static int mac_init_fn(struct net_device *ndev); |
c5c77ba1 JK |
39 | static struct net_device_stats *mac_stats(struct net_device *dev); |
40 | static int mac_ioctl(struct net_device *ndev, struct ifreq *req, int cmd); | |
41 | static void wilc_set_multicast_list(struct net_device *dev); | |
750ffe9b | 42 | |
0e1af73d | 43 | bool wilc_enable_ps = true; |
c5c77ba1 | 44 | |
c5c77ba1 JK |
45 | static const struct net_device_ops wilc_netdev_ops = { |
46 | .ndo_init = mac_init_fn, | |
0e1af73d AB |
47 | .ndo_open = wilc_mac_open, |
48 | .ndo_stop = wilc_mac_close, | |
49 | .ndo_start_xmit = wilc_mac_xmit, | |
c5c77ba1 JK |
50 | .ndo_do_ioctl = mac_ioctl, |
51 | .ndo_get_stats = mac_stats, | |
52 | .ndo_set_rx_mode = wilc_set_multicast_list, | |
53 | ||
54 | }; | |
c5c77ba1 | 55 | |
885cabcc LK |
56 | static int dev_state_ev_handler(struct notifier_block *this, |
57 | unsigned long event, void *ptr) | |
c5c77ba1 | 58 | { |
d5c89442 | 59 | struct in_ifaddr *dev_iface = ptr; |
2726887c | 60 | struct wilc_priv *priv; |
f24374aa | 61 | struct host_if_drv *hif_drv; |
c5c77ba1 | 62 | struct net_device *dev; |
eac3e8f6 | 63 | u8 *ip_addr_buf; |
a4cac481 | 64 | struct wilc_vif *vif; |
63d03e47 | 65 | u8 null_ip[4] = {0}; |
c5c77ba1 JK |
66 | char wlan_dev_name[5] = "wlan0"; |
67 | ||
1408603c | 68 | if (!dev_iface || !dev_iface->ifa_dev || !dev_iface->ifa_dev->dev) |
c5c77ba1 | 69 | return NOTIFY_DONE; |
c5c77ba1 | 70 | |
582f8a27 | 71 | if (memcmp(dev_iface->ifa_label, "wlan0", 5) && |
1408603c | 72 | memcmp(dev_iface->ifa_label, "p2p0", 4)) |
c5c77ba1 | 73 | return NOTIFY_DONE; |
c5c77ba1 JK |
74 | |
75 | dev = (struct net_device *)dev_iface->ifa_dev->dev; | |
1408603c | 76 | if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) |
c5c77ba1 | 77 | return NOTIFY_DONE; |
1408603c | 78 | |
c5c77ba1 | 79 | priv = wiphy_priv(dev->ieee80211_ptr->wiphy); |
1408603c | 80 | if (!priv) |
c5c77ba1 | 81 | return NOTIFY_DONE; |
1408603c | 82 | |
48b28df9 | 83 | hif_drv = (struct host_if_drv *)priv->hif_drv; |
a4cac481 | 84 | vif = netdev_priv(dev); |
1408603c | 85 | if (!vif || !hif_drv) |
c5c77ba1 | 86 | return NOTIFY_DONE; |
c5c77ba1 JK |
87 | |
88 | switch (event) { | |
89 | case NETDEV_UP: | |
a4cac481 | 90 | if (vif->iftype == STATION_MODE || vif->iftype == CLIENT_MODE) { |
f24374aa | 91 | hif_drv->IFC_UP = 1; |
0e1af73d AB |
92 | wilc_optaining_ip = false; |
93 | del_timer(&wilc_during_ip_timer); | |
c5c77ba1 JK |
94 | } |
95 | ||
0e1af73d | 96 | if (wilc_enable_ps) |
fbf5379b | 97 | wilc_set_power_mgmt(vif, 1, 0); |
c5c77ba1 | 98 | |
5ac24427 | 99 | netdev_dbg(dev, "[%s] Up IP\n", dev_iface->ifa_label); |
c5c77ba1 | 100 | |
eac3e8f6 | 101 | ip_addr_buf = (char *)&dev_iface->ifa_address; |
5ac24427 LK |
102 | netdev_dbg(dev, "IP add=%d:%d:%d:%d\n", |
103 | ip_addr_buf[0], ip_addr_buf[1], | |
104 | ip_addr_buf[2], ip_addr_buf[3]); | |
6750140d | 105 | wilc_setup_ipaddress(vif, ip_addr_buf, vif->idx); |
c5c77ba1 JK |
106 | |
107 | break; | |
108 | ||
109 | case NETDEV_DOWN: | |
a4cac481 | 110 | if (vif->iftype == STATION_MODE || vif->iftype == CLIENT_MODE) { |
f24374aa | 111 | hif_drv->IFC_UP = 0; |
0e1af73d | 112 | wilc_optaining_ip = false; |
c5c77ba1 JK |
113 | } |
114 | ||
115 | if (memcmp(dev_iface->ifa_label, wlan_dev_name, 5) == 0) | |
fbf5379b | 116 | wilc_set_power_mgmt(vif, 0, 0); |
c5c77ba1 | 117 | |
fbf5379b | 118 | wilc_resolve_disconnect_aberration(vif); |
c5c77ba1 | 119 | |
5ac24427 | 120 | netdev_dbg(dev, "[%s] Down IP\n", dev_iface->ifa_label); |
c5c77ba1 | 121 | |
eac3e8f6 | 122 | ip_addr_buf = null_ip; |
5ac24427 LK |
123 | netdev_dbg(dev, "IP add=%d:%d:%d:%d\n", |
124 | ip_addr_buf[0], ip_addr_buf[1], | |
125 | ip_addr_buf[2], ip_addr_buf[3]); | |
c5c77ba1 | 126 | |
6750140d | 127 | wilc_setup_ipaddress(vif, ip_addr_buf, vif->idx); |
c5c77ba1 JK |
128 | |
129 | break; | |
130 | ||
131 | default: | |
c5c77ba1 JK |
132 | break; |
133 | } | |
134 | ||
135 | return NOTIFY_DONE; | |
c5c77ba1 | 136 | } |
c5c77ba1 | 137 | |
c5c77ba1 JK |
138 | static irqreturn_t isr_uh_routine(int irq, void *user_data) |
139 | { | |
a4cac481 | 140 | struct wilc_vif *vif; |
3948362d | 141 | struct wilc *wilc; |
d5c89442 | 142 | struct net_device *dev = user_data; |
3948362d | 143 | |
a4cac481 GL |
144 | vif = netdev_priv(dev); |
145 | wilc = vif->wilc; | |
c5c77ba1 | 146 | |
3948362d | 147 | if (wilc->close) { |
5ac24427 | 148 | netdev_err(dev, "Can't handle UH interrupt\n"); |
c5c77ba1 | 149 | return IRQ_HANDLED; |
c5c77ba1 | 150 | } |
c5c77ba1 | 151 | return IRQ_WAKE_THREAD; |
c5c77ba1 | 152 | } |
c5c77ba1 | 153 | |
1608c403 | 154 | static irqreturn_t isr_bh_routine(int irq, void *userdata) |
c5c77ba1 | 155 | { |
a4cac481 | 156 | struct wilc_vif *vif; |
2e7933d0 | 157 | struct wilc *wilc; |
d5c89442 | 158 | struct net_device *dev = userdata; |
2e7933d0 | 159 | |
a4cac481 GL |
160 | vif = netdev_priv(userdata); |
161 | wilc = vif->wilc; | |
2e7933d0 | 162 | |
2e7933d0 | 163 | if (wilc->close) { |
5ac24427 | 164 | netdev_err(dev, "Can't handle BH interrupt\n"); |
c5c77ba1 | 165 | return IRQ_HANDLED; |
c5c77ba1 JK |
166 | } |
167 | ||
50b929e0 | 168 | wilc_handle_isr(wilc); |
c5c77ba1 | 169 | |
c5c77ba1 | 170 | return IRQ_HANDLED; |
c5c77ba1 | 171 | } |
c5c77ba1 | 172 | |
2c1d05d1 | 173 | static int init_irq(struct net_device *dev) |
c5c77ba1 JK |
174 | { |
175 | int ret = 0; | |
a4cac481 | 176 | struct wilc_vif *vif; |
2c1d05d1 GL |
177 | struct wilc *wl; |
178 | ||
a4cac481 GL |
179 | vif = netdev_priv(dev); |
180 | wl = vif->wilc; | |
c5c77ba1 | 181 | |
c4d139cb AB |
182 | if ((gpio_request(wl->gpio, "WILC_INTR") == 0) && |
183 | (gpio_direction_input(wl->gpio) == 0)) { | |
184 | wl->dev_irq_num = gpio_to_irq(wl->gpio); | |
c5c77ba1 JK |
185 | } else { |
186 | ret = -1; | |
5ac24427 | 187 | netdev_err(dev, "could not obtain gpio for WILC_INTR\n"); |
c5c77ba1 JK |
188 | } |
189 | ||
83231b72 LK |
190 | if (ret != -1 && request_threaded_irq(wl->dev_irq_num, |
191 | isr_uh_routine, | |
192 | isr_bh_routine, | |
193 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | |
194 | "WILC_IRQ", dev) < 0) { | |
5ac24427 | 195 | netdev_err(dev, "Failed to request IRQ GPIO: %d\n", wl->gpio); |
c4d139cb | 196 | gpio_free(wl->gpio); |
c5c77ba1 JK |
197 | ret = -1; |
198 | } else { | |
5ac24427 LK |
199 | netdev_dbg(dev, |
200 | "IRQ request succeeded IRQ-NUM= %d on GPIO: %d\n", | |
201 | wl->dev_irq_num, wl->gpio); | |
c5c77ba1 JK |
202 | } |
203 | ||
204 | return ret; | |
205 | } | |
c5c77ba1 | 206 | |
ec5cc750 | 207 | static void deinit_irq(struct net_device *dev) |
c5c77ba1 | 208 | { |
a4cac481 | 209 | struct wilc_vif *vif; |
ec5cc750 GL |
210 | struct wilc *wilc; |
211 | ||
a4cac481 GL |
212 | vif = netdev_priv(dev); |
213 | wilc = vif->wilc; | |
ec5cc750 | 214 | |
c4d139cb AB |
215 | /* Deintialize IRQ */ |
216 | if (wilc->dev_irq_num) { | |
ec5cc750 | 217 | free_irq(wilc->dev_irq_num, wilc); |
c4d139cb | 218 | gpio_free(wilc->gpio); |
c5c77ba1 | 219 | } |
c5c77ba1 JK |
220 | } |
221 | ||
562ed3f1 | 222 | int wilc_lock_timeout(struct wilc *nic, void *vp, u32 timeout) |
c5c77ba1 | 223 | { |
562ed3f1 | 224 | /* FIXME: replace with mutex_lock or wait_for_completion */ |
c5c77ba1 | 225 | int error = -1; |
8dfaafd6 | 226 | |
3a147c07 | 227 | if (vp) |
d5c89442 | 228 | error = down_timeout(vp, |
582f8a27 | 229 | msecs_to_jiffies(timeout)); |
c5c77ba1 JK |
230 | return error; |
231 | } | |
232 | ||
562ed3f1 | 233 | void wilc_mac_indicate(struct wilc *wilc, int flag) |
c5c77ba1 | 234 | { |
c5c77ba1 JK |
235 | int status; |
236 | ||
237 | if (flag == WILC_MAC_INDICATE_STATUS) { | |
582f8a27 LK |
238 | wilc_wlan_cfg_get_val(WID_STATUS, |
239 | (unsigned char *)&status, 4); | |
64f2b71b GL |
240 | if (wilc->mac_status == WILC_MAC_STATUS_INIT) { |
241 | wilc->mac_status = status; | |
04247e7d | 242 | complete(&wilc->sync_event); |
c5c77ba1 | 243 | } else { |
64f2b71b | 244 | wilc->mac_status = status; |
c5c77ba1 | 245 | } |
c5c77ba1 | 246 | } |
c5c77ba1 JK |
247 | } |
248 | ||
1608c403 | 249 | static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header) |
c5c77ba1 | 250 | { |
660786ea | 251 | u8 *bssid, *bssid1; |
c5c77ba1 JK |
252 | int i = 0; |
253 | ||
d239222e | 254 | bssid = mac_header + 10; |
660786ea | 255 | bssid1 = mac_header + 4; |
c5c77ba1 | 256 | |
ba615f1e GL |
257 | for (i = 0; i < wilc->vif_num; i++) { |
258 | if (wilc->vif[i]->mode == STATION_MODE) | |
fa633941 LK |
259 | if (ether_addr_equal_unaligned(bssid, |
260 | wilc->vif[i]->bssid)) | |
ba615f1e GL |
261 | return wilc->vif[i]->ndev; |
262 | if (wilc->vif[i]->mode == AP_MODE) | |
fa633941 LK |
263 | if (ether_addr_equal_unaligned(bssid1, |
264 | wilc->vif[i]->bssid)) | |
ba615f1e GL |
265 | return wilc->vif[i]->ndev; |
266 | } | |
8259a53e | 267 | |
c5c77ba1 JK |
268 | return NULL; |
269 | } | |
270 | ||
ba615f1e | 271 | int wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode) |
c5c77ba1 JK |
272 | { |
273 | int i = 0; | |
274 | int ret = -1; | |
a4cac481 | 275 | struct wilc_vif *vif; |
472791a9 | 276 | struct wilc *wilc; |
c5c77ba1 | 277 | |
a4cac481 GL |
278 | vif = netdev_priv(wilc_netdev); |
279 | wilc = vif->wilc; | |
472791a9 GL |
280 | |
281 | for (i = 0; i < wilc->vif_num; i++) | |
1f435d2e GL |
282 | if (wilc->vif[i]->ndev == wilc_netdev) { |
283 | memcpy(wilc->vif[i]->bssid, bssid, 6); | |
ba615f1e | 284 | wilc->vif[i]->mode = mode; |
c5c77ba1 JK |
285 | ret = 0; |
286 | break; | |
287 | } | |
8259a53e | 288 | |
c5c77ba1 JK |
289 | return ret; |
290 | } | |
291 | ||
562ed3f1 | 292 | int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) |
c5c77ba1 | 293 | { |
51e825f7 CL |
294 | u8 i = 0; |
295 | u8 null_bssid[6] = {0}; | |
296 | u8 ret_val = 0; | |
c5c77ba1 | 297 | |
562ed3f1 | 298 | for (i = 0; i < wilc->vif_num; i++) |
1f435d2e | 299 | if (memcmp(wilc->vif[i]->bssid, null_bssid, 6)) |
c5c77ba1 | 300 | ret_val++; |
8259a53e | 301 | |
c5c77ba1 JK |
302 | return ret_val; |
303 | } | |
304 | ||
c5c77ba1 JK |
305 | static int linux_wlan_txq_task(void *vp) |
306 | { | |
307 | int ret, txq_count; | |
a4cac481 | 308 | struct wilc_vif *vif; |
88687584 GL |
309 | struct wilc *wl; |
310 | struct net_device *dev = vp; | |
c5c77ba1 | 311 | |
a4cac481 GL |
312 | vif = netdev_priv(dev); |
313 | wl = vif->wilc; | |
88687584 | 314 | |
11a54b3f | 315 | complete(&wl->txq_thread_started); |
c5c77ba1 | 316 | while (1) { |
b27a6d5e | 317 | wait_for_completion(&wl->txq_event); |
c5c77ba1 | 318 | |
88687584 | 319 | if (wl->close) { |
11a54b3f | 320 | complete(&wl->txq_thread_started); |
c5c77ba1 JK |
321 | |
322 | while (!kthread_should_stop()) | |
323 | schedule(); | |
c5c77ba1 JK |
324 | break; |
325 | } | |
c5c77ba1 | 326 | do { |
a1332cad | 327 | ret = wilc_wlan_handle_txq(dev, &txq_count); |
98b89847 | 328 | if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) { |
1f435d2e GL |
329 | if (netif_queue_stopped(wl->vif[0]->ndev)) |
330 | netif_wake_queue(wl->vif[0]->ndev); | |
331 | if (netif_queue_stopped(wl->vif[1]->ndev)) | |
332 | netif_wake_queue(wl->vif[1]->ndev); | |
c5c77ba1 | 333 | } |
98b89847 | 334 | } while (ret == WILC_TX_ERR_NO_BUF && !wl->close); |
c5c77ba1 JK |
335 | } |
336 | return 0; | |
337 | } | |
338 | ||
0e1af73d | 339 | int wilc_wlan_get_firmware(struct net_device *dev) |
c5c77ba1 | 340 | { |
a4cac481 | 341 | struct wilc_vif *vif; |
65c8adcf | 342 | struct wilc *wilc; |
14823bf2 | 343 | int chip_id, ret = 0; |
c5c77ba1 JK |
344 | const struct firmware *wilc_firmware; |
345 | char *firmware; | |
346 | ||
a4cac481 GL |
347 | vif = netdev_priv(dev); |
348 | wilc = vif->wilc; | |
65c8adcf | 349 | |
65c3f000 | 350 | chip_id = wilc_get_chipid(wilc, false); |
14823bf2 GL |
351 | |
352 | if (chip_id < 0x1003a0) | |
353 | firmware = FIRMWARE_1002; | |
354 | else | |
355 | firmware = FIRMWARE_1003; | |
356 | ||
357 | netdev_info(dev, "loading firmware %s\n", firmware); | |
c5c77ba1 | 358 | |
1408603c | 359 | if (!(&vif->ndev->dev)) |
c5c77ba1 | 360 | goto _fail_; |
c5c77ba1 | 361 | |
b03314e2 | 362 | if (request_firmware(&wilc_firmware, firmware, wilc->dev) != 0) { |
b9811891 | 363 | netdev_err(dev, "%s - firmware not available\n", firmware); |
c5c77ba1 JK |
364 | ret = -1; |
365 | goto _fail_; | |
366 | } | |
65c8adcf | 367 | wilc->firmware = wilc_firmware; |
c5c77ba1 JK |
368 | |
369 | _fail_: | |
370 | ||
371 | return ret; | |
c5c77ba1 JK |
372 | } |
373 | ||
9bf3d727 | 374 | static int linux_wlan_start_firmware(struct net_device *dev) |
c5c77ba1 | 375 | { |
a4cac481 | 376 | struct wilc_vif *vif; |
9bf3d727 | 377 | struct wilc *wilc; |
c5c77ba1 | 378 | int ret = 0; |
9bf3d727 | 379 | |
a4cac481 GL |
380 | vif = netdev_priv(dev); |
381 | wilc = vif->wilc; | |
9bf3d727 | 382 | |
562ed3f1 | 383 | ret = wilc_wlan_start(wilc); |
1408603c | 384 | if (ret < 0) |
0aeea1ad | 385 | return ret; |
c5c77ba1 | 386 | |
04247e7d BJ |
387 | if (!wait_for_completion_timeout(&wilc->sync_event, |
388 | msecs_to_jiffies(5000))) | |
389 | return -ETIME; | |
c5c77ba1 | 390 | |
0aeea1ad | 391 | return 0; |
c5c77ba1 | 392 | } |
a40b22c5 | 393 | |
562ed3f1 | 394 | static int wilc1000_firmware_download(struct net_device *dev) |
c5c77ba1 | 395 | { |
a4cac481 | 396 | struct wilc_vif *vif; |
ed760b67 | 397 | struct wilc *wilc; |
c5c77ba1 JK |
398 | int ret = 0; |
399 | ||
a4cac481 GL |
400 | vif = netdev_priv(dev); |
401 | wilc = vif->wilc; | |
ed760b67 GL |
402 | |
403 | if (!wilc->firmware) { | |
5ac24427 | 404 | netdev_err(dev, "Firmware buffer is NULL\n"); |
14b1821d | 405 | return -ENOBUFS; |
c5c77ba1 | 406 | } |
1408603c | 407 | |
562ed3f1 | 408 | ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data, |
ed760b67 | 409 | wilc->firmware->size); |
fc4b95d6 | 410 | if (ret < 0) |
14b1821d | 411 | return ret; |
c5c77ba1 | 412 | |
ed760b67 | 413 | release_firmware(wilc->firmware); |
6f72ed75 | 414 | wilc->firmware = NULL; |
c5c77ba1 | 415 | |
5ac24427 | 416 | netdev_dbg(dev, "Download Succeeded\n"); |
c5c77ba1 | 417 | |
14b1821d | 418 | return 0; |
c5c77ba1 JK |
419 | } |
420 | ||
00215dde | 421 | static int linux_wlan_init_test_config(struct net_device *dev, |
b8f6ca0b | 422 | struct wilc_vif *vif) |
c5c77ba1 | 423 | { |
c5c77ba1 | 424 | unsigned char c_val[64]; |
b8f6ca0b | 425 | struct wilc *wilc = vif->wilc; |
2726887c | 426 | struct wilc_priv *priv; |
0fa66c71 | 427 | struct host_if_drv *hif_drv; |
c5c77ba1 | 428 | |
5ac24427 | 429 | netdev_dbg(dev, "Start configuring Firmware\n"); |
c5c77ba1 | 430 | priv = wiphy_priv(dev->ieee80211_ptr->wiphy); |
48b28df9 | 431 | hif_drv = (struct host_if_drv *)priv->hif_drv; |
5ac24427 | 432 | netdev_dbg(dev, "Host = %p\n", hif_drv); |
65c3f000 | 433 | wilc_get_chipid(wilc, false); |
c5c77ba1 | 434 | |
e5d57e91 | 435 | *(int *)c_val = 1; |
c5c77ba1 | 436 | |
79df6a49 | 437 | if (!wilc_wlan_cfg_set(vif, 1, WID_SET_DRV_HANDLER, c_val, 4, 0, 0)) |
c5c77ba1 JK |
438 | goto _fail_; |
439 | ||
c5c77ba1 | 440 | c_val[0] = 0; |
79df6a49 | 441 | if (!wilc_wlan_cfg_set(vif, 0, WID_PC_TEST_MODE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
442 | goto _fail_; |
443 | ||
444 | c_val[0] = INFRASTRUCTURE; | |
79df6a49 | 445 | if (!wilc_wlan_cfg_set(vif, 0, WID_BSS_TYPE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
446 | goto _fail_; |
447 | ||
c5c77ba1 | 448 | c_val[0] = RATE_AUTO; |
79df6a49 | 449 | if (!wilc_wlan_cfg_set(vif, 0, WID_CURRENT_TX_RATE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
450 | goto _fail_; |
451 | ||
452 | c_val[0] = G_MIXED_11B_2_MODE; | |
79df6a49 | 453 | if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, c_val, 1, 0, |
89758e13 | 454 | 0)) |
c5c77ba1 JK |
455 | goto _fail_; |
456 | ||
457 | c_val[0] = 1; | |
79df6a49 | 458 | if (!wilc_wlan_cfg_set(vif, 0, WID_CURRENT_CHANNEL, c_val, 1, 0, 0)) |
c5c77ba1 JK |
459 | goto _fail_; |
460 | ||
461 | c_val[0] = G_SHORT_PREAMBLE; | |
79df6a49 | 462 | if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
463 | goto _fail_; |
464 | ||
465 | c_val[0] = AUTO_PROT; | |
79df6a49 | 466 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_PROT_MECH, c_val, 1, 0, 0)) |
c5c77ba1 JK |
467 | goto _fail_; |
468 | ||
c5c77ba1 | 469 | c_val[0] = ACTIVE_SCAN; |
79df6a49 | 470 | if (!wilc_wlan_cfg_set(vif, 0, WID_SCAN_TYPE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
471 | goto _fail_; |
472 | ||
473 | c_val[0] = SITE_SURVEY_OFF; | |
79df6a49 | 474 | if (!wilc_wlan_cfg_set(vif, 0, WID_SITE_SURVEY, c_val, 1, 0, 0)) |
c5c77ba1 JK |
475 | goto _fail_; |
476 | ||
98b89847 | 477 | *((int *)c_val) = 0xffff; |
79df6a49 | 478 | if (!wilc_wlan_cfg_set(vif, 0, WID_RTS_THRESHOLD, c_val, 2, 0, 0)) |
c5c77ba1 JK |
479 | goto _fail_; |
480 | ||
481 | *((int *)c_val) = 2346; | |
79df6a49 | 482 | if (!wilc_wlan_cfg_set(vif, 0, WID_FRAG_THRESHOLD, c_val, 2, 0, 0)) |
c5c77ba1 JK |
483 | goto _fail_; |
484 | ||
c5c77ba1 | 485 | c_val[0] = 0; |
79df6a49 | 486 | if (!wilc_wlan_cfg_set(vif, 0, WID_BCAST_SSID, c_val, 1, 0, 0)) |
c5c77ba1 JK |
487 | goto _fail_; |
488 | ||
489 | c_val[0] = 1; | |
79df6a49 | 490 | if (!wilc_wlan_cfg_set(vif, 0, WID_QOS_ENABLE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
491 | goto _fail_; |
492 | ||
493 | c_val[0] = NO_POWERSAVE; | |
79df6a49 | 494 | if (!wilc_wlan_cfg_set(vif, 0, WID_POWER_MANAGEMENT, c_val, 1, 0, 0)) |
c5c77ba1 JK |
495 | goto _fail_; |
496 | ||
b4d04c15 | 497 | c_val[0] = NO_SECURITY; /* NO_ENCRYPT, 0x79 */ |
79df6a49 | 498 | if (!wilc_wlan_cfg_set(vif, 0, WID_11I_MODE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
499 | goto _fail_; |
500 | ||
501 | c_val[0] = OPEN_SYSTEM; | |
79df6a49 | 502 | if (!wilc_wlan_cfg_set(vif, 0, WID_AUTH_TYPE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
503 | goto _fail_; |
504 | ||
c5c77ba1 | 505 | strcpy(c_val, "123456790abcdef1234567890"); |
79df6a49 | 506 | if (!wilc_wlan_cfg_set(vif, 0, WID_WEP_KEY_VALUE, c_val, |
89758e13 | 507 | (strlen(c_val) + 1), 0, 0)) |
c5c77ba1 JK |
508 | goto _fail_; |
509 | ||
c5c77ba1 | 510 | strcpy(c_val, "12345678"); |
79df6a49 | 511 | if (!wilc_wlan_cfg_set(vif, 0, WID_11I_PSK, c_val, (strlen(c_val)), 0, |
89758e13 | 512 | 0)) |
c5c77ba1 JK |
513 | goto _fail_; |
514 | ||
c5c77ba1 | 515 | strcpy(c_val, "password"); |
79df6a49 | 516 | if (!wilc_wlan_cfg_set(vif, 0, WID_1X_KEY, c_val, (strlen(c_val) + 1), |
89758e13 | 517 | 0, 0)) |
c5c77ba1 JK |
518 | goto _fail_; |
519 | ||
c5c77ba1 JK |
520 | c_val[0] = 192; |
521 | c_val[1] = 168; | |
522 | c_val[2] = 1; | |
523 | c_val[3] = 112; | |
79df6a49 | 524 | if (!wilc_wlan_cfg_set(vif, 0, WID_1X_SERV_ADDR, c_val, 4, 0, 0)) |
c5c77ba1 JK |
525 | goto _fail_; |
526 | ||
527 | c_val[0] = 3; | |
79df6a49 | 528 | if (!wilc_wlan_cfg_set(vif, 0, WID_LISTEN_INTERVAL, c_val, 1, 0, 0)) |
c5c77ba1 JK |
529 | goto _fail_; |
530 | ||
531 | c_val[0] = 3; | |
79df6a49 | 532 | if (!wilc_wlan_cfg_set(vif, 0, WID_DTIM_PERIOD, c_val, 1, 0, 0)) |
c5c77ba1 JK |
533 | goto _fail_; |
534 | ||
535 | c_val[0] = NORMAL_ACK; | |
79df6a49 | 536 | if (!wilc_wlan_cfg_set(vif, 0, WID_ACK_POLICY, c_val, 1, 0, 0)) |
c5c77ba1 JK |
537 | goto _fail_; |
538 | ||
539 | c_val[0] = 0; | |
79df6a49 | 540 | if (!wilc_wlan_cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, c_val, 1, |
89758e13 | 541 | 0, 0)) |
c5c77ba1 JK |
542 | goto _fail_; |
543 | ||
544 | c_val[0] = 48; | |
79df6a49 | 545 | if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, c_val, 1, 0, |
89758e13 | 546 | 0)) |
c5c77ba1 JK |
547 | goto _fail_; |
548 | ||
549 | c_val[0] = 28; | |
79df6a49 | 550 | if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, c_val, 1, 0, |
89758e13 | 551 | 0)) |
c5c77ba1 JK |
552 | goto _fail_; |
553 | ||
c5c77ba1 | 554 | *((int *)c_val) = 100; |
79df6a49 | 555 | if (!wilc_wlan_cfg_set(vif, 0, WID_BEACON_INTERVAL, c_val, 2, 0, 0)) |
c5c77ba1 JK |
556 | goto _fail_; |
557 | ||
558 | c_val[0] = REKEY_DISABLE; | |
79df6a49 | 559 | if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_POLICY, c_val, 1, 0, 0)) |
c5c77ba1 JK |
560 | goto _fail_; |
561 | ||
c5c77ba1 | 562 | *((int *)c_val) = 84600; |
79df6a49 | 563 | if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PERIOD, c_val, 4, 0, 0)) |
c5c77ba1 JK |
564 | goto _fail_; |
565 | ||
c5c77ba1 | 566 | *((int *)c_val) = 500; |
79df6a49 | 567 | if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, c_val, 4, 0, |
89758e13 | 568 | 0)) |
c5c77ba1 JK |
569 | goto _fail_; |
570 | ||
571 | c_val[0] = 1; | |
79df6a49 | 572 | if (!wilc_wlan_cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, c_val, 1, 0, |
89758e13 | 573 | 0)) |
c5c77ba1 JK |
574 | goto _fail_; |
575 | ||
576 | c_val[0] = G_SELF_CTS_PROT; | |
79df6a49 | 577 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
578 | goto _fail_; |
579 | ||
98b89847 | 580 | c_val[0] = 1; |
79df6a49 | 581 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ENABLE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
582 | goto _fail_; |
583 | ||
584 | c_val[0] = HT_MIXED_MODE; | |
79df6a49 | 585 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OPERATING_MODE, c_val, 1, 0, |
89758e13 | 586 | 0)) |
c5c77ba1 JK |
587 | goto _fail_; |
588 | ||
98b89847 | 589 | c_val[0] = 1; |
79df6a49 | 590 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, c_val, 1, 0, |
89758e13 | 591 | 0)) |
c5c77ba1 JK |
592 | goto _fail_; |
593 | ||
c5c77ba1 | 594 | c_val[0] = DETECT_PROTECT_REPORT; |
79df6a49 | 595 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, c_val, 1, |
89758e13 | 596 | 0, 0)) |
c5c77ba1 JK |
597 | goto _fail_; |
598 | ||
599 | c_val[0] = RTS_CTS_NONHT_PROT; | |
79df6a49 | 600 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
601 | goto _fail_; |
602 | ||
603 | c_val[0] = 0; | |
79df6a49 | 604 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, c_val, 1, 0, |
89758e13 | 605 | 0)) |
c5c77ba1 JK |
606 | goto _fail_; |
607 | ||
608 | c_val[0] = MIMO_MODE; | |
79df6a49 | 609 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_SMPS_MODE, c_val, 1, 0, 0)) |
c5c77ba1 JK |
610 | goto _fail_; |
611 | ||
612 | c_val[0] = 7; | |
79df6a49 | 613 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, c_val, 1, 0, |
89758e13 | 614 | 0)) |
c5c77ba1 JK |
615 | goto _fail_; |
616 | ||
98b89847 | 617 | c_val[0] = 1; |
79df6a49 | 618 | if (!wilc_wlan_cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, c_val, 1, |
89758e13 | 619 | 1, 1)) |
c5c77ba1 JK |
620 | goto _fail_; |
621 | ||
622 | return 0; | |
623 | ||
624 | _fail_: | |
625 | return -1; | |
626 | } | |
627 | ||
53dc0cfe | 628 | void wilc1000_wlan_deinit(struct net_device *dev) |
c5c77ba1 | 629 | { |
a4cac481 | 630 | struct wilc_vif *vif; |
53dc0cfe | 631 | struct wilc *wl; |
c5c77ba1 | 632 | |
a4cac481 GL |
633 | vif = netdev_priv(dev); |
634 | wl = vif->wilc; | |
53dc0cfe GL |
635 | |
636 | if (!wl) { | |
637 | netdev_err(dev, "wl is NULL\n"); | |
638 | return; | |
639 | } | |
640 | ||
641 | if (wl->initialized) { | |
642 | netdev_info(dev, "Deinitializing wilc1000...\n"); | |
c5c77ba1 | 643 | |
5547c1f0 | 644 | if (!wl->dev_irq_num && |
af9ae09a | 645 | wl->hif_func->disable_interrupt) { |
c4d139cb | 646 | mutex_lock(&wl->hif_cs); |
af9ae09a | 647 | wl->hif_func->disable_interrupt(wl); |
c4d139cb AB |
648 | mutex_unlock(&wl->hif_cs); |
649 | } | |
3a147c07 | 650 | if (&wl->txq_event) |
23535c13 | 651 | complete(&wl->txq_event); |
c5c77ba1 | 652 | |
32dd51bc | 653 | wlan_deinitialize_threads(dev); |
ec5cc750 | 654 | deinit_irq(dev); |
c5c77ba1 | 655 | |
562ed3f1 | 656 | wilc_wlan_stop(wl); |
2de7cbec | 657 | wilc_wlan_cleanup(dev); |
7c67c053 | 658 | wlan_deinit_locks(dev); |
c5c77ba1 | 659 | |
53dc0cfe | 660 | wl->initialized = false; |
c5c77ba1 | 661 | |
5ac24427 | 662 | netdev_dbg(dev, "wilc1000 deinitialization Done\n"); |
c5c77ba1 | 663 | } else { |
5ac24427 | 664 | netdev_dbg(dev, "wilc1000 is not initialized\n"); |
c5c77ba1 | 665 | } |
c5c77ba1 JK |
666 | } |
667 | ||
1608c403 | 668 | static int wlan_init_locks(struct net_device *dev) |
c5c77ba1 | 669 | { |
a4cac481 | 670 | struct wilc_vif *vif; |
38afb390 GL |
671 | struct wilc *wl; |
672 | ||
a4cac481 GL |
673 | vif = netdev_priv(dev); |
674 | wl = vif->wilc; | |
c5c77ba1 | 675 | |
38afb390 GL |
676 | mutex_init(&wl->hif_cs); |
677 | mutex_init(&wl->rxq_cs); | |
c5c77ba1 | 678 | |
38afb390 | 679 | spin_lock_init(&wl->txq_spinlock); |
334bed08 | 680 | mutex_init(&wl->txq_add_to_head_cs); |
c5c77ba1 | 681 | |
b27a6d5e | 682 | init_completion(&wl->txq_event); |
c5c77ba1 | 683 | |
fa659698 | 684 | init_completion(&wl->cfg_event); |
04247e7d | 685 | init_completion(&wl->sync_event); |
11a54b3f | 686 | init_completion(&wl->txq_thread_started); |
c5c77ba1 | 687 | |
c5c77ba1 JK |
688 | return 0; |
689 | } | |
690 | ||
7c67c053 | 691 | static int wlan_deinit_locks(struct net_device *dev) |
c5c77ba1 | 692 | { |
a4cac481 | 693 | struct wilc_vif *vif; |
7c67c053 GL |
694 | struct wilc *wilc; |
695 | ||
a4cac481 GL |
696 | vif = netdev_priv(dev); |
697 | wilc = vif->wilc; | |
7c67c053 | 698 | |
3a147c07 | 699 | if (&wilc->hif_cs) |
7c67c053 | 700 | mutex_destroy(&wilc->hif_cs); |
c5c77ba1 | 701 | |
3a147c07 | 702 | if (&wilc->rxq_cs) |
7c67c053 | 703 | mutex_destroy(&wilc->rxq_cs); |
c5c77ba1 | 704 | |
c5c77ba1 JK |
705 | return 0; |
706 | } | |
a40b22c5 | 707 | |
1608c403 | 708 | static int wlan_initialize_threads(struct net_device *dev) |
c5c77ba1 | 709 | { |
a4cac481 | 710 | struct wilc_vif *vif; |
75a94665 | 711 | struct wilc *wilc; |
8dfaafd6 | 712 | |
a4cac481 GL |
713 | vif = netdev_priv(dev); |
714 | wilc = vif->wilc; | |
75a94665 | 715 | |
88687584 | 716 | wilc->txq_thread = kthread_run(linux_wlan_txq_task, (void *)dev, |
75a94665 | 717 | "K_TXQ_TASK"); |
b3e6916d | 718 | if (IS_ERR(wilc->txq_thread)) { |
5ac24427 | 719 | netdev_err(dev, "couldn't create TXQ thread\n"); |
6bc72c5a | 720 | wilc->close = 0; |
b3e6916d | 721 | return PTR_ERR(wilc->txq_thread); |
c5c77ba1 | 722 | } |
11a54b3f | 723 | wait_for_completion(&wilc->txq_thread_started); |
c5c77ba1 JK |
724 | |
725 | return 0; | |
c5c77ba1 JK |
726 | } |
727 | ||
32dd51bc | 728 | static void wlan_deinitialize_threads(struct net_device *dev) |
c5c77ba1 | 729 | { |
a4cac481 | 730 | struct wilc_vif *vif; |
32dd51bc | 731 | struct wilc *wl; |
fa8b23c6 | 732 | |
a4cac481 GL |
733 | vif = netdev_priv(dev); |
734 | wl = vif->wilc; | |
c5c77ba1 | 735 | |
32dd51bc | 736 | wl->close = 1; |
c5c77ba1 | 737 | |
3a147c07 | 738 | if (&wl->txq_event) |
b27a6d5e | 739 | complete(&wl->txq_event); |
c5c77ba1 | 740 | |
3a147c07 | 741 | if (wl->txq_thread) { |
32dd51bc GL |
742 | kthread_stop(wl->txq_thread); |
743 | wl->txq_thread = NULL; | |
c5c77ba1 | 744 | } |
c5c77ba1 JK |
745 | } |
746 | ||
a4cac481 | 747 | int wilc1000_wlan_init(struct net_device *dev, struct wilc_vif *vif) |
c5c77ba1 | 748 | { |
c5c77ba1 | 749 | int ret = 0; |
a4cac481 | 750 | struct wilc *wl = vif->wilc; |
c5c77ba1 | 751 | |
0fa683b6 GL |
752 | if (!wl->initialized) { |
753 | wl->mac_status = WILC_MAC_STATUS_INIT; | |
754 | wl->close = 0; | |
c5c77ba1 | 755 | |
38afb390 | 756 | wlan_init_locks(dev); |
c5c77ba1 | 757 | |
4bd7baf0 | 758 | ret = wilc_wlan_init(dev); |
c5c77ba1 | 759 | if (ret < 0) { |
c5c77ba1 JK |
760 | ret = -EIO; |
761 | goto _fail_locks_; | |
762 | } | |
c5c77ba1 | 763 | |
c4d139cb | 764 | if (wl->gpio >= 0 && init_irq(dev)) { |
c5c77ba1 | 765 | ret = -EIO; |
b46d6882 | 766 | goto _fail_locks_; |
c5c77ba1 | 767 | } |
c5c77ba1 | 768 | |
75a94665 | 769 | ret = wlan_initialize_threads(dev); |
b46d6882 | 770 | if (ret < 0) { |
b46d6882 TC |
771 | ret = -EIO; |
772 | goto _fail_wilc_wlan_; | |
773 | } | |
774 | ||
5547c1f0 | 775 | if (!wl->dev_irq_num && |
af9ae09a GL |
776 | wl->hif_func->enable_interrupt && |
777 | wl->hif_func->enable_interrupt(wl)) { | |
c5c77ba1 JK |
778 | ret = -EIO; |
779 | goto _fail_irq_init_; | |
780 | } | |
c5c77ba1 | 781 | |
0e1af73d | 782 | if (wilc_wlan_get_firmware(dev)) { |
c5c77ba1 JK |
783 | ret = -EIO; |
784 | goto _fail_irq_enable_; | |
785 | } | |
786 | ||
562ed3f1 | 787 | ret = wilc1000_firmware_download(dev); |
c5c77ba1 | 788 | if (ret < 0) { |
c5c77ba1 JK |
789 | ret = -EIO; |
790 | goto _fail_irq_enable_; | |
791 | } | |
792 | ||
9bf3d727 | 793 | ret = linux_wlan_start_firmware(dev); |
c5c77ba1 | 794 | if (ret < 0) { |
c5c77ba1 JK |
795 | ret = -EIO; |
796 | goto _fail_irq_enable_; | |
797 | } | |
798 | ||
79df6a49 | 799 | if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) { |
c5c77ba1 | 800 | int size; |
5ac24427 | 801 | char firmware_ver[20]; |
8dfaafd6 | 802 | |
5ac24427 LK |
803 | size = wilc_wlan_cfg_get_val(WID_FIRMWARE_VERSION, |
804 | firmware_ver, | |
805 | sizeof(firmware_ver)); | |
806 | firmware_ver[size] = '\0'; | |
807 | netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver); | |
c5c77ba1 | 808 | } |
b8f6ca0b | 809 | ret = linux_wlan_init_test_config(dev, vif); |
c5c77ba1 JK |
810 | |
811 | if (ret < 0) { | |
5ac24427 | 812 | netdev_err(dev, "Failed to configure firmware\n"); |
c5c77ba1 JK |
813 | ret = -EIO; |
814 | goto _fail_fw_start_; | |
815 | } | |
816 | ||
0fa683b6 | 817 | wl->initialized = true; |
98b89847 | 818 | return 0; |
c5c77ba1 | 819 | |
c5c77ba1 | 820 | _fail_fw_start_: |
562ed3f1 | 821 | wilc_wlan_stop(wl); |
c5c77ba1 JK |
822 | |
823 | _fail_irq_enable_: | |
5547c1f0 | 824 | if (!wl->dev_irq_num && |
af9ae09a GL |
825 | wl->hif_func->disable_interrupt) |
826 | wl->hif_func->disable_interrupt(wl); | |
c5c77ba1 | 827 | _fail_irq_init_: |
c4d139cb AB |
828 | if (wl->dev_irq_num) |
829 | deinit_irq(dev); | |
c5c77ba1 | 830 | |
32dd51bc | 831 | wlan_deinitialize_threads(dev); |
c5c77ba1 | 832 | _fail_wilc_wlan_: |
2de7cbec | 833 | wilc_wlan_cleanup(dev); |
c5c77ba1 | 834 | _fail_locks_: |
7c67c053 | 835 | wlan_deinit_locks(dev); |
5ac24427 | 836 | netdev_err(dev, "WLAN Iinitialization FAILED\n"); |
c5c77ba1 | 837 | } else { |
5ac24427 | 838 | netdev_dbg(dev, "wilc1000 already initialized\n"); |
c5c77ba1 JK |
839 | } |
840 | return ret; | |
841 | } | |
842 | ||
1608c403 | 843 | static int mac_init_fn(struct net_device *ndev) |
c5c77ba1 | 844 | { |
98b89847 LK |
845 | netif_start_queue(ndev); |
846 | netif_stop_queue(ndev); | |
c5c77ba1 JK |
847 | |
848 | return 0; | |
849 | } | |
c5c77ba1 | 850 | |
0e1af73d | 851 | int wilc_mac_open(struct net_device *ndev) |
c5c77ba1 | 852 | { |
a4cac481 | 853 | struct wilc_vif *vif; |
c5c77ba1 | 854 | |
c5c77ba1 | 855 | unsigned char mac_add[ETH_ALEN] = {0}; |
c5c77ba1 JK |
856 | int ret = 0; |
857 | int i = 0; | |
f3c1366e GL |
858 | struct wilc *wl; |
859 | ||
a4cac481 GL |
860 | vif = netdev_priv(ndev); |
861 | wl = vif->wilc; | |
c5c77ba1 | 862 | |
40095ad9 | 863 | if (!wl || !wl->dev) { |
90fd4cc5 | 864 | netdev_err(ndev, "device not ready\n"); |
7d05652c CG |
865 | return -ENODEV; |
866 | } | |
b03314e2 | 867 | |
5ac24427 | 868 | netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev); |
c5c77ba1 | 869 | |
dd4b6a83 | 870 | ret = wilc_init_host_int(ndev); |
1408603c | 871 | if (ret < 0) |
c5c77ba1 | 872 | return ret; |
c5c77ba1 | 873 | |
a4cac481 | 874 | ret = wilc1000_wlan_init(ndev, vif); |
c5c77ba1 | 875 | if (ret < 0) { |
a9a16823 | 876 | wilc_deinit_host_int(ndev); |
c5c77ba1 JK |
877 | return ret; |
878 | } | |
879 | ||
f3c1366e | 880 | for (i = 0; i < wl->vif_num; i++) { |
1f435d2e | 881 | if (ndev == wl->vif[i]->ndev) { |
b3306865 GL |
882 | if (vif->iftype == AP_MODE) { |
883 | wilc_set_wfi_drv_handler(vif, | |
884 | wilc_get_vif_idx(vif), | |
885 | 0); | |
ba750473 | 886 | } else if (!wilc_wlan_get_num_conn_ifcs(wl)) { |
b3306865 GL |
887 | wilc_set_wfi_drv_handler(vif, |
888 | wilc_get_vif_idx(vif), | |
ba750473 | 889 | wl->open_ifcs); |
b3306865 | 890 | } else { |
ba750473 LK |
891 | if (memcmp(wl->vif[i ^ 1]->bssid, |
892 | wl->vif[i ^ 1]->src_addr, 6)) | |
b3306865 GL |
893 | wilc_set_wfi_drv_handler(vif, |
894 | wilc_get_vif_idx(vif), | |
895 | 0); | |
896 | else | |
897 | wilc_set_wfi_drv_handler(vif, | |
898 | wilc_get_vif_idx(vif), | |
899 | 1); | |
900 | } | |
e32737e9 | 901 | wilc_set_operation_mode(vif, vif->iftype); |
32cee999 GL |
902 | |
903 | wilc_get_mac_address(vif, mac_add); | |
904 | netdev_dbg(ndev, "Mac address: %pM\n", mac_add); | |
905 | memcpy(wl->vif[i]->src_addr, mac_add, ETH_ALEN); | |
906 | ||
c5c77ba1 JK |
907 | break; |
908 | } | |
909 | } | |
910 | ||
1f435d2e | 911 | memcpy(ndev->dev_addr, wl->vif[i]->src_addr, ETH_ALEN); |
c5c77ba1 JK |
912 | |
913 | if (!is_valid_ether_addr(ndev->dev_addr)) { | |
5ac24427 | 914 | netdev_err(ndev, "Wrong MAC address\n"); |
339d244a LK |
915 | wilc_deinit_host_int(ndev); |
916 | wilc1000_wlan_deinit(ndev); | |
917 | return -EINVAL; | |
c5c77ba1 JK |
918 | } |
919 | ||
1006b5c7 GL |
920 | wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy, |
921 | vif->ndev->ieee80211_ptr, | |
340a84ff | 922 | vif->frame_reg[0].type, |
89febb21 | 923 | vif->frame_reg[0].reg); |
1006b5c7 GL |
924 | wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy, |
925 | vif->ndev->ieee80211_ptr, | |
340a84ff | 926 | vif->frame_reg[1].type, |
89febb21 | 927 | vif->frame_reg[1].reg); |
c5c77ba1 | 928 | netif_wake_queue(ndev); |
f3c1366e | 929 | wl->open_ifcs++; |
a4cac481 | 930 | vif->mac_opened = 1; |
c5c77ba1 | 931 | return 0; |
c5c77ba1 | 932 | } |
c5c77ba1 | 933 | |
1608c403 | 934 | static struct net_device_stats *mac_stats(struct net_device *dev) |
c5c77ba1 | 935 | { |
40095ad9 | 936 | struct wilc_vif *vif = netdev_priv(dev); |
c5c77ba1 | 937 | |
a4cac481 | 938 | return &vif->netstats; |
c5c77ba1 JK |
939 | } |
940 | ||
c5c77ba1 JK |
941 | static void wilc_set_multicast_list(struct net_device *dev) |
942 | { | |
c5c77ba1 | 943 | struct netdev_hw_addr *ha; |
cf60106b | 944 | struct wilc_vif *vif; |
c5c77ba1 | 945 | int i = 0; |
8dfaafd6 | 946 | |
cf60106b | 947 | vif = netdev_priv(dev); |
c5c77ba1 | 948 | |
1408603c | 949 | if (dev->flags & IFF_PROMISC) |
c5c77ba1 | 950 | return; |
c5c77ba1 | 951 | |
582f8a27 LK |
952 | if ((dev->flags & IFF_ALLMULTI) || |
953 | (dev->mc.count) > WILC_MULTICAST_TABLE_SIZE) { | |
fbf5379b | 954 | wilc_setup_multicast_filter(vif, false, 0); |
c5c77ba1 JK |
955 | return; |
956 | } | |
957 | ||
c5c77ba1 | 958 | if ((dev->mc.count) == 0) { |
fbf5379b | 959 | wilc_setup_multicast_filter(vif, true, 0); |
c5c77ba1 JK |
960 | return; |
961 | } | |
962 | ||
c8537e6d | 963 | netdev_for_each_mc_addr(ha, dev) { |
0e1af73d | 964 | memcpy(wilc_multicast_mac_addr_list[i], ha->addr, ETH_ALEN); |
5ac24427 LK |
965 | netdev_dbg(dev, "Entry[%d]: %x:%x:%x:%x:%x:%x\n", i, |
966 | wilc_multicast_mac_addr_list[i][0], | |
967 | wilc_multicast_mac_addr_list[i][1], | |
968 | wilc_multicast_mac_addr_list[i][2], | |
969 | wilc_multicast_mac_addr_list[i][3], | |
970 | wilc_multicast_mac_addr_list[i][4], | |
971 | wilc_multicast_mac_addr_list[i][5]); | |
c5c77ba1 JK |
972 | i++; |
973 | } | |
974 | ||
fbf5379b | 975 | wilc_setup_multicast_filter(vif, true, (dev->mc.count)); |
c5c77ba1 JK |
976 | } |
977 | ||
c5c77ba1 JK |
978 | static void linux_wlan_tx_complete(void *priv, int status) |
979 | { | |
d5c89442 | 980 | struct tx_complete_data *pv_data = priv; |
8dfaafd6 | 981 | |
c5c77ba1 | 982 | dev_kfree_skb(pv_data->skb); |
a18dd630 | 983 | kfree(pv_data); |
c5c77ba1 JK |
984 | } |
985 | ||
0e1af73d | 986 | int wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) |
c5c77ba1 | 987 | { |
a4cac481 | 988 | struct wilc_vif *vif; |
c5c77ba1 | 989 | struct tx_complete_data *tx_data = NULL; |
44ec3b75 | 990 | int queue_count; |
fd8f0367 | 991 | char *udp_buf; |
c5c77ba1 JK |
992 | struct iphdr *ih; |
993 | struct ethhdr *eth_h; | |
b7495be5 | 994 | struct wilc *wilc; |
8dfaafd6 | 995 | |
a4cac481 GL |
996 | vif = netdev_priv(ndev); |
997 | wilc = vif->wilc; | |
c5c77ba1 | 998 | |
c5c77ba1 | 999 | if (skb->dev != ndev) { |
5ac24427 | 1000 | netdev_err(ndev, "Packet not destined to this device\n"); |
c5c77ba1 JK |
1001 | return 0; |
1002 | } | |
1003 | ||
b38e9030 | 1004 | tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC); |
3a147c07 | 1005 | if (!tx_data) { |
c5c77ba1 JK |
1006 | dev_kfree_skb(skb); |
1007 | netif_wake_queue(ndev); | |
1008 | return 0; | |
1009 | } | |
1010 | ||
1011 | tx_data->buff = skb->data; | |
1012 | tx_data->size = skb->len; | |
1013 | tx_data->skb = skb; | |
1014 | ||
1015 | eth_h = (struct ethhdr *)(skb->data); | |
fc4b95d6 | 1016 | if (eth_h->h_proto == 0x8e88) |
5ac24427 | 1017 | netdev_dbg(ndev, "EAPOL transmitted\n"); |
c5c77ba1 | 1018 | |
c5c77ba1 JK |
1019 | ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr)); |
1020 | ||
fd8f0367 LK |
1021 | udp_buf = (char *)ih + sizeof(struct iphdr); |
1022 | if ((udp_buf[1] == 68 && udp_buf[3] == 67) || | |
1023 | (udp_buf[1] == 67 && udp_buf[3] == 68)) | |
5ac24427 LK |
1024 | netdev_dbg(ndev, "DHCP Message transmitted, type:%x %x %x\n", |
1025 | udp_buf[248], udp_buf[249], udp_buf[250]); | |
c5c77ba1 | 1026 | |
a4cac481 GL |
1027 | vif->netstats.tx_packets++; |
1028 | vif->netstats.tx_bytes += tx_data->size; | |
6750140d | 1029 | tx_data->bssid = wilc->vif[vif->idx]->bssid; |
44ec3b75 LK |
1030 | queue_count = wilc_wlan_txq_add_net_pkt(ndev, (void *)tx_data, |
1031 | tx_data->buff, tx_data->size, | |
1032 | linux_wlan_tx_complete); | |
c5c77ba1 | 1033 | |
44ec3b75 | 1034 | if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) { |
1f435d2e GL |
1035 | netif_stop_queue(wilc->vif[0]->ndev); |
1036 | netif_stop_queue(wilc->vif[1]->ndev); | |
c5c77ba1 JK |
1037 | } |
1038 | ||
1039 | return 0; | |
1040 | } | |
1041 | ||
0e1af73d | 1042 | int wilc_mac_close(struct net_device *ndev) |
c5c77ba1 | 1043 | { |
2726887c | 1044 | struct wilc_priv *priv; |
a4cac481 | 1045 | struct wilc_vif *vif; |
2db2c8a7 | 1046 | struct host_if_drv *hif_drv; |
ca64ad6e | 1047 | struct wilc *wl; |
c5c77ba1 | 1048 | |
a4cac481 | 1049 | vif = netdev_priv(ndev); |
c5c77ba1 | 1050 | |
1006b5c7 | 1051 | if (!vif || !vif->ndev || !vif->ndev->ieee80211_ptr || |
1408603c | 1052 | !vif->ndev->ieee80211_ptr->wiphy) |
c5c77ba1 | 1053 | return 0; |
c5c77ba1 | 1054 | |
1006b5c7 | 1055 | priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy); |
a4cac481 | 1056 | wl = vif->wilc; |
c5c77ba1 | 1057 | |
1408603c | 1058 | if (!priv) |
c5c77ba1 | 1059 | return 0; |
c5c77ba1 | 1060 | |
48b28df9 | 1061 | hif_drv = (struct host_if_drv *)priv->hif_drv; |
c5c77ba1 | 1062 | |
5ac24427 | 1063 | netdev_dbg(ndev, "Mac close\n"); |
c5c77ba1 | 1064 | |
1408603c | 1065 | if (!wl) |
c5c77ba1 | 1066 | return 0; |
c5c77ba1 | 1067 | |
1408603c | 1068 | if (!hif_drv) |
c5c77ba1 | 1069 | return 0; |
c5c77ba1 | 1070 | |
1408603c | 1071 | if ((wl->open_ifcs) > 0) |
ca64ad6e | 1072 | wl->open_ifcs--; |
1408603c | 1073 | else |
c5c77ba1 | 1074 | return 0; |
c5c77ba1 | 1075 | |
1006b5c7 GL |
1076 | if (vif->ndev) { |
1077 | netif_stop_queue(vif->ndev); | |
c5c77ba1 | 1078 | |
1006b5c7 | 1079 | wilc_deinit_host_int(vif->ndev); |
c5c77ba1 JK |
1080 | } |
1081 | ||
ca64ad6e | 1082 | if (wl->open_ifcs == 0) { |
5ac24427 | 1083 | netdev_dbg(ndev, "Deinitializing wilc1000\n"); |
ca64ad6e | 1084 | wl->close = 1; |
53dc0cfe | 1085 | wilc1000_wlan_deinit(ndev); |
c5c77ba1 | 1086 | WILC_WFI_deinit_mon_interface(); |
c5c77ba1 JK |
1087 | } |
1088 | ||
a4cac481 | 1089 | vif->mac_opened = 0; |
c5c77ba1 JK |
1090 | |
1091 | return 0; | |
1092 | } | |
1093 | ||
1608c403 | 1094 | static int mac_ioctl(struct net_device *ndev, struct ifreq *req, int cmd) |
c5c77ba1 | 1095 | { |
63d03e47 | 1096 | u8 *buff = NULL; |
ca356ada | 1097 | s8 rssi; |
4e4467fd | 1098 | u32 size = 0, length = 0; |
a4cac481 | 1099 | struct wilc_vif *vif; |
9457b05e | 1100 | s32 ret = 0; |
07320b6b | 1101 | struct wilc *wilc; |
c5c77ba1 | 1102 | |
a4cac481 GL |
1103 | vif = netdev_priv(ndev); |
1104 | wilc = vif->wilc; | |
c5c77ba1 | 1105 | |
07320b6b | 1106 | if (!wilc->initialized) |
c5c77ba1 JK |
1107 | return 0; |
1108 | ||
c5c77ba1 | 1109 | switch (cmd) { |
c5c77ba1 JK |
1110 | case SIOCSIWPRIV: |
1111 | { | |
f05ab249 | 1112 | struct iwreq *wrq = (struct iwreq *)req; |
c5c77ba1 JK |
1113 | |
1114 | size = wrq->u.data.length; | |
1115 | ||
1116 | if (size && wrq->u.data.pointer) { | |
582f8a27 LK |
1117 | buff = memdup_user(wrq->u.data.pointer, |
1118 | wrq->u.data.length); | |
360e27a9 SM |
1119 | if (IS_ERR(buff)) |
1120 | return PTR_ERR(buff); | |
c5c77ba1 JK |
1121 | |
1122 | if (strncasecmp(buff, "RSSI", length) == 0) { | |
fbf5379b | 1123 | ret = wilc_get_rssi(vif, &rssi); |
5ac24427 | 1124 | netdev_info(ndev, "RSSI :%d\n", rssi); |
c5c77ba1 | 1125 | |
c5c77ba1 JK |
1126 | rssi += 5; |
1127 | ||
1128 | snprintf(buff, size, "rssi %d", rssi); | |
1129 | ||
1130 | if (copy_to_user(wrq->u.data.pointer, buff, size)) { | |
5ac24427 | 1131 | netdev_err(ndev, "failed to copy\n"); |
9457b05e | 1132 | ret = -EFAULT; |
c5c77ba1 JK |
1133 | goto done; |
1134 | } | |
1135 | } | |
1136 | } | |
1137 | } | |
1138 | break; | |
1139 | ||
1140 | default: | |
1141 | { | |
5ac24427 | 1142 | netdev_info(ndev, "Command - %d - has been received\n", cmd); |
9457b05e | 1143 | ret = -EOPNOTSUPP; |
c5c77ba1 JK |
1144 | goto done; |
1145 | } | |
1146 | } | |
1147 | ||
1148 | done: | |
1149 | ||
642ac6c0 | 1150 | kfree(buff); |
c5c77ba1 | 1151 | |
9457b05e | 1152 | return ret; |
c5c77ba1 JK |
1153 | } |
1154 | ||
562ed3f1 | 1155 | void wilc_frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset) |
c5c77ba1 | 1156 | { |
c5c77ba1 JK |
1157 | unsigned int frame_len = 0; |
1158 | int stats; | |
1159 | unsigned char *buff_to_send = NULL; | |
1160 | struct sk_buff *skb; | |
c5c77ba1 | 1161 | struct net_device *wilc_netdev; |
a4cac481 | 1162 | struct wilc_vif *vif; |
c5c77ba1 | 1163 | |
0953a2e3 LK |
1164 | if (!wilc) |
1165 | return; | |
1166 | ||
7e725b47 | 1167 | wilc_netdev = get_if_handler(wilc, buff); |
3a147c07 | 1168 | if (!wilc_netdev) |
c5c77ba1 JK |
1169 | return; |
1170 | ||
1171 | buff += pkt_offset; | |
a4cac481 | 1172 | vif = netdev_priv(wilc_netdev); |
c5c77ba1 JK |
1173 | |
1174 | if (size > 0) { | |
c5c77ba1 JK |
1175 | frame_len = size; |
1176 | buff_to_send = buff; | |
1177 | ||
c5c77ba1 | 1178 | skb = dev_alloc_skb(frame_len); |
1408603c | 1179 | if (!skb) |
c5c77ba1 | 1180 | return; |
1408603c | 1181 | |
c5c77ba1 JK |
1182 | skb->dev = wilc_netdev; |
1183 | ||
c5c77ba1 JK |
1184 | memcpy(skb_put(skb, frame_len), buff_to_send, frame_len); |
1185 | ||
c5c77ba1 | 1186 | skb->protocol = eth_type_trans(skb, wilc_netdev); |
a4cac481 GL |
1187 | vif->netstats.rx_packets++; |
1188 | vif->netstats.rx_bytes += frame_len; | |
c5c77ba1 JK |
1189 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1190 | stats = netif_rx(skb); | |
5ac24427 | 1191 | netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); |
c5c77ba1 | 1192 | } |
c5c77ba1 JK |
1193 | } |
1194 | ||
11f4b2ee | 1195 | void WILC_WFI_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size) |
c5c77ba1 JK |
1196 | { |
1197 | int i = 0; | |
a4cac481 | 1198 | struct wilc_vif *vif; |
c5c77ba1 | 1199 | |
11f4b2ee | 1200 | for (i = 0; i < wilc->vif_num; i++) { |
1f435d2e | 1201 | vif = netdev_priv(wilc->vif[i]->ndev); |
a4cac481 | 1202 | if (vif->monitor_flag) { |
c5c77ba1 JK |
1203 | WILC_WFI_monitor_rx(buff, size); |
1204 | return; | |
1205 | } | |
1206 | } | |
1207 | ||
1f435d2e | 1208 | vif = netdev_priv(wilc->vif[1]->ndev); |
340a84ff LK |
1209 | if ((buff[0] == vif->frame_reg[0].type && vif->frame_reg[0].reg) || |
1210 | (buff[0] == vif->frame_reg[1].type && vif->frame_reg[1].reg)) | |
1f435d2e | 1211 | WILC_WFI_p2p_rx(wilc->vif[1]->ndev, buff, size); |
c5c77ba1 JK |
1212 | } |
1213 | ||
857c7b00 | 1214 | void wilc_netdev_cleanup(struct wilc *wilc) |
4875c499 TC |
1215 | { |
1216 | int i = 0; | |
a4cac481 | 1217 | struct wilc_vif *vif[NUM_CONCURRENT_IFC]; |
4875c499 | 1218 | |
1f435d2e | 1219 | if (wilc && (wilc->vif[0]->ndev || wilc->vif[1]->ndev)) { |
4875c499 TC |
1220 | unregister_inetaddr_notifier(&g_dev_notifier); |
1221 | ||
1222 | for (i = 0; i < NUM_CONCURRENT_IFC; i++) | |
1f435d2e | 1223 | vif[i] = netdev_priv(wilc->vif[i]->ndev); |
4875c499 TC |
1224 | } |
1225 | ||
3f626cf4 | 1226 | if (wilc && wilc->firmware) { |
90b984c8 | 1227 | release_firmware(wilc->firmware); |
3f626cf4 LK |
1228 | wilc->firmware = NULL; |
1229 | } | |
4875c499 | 1230 | |
1f435d2e | 1231 | if (wilc && (wilc->vif[0]->ndev || wilc->vif[1]->ndev)) { |
4875c499 | 1232 | for (i = 0; i < NUM_CONCURRENT_IFC; i++) |
1f435d2e | 1233 | if (wilc->vif[i]->ndev) |
a4cac481 | 1234 | if (vif[i]->mac_opened) |
1f435d2e | 1235 | wilc_mac_close(wilc->vif[i]->ndev); |
4875c499 TC |
1236 | |
1237 | for (i = 0; i < NUM_CONCURRENT_IFC; i++) { | |
1f435d2e GL |
1238 | unregister_netdev(wilc->vif[i]->ndev); |
1239 | wilc_free_wiphy(wilc->vif[i]->ndev); | |
1240 | free_netdev(wilc->vif[i]->ndev); | |
4875c499 TC |
1241 | } |
1242 | } | |
1243 | ||
90b984c8 | 1244 | kfree(wilc); |
4875c499 | 1245 | } |
750ffe9b | 1246 | EXPORT_SYMBOL_GPL(wilc_netdev_cleanup); |
4875c499 | 1247 | |
7d37a4a1 AB |
1248 | int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type, |
1249 | int gpio, const struct wilc_hif_func *ops) | |
c5c77ba1 | 1250 | { |
fe747f0f | 1251 | int i, ret; |
a4cac481 | 1252 | struct wilc_vif *vif; |
c5c77ba1 | 1253 | struct net_device *ndev; |
562ed3f1 | 1254 | struct wilc *wl; |
c5c77ba1 | 1255 | |
825b966f | 1256 | wl = kzalloc(sizeof(*wl), GFP_KERNEL); |
562ed3f1 | 1257 | if (!wl) |
ac61ef86 | 1258 | return -ENOMEM; |
c5c77ba1 | 1259 | |
562ed3f1 AB |
1260 | *wilc = wl; |
1261 | wl->io_type = io_type; | |
1262 | wl->gpio = gpio; | |
af9ae09a | 1263 | wl->hif_func = ops; |
c4d139cb | 1264 | |
c5c77ba1 | 1265 | register_inetaddr_notifier(&g_dev_notifier); |
c5c77ba1 JK |
1266 | |
1267 | for (i = 0; i < NUM_CONCURRENT_IFC; i++) { | |
a4cac481 | 1268 | ndev = alloc_etherdev(sizeof(struct wilc_vif)); |
1408603c | 1269 | if (!ndev) |
fe747f0f | 1270 | return -ENOMEM; |
c5c77ba1 | 1271 | |
a4cac481 GL |
1272 | vif = netdev_priv(ndev); |
1273 | memset(vif, 0, sizeof(struct wilc_vif)); | |
c5c77ba1 | 1274 | |
84d3b87e | 1275 | if (i == 0) |
c5c77ba1 | 1276 | strcpy(ndev->name, "wlan%d"); |
84d3b87e | 1277 | else |
c5c77ba1 JK |
1278 | strcpy(ndev->name, "p2p%d"); |
1279 | ||
6750140d | 1280 | vif->idx = wl->vif_num; |
a4cac481 | 1281 | vif->wilc = *wilc; |
1f435d2e GL |
1282 | wl->vif[i] = vif; |
1283 | wl->vif[wl->vif_num]->ndev = ndev; | |
562ed3f1 | 1284 | wl->vif_num++; |
e5af0561 | 1285 | ndev->netdev_ops = &wilc_netdev_ops; |
c5c77ba1 | 1286 | |
c5c77ba1 JK |
1287 | { |
1288 | struct wireless_dev *wdev; | |
fa8b23c6 | 1289 | |
2e7d5377 | 1290 | wdev = wilc_create_wiphy(ndev, dev); |
c5c77ba1 | 1291 | |
67039928 AB |
1292 | if (dev) |
1293 | SET_NETDEV_DEV(ndev, dev); | |
c5c77ba1 | 1294 | |
3a147c07 | 1295 | if (!wdev) { |
5ac24427 | 1296 | netdev_err(ndev, "Can't register WILC Wiphy\n"); |
c5c77ba1 JK |
1297 | return -1; |
1298 | } | |
1299 | ||
1006b5c7 GL |
1300 | vif->ndev->ieee80211_ptr = wdev; |
1301 | vif->ndev->ml_priv = vif; | |
1302 | wdev->netdev = vif->ndev; | |
a4cac481 GL |
1303 | vif->netstats.rx_packets = 0; |
1304 | vif->netstats.tx_packets = 0; | |
1305 | vif->netstats.rx_bytes = 0; | |
1306 | vif->netstats.tx_bytes = 0; | |
c5c77ba1 | 1307 | } |
c5c77ba1 | 1308 | |
fe747f0f AKC |
1309 | ret = register_netdev(ndev); |
1310 | if (ret) | |
1311 | return ret; | |
c5c77ba1 | 1312 | |
a4cac481 GL |
1313 | vif->iftype = STATION_MODE; |
1314 | vif->mac_opened = 0; | |
c5c77ba1 JK |
1315 | } |
1316 | ||
c5c77ba1 JK |
1317 | return 0; |
1318 | } | |
750ffe9b | 1319 | EXPORT_SYMBOL_GPL(wilc_netdev_init); |
c94f05ee AB |
1320 | |
1321 | MODULE_LICENSE("GPL"); |