summaryrefslogtreecommitdiffstats
path: root/drivers/staging/wlan-ng/p80211netdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/wlan-ng/p80211netdev.c')
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.c1031
1 files changed, 1031 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..e04fc666d
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211netdev.c
@@ -0,0 +1,1031 @@
+// 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:
+ /* printk("assocreq"); */
+ wlandev->rx.assocreq++;
+ break;
+ case WLAN_FSTYPE_ASSOCRESP:
+ /* printk("assocresp"); */
+ wlandev->rx.assocresp++;
+ break;
+ case WLAN_FSTYPE_REASSOCREQ:
+ /* printk("reassocreq"); */
+ wlandev->rx.reassocreq++;
+ break;
+ case WLAN_FSTYPE_REASSOCRESP:
+ /* printk("reassocresp"); */
+ wlandev->rx.reassocresp++;
+ break;
+ case WLAN_FSTYPE_PROBEREQ:
+ /* printk("probereq"); */
+ wlandev->rx.probereq++;
+ break;
+ case WLAN_FSTYPE_PROBERESP:
+ /* printk("proberesp"); */
+ wlandev->rx.proberesp++;
+ break;
+ case WLAN_FSTYPE_BEACON:
+ /* printk("beacon"); */
+ wlandev->rx.beacon++;
+ break;
+ case WLAN_FSTYPE_ATIM:
+ /* printk("atim"); */
+ wlandev->rx.atim++;
+ break;
+ case WLAN_FSTYPE_DISASSOC:
+ /* printk("disassoc"); */
+ wlandev->rx.disassoc++;
+ break;
+ case WLAN_FSTYPE_AUTHEN:
+ /* printk("authen"); */
+ wlandev->rx.authen++;
+ break;
+ case WLAN_FSTYPE_DEAUTHEN:
+ /* printk("deauthen"); */
+ wlandev->rx.deauthen++;
+ break;
+ default:
+ /* printk("unknown"); */
+ wlandev->rx.mgmt_unknown++;
+ break;
+ }
+ /* printk("\n"); */
+ 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:
+ /* printk("pspoll"); */
+ wlandev->rx.pspoll++;
+ break;
+ case WLAN_FSTYPE_RTS:
+ /* printk("rts"); */
+ wlandev->rx.rts++;
+ break;
+ case WLAN_FSTYPE_CTS:
+ /* printk("cts"); */
+ wlandev->rx.cts++;
+ break;
+ case WLAN_FSTYPE_ACK:
+ /* printk("ack"); */
+ wlandev->rx.ack++;
+ break;
+ case WLAN_FSTYPE_CFEND:
+ /* printk("cfend"); */
+ wlandev->rx.cfend++;
+ break;
+ case WLAN_FSTYPE_CFENDCFACK:
+ /* printk("cfendcfack"); */
+ wlandev->rx.cfendcfack++;
+ break;
+ default:
+ /* printk("unknown"); */
+ wlandev->rx.ctl_unknown++;
+ break;
+ }
+ /* printk("\n"); */
+ 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:
+ /* printk("unknown"); */
+ 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);
+ }
+}