📄 ieee80211_wireless.c
字号:
/*- * Copyright (c) 2002-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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * $Id: ieee80211_wireless.c 1676 2006-07-06 03:23:08Z brian $ *//* * Wireless extensions support for 802.11 common code. */#include <linux/config.h>#ifdef CONFIG_NET_WIRELESS#include <linux/version.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/utsname.h>#include <linux/if_arp.h> /* XXX for ARPHRD_ETHER */#include <linux/delay.h>#include <net/iw_handler.h>#if WIRELESS_EXT < 14#error "Wireless extensions v14 or better is needed."#endif#include <asm/uaccess.h>#include "if_media.h"#include <net80211/ieee80211_var.h>#include <net80211/ieee80211_linux.h>#define IS_UP(_dev) \ (((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))#define IS_UP_AUTO(_vap) \ (IS_UP((_vap)->iv_dev) && \ (_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO)#define RESCAN 1/* * Compatibility definition of statistics flags * (bitmask in (struct iw_quality *)->updated) */#ifndef IW_QUAL_QUAL_UPDATED#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */#define IW_QUAL_LEVEL_UPDATED 0x02#define IW_QUAL_NOISE_UPDATED 0x04#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */#define IW_QUAL_LEVEL_INVALID 0x20#define IW_QUAL_NOISE_INVALID 0x40#endif /* IW_QUAL_QUAL_UPDATED */#ifndef IW_QUAL_ALL_UPDATED#define IW_QUAL_ALL_UPDATED \ (IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED)#endif#ifndef IW_QUAL_ALL_INVALID#define IW_QUAL_ALL_INVALID \ (IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID | IW_QUAL_NOISE_INVALID)#endif/* * Units are in db above the noise floor. That means the * rssi values reported in the tx/rx descriptors in the * driver are the SNR expressed in db. * * If you assume that the noise floor is -95, which is an * excellent assumption 99.5 % of the time, then you can * derive the absolute signal level (i.e. -95 + rssi). * There are some other slight factors to take into account * depending on whether the rssi measurement is from 11b, * 11g, or 11a. These differences are at most 2db and * can be documented. * * NB: various calculations are based on the orinoco/wavelan * drivers for compatibility */static voidset_quality(struct iw_quality *iq, u_int rssi){ iq->qual = rssi; /* NB: max is 94 because noise is hardcoded to 161 */ if (iq->qual > 94) iq->qual = 94; iq->noise = 161; /* -95dBm */ iq->level = iq->noise + iq->qual; iq->updated = IW_QUAL_ALL_UPDATED;}static voidpreempt_scan(struct net_device *dev, int max_grace, int max_wait){ struct ieee80211vap *vap = dev->priv; struct ieee80211com *ic = vap->iv_ic; int total_delay = 0; int cancelled = 0, ready = 0; while (!ready && total_delay < max_grace + max_wait) { if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { ready = 1; } else { if (!cancelled && total_delay > max_grace) { /* Cancel any existing active scan, so that any new parameters in this scan ioctl (or the defaults) can be honored, then wait around a while to see if the scan cancels properly. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: cancel pending scan request\n", __func__); (void) ieee80211_cancel_scan(vap); cancelled = 1; } mdelay (1); total_delay += 1; } } if (!ready) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: Timeout cancelling current scan.\n", __func__); }} static struct iw_statistics *ieee80211_iw_getstats(struct net_device *dev){ struct ieee80211vap *vap = dev->priv; struct iw_statistics *is = &vap->iv_iwstats; set_quality(&is->qual, ieee80211_getrssi(vap->iv_ic)); is->status = vap->iv_state; is->discard.nwid = vap->iv_stats.is_rx_wrongbss + vap->iv_stats.is_rx_ssidmismatch; is->discard.code = vap->iv_stats.is_rx_wepfail + vap->iv_stats.is_rx_decryptcrc; is->discard.fragment = 0; is->discard.retries = 0; is->discard.misc = 0; is->miss.beacon = 0; return is;}static intieee80211_ioctl_giwname(struct net_device *dev, struct iw_request_info *info, char *name, char *extra){ struct ieee80211vap *vap = dev->priv; struct ieee80211_channel *c = vap->iv_ic->ic_curchan; if (IEEE80211_IS_CHAN_108G(c)) strncpy(name, "IEEE 802.11Tg", IFNAMSIZ); else if (IEEE80211_IS_CHAN_108A(c)) strncpy(name, "IEEE 802.11Ta", IFNAMSIZ); else if (IEEE80211_IS_CHAN_TURBO(c)) strncpy(name, "IEEE 802.11T", IFNAMSIZ); else if (IEEE80211_IS_CHAN_ANYG(c)) strncpy(name, "IEEE 802.11g", IFNAMSIZ); else if (IEEE80211_IS_CHAN_A(c)) strncpy(name, "IEEE 802.11a", IFNAMSIZ); else if (IEEE80211_IS_CHAN_B(c)) strncpy(name, "IEEE 802.11b", IFNAMSIZ); else strncpy(name, "IEEE 802.11", IFNAMSIZ); /* XXX FHSS */ return 0;}/* * Get a key index from a request. If nothing is * specified in the request we use the current xmit * key index. Otherwise we just convert the index * to be base zero. */static intgetiwkeyix(struct ieee80211vap *vap, const struct iw_point* erq, int *kix){ int kid; kid = erq->flags & IW_ENCODE_INDEX; if (kid < 1 || kid > IEEE80211_WEP_NKID) { kid = vap->iv_def_txkey; if (kid == IEEE80211_KEYIX_NONE) kid = 0; } else --kid; if (0 <= kid && kid < IEEE80211_WEP_NKID) { *kix = kid; return 0; } else return -EINVAL;}static intieee80211_ioctl_siwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf){ struct ieee80211vap *vap = dev->priv; int kid, error; int wepchange = 0; if ((erq->flags & IW_ENCODE_DISABLED) == 0) { /* * Enable crypto, set key contents, and * set the default transmit key. */ error = getiwkeyix(vap, erq, &kid); if (error < 0) return error; if (erq->length > IEEE80211_KEYBUF_SIZE) return -EINVAL; /* XXX no way to install 0-length key */ ieee80211_key_update_begin(vap); if (erq->length > 0) { struct ieee80211_key *k = &vap->iv_nw_keys[kid]; /* * Set key contents. This interface only supports WEP. * Indicate intended key index. */ k->wk_keyix = kid; if (ieee80211_crypto_newkey(vap, IEEE80211_CIPHER_WEP, IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) { k->wk_keylen = erq->length; memcpy(k->wk_key, keybuf, erq->length); memset(k->wk_key + erq->length, 0, IEEE80211_KEYBUF_SIZE - erq->length); if (!ieee80211_crypto_setkey(vap, k, vap->iv_myaddr, NULL)) error = -EINVAL; /* XXX */ } else error = -EINVAL; } else { /* * When the length is zero the request only changes * the default transmit key. Verify the new key has * a non-zero length. */ if (vap->iv_nw_keys[kid].wk_keylen == 0) error = -EINVAL; } if (error == 0) { /* * The default transmit key is only changed when: * 1. Privacy is enabled and no key matter is * specified. * 2. Privacy is currently disabled. * This is deduced from the iwconfig man page. */ if (erq->length == 0 || (vap->iv_flags & IEEE80211_F_PRIVACY) == 0) vap->iv_def_txkey = kid; wepchange = (vap->iv_flags & IEEE80211_F_PRIVACY) == 0; vap->iv_flags |= IEEE80211_F_PRIVACY; } ieee80211_key_update_end(vap); } else { if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) return 0; vap->iv_flags &= ~IEEE80211_F_PRIVACY; wepchange = 1; error = 0; } if (error == 0) { /* Set policy for unencrypted frames */ if ((erq->flags & IW_ENCODE_OPEN) && (!(erq->flags & IW_ENCODE_RESTRICTED))) { vap->iv_flags &= ~IEEE80211_F_DROPUNENC; } else if (!(erq->flags & IW_ENCODE_OPEN) && (erq->flags & IW_ENCODE_RESTRICTED)) { vap->iv_flags |= IEEE80211_F_DROPUNENC; } else { /* Default policy */ if (vap->iv_flags & IEEE80211_F_PRIVACY) vap->iv_flags |= IEEE80211_F_DROPUNENC; else vap->iv_flags &= ~IEEE80211_F_DROPUNENC; } } if (error == 0 && IS_UP(vap->iv_dev)) { /* * Device is up and running; we must kick it to * effect the change. If we're enabling/disabling * crypto use then we must re-initialize the device * so the 802.11 state machine is reset. Otherwise * the key state should have been updated above. */ if (wepchange && IS_UP_AUTO(vap)) ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); }#ifdef ATH_SUPERG_XR /* set the same params on the xr vap device if exists */ if (!error && vap->iv_xrvap && !(vap->iv_flags & IEEE80211_F_XR)) ieee80211_ioctl_siwencode(vap->iv_xrvap->iv_dev, info, erq, keybuf);#endif return error;}static intieee80211_ioctl_giwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *key){ struct ieee80211vap *vap = dev->priv; struct ieee80211_key *k; int error, kid; if (vap->iv_flags & IEEE80211_F_PRIVACY) { error = getiwkeyix(vap, erq, &kid); if (error < 0) return error; k = &vap->iv_nw_keys[kid]; /* XXX no way to return cipher/key type */ erq->flags = kid + 1; /* NB: base 1 */ if (erq->length > k->wk_keylen) erq->length = k->wk_keylen; memcpy(key, k->wk_key, erq->length); erq->flags |= IW_ENCODE_ENABLED; } else { erq->length = 0; erq->flags = IW_ENCODE_DISABLED; } if (vap->iv_flags & IEEE80211_F_DROPUNENC) erq->flags |= IW_ENCODE_RESTRICTED; else erq->flags |= IW_ENCODE_OPEN; return 0;}#ifndef ifr_media#define ifr_media ifr_ifru.ifru_ivalue#endifstatic intieee80211_ioctl_siwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra){ static const u_int mopts[] = { IFM_AUTO, IFM_IEEE80211_11A, IFM_IEEE80211_11B, IFM_IEEE80211_11G, IFM_IEEE80211_FH, IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, }; struct ieee80211vap *vap = dev->priv; struct ieee80211com *ic = vap->iv_ic; struct ifreq ifr; int rate, retv; if (vap->iv_media.ifm_cur == NULL) return -EINVAL; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_media = vap->iv_media.ifm_cur->ifm_media &~ (IFM_MMASK|IFM_TMASK); ifr.ifr_media |= mopts[vap->iv_des_mode]; if (rrq->fixed) { /* XXX fudge checking rates */ rate = ieee80211_rate2media(ic, 2 * rrq->value / 1000000, vap->iv_des_mode); if (rate == IFM_AUTO) /* NB: unknown rate */ return -EINVAL; } else rate = IFM_AUTO; ifr.ifr_media |= IFM_SUBTYPE(rate); /* refresh media capabilities based on channel */ ifmedia_removeall(&vap->iv_media); (void) ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, vap->iv_media.ifm_change, vap->iv_media.ifm_status); retv = ifmedia_ioctl(vap->iv_dev, &ifr, &vap->iv_media, SIOCSIFMEDIA); if (retv == -ENETRESET) retv = IS_UP_AUTO(vap) ? ieee80211_open(vap->iv_dev) : 0; return retv;}static intieee80211_ioctl_giwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra){ struct ieee80211vap *vap = dev->priv; struct ifmediareq imr; int rate; memset(&imr, 0, sizeof(imr)); vap->iv_media.ifm_status(vap->iv_dev, &imr); rrq->fixed = IFM_SUBTYPE(vap->iv_media.ifm_media) != IFM_AUTO; /* media status will have the current xmit rate if available */ rate = ieee80211_media2rate(imr.ifm_active); if (rate == -1) /* IFM_AUTO */ rate = 0; rrq->value = 1000000 * (rate / 2); return 0;}static intieee80211_ioctl_siwsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *sens, char *extra){ return 0;}static intieee80211_ioctl_giwsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *sens, char *extra){ sens->value = 0; sens->fixed = 1; return 0;}static intieee80211_ioctl_siwrts(struct net_device *dev, struct iw_request_info *info, struct iw_param *rts, char *extra){ struct ieee80211vap *vap = dev->priv; struct ieee80211com *ic = vap->iv_ic; u16 val; if (rts->disabled) val = IEEE80211_RTS_MAX; else if (IEEE80211_RTS_MIN <= rts->value && rts->value <= IEEE80211_RTS_MAX) val = rts->value; else return -EINVAL; if (val != vap->iv_rtsthreshold) { vap->iv_rtsthreshold = val; if (IS_UP(vap->iv_dev)) return ic->ic_reset(ic->ic_dev); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -