1 | // SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) |
2 | /* |
3 | * |
4 | * Linux Kernel net device interface |
5 | * |
6 | * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. |
7 | * -------------------------------------------------------------------- |
8 | * |
9 | * linux-wlan |
10 | * |
11 | * -------------------------------------------------------------------- |
12 | * |
13 | * Inquiries regarding the linux-wlan Open Source project can be |
14 | * made directly to: |
15 | * |
16 | * AbsoluteValue Systems Inc. |
17 | * info@linux-wlan.com |
18 | * http://www.linux-wlan.com |
19 | * |
20 | * -------------------------------------------------------------------- |
21 | * |
22 | * Portions of the development of this software were funded by |
23 | * Intersil Corporation as part of PRISM(R) chipset product development. |
24 | * |
25 | * -------------------------------------------------------------------- |
26 | * |
27 | * The functions required for a Linux network device are defined here. |
28 | * |
29 | * -------------------------------------------------------------------- |
30 | */ |
31 | |
32 | #include <linux/module.h> |
33 | #include <linux/kernel.h> |
34 | #include <linux/sched.h> |
35 | #include <linux/types.h> |
36 | #include <linux/skbuff.h> |
37 | #include <linux/slab.h> |
38 | #include <linux/proc_fs.h> |
39 | #include <linux/interrupt.h> |
40 | #include <linux/netdevice.h> |
41 | #include <linux/kmod.h> |
42 | #include <linux/if_arp.h> |
43 | #include <linux/wireless.h> |
44 | #include <linux/sockios.h> |
45 | #include <linux/etherdevice.h> |
46 | #include <linux/if_ether.h> |
47 | #include <linux/byteorder/generic.h> |
48 | #include <linux/bitops.h> |
49 | #include <linux/uaccess.h> |
50 | #include <asm/byteorder.h> |
51 | |
52 | #ifdef SIOCETHTOOL |
53 | #include <linux/ethtool.h> |
54 | #endif |
55 | |
56 | #include <net/iw_handler.h> |
57 | #include <net/net_namespace.h> |
58 | #include <net/cfg80211.h> |
59 | |
60 | #include "p80211types.h" |
61 | #include "p80211hdr.h" |
62 | #include "p80211conv.h" |
63 | #include "p80211mgmt.h" |
64 | #include "p80211msg.h" |
65 | #include "p80211netdev.h" |
66 | #include "p80211ioctl.h" |
67 | #include "p80211req.h" |
68 | #include "p80211metastruct.h" |
69 | #include "p80211metadef.h" |
70 | |
71 | #include "cfg80211.c" |
72 | |
73 | /* netdevice method functions */ |
74 | static int p80211knetdev_init(struct net_device *netdev); |
75 | static int p80211knetdev_open(struct net_device *netdev); |
76 | static int p80211knetdev_stop(struct net_device *netdev); |
77 | static netdev_tx_t p80211knetdev_hard_start_xmit(struct sk_buff *skb, |
78 | struct net_device *netdev); |
79 | static void p80211knetdev_set_multicast_list(struct net_device *dev); |
80 | static int p80211knetdev_siocdevprivate(struct net_device *dev, struct ifreq *ifr, |
81 | void __user *data, int cmd); |
82 | static int p80211knetdev_set_mac_address(struct net_device *dev, void *addr); |
83 | static void p80211knetdev_tx_timeout(struct net_device *netdev, unsigned int txqueue); |
84 | static int p80211_rx_typedrop(struct wlandevice *wlandev, u16 fc); |
85 | |
86 | int wlan_watchdog = 5000; |
87 | module_param(wlan_watchdog, int, 0644); |
88 | MODULE_PARM_DESC(wlan_watchdog, "transmit timeout in milliseconds" ); |
89 | |
90 | int wlan_wext_write = 1; |
91 | module_param(wlan_wext_write, int, 0644); |
92 | MODULE_PARM_DESC(wlan_wext_write, "enable write wireless extensions" ); |
93 | |
94 | /*---------------------------------------------------------------- |
95 | * p80211knetdev_init |
96 | * |
97 | * Init method for a Linux netdevice. Called in response to |
98 | * register_netdev. |
99 | * |
100 | * Arguments: |
101 | * none |
102 | * |
103 | * Returns: |
104 | * nothing |
105 | *---------------------------------------------------------------- |
106 | */ |
107 | static int p80211knetdev_init(struct net_device *netdev) |
108 | { |
109 | /* Called in response to register_netdev */ |
110 | /* This is usually the probe function, but the probe has */ |
111 | /* already been done by the MSD and the create_kdev */ |
112 | /* function. All we do here is return success */ |
113 | return 0; |
114 | } |
115 | |
116 | /*---------------------------------------------------------------- |
117 | * p80211knetdev_open |
118 | * |
119 | * Linux netdevice open method. Following a successful call here, |
120 | * the device is supposed to be ready for tx and rx. In our |
121 | * situation that may not be entirely true due to the state of the |
122 | * MAC below. |
123 | * |
124 | * Arguments: |
125 | * netdev Linux network device structure |
126 | * |
127 | * Returns: |
128 | * zero on success, non-zero otherwise |
129 | *---------------------------------------------------------------- |
130 | */ |
131 | static int p80211knetdev_open(struct net_device *netdev) |
132 | { |
133 | int result = 0; /* success */ |
134 | struct wlandevice *wlandev = netdev->ml_priv; |
135 | |
136 | /* Check to make sure the MSD is running */ |
137 | if (wlandev->msdstate != WLAN_MSD_RUNNING) |
138 | return -ENODEV; |
139 | |
140 | /* Tell the MSD to open */ |
141 | if (wlandev->open) { |
142 | result = wlandev->open(wlandev); |
143 | if (result == 0) { |
144 | netif_start_queue(dev: wlandev->netdev); |
145 | wlandev->state = WLAN_DEVICE_OPEN; |
146 | } |
147 | } else { |
148 | result = -EAGAIN; |
149 | } |
150 | |
151 | return result; |
152 | } |
153 | |
154 | /*---------------------------------------------------------------- |
155 | * p80211knetdev_stop |
156 | * |
157 | * Linux netdevice stop (close) method. Following this call, |
158 | * no frames should go up or down through this interface. |
159 | * |
160 | * Arguments: |
161 | * netdev Linux network device structure |
162 | * |
163 | * Returns: |
164 | * zero on success, non-zero otherwise |
165 | *---------------------------------------------------------------- |
166 | */ |
167 | static int p80211knetdev_stop(struct net_device *netdev) |
168 | { |
169 | int result = 0; |
170 | struct wlandevice *wlandev = netdev->ml_priv; |
171 | |
172 | if (wlandev->close) |
173 | result = wlandev->close(wlandev); |
174 | |
175 | netif_stop_queue(dev: wlandev->netdev); |
176 | wlandev->state = WLAN_DEVICE_CLOSED; |
177 | |
178 | return result; |
179 | } |
180 | |
181 | /*---------------------------------------------------------------- |
182 | * p80211netdev_rx |
183 | * |
184 | * Frame receive function called by the mac specific driver. |
185 | * |
186 | * Arguments: |
187 | * wlandev WLAN network device structure |
188 | * skb skbuff containing a full 802.11 frame. |
189 | * Returns: |
190 | * nothing |
191 | * Side effects: |
192 | * |
193 | *---------------------------------------------------------------- |
194 | */ |
195 | void p80211netdev_rx(struct wlandevice *wlandev, struct sk_buff *skb) |
196 | { |
197 | /* Enqueue for post-irq processing */ |
198 | skb_queue_tail(list: &wlandev->nsd_rxq, newsk: skb); |
199 | tasklet_schedule(t: &wlandev->rx_bh); |
200 | } |
201 | |
202 | #define CONV_TO_ETHER_SKIPPED 0x01 |
203 | #define CONV_TO_ETHER_FAILED 0x02 |
204 | |
205 | /** |
206 | * p80211_convert_to_ether - conversion from 802.11 frame to ethernet frame |
207 | * @wlandev: pointer to WLAN device |
208 | * @skb: pointer to socket buffer |
209 | * |
210 | * Returns: 0 if conversion succeeded |
211 | * CONV_TO_ETHER_FAILED if conversion failed |
212 | * CONV_TO_ETHER_SKIPPED if frame is ignored |
213 | */ |
214 | static int p80211_convert_to_ether(struct wlandevice *wlandev, |
215 | struct sk_buff *skb) |
216 | { |
217 | struct p80211_hdr *hdr; |
218 | |
219 | hdr = (struct p80211_hdr *)skb->data; |
220 | if (p80211_rx_typedrop(wlandev, le16_to_cpu(hdr->frame_control))) |
221 | return CONV_TO_ETHER_SKIPPED; |
222 | |
223 | /* perform mcast filtering: allow my local address through but reject |
224 | * anything else that isn't multicast |
225 | */ |
226 | if (wlandev->netdev->flags & IFF_ALLMULTI) { |
227 | if (!ether_addr_equal_unaligned(addr1: wlandev->netdev->dev_addr, |
228 | addr2: hdr->address1)) { |
229 | if (!is_multicast_ether_addr(addr: hdr->address1)) |
230 | return CONV_TO_ETHER_SKIPPED; |
231 | } |
232 | } |
233 | |
234 | if (skb_p80211_to_ether(wlandev, ethconv: wlandev->ethconv, skb) == 0) { |
235 | wlandev->netdev->stats.rx_packets++; |
236 | wlandev->netdev->stats.rx_bytes += skb->len; |
237 | netif_rx(skb); |
238 | return 0; |
239 | } |
240 | |
241 | netdev_dbg(wlandev->netdev, "%s failed.\n" , __func__); |
242 | return CONV_TO_ETHER_FAILED; |
243 | } |
244 | |
245 | /** |
246 | * p80211netdev_rx_bh - deferred processing of all received frames |
247 | * |
248 | * @t: pointer to the tasklet associated with this handler |
249 | */ |
250 | static void p80211netdev_rx_bh(struct tasklet_struct *t) |
251 | { |
252 | struct wlandevice *wlandev = from_tasklet(wlandev, t, rx_bh); |
253 | struct sk_buff *skb = NULL; |
254 | struct net_device *dev = wlandev->netdev; |
255 | |
256 | /* Let's empty our queue */ |
257 | while ((skb = skb_dequeue(list: &wlandev->nsd_rxq))) { |
258 | if (wlandev->state == WLAN_DEVICE_OPEN) { |
259 | if (dev->type != ARPHRD_ETHER) { |
260 | /* RAW frame; we shouldn't convert it */ |
261 | /* XXX Append the Prism Header here instead. */ |
262 | |
263 | /* set up various data fields */ |
264 | skb->dev = dev; |
265 | skb_reset_mac_header(skb); |
266 | skb->ip_summed = CHECKSUM_NONE; |
267 | skb->pkt_type = PACKET_OTHERHOST; |
268 | skb->protocol = htons(ETH_P_80211_RAW); |
269 | |
270 | dev->stats.rx_packets++; |
271 | dev->stats.rx_bytes += skb->len; |
272 | netif_rx(skb); |
273 | continue; |
274 | } else { |
275 | if (!p80211_convert_to_ether(wlandev, skb)) |
276 | continue; |
277 | } |
278 | } |
279 | dev_kfree_skb(skb); |
280 | } |
281 | } |
282 | |
283 | /*---------------------------------------------------------------- |
284 | * p80211knetdev_hard_start_xmit |
285 | * |
286 | * Linux netdevice method for transmitting a frame. |
287 | * |
288 | * Arguments: |
289 | * skb Linux sk_buff containing the frame. |
290 | * netdev Linux netdevice. |
291 | * |
292 | * Side effects: |
293 | * If the lower layers report that buffers are full. netdev->tbusy |
294 | * will be set to prevent higher layers from sending more traffic. |
295 | * |
296 | * Note: If this function returns non-zero, higher layers retain |
297 | * ownership of the skb. |
298 | * |
299 | * Returns: |
300 | * zero on success, non-zero on failure. |
301 | *---------------------------------------------------------------- |
302 | */ |
303 | static netdev_tx_t p80211knetdev_hard_start_xmit(struct sk_buff *skb, |
304 | struct net_device *netdev) |
305 | { |
306 | int result = 0; |
307 | int txresult; |
308 | struct wlandevice *wlandev = netdev->ml_priv; |
309 | struct p80211_hdr p80211_hdr; |
310 | struct p80211_metawep p80211_wep; |
311 | |
312 | p80211_wep.data = NULL; |
313 | |
314 | if (!skb) |
315 | return NETDEV_TX_OK; |
316 | |
317 | if (wlandev->state != WLAN_DEVICE_OPEN) { |
318 | result = 1; |
319 | goto failed; |
320 | } |
321 | |
322 | memset(&p80211_hdr, 0, sizeof(p80211_hdr)); |
323 | memset(&p80211_wep, 0, sizeof(p80211_wep)); |
324 | |
325 | if (netif_queue_stopped(dev: netdev)) { |
326 | netdev_dbg(netdev, "called when queue stopped.\n" ); |
327 | result = 1; |
328 | goto failed; |
329 | } |
330 | |
331 | netif_stop_queue(dev: netdev); |
332 | |
333 | /* Check to see that a valid mode is set */ |
334 | switch (wlandev->macmode) { |
335 | case WLAN_MACMODE_IBSS_STA: |
336 | case WLAN_MACMODE_ESS_STA: |
337 | case WLAN_MACMODE_ESS_AP: |
338 | break; |
339 | default: |
340 | /* Mode isn't set yet, just drop the frame |
341 | * and return success . |
342 | * TODO: we need a saner way to handle this |
343 | */ |
344 | if (be16_to_cpu(skb->protocol) != ETH_P_80211_RAW) { |
345 | netif_start_queue(dev: wlandev->netdev); |
346 | netdev_notice(dev: netdev, format: "Tx attempt prior to association, frame dropped.\n" ); |
347 | netdev->stats.tx_dropped++; |
348 | result = 0; |
349 | goto failed; |
350 | } |
351 | break; |
352 | } |
353 | |
354 | /* Check for raw transmits */ |
355 | if (be16_to_cpu(skb->protocol) == ETH_P_80211_RAW) { |
356 | if (!capable(CAP_NET_ADMIN)) { |
357 | result = 1; |
358 | goto failed; |
359 | } |
360 | /* move the header over */ |
361 | memcpy(&p80211_hdr, skb->data, sizeof(p80211_hdr)); |
362 | skb_pull(skb, len: sizeof(p80211_hdr)); |
363 | } else { |
364 | if (skb_ether_to_p80211 |
365 | (wlandev, ethconv: wlandev->ethconv, skb, p80211_hdr: &p80211_hdr, |
366 | p80211_wep: &p80211_wep) != 0) { |
367 | /* convert failed */ |
368 | netdev_dbg(netdev, "ether_to_80211(%d) failed.\n" , |
369 | wlandev->ethconv); |
370 | result = 1; |
371 | goto failed; |
372 | } |
373 | } |
374 | if (!wlandev->txframe) { |
375 | result = 1; |
376 | goto failed; |
377 | } |
378 | |
379 | netif_trans_update(dev: netdev); |
380 | |
381 | netdev->stats.tx_packets++; |
382 | /* count only the packet payload */ |
383 | netdev->stats.tx_bytes += skb->len; |
384 | |
385 | txresult = wlandev->txframe(wlandev, skb, &p80211_hdr, &p80211_wep); |
386 | |
387 | if (txresult == 0) { |
388 | /* success and more buf */ |
389 | /* avail, re: hw_txdata */ |
390 | netif_wake_queue(dev: wlandev->netdev); |
391 | result = NETDEV_TX_OK; |
392 | } else if (txresult == 1) { |
393 | /* success, no more avail */ |
394 | netdev_dbg(netdev, "txframe success, no more bufs\n" ); |
395 | /* netdev->tbusy = 1; don't set here, irqhdlr */ |
396 | /* may have already cleared it */ |
397 | result = NETDEV_TX_OK; |
398 | } else if (txresult == 2) { |
399 | /* alloc failure, drop frame */ |
400 | netdev_dbg(netdev, "txframe returned alloc_fail\n" ); |
401 | result = NETDEV_TX_BUSY; |
402 | } else { |
403 | /* buffer full or queue busy, drop frame. */ |
404 | netdev_dbg(netdev, "txframe returned full or busy\n" ); |
405 | result = NETDEV_TX_BUSY; |
406 | } |
407 | |
408 | failed: |
409 | /* Free up the WEP buffer if it's not the same as the skb */ |
410 | if ((p80211_wep.data) && (p80211_wep.data != skb->data)) |
411 | kfree_sensitive(objp: p80211_wep.data); |
412 | |
413 | /* we always free the skb here, never in a lower level. */ |
414 | if (!result) |
415 | dev_kfree_skb(skb); |
416 | |
417 | return result; |
418 | } |
419 | |
420 | /*---------------------------------------------------------------- |
421 | * p80211knetdev_set_multicast_list |
422 | * |
423 | * Called from higher layers whenever there's a need to set/clear |
424 | * promiscuous mode or rewrite the multicast list. |
425 | * |
426 | * Arguments: |
427 | * none |
428 | * |
429 | * Returns: |
430 | * nothing |
431 | *---------------------------------------------------------------- |
432 | */ |
433 | static void p80211knetdev_set_multicast_list(struct net_device *dev) |
434 | { |
435 | struct wlandevice *wlandev = dev->ml_priv; |
436 | |
437 | /* TODO: real multicast support as well */ |
438 | |
439 | if (wlandev->set_multicast_list) |
440 | wlandev->set_multicast_list(wlandev, dev); |
441 | } |
442 | |
443 | /*---------------------------------------------------------------- |
444 | * p80211knetdev_siocdevprivate |
445 | * |
446 | * Handle an ioctl call on one of our devices. Everything Linux |
447 | * ioctl specific is done here. Then we pass the contents of the |
448 | * ifr->data to the request message handler. |
449 | * |
450 | * Arguments: |
451 | * dev Linux kernel netdevice |
452 | * ifr Our private ioctl request structure, typed for the |
453 | * generic struct ifreq so we can use ptr to func |
454 | * w/o cast. |
455 | * |
456 | * Returns: |
457 | * zero on success, a negative errno on failure. Possible values: |
458 | * -ENETDOWN Device isn't up. |
459 | * -EBUSY cmd already in progress |
460 | * -ETIME p80211 cmd timed out (MSD may have its own timers) |
461 | * -EFAULT memory fault copying msg from user buffer |
462 | * -ENOMEM unable to allocate kernel msg buffer |
463 | * -EINVAL bad magic, it the cmd really for us? |
464 | * -EintR sleeping on cmd, awakened by signal, cmd cancelled. |
465 | * |
466 | * Call Context: |
467 | * Process thread (ioctl caller). TODO: SMP support may require |
468 | * locks. |
469 | *---------------------------------------------------------------- |
470 | */ |
471 | static int p80211knetdev_siocdevprivate(struct net_device *dev, |
472 | struct ifreq *ifr, |
473 | void __user *data, int cmd) |
474 | { |
475 | int result = 0; |
476 | struct p80211ioctl_req *req = (struct p80211ioctl_req *)ifr; |
477 | struct wlandevice *wlandev = dev->ml_priv; |
478 | u8 *msgbuf; |
479 | |
480 | netdev_dbg(dev, "rx'd ioctl, cmd=%d, len=%d\n" , cmd, req->len); |
481 | |
482 | if (in_compat_syscall()) |
483 | return -EOPNOTSUPP; |
484 | |
485 | /* Test the magic, assume ifr is good if it's there */ |
486 | if (req->magic != P80211_IOCTL_MAGIC) { |
487 | result = -EINVAL; |
488 | goto bail; |
489 | } |
490 | |
491 | if (cmd == P80211_IFTEST) { |
492 | result = 0; |
493 | goto bail; |
494 | } else if (cmd != P80211_IFREQ) { |
495 | result = -EINVAL; |
496 | goto bail; |
497 | } |
498 | |
499 | msgbuf = memdup_user(data, req->len); |
500 | if (IS_ERR(ptr: msgbuf)) { |
501 | result = PTR_ERR(ptr: msgbuf); |
502 | goto bail; |
503 | } |
504 | |
505 | result = p80211req_dorequest(wlandev, msgbuf); |
506 | |
507 | if (result == 0) { |
508 | if (copy_to_user(to: data, from: msgbuf, n: req->len)) |
509 | result = -EFAULT; |
510 | } |
511 | kfree(objp: msgbuf); |
512 | |
513 | bail: |
514 | /* If allocate,copyfrom or copyto fails, return errno */ |
515 | return result; |
516 | } |
517 | |
518 | /*---------------------------------------------------------------- |
519 | * p80211knetdev_set_mac_address |
520 | * |
521 | * Handles the ioctl for changing the MACAddress of a netdevice |
522 | * |
523 | * references: linux/netdevice.h and drivers/net/net_init.c |
524 | * |
525 | * NOTE: [MSM] We only prevent address changes when the netdev is |
526 | * up. We don't control anything based on dot11 state. If the |
527 | * address is changed on a STA that's currently associated, you |
528 | * will probably lose the ability to send and receive data frames. |
529 | * Just be aware. Therefore, this should usually only be done |
530 | * prior to scan/join/auth/assoc. |
531 | * |
532 | * Arguments: |
533 | * dev netdevice struct |
534 | * addr the new MACAddress (a struct) |
535 | * |
536 | * Returns: |
537 | * zero on success, a negative errno on failure. Possible values: |
538 | * -EBUSY device is bussy (cmd not possible) |
539 | * -and errors returned by: p80211req_dorequest(..) |
540 | * |
541 | * by: Collin R. Mulliner <collin@mulliner.org> |
542 | *---------------------------------------------------------------- |
543 | */ |
544 | static int p80211knetdev_set_mac_address(struct net_device *dev, void *addr) |
545 | { |
546 | struct sockaddr *new_addr = addr; |
547 | struct p80211msg_dot11req_mibset dot11req; |
548 | struct p80211item_unk392 *mibattr; |
549 | struct p80211item_pstr6 *macaddr; |
550 | struct p80211item_uint32 *resultcode; |
551 | int result; |
552 | |
553 | /* If we're running, we don't allow MAC address changes */ |
554 | if (netif_running(dev)) |
555 | return -EBUSY; |
556 | |
557 | /* Set up some convenience pointers. */ |
558 | mibattr = &dot11req.mibattribute; |
559 | macaddr = (struct p80211item_pstr6 *)&mibattr->data; |
560 | resultcode = &dot11req.resultcode; |
561 | |
562 | /* Set up a dot11req_mibset */ |
563 | memset(&dot11req, 0, sizeof(dot11req)); |
564 | dot11req.msgcode = DIDMSG_DOT11REQ_MIBSET; |
565 | dot11req.msglen = sizeof(dot11req); |
566 | memcpy(dot11req.devname, |
567 | ((struct wlandevice *)dev->ml_priv)->name, |
568 | WLAN_DEVNAMELEN_MAX - 1); |
569 | |
570 | /* Set up the mibattribute argument */ |
571 | mibattr->did = DIDMSG_DOT11REQ_MIBSET_MIBATTRIBUTE; |
572 | mibattr->status = P80211ENUM_msgitem_status_data_ok; |
573 | mibattr->len = sizeof(mibattr->data); |
574 | |
575 | macaddr->did = DIDMIB_DOT11MAC_OPERATIONTABLE_MACADDRESS; |
576 | macaddr->status = P80211ENUM_msgitem_status_data_ok; |
577 | macaddr->len = sizeof(macaddr->data); |
578 | macaddr->data.len = ETH_ALEN; |
579 | memcpy(&macaddr->data.data, new_addr->sa_data, ETH_ALEN); |
580 | |
581 | /* Set up the resultcode argument */ |
582 | resultcode->did = DIDMSG_DOT11REQ_MIBSET_RESULTCODE; |
583 | resultcode->status = P80211ENUM_msgitem_status_no_value; |
584 | resultcode->len = sizeof(resultcode->data); |
585 | resultcode->data = 0; |
586 | |
587 | /* now fire the request */ |
588 | result = p80211req_dorequest(wlandev: dev->ml_priv, msgbuf: (u8 *)&dot11req); |
589 | |
590 | /* If the request wasn't successful, report an error and don't |
591 | * change the netdev address |
592 | */ |
593 | if (result != 0 || resultcode->data != P80211ENUM_resultcode_success) { |
594 | netdev_err(dev, format: "Low-level driver failed dot11req_mibset(dot11MACAddress).\n" ); |
595 | result = -EADDRNOTAVAIL; |
596 | } else { |
597 | /* everything's ok, change the addr in netdev */ |
598 | eth_hw_addr_set(dev, addr: new_addr->sa_data); |
599 | } |
600 | |
601 | return result; |
602 | } |
603 | |
604 | static const struct net_device_ops p80211_netdev_ops = { |
605 | .ndo_init = p80211knetdev_init, |
606 | .ndo_open = p80211knetdev_open, |
607 | .ndo_stop = p80211knetdev_stop, |
608 | .ndo_start_xmit = p80211knetdev_hard_start_xmit, |
609 | .ndo_set_rx_mode = p80211knetdev_set_multicast_list, |
610 | .ndo_siocdevprivate = p80211knetdev_siocdevprivate, |
611 | .ndo_set_mac_address = p80211knetdev_set_mac_address, |
612 | .ndo_tx_timeout = p80211knetdev_tx_timeout, |
613 | .ndo_validate_addr = eth_validate_addr, |
614 | }; |
615 | |
616 | /*---------------------------------------------------------------- |
617 | * wlan_setup |
618 | * |
619 | * Roughly matches the functionality of ether_setup. Here |
620 | * we set up any members of the wlandevice structure that are common |
621 | * to all devices. Additionally, we allocate a linux 'struct device' |
622 | * and perform the same setup as ether_setup. |
623 | * |
624 | * Note: It's important that the caller have setup the wlandev->name |
625 | * ptr prior to calling this function. |
626 | * |
627 | * Arguments: |
628 | * wlandev ptr to the wlandev structure for the |
629 | * interface. |
630 | * physdev ptr to usb device |
631 | * Returns: |
632 | * zero on success, non-zero otherwise. |
633 | * Call Context: |
634 | * Should be process thread. We'll assume it might be |
635 | * interrupt though. When we add support for statically |
636 | * compiled drivers, this function will be called in the |
637 | * context of the kernel startup code. |
638 | *---------------------------------------------------------------- |
639 | */ |
640 | int wlan_setup(struct wlandevice *wlandev, struct device *physdev) |
641 | { |
642 | int result = 0; |
643 | struct net_device *netdev; |
644 | struct wiphy *wiphy; |
645 | struct wireless_dev *wdev; |
646 | |
647 | /* Set up the wlandev */ |
648 | wlandev->state = WLAN_DEVICE_CLOSED; |
649 | wlandev->ethconv = WLAN_ETHCONV_8021h; |
650 | wlandev->macmode = WLAN_MACMODE_NONE; |
651 | |
652 | /* Set up the rx queue */ |
653 | skb_queue_head_init(list: &wlandev->nsd_rxq); |
654 | tasklet_setup(t: &wlandev->rx_bh, callback: p80211netdev_rx_bh); |
655 | |
656 | /* Allocate and initialize the wiphy struct */ |
657 | wiphy = wlan_create_wiphy(dev: physdev, wlandev); |
658 | if (!wiphy) { |
659 | dev_err(physdev, "Failed to alloc wiphy.\n" ); |
660 | return 1; |
661 | } |
662 | |
663 | /* Allocate and initialize the struct device */ |
664 | netdev = alloc_netdev(sizeof(struct wireless_dev), "wlan%d" , |
665 | NET_NAME_UNKNOWN, ether_setup); |
666 | if (!netdev) { |
667 | dev_err(physdev, "Failed to alloc netdev.\n" ); |
668 | wlan_free_wiphy(wiphy); |
669 | result = 1; |
670 | } else { |
671 | wlandev->netdev = netdev; |
672 | netdev->ml_priv = wlandev; |
673 | netdev->netdev_ops = &p80211_netdev_ops; |
674 | wdev = netdev_priv(dev: netdev); |
675 | wdev->wiphy = wiphy; |
676 | wdev->iftype = NL80211_IFTYPE_STATION; |
677 | netdev->ieee80211_ptr = wdev; |
678 | netdev->min_mtu = 68; |
679 | /* 2312 is max 802.11 payload, 20 is overhead, |
680 | * (ether + llc + snap) and another 8 for wep. |
681 | */ |
682 | netdev->max_mtu = (2312 - 20 - 8); |
683 | |
684 | netif_stop_queue(dev: netdev); |
685 | netif_carrier_off(dev: netdev); |
686 | } |
687 | |
688 | return result; |
689 | } |
690 | |
691 | /*---------------------------------------------------------------- |
692 | * wlan_unsetup |
693 | * |
694 | * This function is paired with the wlan_setup routine. It should |
695 | * be called after unregister_wlandev. Basically, all it does is |
696 | * free the 'struct device' that's associated with the wlandev. |
697 | * We do it here because the 'struct device' isn't allocated |
698 | * explicitly in the driver code, it's done in wlan_setup. To |
699 | * do the free in the driver might seem like 'magic'. |
700 | * |
701 | * Arguments: |
702 | * wlandev ptr to the wlandev structure for the |
703 | * interface. |
704 | * Call Context: |
705 | * Should be process thread. We'll assume it might be |
706 | * interrupt though. When we add support for statically |
707 | * compiled drivers, this function will be called in the |
708 | * context of the kernel startup code. |
709 | *---------------------------------------------------------------- |
710 | */ |
711 | void wlan_unsetup(struct wlandevice *wlandev) |
712 | { |
713 | struct wireless_dev *wdev; |
714 | |
715 | tasklet_kill(t: &wlandev->rx_bh); |
716 | |
717 | if (wlandev->netdev) { |
718 | wdev = netdev_priv(dev: wlandev->netdev); |
719 | if (wdev->wiphy) |
720 | wlan_free_wiphy(wiphy: wdev->wiphy); |
721 | free_netdev(dev: wlandev->netdev); |
722 | wlandev->netdev = NULL; |
723 | } |
724 | } |
725 | |
726 | /*---------------------------------------------------------------- |
727 | * register_wlandev |
728 | * |
729 | * Roughly matches the functionality of register_netdev. This function |
730 | * is called after the driver has successfully probed and set up the |
731 | * resources for the device. It's now ready to become a named device |
732 | * in the Linux system. |
733 | * |
734 | * First we allocate a name for the device (if not already set), then |
735 | * we call the Linux function register_netdevice. |
736 | * |
737 | * Arguments: |
738 | * wlandev ptr to the wlandev structure for the |
739 | * interface. |
740 | * Returns: |
741 | * zero on success, non-zero otherwise. |
742 | * Call Context: |
743 | * Can be either interrupt or not. |
744 | *---------------------------------------------------------------- |
745 | */ |
746 | int register_wlandev(struct wlandevice *wlandev) |
747 | { |
748 | return register_netdev(dev: wlandev->netdev); |
749 | } |
750 | |
751 | /*---------------------------------------------------------------- |
752 | * unregister_wlandev |
753 | * |
754 | * Roughly matches the functionality of unregister_netdev. This |
755 | * function is called to remove a named device from the system. |
756 | * |
757 | * First we tell linux that the device should no longer exist. |
758 | * Then we remove it from the list of known wlan devices. |
759 | * |
760 | * Arguments: |
761 | * wlandev ptr to the wlandev structure for the |
762 | * interface. |
763 | * Returns: |
764 | * zero on success, non-zero otherwise. |
765 | * Call Context: |
766 | * Can be either interrupt or not. |
767 | *---------------------------------------------------------------- |
768 | */ |
769 | int unregister_wlandev(struct wlandevice *wlandev) |
770 | { |
771 | struct sk_buff *skb; |
772 | |
773 | unregister_netdev(dev: wlandev->netdev); |
774 | |
775 | /* Now to clean out the rx queue */ |
776 | while ((skb = skb_dequeue(list: &wlandev->nsd_rxq))) |
777 | dev_kfree_skb(skb); |
778 | |
779 | return 0; |
780 | } |
781 | |
782 | /*---------------------------------------------------------------- |
783 | * p80211netdev_hwremoved |
784 | * |
785 | * Hardware removed notification. This function should be called |
786 | * immediately after an MSD has detected that the underlying hardware |
787 | * has been yanked out from under us. The primary things we need |
788 | * to do are: |
789 | * - Mark the wlandev |
790 | * - Prevent any further traffic from the knetdev i/f |
791 | * - Prevent any further requests from mgmt i/f |
792 | * - If there are any waitq'd mgmt requests or mgmt-frame exchanges, |
793 | * shut them down. |
794 | * - Call the MSD hwremoved function. |
795 | * |
796 | * The remainder of the cleanup will be handled by unregister(). |
797 | * Our primary goal here is to prevent as much tickling of the MSD |
798 | * as possible since the MSD is already in a 'wounded' state. |
799 | * |
800 | * TODO: As new features are added, this function should be |
801 | * updated. |
802 | * |
803 | * Arguments: |
804 | * wlandev WLAN network device structure |
805 | * Returns: |
806 | * nothing |
807 | * Side effects: |
808 | * |
809 | * Call context: |
810 | * Usually interrupt. |
811 | *---------------------------------------------------------------- |
812 | */ |
813 | void p80211netdev_hwremoved(struct wlandevice *wlandev) |
814 | { |
815 | wlandev->hwremoved = 1; |
816 | if (wlandev->state == WLAN_DEVICE_OPEN) |
817 | netif_stop_queue(dev: wlandev->netdev); |
818 | |
819 | netif_device_detach(dev: wlandev->netdev); |
820 | } |
821 | |
822 | /*---------------------------------------------------------------- |
823 | * p80211_rx_typedrop |
824 | * |
825 | * Classifies the frame, increments the appropriate counter, and |
826 | * returns 0|1|2 indicating whether the driver should handle, ignore, or |
827 | * drop the frame |
828 | * |
829 | * Arguments: |
830 | * wlandev wlan device structure |
831 | * fc frame control field |
832 | * |
833 | * Returns: |
834 | * zero if the frame should be handled by the driver, |
835 | * one if the frame should be ignored |
836 | * anything else means we drop it. |
837 | * |
838 | * Side effects: |
839 | * |
840 | * Call context: |
841 | * interrupt |
842 | *---------------------------------------------------------------- |
843 | */ |
844 | static int p80211_rx_typedrop(struct wlandevice *wlandev, u16 fc) |
845 | { |
846 | u16 ftype; |
847 | u16 fstype; |
848 | int drop = 0; |
849 | /* Classify frame, increment counter */ |
850 | ftype = WLAN_GET_FC_FTYPE(fc); |
851 | fstype = WLAN_GET_FC_FSTYPE(fc); |
852 | switch (ftype) { |
853 | case WLAN_FTYPE_MGMT: |
854 | if ((wlandev->netdev->flags & IFF_PROMISC) || |
855 | (wlandev->netdev->flags & IFF_ALLMULTI)) { |
856 | drop = 1; |
857 | break; |
858 | } |
859 | netdev_dbg(wlandev->netdev, "rx'd mgmt:\n" ); |
860 | wlandev->rx.mgmt++; |
861 | switch (fstype) { |
862 | case WLAN_FSTYPE_ASSOCREQ: |
863 | wlandev->rx.assocreq++; |
864 | break; |
865 | case WLAN_FSTYPE_ASSOCRESP: |
866 | wlandev->rx.assocresp++; |
867 | break; |
868 | case WLAN_FSTYPE_REASSOCREQ: |
869 | wlandev->rx.reassocreq++; |
870 | break; |
871 | case WLAN_FSTYPE_REASSOCRESP: |
872 | wlandev->rx.reassocresp++; |
873 | break; |
874 | case WLAN_FSTYPE_PROBEREQ: |
875 | wlandev->rx.probereq++; |
876 | break; |
877 | case WLAN_FSTYPE_PROBERESP: |
878 | wlandev->rx.proberesp++; |
879 | break; |
880 | case WLAN_FSTYPE_BEACON: |
881 | wlandev->rx.beacon++; |
882 | break; |
883 | case WLAN_FSTYPE_ATIM: |
884 | wlandev->rx.atim++; |
885 | break; |
886 | case WLAN_FSTYPE_DISASSOC: |
887 | wlandev->rx.disassoc++; |
888 | break; |
889 | case WLAN_FSTYPE_AUTHEN: |
890 | wlandev->rx.authen++; |
891 | break; |
892 | case WLAN_FSTYPE_DEAUTHEN: |
893 | wlandev->rx.deauthen++; |
894 | break; |
895 | default: |
896 | wlandev->rx.mgmt_unknown++; |
897 | break; |
898 | } |
899 | drop = 2; |
900 | break; |
901 | |
902 | case WLAN_FTYPE_CTL: |
903 | if ((wlandev->netdev->flags & IFF_PROMISC) || |
904 | (wlandev->netdev->flags & IFF_ALLMULTI)) { |
905 | drop = 1; |
906 | break; |
907 | } |
908 | netdev_dbg(wlandev->netdev, "rx'd ctl:\n" ); |
909 | wlandev->rx.ctl++; |
910 | switch (fstype) { |
911 | case WLAN_FSTYPE_PSPOLL: |
912 | wlandev->rx.pspoll++; |
913 | break; |
914 | case WLAN_FSTYPE_RTS: |
915 | wlandev->rx.rts++; |
916 | break; |
917 | case WLAN_FSTYPE_CTS: |
918 | wlandev->rx.cts++; |
919 | break; |
920 | case WLAN_FSTYPE_ACK: |
921 | wlandev->rx.ack++; |
922 | break; |
923 | case WLAN_FSTYPE_CFEND: |
924 | wlandev->rx.cfend++; |
925 | break; |
926 | case WLAN_FSTYPE_CFENDCFACK: |
927 | wlandev->rx.cfendcfack++; |
928 | break; |
929 | default: |
930 | wlandev->rx.ctl_unknown++; |
931 | break; |
932 | } |
933 | drop = 2; |
934 | break; |
935 | |
936 | case WLAN_FTYPE_DATA: |
937 | wlandev->rx.data++; |
938 | switch (fstype) { |
939 | case WLAN_FSTYPE_DATAONLY: |
940 | wlandev->rx.dataonly++; |
941 | break; |
942 | case WLAN_FSTYPE_DATA_CFACK: |
943 | wlandev->rx.data_cfack++; |
944 | break; |
945 | case WLAN_FSTYPE_DATA_CFPOLL: |
946 | wlandev->rx.data_cfpoll++; |
947 | break; |
948 | case WLAN_FSTYPE_DATA_CFACK_CFPOLL: |
949 | wlandev->rx.data__cfack_cfpoll++; |
950 | break; |
951 | case WLAN_FSTYPE_NULL: |
952 | netdev_dbg(wlandev->netdev, "rx'd data:null\n" ); |
953 | wlandev->rx.null++; |
954 | break; |
955 | case WLAN_FSTYPE_CFACK: |
956 | netdev_dbg(wlandev->netdev, "rx'd data:cfack\n" ); |
957 | wlandev->rx.cfack++; |
958 | break; |
959 | case WLAN_FSTYPE_CFPOLL: |
960 | netdev_dbg(wlandev->netdev, "rx'd data:cfpoll\n" ); |
961 | wlandev->rx.cfpoll++; |
962 | break; |
963 | case WLAN_FSTYPE_CFACK_CFPOLL: |
964 | netdev_dbg(wlandev->netdev, "rx'd data:cfack_cfpoll\n" ); |
965 | wlandev->rx.cfack_cfpoll++; |
966 | break; |
967 | default: |
968 | wlandev->rx.data_unknown++; |
969 | break; |
970 | } |
971 | |
972 | break; |
973 | } |
974 | return drop; |
975 | } |
976 | |
977 | static void p80211knetdev_tx_timeout(struct net_device *netdev, unsigned int txqueue) |
978 | { |
979 | struct wlandevice *wlandev = netdev->ml_priv; |
980 | |
981 | if (wlandev->tx_timeout) { |
982 | wlandev->tx_timeout(wlandev); |
983 | } else { |
984 | netdev_warn(dev: netdev, format: "Implement tx_timeout for %s\n" , |
985 | wlandev->nsdname); |
986 | netif_wake_queue(dev: wlandev->netdev); |
987 | } |
988 | } |
989 | |