madwifi/net80211/ieee80211_linux.c

1242 lines
33 KiB
C
Raw Normal View History

/*-
* Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id$
*/
#ifndef EXPORT_SYMTAB
#define EXPORT_SYMTAB
#endif
/*
* IEEE 802.11 support (Linux-specific code)
*/
#if !defined(AUTOCONF_INCLUDED) && !defined(CONFIG_LOCALVERSION)
#include <linux/config.h>
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/sysctl.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <net/iw_handler.h>
#include <linux/wireless.h>
#include <linux/if_arp.h> /* XXX for ARPHRD_* */
#include <asm/uaccess.h>
#include "if_media.h"
#include "if_ethersubr.h"
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_monitor.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
#include <linux/device.h>
/* madwifi_name_type - device name type:
* values: 0: automatically assigned
* 1: administratively assigned
* else: reserved */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
static ssize_t show_madwifi_name_type(struct device *dev,
struct device_attribute *attr, char *buf)
#else
static ssize_t show_madwifi_name_type(struct class_device *cdev,
char *buf)
#endif
{
ssize_t len = 0;
len = snprintf(buf, PAGE_SIZE, "1");
return len;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
static DEVICE_ATTR(madwifi_name_type, S_IRUGO, show_madwifi_name_type, NULL);
#else
static CLASS_DEVICE_ATTR(madwifi_name_type, S_IRUGO, show_madwifi_name_type, NULL);
#endif
static struct attribute *ieee80211_sysfs_attrs[] = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
&dev_attr_madwifi_name_type.attr,
#else
&class_device_attr_madwifi_name_type.attr,
#endif
NULL
};
static struct attribute_group ieee80211_attr_grp = {
.name = NULL, /* No separate (sub-)directory */
.attrs = ieee80211_sysfs_attrs
};
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#define proc_net init_net.proc_net
#endif
/*
* Print a console message with the device name prepended.
*/
void
if_printf(struct net_device *dev, const char *fmt, ...)
{
va_list ap;
char buf[512]; /* XXX */
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
printk("%s: %s", dev->name, buf);
}
/*
* Allocate and setup a management frame of the specified
* size. We return the sk_buff and a pointer to the start
* of the contiguous data area that's been reserved based
* on the packet length. The data area is forced to 32-bit
* alignment and the buffer length to a multiple of 4 bytes.
* This is done mainly so beacon frames (that require this)
* can use this interface too.
*/
struct sk_buff *
This patch augments the current reference counting code with: * Counters for total outstanding instances for each resource type (skb, ath_node and ath_buf) * One pair of acquisition/release functions per resource type in unlocked and one in locked * Adds some more _debug versions of functions in the call chain that acquire/release resources so that the original func/line in the driver as well as the func/line that affected the resource use can be shown in the trace. Intermediate stack frames aren't necessary to trace the leaks. * Changes naming convention for "lock-required" functions to suffix _locked for the versions that expect locking, to be consistent with some other places in the code. * Consolidate debug messages to the helper functions that actually affect the reference count or acquire/release a resource * Additional sanity checks and leak detection (esp for detecting node ref leaks through skb) * skb references are nulled out by the new sbk unref/free function. I've tested these changes extensively and found lots of cases where we didn't get enough node references when cloning skbuff, and where the kernel drops packets due to performance issues and leaks our node references. With these changes and the tracing enabled I have verified that: * TX BUF: tx buffers always go down to zero when the tx queue is done, and you can watch tx queue usage ratio go up and down again as the driver is working. There are no leaks here at the moment, although there *are* some in the madwifi-dfs branch during CAC at the moment. * skbuff leaks in all the common flows are fixed. We were leaking node references in a lot of places where kernel was dropping skb's due to congestion and we were failing to increment node references when cloning skbuffs. These are now detected, as are skbuffs that are reaped by the kernel while still holding a node reference. * the ath_node count works correctly and on an idle system we get about 5 references per station table node, with 2 node instances per VAP. One for the bss and one for the node in the station table, I believe. The ath_node count goes up and down but always lands back at the stable number based on the vaps you have configured and the number of actual stations in the station table. The point here is that it's pretty constant what you will see over time, despite excessive node creation/release in our code during input (esp input_all). Thank god for the slab allocator. git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@2902 0192ed92-7a03-0410-a25b-9323aeb14dbd
2007-11-21 23:14:11 +03:00
#ifdef IEEE80211_DEBUG_REFCNT
ieee80211_getmgtframe_debug(u_int8_t **frm, u_int pktlen,
const char *func, int line)
This patch augments the current reference counting code with: * Counters for total outstanding instances for each resource type (skb, ath_node and ath_buf) * One pair of acquisition/release functions per resource type in unlocked and one in locked * Adds some more _debug versions of functions in the call chain that acquire/release resources so that the original func/line in the driver as well as the func/line that affected the resource use can be shown in the trace. Intermediate stack frames aren't necessary to trace the leaks. * Changes naming convention for "lock-required" functions to suffix _locked for the versions that expect locking, to be consistent with some other places in the code. * Consolidate debug messages to the helper functions that actually affect the reference count or acquire/release a resource * Additional sanity checks and leak detection (esp for detecting node ref leaks through skb) * skb references are nulled out by the new sbk unref/free function. I've tested these changes extensively and found lots of cases where we didn't get enough node references when cloning skbuff, and where the kernel drops packets due to performance issues and leaks our node references. With these changes and the tracing enabled I have verified that: * TX BUF: tx buffers always go down to zero when the tx queue is done, and you can watch tx queue usage ratio go up and down again as the driver is working. There are no leaks here at the moment, although there *are* some in the madwifi-dfs branch during CAC at the moment. * skbuff leaks in all the common flows are fixed. We were leaking node references in a lot of places where kernel was dropping skb's due to congestion and we were failing to increment node references when cloning skbuffs. These are now detected, as are skbuffs that are reaped by the kernel while still holding a node reference. * the ath_node count works correctly and on an idle system we get about 5 references per station table node, with 2 node instances per VAP. One for the bss and one for the node in the station table, I believe. The ath_node count goes up and down but always lands back at the stable number based on the vaps you have configured and the number of actual stations in the station table. The point here is that it's pretty constant what you will see over time, despite excessive node creation/release in our code during input (esp input_all). Thank god for the slab allocator. git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@2902 0192ed92-7a03-0410-a25b-9323aeb14dbd
2007-11-21 23:14:11 +03:00
#else
ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
This patch augments the current reference counting code with: * Counters for total outstanding instances for each resource type (skb, ath_node and ath_buf) * One pair of acquisition/release functions per resource type in unlocked and one in locked * Adds some more _debug versions of functions in the call chain that acquire/release resources so that the original func/line in the driver as well as the func/line that affected the resource use can be shown in the trace. Intermediate stack frames aren't necessary to trace the leaks. * Changes naming convention for "lock-required" functions to suffix _locked for the versions that expect locking, to be consistent with some other places in the code. * Consolidate debug messages to the helper functions that actually affect the reference count or acquire/release a resource * Additional sanity checks and leak detection (esp for detecting node ref leaks through skb) * skb references are nulled out by the new sbk unref/free function. I've tested these changes extensively and found lots of cases where we didn't get enough node references when cloning skbuff, and where the kernel drops packets due to performance issues and leaks our node references. With these changes and the tracing enabled I have verified that: * TX BUF: tx buffers always go down to zero when the tx queue is done, and you can watch tx queue usage ratio go up and down again as the driver is working. There are no leaks here at the moment, although there *are* some in the madwifi-dfs branch during CAC at the moment. * skbuff leaks in all the common flows are fixed. We were leaking node references in a lot of places where kernel was dropping skb's due to congestion and we were failing to increment node references when cloning skbuffs. These are now detected, as are skbuffs that are reaped by the kernel while still holding a node reference. * the ath_node count works correctly and on an idle system we get about 5 references per station table node, with 2 node instances per VAP. One for the bss and one for the node in the station table, I believe. The ath_node count goes up and down but always lands back at the stable number based on the vaps you have configured and the number of actual stations in the station table. The point here is that it's pretty constant what you will see over time, despite excessive node creation/release in our code during input (esp input_all). Thank god for the slab allocator. git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@2902 0192ed92-7a03-0410-a25b-9323aeb14dbd
2007-11-21 23:14:11 +03:00
#endif
{
const u_int align = sizeof(u_int32_t);
struct sk_buff *skb;
u_int len;
len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
This patch augments the current reference counting code with: * Counters for total outstanding instances for each resource type (skb, ath_node and ath_buf) * One pair of acquisition/release functions per resource type in unlocked and one in locked * Adds some more _debug versions of functions in the call chain that acquire/release resources so that the original func/line in the driver as well as the func/line that affected the resource use can be shown in the trace. Intermediate stack frames aren't necessary to trace the leaks. * Changes naming convention for "lock-required" functions to suffix _locked for the versions that expect locking, to be consistent with some other places in the code. * Consolidate debug messages to the helper functions that actually affect the reference count or acquire/release a resource * Additional sanity checks and leak detection (esp for detecting node ref leaks through skb) * skb references are nulled out by the new sbk unref/free function. I've tested these changes extensively and found lots of cases where we didn't get enough node references when cloning skbuff, and where the kernel drops packets due to performance issues and leaks our node references. With these changes and the tracing enabled I have verified that: * TX BUF: tx buffers always go down to zero when the tx queue is done, and you can watch tx queue usage ratio go up and down again as the driver is working. There are no leaks here at the moment, although there *are* some in the madwifi-dfs branch during CAC at the moment. * skbuff leaks in all the common flows are fixed. We were leaking node references in a lot of places where kernel was dropping skb's due to congestion and we were failing to increment node references when cloning skbuffs. These are now detected, as are skbuffs that are reaped by the kernel while still holding a node reference. * the ath_node count works correctly and on an idle system we get about 5 references per station table node, with 2 node instances per VAP. One for the bss and one for the node in the station table, I believe. The ath_node count goes up and down but always lands back at the stable number based on the vaps you have configured and the number of actual stations in the station table. The point here is that it's pretty constant what you will see over time, despite excessive node creation/release in our code during input (esp input_all). Thank god for the slab allocator. git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@2902 0192ed92-7a03-0410-a25b-9323aeb14dbd
2007-11-21 23:14:11 +03:00
skb = ieee80211_dev_alloc_skb(len + align - 1);
if (skb != NULL) {
u_int off = ((unsigned long) skb->data) % align;
if (off != 0)
skb_reserve(skb, align - off);
SKB_NI(skb) = NULL;
SKB_CB(skb)->flags = 0;
skb_reserve(skb, sizeof(struct ieee80211_frame));
*frm = skb_put(skb, pktlen);
}
return skb;
}
This patch augments the current reference counting code with: * Counters for total outstanding instances for each resource type (skb, ath_node and ath_buf) * One pair of acquisition/release functions per resource type in unlocked and one in locked * Adds some more _debug versions of functions in the call chain that acquire/release resources so that the original func/line in the driver as well as the func/line that affected the resource use can be shown in the trace. Intermediate stack frames aren't necessary to trace the leaks. * Changes naming convention for "lock-required" functions to suffix _locked for the versions that expect locking, to be consistent with some other places in the code. * Consolidate debug messages to the helper functions that actually affect the reference count or acquire/release a resource * Additional sanity checks and leak detection (esp for detecting node ref leaks through skb) * skb references are nulled out by the new sbk unref/free function. I've tested these changes extensively and found lots of cases where we didn't get enough node references when cloning skbuff, and where the kernel drops packets due to performance issues and leaks our node references. With these changes and the tracing enabled I have verified that: * TX BUF: tx buffers always go down to zero when the tx queue is done, and you can watch tx queue usage ratio go up and down again as the driver is working. There are no leaks here at the moment, although there *are* some in the madwifi-dfs branch during CAC at the moment. * skbuff leaks in all the common flows are fixed. We were leaking node references in a lot of places where kernel was dropping skb's due to congestion and we were failing to increment node references when cloning skbuffs. These are now detected, as are skbuffs that are reaped by the kernel while still holding a node reference. * the ath_node count works correctly and on an idle system we get about 5 references per station table node, with 2 node instances per VAP. One for the bss and one for the node in the station table, I believe. The ath_node count goes up and down but always lands back at the stable number based on the vaps you have configured and the number of actual stations in the station table. The point here is that it's pretty constant what you will see over time, despite excessive node creation/release in our code during input (esp input_all). Thank god for the slab allocator. git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@2902 0192ed92-7a03-0410-a25b-9323aeb14dbd
2007-11-21 23:14:11 +03:00
#ifdef IEEE80211_DEBUG_REFCNT
EXPORT_SYMBOL(ieee80211_getmgtframe_debug);
#else
EXPORT_SYMBOL(ieee80211_getmgtframe);
#endif
#if IEEE80211_VLAN_TAG_USED
/*
* VLAN support.
*/
/*
* Register a vlan group.
*/
void
ieee80211_vlan_register(struct net_device *dev, struct vlan_group *grp)
{
struct ieee80211vap *vap = netdev_priv(dev);
vap->iv_vlgrp = grp;
}
/*
* Add an rx vlan identifier
*/
void
ieee80211_vlan_add_vid(struct net_device *dev, unsigned short vid)
{
struct ieee80211vap *vap = netdev_priv(dev);
if (vap->iv_vlgrp != NULL)
vap->iv_bss->ni_vlan = vid;
}
/*
* Kill (i.e. delete) a vlan identifier.
*/
void
ieee80211_vlan_kill_vid(struct net_device *dev, unsigned short vid)
{
struct ieee80211vap *vap = netdev_priv(dev);
if (vap->iv_vlgrp != NULL)
vlan_group_set_device(vap->iv_vlgrp, vid, NULL);
}
#endif /* IEEE80211_VLAN_TAG_USED */
void
ieee80211_vlan_vattach(struct ieee80211vap *vap)
{
#if IEEE80211_VLAN_TAG_USED
struct net_device *dev = vap->iv_dev;
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
NETIF_F_HW_VLAN_FILTER;
#endif /* IEEE80211_VLAN_TAG_USED */
}
void
ieee80211_vlan_vdetach(struct ieee80211vap *vap)
{
}
void
ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
{
struct ieee80211vap *vap = ni->ni_vap;
struct net_device *dev = vap->iv_dev;
union iwreq_data wreq;
if (ni == vap->iv_bss) {
if (newassoc)
netif_carrier_on(dev);
memset(&wreq, 0, sizeof(wreq));
IEEE80211_ADDR_COPY(wreq.addr.sa_data, vap->iv_bssid);
wreq.addr.sa_family = ARPHRD_ETHER;
#ifdef ATH_SUPERG_XR
if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR)
dev = vap->iv_xrvap->iv_dev;
#endif
wireless_send_event(dev, SIOCGIWAP, &wreq, NULL);
} else {
memset(&wreq, 0, sizeof(wreq));
IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
wreq.addr.sa_family = ARPHRD_ETHER;
#ifdef ATH_SUPERG_XR
if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR)
dev = vap->iv_xrvap->iv_dev;
#endif
wireless_send_event(dev, IWEVREGISTERED, &wreq, NULL);
}
}
void
ieee80211_notify_node_leave(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct net_device *dev = vap->iv_dev;
union iwreq_data wreq;
if (ni == vap->iv_bss) {
netif_carrier_off(dev);
memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN);
wreq.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, SIOCGIWAP, &wreq, NULL);
} else {
/* fire off wireless event station leaving */
memset(&wreq, 0, sizeof(wreq));
IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
wreq.addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, IWEVEXPIRED, &wreq, NULL);
}
}
void
ieee80211_notify_sta_stats(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
static const char *tag = "STA-TRAFFIC-STAT";
struct net_device *dev = vap->iv_dev;
union iwreq_data wreq;
char buf[256];
snprintf(buf, sizeof(buf), "%s\nmac=" MAC_FMT "\nrx_packets=%u\nrx_bytes=%llu\n"
"tx_packets=%u\ntx_bytes=%llu\n", tag,
MAC_ADDR(ni->ni_macaddr), ni->ni_stats.ns_rx_data,
(unsigned long long)ni->ni_stats.ns_rx_bytes,
ni->ni_stats.ns_tx_data,
(unsigned long long)ni->ni_stats.ns_tx_bytes);
memset(&wreq, 0, sizeof(wreq));
wreq.data.length = strlen(buf);
wireless_send_event(dev, IWEVCUSTOM, &wreq, buf);
}
void
ieee80211_notify_scan_done(struct ieee80211vap *vap)
{
struct net_device *dev = vap->iv_dev;
union iwreq_data wreq;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
/* dispatch wireless event indicating scan completed */
wreq.data.length = 0;
wreq.data.flags = 0;
wireless_send_event(dev, SIOCGIWSCAN, &wreq, NULL);
}
void
ieee80211_notify_replay_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, const struct ieee80211_key *k,
u_int64_t rsc)
{
static const char *tag = "MLME-REPLAYFAILURE.indication";
struct net_device *dev = vap->iv_dev;
union iwreq_data wrqu;
char buf[128]; /* XXX */
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"%s replay detected <keyix %d, rsc %llu >",
k->wk_cipher->ic_name, k->wk_keyix,
(unsigned long long)rsc);
/* TODO: needed parameters: count, keyid, key type, src address, TSC */
snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=" MAC_FMT ")", tag,
k->wk_keyix,
IEEE80211_IS_MULTICAST(wh->i_addr2) ? "broad" : "uni",
MAC_ADDR(wh->i_addr2));
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
}
EXPORT_SYMBOL(ieee80211_notify_replay_failure);
void
ieee80211_notify_michael_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, ieee80211_keyix_t keyix)
{
static const char *tag = "MLME-MICHAELMICFAILURE.indication";
struct net_device *dev = vap->iv_dev;
union iwreq_data wrqu;
char buf[128]; /* XXX */
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"Michael MIC verification failed <keyix %d>", keyix);
vap->iv_stats.is_rx_tkipmic++;
/* TODO: needed parameters: count, keyid, key type, src address, TSC */
snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=" MAC_FMT ")", tag,
keyix, IEEE80211_IS_MULTICAST(wh->i_addr2) ? "broad" : "uni",
MAC_ADDR(wh->i_addr2));
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
}
EXPORT_SYMBOL(ieee80211_notify_michael_failure);
/* This function might sleep. Therefore:
* Context: process
*
* Note that a successful call to this function does not guarantee that
* the services provided by the requested module are available:
*
* "Note that a successful module load does not mean the module did not
* then unload and exit on an error of its own. Callers must check that
* the service they requested is now available not blindly invoke it."
* http://kernelnewbies.org/documents/kdoc/kernel-api/r7338.html
*/
int
ieee80211_load_module(const char *modname)
{
#if defined(CONFIG_KMOD) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
int rv;
rv = request_module("%s", modname);
if (rv < 0)
printk(KERN_ERR "failed to automatically load module: %s; " \
"errno: %d\n", modname, rv);
return rv;
#else /* CONFIG_KMOD || 2.6.27+ */
printk(KERN_ERR "Unable to load needed module: %s; no support for " \
"automatic module loading\n", modname);
return -ENOSYS;
#endif /* CONFIG_KMOD || 2.6.27+ */
}
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_madwifi;
static int proc_madwifi_count = 0;
static int
proc_read_nodes(struct ieee80211vap *vap, char *buf, int space)
{
char *p = buf;
struct ieee80211_node *ni;
struct ieee80211_node_table *nt =
(struct ieee80211_node_table *)&vap->iv_ic->ic_sta;
IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
struct timespec t;
/* Assume each node needs 500 bytes */
if (buf + space < p + 500)
break;
if ((ni->ni_vap == vap) && (memcmp(vap->iv_myaddr,
ni->ni_macaddr, IEEE80211_ADDR_LEN) != 0)) {
jiffies_to_timespec(jiffies - ni->ni_last_rx, &t);
p += sprintf(p, "macaddr: <" MAC_FMT ">\n",
MAC_ADDR(ni->ni_macaddr));
p += sprintf(p, " RSSI %d\n", ni->ni_rssi);
p += sprintf(p, " last_rx %ld.%06ld\n",
t.tv_sec, t.tv_nsec / 1000);
p += sprintf(p, " ni_tstamp %10llu ni_rtsf %10llu\n",
(unsigned long long)le64_to_cpu(ni->ni_tstamp.tsf),
(unsigned long long)ni->ni_rtsf);
}
}
IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
return (p - buf);
}
static int
proc_doth_print(struct ieee80211vap *vap, char *buf, int space)
{
struct ieee80211com *ic = vap->iv_ic;
char *p = buf;
struct ieee80211_channel *channel;
int i;
char str[50];
for (i = 0; i < ic->ic_nchans; i++) {
/* Assume each lines needs 500 bytes max */
if (buf + space < p + 500)
break;
channel = &ic->ic_channels[i];
if ((ic->ic_chan_non_occupy[i].tv_sec == 0) &&
(ic->ic_chan_non_occupy[i].tv_usec) == 0) {
str[0] = 0; /* empty string */
} else {
sprintf(str, " End: %ld.%06ld",
ic->ic_chan_non_occupy[i].tv_sec,
(long)ic->ic_chan_non_occupy[i].tv_usec);
}
p += sprintf(p,
"Channel %3d (%4d MHz) : %s %s %s%s%s%s\n",
channel->ic_ieee,
channel->ic_freq,
isset(ic->ic_chan_active, channel->ic_ieee) ?
" Active" : "Inactive",
IEEE80211_IS_CHAN_PASSIVE(channel) ? " Dfs":"NoDfs",
IEEE80211_IS_CHAN_RADAR(channel) ? " Radar":"NoRadar",
IEEE80211_IS_CHAN_INDOOR(channel) ? " Indoor" : "",
IEEE80211_IS_CHAN_OUTDOOR(channel) ? " Outdoor" : "",
str);
}
return (p - buf);
}
static int
proc_doth_state_print(struct ieee80211vap *vap, char *buf, int space)
{
struct ieee80211com *ic = vap->iv_ic;
char *p = buf;
struct net_device *dev = ic->ic_dev;
struct ath_softc *sc = netdev_priv(dev);
p += sprintf(p,
"sc_curchan: --- (%4d MHz)\n",
sc->sc_curchan.channel);
p += sprintf(p,
" CHANNEL_DFS:%d\n"
" CHANNEL_DFS_CLEAR:%d\n"
" CHANNEL_INTERFERENCE:%d\n"
" CAC in progress:%d\n",
sc->sc_curchan.privFlags & CHANNEL_DFS ? 1 : 0,
sc->sc_curchan.privFlags & CHANNEL_DFS_CLEAR ? 1 : 0,
sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE ? 1 : 0,
timer_pending(&sc->sc_dfs_cac_timer));
p += sprintf(p,
"ic_curchan: %3d (%4d MHz)\n"
" IEEE80211_F_DOTH:%d\n"
" IEEE80211_IS_CHAN_RADAR:%d\n",
ic->ic_curchan->ic_ieee,
ic->ic_curchan->ic_freq,
ic->ic_flags & IEEE80211_F_DOTH ? 1 : 0,
IEEE80211_IS_CHAN_RADAR(ic->ic_curchan) ? 1 : 0);
return (p - buf);
}
static int
proc_iv_bss_print(struct ieee80211vap *vap, char *buf, int space)
{
char *p = buf;
const struct ieee80211_node *ni = vap->iv_bss;
if (ni == NULL) {
p += sprintf(p, "ni: NULL\n");
return (p - buf);
}
p += sprintf(p, "ni_macaddr: " MAC_FMT "\n"
"ni_bssid: " MAC_FMT "\n"
"ni_tstamp: 0x%llx us\n"
"ni_intval: %u TU\n"
"ni_capinfo: 0x%x%s%s%s%s%s%s%s%s%s%s%s%s\n",
MAC_ADDR(ni->ni_macaddr),
MAC_ADDR(ni->ni_bssid),
le64_to_cpu(ni->ni_tstamp.tsf),
ni->ni_intval,
ni->ni_capinfo,
ni->ni_capinfo & IEEE80211_CAPINFO_ESS ? " ESS" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_IBSS ? " IBSS" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLABLE ?
" CF_POLLABLE" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLREQ ?
" CF_POLLREQ" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY ?
" PRIVACY" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE ?
" SHORT_PREAMBLE" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_PBCC ? " PBCC" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_CHNL_AGILITY ?
" CHANNEL_AGILITY" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT ?
" SPECTRUM_MGMT" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME ?
" SHORT_SLOTTIME" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_RSN ? " RSN" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_DSSSOFDM ?
"DSSSOFDM" : "");
if (ni->ni_chan == NULL) {
p += sprintf(p, "ni_chan: NULL\n");
} else if (ni->ni_chan == IEEE80211_CHAN_ANYC) {
p += sprintf(p, "ni_chan: ANY\n");
} else {
p += sprintf(p,
"ni_chan: Frequency:%4u MHz Channel:%3u\n",
ni->ni_chan->ic_freq, ni->ni_chan->ic_ieee);
}
return (p - buf);
}
static ssize_t
proc_ieee80211_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
{
loff_t pos = *offset;
struct proc_ieee80211_priv *pv =
(struct proc_ieee80211_priv *)file->private_data;
if (!pv->rbuf)
return -EINVAL;
if (pos < 0)
return -EINVAL;
if (pos > pv->rlen)
return -EFAULT;
if (len > pv->rlen - pos)
len = pv->rlen - pos;
if (copy_to_user(buf, pv->rbuf + pos, len))
return -EFAULT;
*offset = pos + len;
return len;
}
static int
proc_common_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv;
if (!(file->private_data = kzalloc(sizeof(struct proc_ieee80211_priv),
GFP_KERNEL)))
return -ENOMEM;
/* initially allocate both read and write buffers */
pv = (struct proc_ieee80211_priv *)file->private_data;
pv->rbuf = vmalloc(MAX_PROC_IEEE80211_SIZE);
if (!pv->rbuf) {
kfree(pv);
return -ENOMEM;
}
pv->wbuf = vmalloc(MAX_PROC_IEEE80211_SIZE);
if (!pv->wbuf) {
vfree(pv->rbuf);
kfree(pv);
return -ENOMEM;
}
memset(pv->wbuf, 0, MAX_PROC_IEEE80211_SIZE);
memset(pv->rbuf, 0, MAX_PROC_IEEE80211_SIZE);
pv->max_wlen = MAX_PROC_IEEE80211_SIZE;
pv->max_rlen = MAX_PROC_IEEE80211_SIZE;
return 0;
}
static int
proc_ieee80211_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct ieee80211vap *vap = PDE_DATA(inode);
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_read_nodes(vap, pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static int
proc_doth_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct ieee80211vap *vap = PDE_DATA(inode);
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_doth_print(vap, pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static int
proc_doth_state_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct ieee80211vap *vap = PDE_DATA(inode);
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_doth_state_print(vap,
pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static int
proc_iv_bss_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct ieee80211vap *vap = PDE_DATA(inode);
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_iv_bss_print(vap, pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static ssize_t
proc_ieee80211_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
{
loff_t pos = *offset;
struct proc_ieee80211_priv *pv =
(struct proc_ieee80211_priv *)file->private_data;
if (!pv->wbuf)
return -EINVAL;
if (pos < 0)
return -EINVAL;
if (pos >= pv->max_wlen)
return 0;
if (len > pv->max_wlen - pos)
len = pv->max_wlen - pos;
if (copy_from_user(pv->wbuf + pos, buf, len))
return -EFAULT;
if (pos + len > pv->wlen)
pv->wlen = pos + len;
*offset = pos + len;
return len;
}
static int
proc_ieee80211_close(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv =
(struct proc_ieee80211_priv *)file->private_data;
if (pv->rbuf)
vfree(pv->rbuf);
if (pv->wbuf)
vfree(pv->wbuf);
kfree(pv);
return 0;
}
static struct file_operations proc_ieee80211_ops = {
.read = proc_ieee80211_read,
.write = proc_ieee80211_write,
.open = proc_ieee80211_open,
.release = proc_ieee80211_close,
};
static struct file_operations proc_doth_ops = {
.read = proc_ieee80211_read,
.write = proc_ieee80211_write,
.open = proc_doth_open,
.release = proc_ieee80211_close,
};
static struct file_operations proc_doth_state_ops = {
.read = proc_ieee80211_read,
.write = proc_ieee80211_write,
.open = proc_doth_state_open,
.release = proc_ieee80211_close,
};
static struct file_operations proc_iv_bss_ops = {
.read = proc_ieee80211_read,
.write = proc_ieee80211_write,
.open = proc_iv_bss_open,
.release = proc_ieee80211_close,
};
#endif /* CONFIG_PROC_FS */
#ifdef IEEE80211_DEBUG
static int
IEEE80211_SYSCTL_DECL(ieee80211_sysctl_debug, ctl, write, filp, buffer,
lenp, ppos)
{
struct ieee80211vap *vap = ctl->extra1;
u_int val;
int ret;
ctl->data = &val;
ctl->maxlen = sizeof(val);
if (write) {
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
if (ret == 0) {
vap->iv_debug = (val & ~IEEE80211_MSG_IC);
vap->iv_ic->ic_debug = (val & IEEE80211_MSG_IC);
printk(KERN_INFO "%s debug flags changed to 0x%08x.\n",
vap->iv_dev->name, val);
}
} else {
/* VAP specific and 'global' debug flags */
val = vap->iv_debug | vap->iv_ic->ic_debug;
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
}
return ret;
}
#endif /* IEEE80211_DEBUG */
static int
IEEE80211_SYSCTL_DECL(ieee80211_sysctl_dev_type, ctl, write, filp, buffer,
lenp, ppos)
{
struct ieee80211vap *vap = ctl->extra1;
u_int val;
int ret;
ctl->data = &val;
ctl->maxlen = sizeof(val);
if (write) {
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
if (ret == 0 && vap->iv_opmode == IEEE80211_M_MONITOR) {
if (val == ARPHRD_IEEE80211_RADIOTAP ||
val == ARPHRD_IEEE80211 ||
val == ARPHRD_IEEE80211_PRISM ||
val == ARPHRD_IEEE80211_ATHDESC) {
vap->iv_dev->type = val;
}
}
} else {
val = vap->iv_dev->type;
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
}
return ret;
}
static int
IEEE80211_SYSCTL_DECL(ieee80211_sysctl_monitor_nods_only, ctl, write, filp, buffer,
lenp, ppos)
{
struct ieee80211vap *vap = ctl->extra1;
u_int val;
int ret;
ctl->data = &val;
ctl->maxlen = sizeof(val);
if (write) {
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
if (ret == 0)
vap->iv_monitor_nods_only = val;
} else {
val = vap->iv_monitor_nods_only;
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
}
return ret;
}
static int
IEEE80211_SYSCTL_DECL(ieee80211_sysctl_monitor_txf_len, ctl, write, filp, buffer,
lenp, ppos)
{
struct ieee80211vap *vap = ctl->extra1;
u_int val;
int ret;
ctl->data = &val;
ctl->maxlen = sizeof(val);
if (write) {
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
if (ret == 0)
vap->iv_monitor_txf_len = val;
} else {
val = vap->iv_monitor_txf_len;
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
}
return ret;
}
static int
IEEE80211_SYSCTL_DECL(ieee80211_sysctl_monitor_phy_errors, ctl, write, filp, buffer,
lenp, ppos)
{
struct ieee80211vap *vap = ctl->extra1;
u_int val;
int ret;
ctl->data = &val;
ctl->maxlen = sizeof(val);
if (write) {
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
if (ret == 0)
vap->iv_monitor_phy_errors = val;
} else {
val = vap->iv_monitor_phy_errors;
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
}
return ret;
}
static int
IEEE80211_SYSCTL_DECL(ieee80211_sysctl_monitor_crc_errors, ctl, write, filp, buffer,
lenp, ppos)
{
struct ieee80211vap *vap = ctl->extra1;
u_int val;
int ret;
ctl->data = &val;
ctl->maxlen = sizeof(val);
if (write) {
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
if (ret == 0)
vap->iv_monitor_crc_errors = val;
} else {
val = vap->iv_monitor_crc_errors;
ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
lenp, ppos);
}
return ret;
}
static const struct ctl_table ieee80211_sysctl_template[] = {
#ifdef IEEE80211_DEBUG
{ ATH_INIT_CTL_NAME(CTL_AUTO)
.procname = "debug",
.mode = 0644,
.proc_handler = ieee80211_sysctl_debug
},
#endif
{ ATH_INIT_CTL_NAME(CTL_AUTO)
.procname = "dev_type",
.mode = 0644,
.proc_handler = ieee80211_sysctl_dev_type
},
{ ATH_INIT_CTL_NAME(CTL_AUTO)
.procname = "monitor_nods_only",
.mode = 0644,
.proc_handler = ieee80211_sysctl_monitor_nods_only
},
{ ATH_INIT_CTL_NAME(CTL_AUTO)
.procname = "monitor_txf_len",
.mode = 0644,
.proc_handler = ieee80211_sysctl_monitor_txf_len
},
{ ATH_INIT_CTL_NAME(CTL_AUTO)
.procname = "monitor_phy_errors",
.mode = 0644,
.proc_handler = ieee80211_sysctl_monitor_phy_errors
},
{ ATH_INIT_CTL_NAME(CTL_AUTO)
.procname = "monitor_crc_errors",
.mode = 0644,
.proc_handler = ieee80211_sysctl_monitor_crc_errors
},
/* NB: must be last entry before NULL */
{ ATH_INIT_CTL_NAME(CTL_AUTO)
.procname = "%parent",
.maxlen = IFNAMSIZ,
.mode = 0444,
.proc_handler = proc_dostring
},
{ }
};
void
ieee80211_virtfs_latevattach(struct ieee80211vap *vap)
{
int i, space;
char *devname = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
int ret;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
ret = sysfs_create_group(&vap->iv_dev->dev.kobj, &ieee80211_attr_grp);
#else
ret = sysfs_create_group(&vap->iv_dev->class_dev.kobj, &ieee80211_attr_grp);
#endif
if (ret) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
sysfs_remove_group(&vap->iv_dev->dev.kobj, &ieee80211_attr_grp);
#else
sysfs_remove_group(&vap->iv_dev->class_dev.kobj, &ieee80211_attr_grp);
#endif
printk("%s: %s - unable to create sysfs attribute group\n",
__func__, vap->iv_dev->name);
return;
}
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */
space = 5 * sizeof(struct ctl_table) + sizeof(ieee80211_sysctl_template);
vap->iv_sysctls = kzalloc(space, GFP_KERNEL);
if (vap->iv_sysctls == NULL) {
printk("%s: no memory for sysctl table!\n", __func__);
return;
}
/*
* Reserve space for the device name outside the net_device structure
* so that if the name changes we know what it used to be.
*/
devname = kmalloc((strlen(vap->iv_dev->name) + 1) * sizeof(char), GFP_KERNEL);
if (devname == NULL) {
printk("%s: no memory for VAP name!\n", __func__);
return;
}
strncpy(devname, vap->iv_dev->name, strlen(vap->iv_dev->name) + 1);
/* setup the table */
ATH_SET_CTL_NAME(vap->iv_sysctls[0], CTL_NET);
vap->iv_sysctls[0].procname = "net";
vap->iv_sysctls[0].mode = 0555;
vap->iv_sysctls[0].child = &vap->iv_sysctls[2];
/* [1] is NULL terminator */
ATH_SET_CTL_NAME(vap->iv_sysctls[2], CTL_AUTO);
vap->iv_sysctls[2].procname = devname; /* XXX bad idea? */
vap->iv_sysctls[2].mode = 0555;
vap->iv_sysctls[2].child = &vap->iv_sysctls[4];
/* [3] is NULL terminator */
/* copy in pre-defined data */
memcpy(&vap->iv_sysctls[4], ieee80211_sysctl_template,
sizeof(ieee80211_sysctl_template));
/* add in dynamic data references */
for (i = 4; vap->iv_sysctls[i].procname; i++)
if (vap->iv_sysctls[i].extra1 == NULL)
vap->iv_sysctls[i].extra1 = vap;
/* tack on back-pointer to parent device */
vap->iv_sysctls[i-1].data = vap->iv_ic->ic_dev->name; /* XXX? */
/* and register everything */
vap->iv_sysctl_header = ATH_REGISTER_SYSCTL_TABLE(vap->iv_sysctls);
if (!vap->iv_sysctl_header) {
printk("%s: failed to register sysctls!\n", vap->iv_dev->name);
kfree(devname);
kfree(vap->iv_sysctls);
vap->iv_sysctls = NULL;
}
#ifdef CONFIG_PROC_FS
/* Ensure the base madwifi directory exists */
if (!proc_madwifi && proc_net != NULL) {
proc_madwifi = proc_mkdir("madwifi", proc_net);
if (!proc_madwifi)
printk(KERN_WARNING "Failed to mkdir /proc/net/madwifi\n");
}
/* Create a proc directory named after the VAP */
if (proc_madwifi) {
proc_madwifi_count++;
vap->iv_proc = proc_mkdir(vap->iv_dev->name, proc_madwifi);
}
/* Create a proc entry listing the associated stations */
ieee80211_proc_vcreate(vap, &proc_ieee80211_ops, "associated_sta");
ieee80211_proc_vcreate(vap, &proc_doth_ops, "doth");
ieee80211_proc_vcreate(vap, &proc_doth_state_ops, "doth_state");
ieee80211_proc_vcreate(vap, &proc_iv_bss_ops, "iv_bss");
/* Recreate any other proc entries that have been registered */
if (vap->iv_proc) {
struct ieee80211_proc_entry *tmp = vap->iv_proc_entries;
while (tmp) {
if (!tmp->entry) {
tmp->entry = proc_create_data(tmp->name,
PROC_IEEE80211_PERM, vap->iv_proc,
tmp->fileops, vap);
}
tmp = tmp->next;
}
}
#endif /* CONFIG_PROC_FS */
}
#ifdef CONFIG_PROC_FS
/* Frees all memory used for the list of proc entries */
void
ieee80211_proc_cleanup(struct ieee80211vap *vap)
{
struct ieee80211_proc_entry *tmp = vap->iv_proc_entries;
struct ieee80211_proc_entry *next = NULL;
while (tmp) {
next = tmp->next;
kfree(tmp);
tmp = next;
}
}
/* Called by other modules to register a proc entry under the vap directory */
int
ieee80211_proc_vcreate(struct ieee80211vap *vap,
struct file_operations *fileops, char *name)
{
struct ieee80211_proc_entry *entry;
struct ieee80211_proc_entry *tmp = NULL;
/* Ignore if already in the list */
if (vap->iv_proc_entries) {
tmp = vap->iv_proc_entries;
do {
if (strcmp(tmp->name, name)==0)
return -1;
/* Check for end of list */
if (!tmp->next)
break;
/* Otherwise move on */
tmp = tmp->next;
} while (1);
}
/* Create an item in our list for the new entry */
entry = kmalloc(sizeof(struct ieee80211_proc_entry), GFP_KERNEL);
if (entry == NULL) {
printk("%s: no memory for new proc entry (%s)!\n", __func__,
name);
return -1;
}
/* Replace null fileops pointers with our standard functions */
if (!fileops->open)
fileops->open = proc_ieee80211_open;
if (!fileops->release)
fileops->release = proc_ieee80211_close;
if (!fileops->read)
fileops->read = proc_ieee80211_read;
if (!fileops->write)
fileops->write = proc_ieee80211_write;
/* Create the entry record */
entry->name = name;
entry->fileops = fileops;
entry->next = NULL;
entry->entry = NULL;
/* Create the actual proc entry */
if (vap->iv_proc) {
entry->entry = proc_create_data(entry->name,
PROC_IEEE80211_PERM, vap->iv_proc,
entry->fileops, vap);
}
/* Add it to the list */
if (!tmp) {
/* Add to the start */
vap->iv_proc_entries = entry;
} else {
/* Add to the end */
tmp->next = entry;
}
return 0;
}
EXPORT_SYMBOL(ieee80211_proc_vcreate);
#endif /* CONFIG_PROC_FS */
void
ieee80211_virtfs_vdetach(struct ieee80211vap *vap)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
sysfs_remove_group(&vap->iv_dev->dev.kobj, &ieee80211_attr_grp);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
sysfs_remove_group(&vap->iv_dev->class_dev.kobj, &ieee80211_attr_grp);
#endif
if (vap->iv_sysctl_header) {
unregister_sysctl_table(vap->iv_sysctl_header);
vap->iv_sysctl_header = NULL;
}
#ifdef CONFIG_PROC_FS
if (vap->iv_proc) {
/* Remove child proc entries but leave them in the list */
struct ieee80211_proc_entry *tmp = vap->iv_proc_entries;
while (tmp) {
if (tmp->entry) {
proc_remove(tmp->entry);
tmp->entry = NULL;
}
tmp = tmp->next;
}
proc_remove(vap->iv_proc);
if (proc_madwifi_count == 1) {
proc_remove(proc_madwifi);
proc_madwifi = NULL;
}
proc_madwifi_count--;
}
#endif /* CONFIG_PROC_FS */
if (vap->iv_sysctls && vap->iv_sysctls[2].procname) {
kfree(vap->iv_sysctls[2].procname);
vap->iv_sysctls[2].procname = NULL;
}
if (vap->iv_sysctls) {
kfree(vap->iv_sysctls);
vap->iv_sysctls = NULL;
}
}
/* Function to handle the device event notifications.
* If the event is a NETDEV_CHANGENAME, and is for an interface
* we are taking care of, then we want to remove its existing
* proc entries (which now have the wrong names) and add
* new, correct, entries.
*/
static int
ieee80211_rcv_dev_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
if (!dev || dev->open != &ieee80211_open)
return 0;
#else
if (!dev || dev->netdev_ops->ndo_open != &ieee80211_open)
return 0;
#endif
switch (event) {
case NETDEV_CHANGENAME:
ieee80211_virtfs_vdetach(netdev_priv(dev));
ieee80211_virtfs_latevattach(netdev_priv(dev));
return NOTIFY_DONE;
default:
break;
}
return 0;
}
static struct notifier_block ieee80211_event_block = {
.notifier_call = ieee80211_rcv_dev_event
};
/*
* Module glue.
*/
#include "release.h"
#if 0
static char *version = RELEASE_VERSION;
static char *dev_info = "wlan";
#endif
MODULE_AUTHOR("Errno Consulting, Sam Leffler");
MODULE_DESCRIPTION("802.11 wireless LAN protocol support");
#ifdef MODULE_VERSION
MODULE_VERSION(RELEASE_VERSION);
#endif
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual BSD/GPL");
#endif
extern void ieee80211_auth_setup(void);
static int __init
init_wlan(void)
{
register_netdevice_notifier(&ieee80211_event_block);
return 0;
}
module_init(init_wlan);
static void __exit
exit_wlan(void)
{
unregister_netdevice_notifier(&ieee80211_event_block);
}
module_exit(exit_wlan);