diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/staging/wlan-ng/p80211netdev.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'drivers/staging/wlan-ng/p80211netdev.c')
-rw-r--r-- | drivers/staging/wlan-ng/p80211netdev.c | 1009 |
1 files changed, 1009 insertions, 0 deletions
diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c new file mode 100644 index 000000000..6bef419e8 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211netdev.c @@ -0,0 +1,1009 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Linux Kernel net device interface + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License version 2 (the "GPL"), in which + * case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use + * your version of this file under the MPL, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * The functions required for a Linux network device are defined here. + * + * -------------------------------------------------------------------- + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/kmod.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> +#include <linux/sockios.h> +#include <linux/etherdevice.h> +#include <linux/if_ether.h> +#include <linux/byteorder/generic.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> +#include <asm/byteorder.h> + +#ifdef SIOCETHTOOL +#include <linux/ethtool.h> +#endif + +#include <net/iw_handler.h> +#include <net/net_namespace.h> +#include <net/cfg80211.h> + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211conv.h" +#include "p80211mgmt.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211ioctl.h" +#include "p80211req.h" +#include "p80211metastruct.h" +#include "p80211metadef.h" + +#include "cfg80211.c" + +/* netdevice method functions */ +static int p80211knetdev_init(struct net_device *netdev); +static int p80211knetdev_open(struct net_device *netdev); +static int p80211knetdev_stop(struct net_device *netdev); +static netdev_tx_t p80211knetdev_hard_start_xmit(struct sk_buff *skb, + struct net_device *netdev); +static void p80211knetdev_set_multicast_list(struct net_device *dev); +static int p80211knetdev_siocdevprivate(struct net_device *dev, struct ifreq *ifr, + void __user *data, int cmd); +static int p80211knetdev_set_mac_address(struct net_device *dev, void *addr); +static void p80211knetdev_tx_timeout(struct net_device *netdev, unsigned int txqueue); +static int p80211_rx_typedrop(struct wlandevice *wlandev, u16 fc); + +int wlan_watchdog = 5000; +module_param(wlan_watchdog, int, 0644); +MODULE_PARM_DESC(wlan_watchdog, "transmit timeout in milliseconds"); + +int wlan_wext_write = 1; +module_param(wlan_wext_write, int, 0644); +MODULE_PARM_DESC(wlan_wext_write, "enable write wireless extensions"); + +/*---------------------------------------------------------------- + * p80211knetdev_init + * + * Init method for a Linux netdevice. Called in response to + * register_netdev. + * + * Arguments: + * none + * + * Returns: + * nothing + *---------------------------------------------------------------- + */ +static int p80211knetdev_init(struct net_device *netdev) +{ + /* Called in response to register_netdev */ + /* This is usually the probe function, but the probe has */ + /* already been done by the MSD and the create_kdev */ + /* function. All we do here is return success */ + return 0; +} + +/*---------------------------------------------------------------- + * p80211knetdev_open + * + * Linux netdevice open method. Following a successful call here, + * the device is supposed to be ready for tx and rx. In our + * situation that may not be entirely true due to the state of the + * MAC below. + * + * Arguments: + * netdev Linux network device structure + * + * Returns: + * zero on success, non-zero otherwise + *---------------------------------------------------------------- + */ +static int p80211knetdev_open(struct net_device *netdev) +{ + int result = 0; /* success */ + struct wlandevice *wlandev = netdev->ml_priv; + + /* Check to make sure the MSD is running */ + if (wlandev->msdstate != WLAN_MSD_RUNNING) + return -ENODEV; + + /* Tell the MSD to open */ + if (wlandev->open) { + result = wlandev->open(wlandev); + if (result == 0) { + netif_start_queue(wlandev->netdev); + wlandev->state = WLAN_DEVICE_OPEN; + } + } else { + result = -EAGAIN; + } + + return result; +} + +/*---------------------------------------------------------------- + * p80211knetdev_stop + * + * Linux netdevice stop (close) method. Following this call, + * no frames should go up or down through this interface. + * + * Arguments: + * netdev Linux network device structure + * + * Returns: + * zero on success, non-zero otherwise + *---------------------------------------------------------------- + */ +static int p80211knetdev_stop(struct net_device *netdev) +{ + int result = 0; + struct wlandevice *wlandev = netdev->ml_priv; + + if (wlandev->close) + result = wlandev->close(wlandev); + + netif_stop_queue(wlandev->netdev); + wlandev->state = WLAN_DEVICE_CLOSED; + + return result; +} + +/*---------------------------------------------------------------- + * p80211netdev_rx + * + * Frame receive function called by the mac specific driver. + * + * Arguments: + * wlandev WLAN network device structure + * skb skbuff containing a full 802.11 frame. + * Returns: + * nothing + * Side effects: + * + *---------------------------------------------------------------- + */ +void p80211netdev_rx(struct wlandevice *wlandev, struct sk_buff *skb) +{ + /* Enqueue for post-irq processing */ + skb_queue_tail(&wlandev->nsd_rxq, skb); + tasklet_schedule(&wlandev->rx_bh); +} + +#define CONV_TO_ETHER_SKIPPED 0x01 +#define CONV_TO_ETHER_FAILED 0x02 + +/** + * p80211_convert_to_ether - conversion from 802.11 frame to ethernet frame + * @wlandev: pointer to WLAN device + * @skb: pointer to socket buffer + * + * Returns: 0 if conversion succeeded + * CONV_TO_ETHER_FAILED if conversion failed + * CONV_TO_ETHER_SKIPPED if frame is ignored + */ +static int p80211_convert_to_ether(struct wlandevice *wlandev, + struct sk_buff *skb) +{ + struct p80211_hdr *hdr; + + hdr = (struct p80211_hdr *)skb->data; + if (p80211_rx_typedrop(wlandev, le16_to_cpu(hdr->frame_control))) + return CONV_TO_ETHER_SKIPPED; + + /* perform mcast filtering: allow my local address through but reject + * anything else that isn't multicast + */ + if (wlandev->netdev->flags & IFF_ALLMULTI) { + if (!ether_addr_equal_unaligned(wlandev->netdev->dev_addr, + hdr->address1)) { + if (!is_multicast_ether_addr(hdr->address1)) + return CONV_TO_ETHER_SKIPPED; + } + } + + if (skb_p80211_to_ether(wlandev, wlandev->ethconv, skb) == 0) { + wlandev->netdev->stats.rx_packets++; + wlandev->netdev->stats.rx_bytes += skb->len; + netif_rx(skb); + return 0; + } + + netdev_dbg(wlandev->netdev, "%s failed.\n", __func__); + return CONV_TO_ETHER_FAILED; +} + +/** + * p80211netdev_rx_bh - deferred processing of all received frames + * + * @t: pointer to the tasklet associated with this handler + */ +static void p80211netdev_rx_bh(struct tasklet_struct *t) +{ + struct wlandevice *wlandev = from_tasklet(wlandev, t, rx_bh); + struct sk_buff *skb = NULL; + struct net_device *dev = wlandev->netdev; + + /* Let's empty our queue */ + while ((skb = skb_dequeue(&wlandev->nsd_rxq))) { + if (wlandev->state == WLAN_DEVICE_OPEN) { + if (dev->type != ARPHRD_ETHER) { + /* RAW frame; we shouldn't convert it */ + /* XXX Append the Prism Header here instead. */ + + /* set up various data fields */ + skb->dev = dev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_80211_RAW); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + netif_rx(skb); + continue; + } else { + if (!p80211_convert_to_ether(wlandev, skb)) + continue; + } + } + dev_kfree_skb(skb); + } +} + +/*---------------------------------------------------------------- + * p80211knetdev_hard_start_xmit + * + * Linux netdevice method for transmitting a frame. + * + * Arguments: + * skb Linux sk_buff containing the frame. + * netdev Linux netdevice. + * + * Side effects: + * If the lower layers report that buffers are full. netdev->tbusy + * will be set to prevent higher layers from sending more traffic. + * + * Note: If this function returns non-zero, higher layers retain + * ownership of the skb. + * + * Returns: + * zero on success, non-zero on failure. + *---------------------------------------------------------------- + */ +static netdev_tx_t p80211knetdev_hard_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + int result = 0; + int txresult; + struct wlandevice *wlandev = netdev->ml_priv; + struct p80211_hdr p80211_hdr; + struct p80211_metawep p80211_wep; + + p80211_wep.data = NULL; + + if (!skb) + return NETDEV_TX_OK; + + if (wlandev->state != WLAN_DEVICE_OPEN) { + result = 1; + goto failed; + } + + memset(&p80211_hdr, 0, sizeof(p80211_hdr)); + memset(&p80211_wep, 0, sizeof(p80211_wep)); + + if (netif_queue_stopped(netdev)) { + netdev_dbg(netdev, "called when queue stopped.\n"); + result = 1; + goto failed; + } + + netif_stop_queue(netdev); + + /* Check to see that a valid mode is set */ + switch (wlandev->macmode) { + case WLAN_MACMODE_IBSS_STA: + case WLAN_MACMODE_ESS_STA: + case WLAN_MACMODE_ESS_AP: + break; + default: + /* Mode isn't set yet, just drop the frame + * and return success . + * TODO: we need a saner way to handle this + */ + if (be16_to_cpu(skb->protocol) != ETH_P_80211_RAW) { + netif_start_queue(wlandev->netdev); + netdev_notice(netdev, "Tx attempt prior to association, frame dropped.\n"); + netdev->stats.tx_dropped++; + result = 0; + goto failed; + } + break; + } + + /* Check for raw transmits */ + if (be16_to_cpu(skb->protocol) == ETH_P_80211_RAW) { + if (!capable(CAP_NET_ADMIN)) { + result = 1; + goto failed; + } + /* move the header over */ + memcpy(&p80211_hdr, skb->data, sizeof(p80211_hdr)); + skb_pull(skb, sizeof(p80211_hdr)); + } else { + if (skb_ether_to_p80211 + (wlandev, wlandev->ethconv, skb, &p80211_hdr, + &p80211_wep) != 0) { + /* convert failed */ + netdev_dbg(netdev, "ether_to_80211(%d) failed.\n", + wlandev->ethconv); + result = 1; + goto failed; + } + } + if (!wlandev->txframe) { + result = 1; + goto failed; + } + + netif_trans_update(netdev); + + netdev->stats.tx_packets++; + /* count only the packet payload */ + netdev->stats.tx_bytes += skb->len; + + txresult = wlandev->txframe(wlandev, skb, &p80211_hdr, &p80211_wep); + + if (txresult == 0) { + /* success and more buf */ + /* avail, re: hw_txdata */ + netif_wake_queue(wlandev->netdev); + result = NETDEV_TX_OK; + } else if (txresult == 1) { + /* success, no more avail */ + netdev_dbg(netdev, "txframe success, no more bufs\n"); + /* netdev->tbusy = 1; don't set here, irqhdlr */ + /* may have already cleared it */ + result = NETDEV_TX_OK; + } else if (txresult == 2) { + /* alloc failure, drop frame */ + netdev_dbg(netdev, "txframe returned alloc_fail\n"); + result = NETDEV_TX_BUSY; + } else { + /* buffer full or queue busy, drop frame. */ + netdev_dbg(netdev, "txframe returned full or busy\n"); + result = NETDEV_TX_BUSY; + } + +failed: + /* Free up the WEP buffer if it's not the same as the skb */ + if ((p80211_wep.data) && (p80211_wep.data != skb->data)) + kfree_sensitive(p80211_wep.data); + + /* we always free the skb here, never in a lower level. */ + if (!result) + dev_kfree_skb(skb); + + return result; +} + +/*---------------------------------------------------------------- + * p80211knetdev_set_multicast_list + * + * Called from higher layers whenever there's a need to set/clear + * promiscuous mode or rewrite the multicast list. + * + * Arguments: + * none + * + * Returns: + * nothing + *---------------------------------------------------------------- + */ +static void p80211knetdev_set_multicast_list(struct net_device *dev) +{ + struct wlandevice *wlandev = dev->ml_priv; + + /* TODO: real multicast support as well */ + + if (wlandev->set_multicast_list) + wlandev->set_multicast_list(wlandev, dev); +} + +/*---------------------------------------------------------------- + * p80211knetdev_siocdevprivate + * + * Handle an ioctl call on one of our devices. Everything Linux + * ioctl specific is done here. Then we pass the contents of the + * ifr->data to the request message handler. + * + * Arguments: + * dev Linux kernel netdevice + * ifr Our private ioctl request structure, typed for the + * generic struct ifreq so we can use ptr to func + * w/o cast. + * + * Returns: + * zero on success, a negative errno on failure. Possible values: + * -ENETDOWN Device isn't up. + * -EBUSY cmd already in progress + * -ETIME p80211 cmd timed out (MSD may have its own timers) + * -EFAULT memory fault copying msg from user buffer + * -ENOMEM unable to allocate kernel msg buffer + * -EINVAL bad magic, it the cmd really for us? + * -EintR sleeping on cmd, awakened by signal, cmd cancelled. + * + * Call Context: + * Process thread (ioctl caller). TODO: SMP support may require + * locks. + *---------------------------------------------------------------- + */ +static int p80211knetdev_siocdevprivate(struct net_device *dev, + struct ifreq *ifr, + void __user *data, int cmd) +{ + int result = 0; + struct p80211ioctl_req *req = (struct p80211ioctl_req *)ifr; + struct wlandevice *wlandev = dev->ml_priv; + u8 *msgbuf; + + netdev_dbg(dev, "rx'd ioctl, cmd=%d, len=%d\n", cmd, req->len); + + if (in_compat_syscall()) + return -EOPNOTSUPP; + + /* Test the magic, assume ifr is good if it's there */ + if (req->magic != P80211_IOCTL_MAGIC) { + result = -EINVAL; + goto bail; + } + + if (cmd == P80211_IFTEST) { + result = 0; + goto bail; + } else if (cmd != P80211_IFREQ) { + result = -EINVAL; + goto bail; + } + + msgbuf = memdup_user(data, req->len); + if (IS_ERR(msgbuf)) { + result = PTR_ERR(msgbuf); + goto bail; + } + + result = p80211req_dorequest(wlandev, msgbuf); + + if (result == 0) { + if (copy_to_user(data, msgbuf, req->len)) + result = -EFAULT; + } + kfree(msgbuf); + +bail: + /* If allocate,copyfrom or copyto fails, return errno */ + return result; +} + +/*---------------------------------------------------------------- + * p80211knetdev_set_mac_address + * + * Handles the ioctl for changing the MACAddress of a netdevice + * + * references: linux/netdevice.h and drivers/net/net_init.c + * + * NOTE: [MSM] We only prevent address changes when the netdev is + * up. We don't control anything based on dot11 state. If the + * address is changed on a STA that's currently associated, you + * will probably lose the ability to send and receive data frames. + * Just be aware. Therefore, this should usually only be done + * prior to scan/join/auth/assoc. + * + * Arguments: + * dev netdevice struct + * addr the new MACAddress (a struct) + * + * Returns: + * zero on success, a negative errno on failure. Possible values: + * -EBUSY device is bussy (cmd not possible) + * -and errors returned by: p80211req_dorequest(..) + * + * by: Collin R. Mulliner <collin@mulliner.org> + *---------------------------------------------------------------- + */ +static int p80211knetdev_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *new_addr = addr; + struct p80211msg_dot11req_mibset dot11req; + struct p80211item_unk392 *mibattr; + struct p80211item_pstr6 *macaddr; + struct p80211item_uint32 *resultcode; + int result; + + /* If we're running, we don't allow MAC address changes */ + if (netif_running(dev)) + return -EBUSY; + + /* Set up some convenience pointers. */ + mibattr = &dot11req.mibattribute; + macaddr = (struct p80211item_pstr6 *)&mibattr->data; + resultcode = &dot11req.resultcode; + + /* Set up a dot11req_mibset */ + memset(&dot11req, 0, sizeof(dot11req)); + dot11req.msgcode = DIDMSG_DOT11REQ_MIBSET; + dot11req.msglen = sizeof(dot11req); + memcpy(dot11req.devname, + ((struct wlandevice *)dev->ml_priv)->name, + WLAN_DEVNAMELEN_MAX - 1); + + /* Set up the mibattribute argument */ + mibattr->did = DIDMSG_DOT11REQ_MIBSET_MIBATTRIBUTE; + mibattr->status = P80211ENUM_msgitem_status_data_ok; + mibattr->len = sizeof(mibattr->data); + + macaddr->did = DIDMIB_DOT11MAC_OPERATIONTABLE_MACADDRESS; + macaddr->status = P80211ENUM_msgitem_status_data_ok; + macaddr->len = sizeof(macaddr->data); + macaddr->data.len = ETH_ALEN; + memcpy(&macaddr->data.data, new_addr->sa_data, ETH_ALEN); + + /* Set up the resultcode argument */ + resultcode->did = DIDMSG_DOT11REQ_MIBSET_RESULTCODE; + resultcode->status = P80211ENUM_msgitem_status_no_value; + resultcode->len = sizeof(resultcode->data); + resultcode->data = 0; + + /* now fire the request */ + result = p80211req_dorequest(dev->ml_priv, (u8 *)&dot11req); + + /* If the request wasn't successful, report an error and don't + * change the netdev address + */ + if (result != 0 || resultcode->data != P80211ENUM_resultcode_success) { + netdev_err(dev, "Low-level driver failed dot11req_mibset(dot11MACAddress).\n"); + result = -EADDRNOTAVAIL; + } else { + /* everything's ok, change the addr in netdev */ + eth_hw_addr_set(dev, new_addr->sa_data); + } + + return result; +} + +static const struct net_device_ops p80211_netdev_ops = { + .ndo_init = p80211knetdev_init, + .ndo_open = p80211knetdev_open, + .ndo_stop = p80211knetdev_stop, + .ndo_start_xmit = p80211knetdev_hard_start_xmit, + .ndo_set_rx_mode = p80211knetdev_set_multicast_list, + .ndo_siocdevprivate = p80211knetdev_siocdevprivate, + .ndo_set_mac_address = p80211knetdev_set_mac_address, + .ndo_tx_timeout = p80211knetdev_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +/*---------------------------------------------------------------- + * wlan_setup + * + * Roughly matches the functionality of ether_setup. Here + * we set up any members of the wlandevice structure that are common + * to all devices. Additionally, we allocate a linux 'struct device' + * and perform the same setup as ether_setup. + * + * Note: It's important that the caller have setup the wlandev->name + * ptr prior to calling this function. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * physdev ptr to usb device + * Returns: + * zero on success, non-zero otherwise. + * Call Context: + * Should be process thread. We'll assume it might be + * interrupt though. When we add support for statically + * compiled drivers, this function will be called in the + * context of the kernel startup code. + *---------------------------------------------------------------- + */ +int wlan_setup(struct wlandevice *wlandev, struct device *physdev) +{ + int result = 0; + struct net_device *netdev; + struct wiphy *wiphy; + struct wireless_dev *wdev; + + /* Set up the wlandev */ + wlandev->state = WLAN_DEVICE_CLOSED; + wlandev->ethconv = WLAN_ETHCONV_8021h; + wlandev->macmode = WLAN_MACMODE_NONE; + + /* Set up the rx queue */ + skb_queue_head_init(&wlandev->nsd_rxq); + tasklet_setup(&wlandev->rx_bh, p80211netdev_rx_bh); + + /* Allocate and initialize the wiphy struct */ + wiphy = wlan_create_wiphy(physdev, wlandev); + if (!wiphy) { + dev_err(physdev, "Failed to alloc wiphy.\n"); + return 1; + } + + /* Allocate and initialize the struct device */ + netdev = alloc_netdev(sizeof(struct wireless_dev), "wlan%d", + NET_NAME_UNKNOWN, ether_setup); + if (!netdev) { + dev_err(physdev, "Failed to alloc netdev.\n"); + wlan_free_wiphy(wiphy); + result = 1; + } else { + wlandev->netdev = netdev; + netdev->ml_priv = wlandev; + netdev->netdev_ops = &p80211_netdev_ops; + wdev = netdev_priv(netdev); + wdev->wiphy = wiphy; + wdev->iftype = NL80211_IFTYPE_STATION; + netdev->ieee80211_ptr = wdev; + netdev->min_mtu = 68; + /* 2312 is max 802.11 payload, 20 is overhead, + * (ether + llc + snap) and another 8 for wep. + */ + netdev->max_mtu = (2312 - 20 - 8); + + netif_stop_queue(netdev); + netif_carrier_off(netdev); + } + + return result; +} + +/*---------------------------------------------------------------- + * wlan_unsetup + * + * This function is paired with the wlan_setup routine. It should + * be called after unregister_wlandev. Basically, all it does is + * free the 'struct device' that's associated with the wlandev. + * We do it here because the 'struct device' isn't allocated + * explicitly in the driver code, it's done in wlan_setup. To + * do the free in the driver might seem like 'magic'. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * Call Context: + * Should be process thread. We'll assume it might be + * interrupt though. When we add support for statically + * compiled drivers, this function will be called in the + * context of the kernel startup code. + *---------------------------------------------------------------- + */ +void wlan_unsetup(struct wlandevice *wlandev) +{ + struct wireless_dev *wdev; + + tasklet_kill(&wlandev->rx_bh); + + if (wlandev->netdev) { + wdev = netdev_priv(wlandev->netdev); + if (wdev->wiphy) + wlan_free_wiphy(wdev->wiphy); + free_netdev(wlandev->netdev); + wlandev->netdev = NULL; + } +} + +/*---------------------------------------------------------------- + * register_wlandev + * + * Roughly matches the functionality of register_netdev. This function + * is called after the driver has successfully probed and set up the + * resources for the device. It's now ready to become a named device + * in the Linux system. + * + * First we allocate a name for the device (if not already set), then + * we call the Linux function register_netdevice. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * Returns: + * zero on success, non-zero otherwise. + * Call Context: + * Can be either interrupt or not. + *---------------------------------------------------------------- + */ +int register_wlandev(struct wlandevice *wlandev) +{ + return register_netdev(wlandev->netdev); +} + +/*---------------------------------------------------------------- + * unregister_wlandev + * + * Roughly matches the functionality of unregister_netdev. This + * function is called to remove a named device from the system. + * + * First we tell linux that the device should no longer exist. + * Then we remove it from the list of known wlan devices. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * Returns: + * zero on success, non-zero otherwise. + * Call Context: + * Can be either interrupt or not. + *---------------------------------------------------------------- + */ +int unregister_wlandev(struct wlandevice *wlandev) +{ + struct sk_buff *skb; + + unregister_netdev(wlandev->netdev); + + /* Now to clean out the rx queue */ + while ((skb = skb_dequeue(&wlandev->nsd_rxq))) + dev_kfree_skb(skb); + + return 0; +} + +/*---------------------------------------------------------------- + * p80211netdev_hwremoved + * + * Hardware removed notification. This function should be called + * immediately after an MSD has detected that the underlying hardware + * has been yanked out from under us. The primary things we need + * to do are: + * - Mark the wlandev + * - Prevent any further traffic from the knetdev i/f + * - Prevent any further requests from mgmt i/f + * - If there are any waitq'd mgmt requests or mgmt-frame exchanges, + * shut them down. + * - Call the MSD hwremoved function. + * + * The remainder of the cleanup will be handled by unregister(). + * Our primary goal here is to prevent as much tickling of the MSD + * as possible since the MSD is already in a 'wounded' state. + * + * TODO: As new features are added, this function should be + * updated. + * + * Arguments: + * wlandev WLAN network device structure + * Returns: + * nothing + * Side effects: + * + * Call context: + * Usually interrupt. + *---------------------------------------------------------------- + */ +void p80211netdev_hwremoved(struct wlandevice *wlandev) +{ + wlandev->hwremoved = 1; + if (wlandev->state == WLAN_DEVICE_OPEN) + netif_stop_queue(wlandev->netdev); + + netif_device_detach(wlandev->netdev); +} + +/*---------------------------------------------------------------- + * p80211_rx_typedrop + * + * Classifies the frame, increments the appropriate counter, and + * returns 0|1|2 indicating whether the driver should handle, ignore, or + * drop the frame + * + * Arguments: + * wlandev wlan device structure + * fc frame control field + * + * Returns: + * zero if the frame should be handled by the driver, + * one if the frame should be ignored + * anything else means we drop it. + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static int p80211_rx_typedrop(struct wlandevice *wlandev, u16 fc) +{ + u16 ftype; + u16 fstype; + int drop = 0; + /* Classify frame, increment counter */ + ftype = WLAN_GET_FC_FTYPE(fc); + fstype = WLAN_GET_FC_FSTYPE(fc); + switch (ftype) { + case WLAN_FTYPE_MGMT: + if ((wlandev->netdev->flags & IFF_PROMISC) || + (wlandev->netdev->flags & IFF_ALLMULTI)) { + drop = 1; + break; + } + netdev_dbg(wlandev->netdev, "rx'd mgmt:\n"); + wlandev->rx.mgmt++; + switch (fstype) { + case WLAN_FSTYPE_ASSOCREQ: + wlandev->rx.assocreq++; + break; + case WLAN_FSTYPE_ASSOCRESP: + wlandev->rx.assocresp++; + break; + case WLAN_FSTYPE_REASSOCREQ: + wlandev->rx.reassocreq++; + break; + case WLAN_FSTYPE_REASSOCRESP: + wlandev->rx.reassocresp++; + break; + case WLAN_FSTYPE_PROBEREQ: + wlandev->rx.probereq++; + break; + case WLAN_FSTYPE_PROBERESP: + wlandev->rx.proberesp++; + break; + case WLAN_FSTYPE_BEACON: + wlandev->rx.beacon++; + break; + case WLAN_FSTYPE_ATIM: + wlandev->rx.atim++; + break; + case WLAN_FSTYPE_DISASSOC: + wlandev->rx.disassoc++; + break; + case WLAN_FSTYPE_AUTHEN: + wlandev->rx.authen++; + break; + case WLAN_FSTYPE_DEAUTHEN: + wlandev->rx.deauthen++; + break; + default: + wlandev->rx.mgmt_unknown++; + break; + } + drop = 2; + break; + + case WLAN_FTYPE_CTL: + if ((wlandev->netdev->flags & IFF_PROMISC) || + (wlandev->netdev->flags & IFF_ALLMULTI)) { + drop = 1; + break; + } + netdev_dbg(wlandev->netdev, "rx'd ctl:\n"); + wlandev->rx.ctl++; + switch (fstype) { + case WLAN_FSTYPE_PSPOLL: + wlandev->rx.pspoll++; + break; + case WLAN_FSTYPE_RTS: + wlandev->rx.rts++; + break; + case WLAN_FSTYPE_CTS: + wlandev->rx.cts++; + break; + case WLAN_FSTYPE_ACK: + wlandev->rx.ack++; + break; + case WLAN_FSTYPE_CFEND: + wlandev->rx.cfend++; + break; + case WLAN_FSTYPE_CFENDCFACK: + wlandev->rx.cfendcfack++; + break; + default: + wlandev->rx.ctl_unknown++; + break; + } + drop = 2; + break; + + case WLAN_FTYPE_DATA: + wlandev->rx.data++; + switch (fstype) { + case WLAN_FSTYPE_DATAONLY: + wlandev->rx.dataonly++; + break; + case WLAN_FSTYPE_DATA_CFACK: + wlandev->rx.data_cfack++; + break; + case WLAN_FSTYPE_DATA_CFPOLL: + wlandev->rx.data_cfpoll++; + break; + case WLAN_FSTYPE_DATA_CFACK_CFPOLL: + wlandev->rx.data__cfack_cfpoll++; + break; + case WLAN_FSTYPE_NULL: + netdev_dbg(wlandev->netdev, "rx'd data:null\n"); + wlandev->rx.null++; + break; + case WLAN_FSTYPE_CFACK: + netdev_dbg(wlandev->netdev, "rx'd data:cfack\n"); + wlandev->rx.cfack++; + break; + case WLAN_FSTYPE_CFPOLL: + netdev_dbg(wlandev->netdev, "rx'd data:cfpoll\n"); + wlandev->rx.cfpoll++; + break; + case WLAN_FSTYPE_CFACK_CFPOLL: + netdev_dbg(wlandev->netdev, "rx'd data:cfack_cfpoll\n"); + wlandev->rx.cfack_cfpoll++; + break; + default: + wlandev->rx.data_unknown++; + break; + } + + break; + } + return drop; +} + +static void p80211knetdev_tx_timeout(struct net_device *netdev, unsigned int txqueue) +{ + struct wlandevice *wlandev = netdev->ml_priv; + + if (wlandev->tx_timeout) { + wlandev->tx_timeout(wlandev); + } else { + netdev_warn(netdev, "Implement tx_timeout for %s\n", + wlandev->nsdname); + netif_wake_queue(wlandev->netdev); + } +} |