From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next 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(). ... --- drivers/staging/wlan-ng/p80211netdev.c | 1009 ++++++++++++++++++++++++++++++++ 1 file changed, 1009 insertions(+) create mode 100644 drivers/staging/wlan-ng/p80211netdev.c (limited to 'drivers/staging/wlan-ng/p80211netdev.c') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SIOCETHTOOL +#include +#endif + +#include +#include +#include + +#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 + *---------------------------------------------------------------- + */ +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); + } +} -- cgit v1.2.3