linux/net/mac80211/ieee80211_ioctl.c
Daniel Drake 7e9ed18874 [MAC80211]: improved short preamble handling
Similarly to CTS protection, whether short preambles are used for 802.11b
transmissions should be a per-subif setting, not device global.

For STAs, this patch makes short preamble handling automatic based on the ERP
IE. For APs, hostapd still uses the prism ioctls, but the write ioctl has been
restricted to AP-only subifs.

ieee80211_txrx_data.short_preamble (an unused field) was removed.

Unfortunately, some API changes were required for the following functions:
 - ieee80211_generic_frame_duration
 - ieee80211_rts_duration
 - ieee80211_ctstoself_duration
 - ieee80211_rts_get
 - ieee80211_ctstoself_get
Affected drivers were updated accordingly.

Signed-off-by: Daniel Drake <dsd@gentoo.org>
Signed-off-by: Jiri Benc <jbenc@suse.cz>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2007-10-10 16:47:38 -07:00

1621 lines
42 KiB
C

/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <asm/uaccess.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "hostapd_ioctl.h"
#include "ieee80211_rate.h"
#include "wpa.h"
#include "aes_ccm.h"
#include "debugfs_key.h"
static void ieee80211_set_hw_encryption(struct net_device *dev,
struct sta_info *sta, u8 addr[ETH_ALEN],
struct ieee80211_key *key)
{
struct ieee80211_key_conf *keyconf = NULL;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
/* default to sw encryption; this will be cleared by low-level
* driver if the hw supports requested encryption */
if (key)
key->force_sw_encrypt = 1;
if (key && local->ops->set_key &&
(keyconf = ieee80211_key_data2conf(local, key))) {
if (local->ops->set_key(local_to_hw(local), SET_KEY, addr,
keyconf, sta ? sta->aid : 0)) {
key->force_sw_encrypt = 1;
key->hw_key_idx = HW_KEY_IDX_INVALID;
} else {
key->force_sw_encrypt =
!!(keyconf->flags & IEEE80211_KEY_FORCE_SW_ENCRYPT);
key->hw_key_idx =
keyconf->hw_key_idx;
}
}
kfree(keyconf);
}
static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
int idx, int alg, int set_tx_key,
const u8 *_key, size_t key_len)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
int ret = 0;
struct sta_info *sta;
struct ieee80211_key *key, *old_key;
int try_hwaccel = 1;
struct ieee80211_key_conf *keyconf;
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (is_broadcast_ether_addr(sta_addr)) {
sta = NULL;
if (idx >= NUM_DEFAULT_KEYS) {
printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n",
dev->name, idx);
return -EINVAL;
}
key = sdata->keys[idx];
/* TODO: consider adding hwaccel support for these; at least
* Atheros key cache should be able to handle this since AP is
* only transmitting frames with default keys. */
/* FIX: hw key cache can be used when only one virtual
* STA is associated with each AP. If more than one STA
* is associated to the same AP, software encryption
* must be used. This should be done automatically
* based on configured station devices. For the time
* being, this can be only set at compile time. */
} else {
set_tx_key = 0;
if (idx != 0) {
printk(KERN_DEBUG "%s: set_encrypt - non-zero idx for "
"individual key\n", dev->name);
return -EINVAL;
}
sta = sta_info_get(local, sta_addr);
if (!sta) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: set_encrypt - unknown addr "
MAC_FMT "\n",
dev->name, MAC_ARG(sta_addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
return -ENOENT;
}
key = sta->key;
}
/* FIX:
* Cannot configure default hwaccel keys with WEP algorithm, if
* any of the virtual interfaces is using static WEP
* configuration because hwaccel would otherwise try to decrypt
* these frames.
*
* For now, just disable WEP hwaccel for broadcast when there is
* possibility of conflict with default keys. This can maybe later be
* optimized by using non-default keys (at least with Atheros ar521x).
*/
if (!sta && alg == ALG_WEP && !local->default_wep_only &&
sdata->type != IEEE80211_IF_TYPE_IBSS &&
sdata->type != IEEE80211_IF_TYPE_AP) {
try_hwaccel = 0;
}
if (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) {
/* Software encryption cannot be used with devices that hide
* encryption from the host system, so always try to use
* hardware acceleration with such devices. */
try_hwaccel = 1;
}
if ((local->hw.flags & IEEE80211_HW_NO_TKIP_WMM_HWACCEL) &&
alg == ALG_TKIP) {
if (sta && (sta->flags & WLAN_STA_WME)) {
/* Hardware does not support hwaccel with TKIP when using WMM.
*/
try_hwaccel = 0;
}
else if (sdata->type == IEEE80211_IF_TYPE_STA) {
sta = sta_info_get(local, sdata->u.sta.bssid);
if (sta) {
if (sta->flags & WLAN_STA_WME) {
try_hwaccel = 0;
}
sta_info_put(sta);
sta = NULL;
}
}
}
if (alg == ALG_NONE) {
keyconf = NULL;
if (try_hwaccel && key &&
key->hw_key_idx != HW_KEY_IDX_INVALID &&
local->ops->set_key &&
(keyconf = ieee80211_key_data2conf(local, key)) != NULL &&
local->ops->set_key(local_to_hw(local), DISABLE_KEY,
sta_addr, keyconf, sta ? sta->aid : 0)) {
printk(KERN_DEBUG "%s: set_encrypt - low-level disable"
" failed\n", dev->name);
ret = -EINVAL;
}
kfree(keyconf);
if (set_tx_key || sdata->default_key == key) {
ieee80211_debugfs_key_remove_default(sdata);
sdata->default_key = NULL;
}
ieee80211_debugfs_key_remove(key);
if (sta)
sta->key = NULL;
else
sdata->keys[idx] = NULL;
ieee80211_key_free(key);
key = NULL;
} else {
old_key = key;
key = ieee80211_key_alloc(sta ? NULL : sdata, idx, key_len,
GFP_KERNEL);
if (!key) {
ret = -ENOMEM;
goto err_out;
}
/* default to sw encryption; low-level driver sets these if the
* requested encryption is supported */
key->hw_key_idx = HW_KEY_IDX_INVALID;
key->force_sw_encrypt = 1;
key->alg = alg;
key->keyidx = idx;
key->keylen = key_len;
memcpy(key->key, _key, key_len);
if (set_tx_key)
key->default_tx_key = 1;
if (alg == ALG_CCMP) {
/* Initialize AES key state here as an optimization
* so that it does not need to be initialized for every
* packet. */
key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
key->key);
if (!key->u.ccmp.tfm) {
ret = -ENOMEM;
goto err_free;
}
}
if (set_tx_key || sdata->default_key == old_key) {
ieee80211_debugfs_key_remove_default(sdata);
sdata->default_key = NULL;
}
ieee80211_debugfs_key_remove(old_key);
if (sta)
sta->key = key;
else
sdata->keys[idx] = key;
ieee80211_key_free(old_key);
ieee80211_debugfs_key_add(local, key);
if (sta)
ieee80211_debugfs_key_sta_link(key, sta);
if (try_hwaccel &&
(alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP))
ieee80211_set_hw_encryption(dev, sta, sta_addr, key);
}
if (set_tx_key || (!sta && !sdata->default_key && key)) {
sdata->default_key = key;
if (key)
ieee80211_debugfs_key_add_default(sdata);
if (local->ops->set_key_idx &&
local->ops->set_key_idx(local_to_hw(local), idx))
printk(KERN_DEBUG "%s: failed to set TX key idx for "
"low-level driver\n", dev->name);
}
if (sta)
sta_info_put(sta);
return 0;
err_free:
ieee80211_key_free(key);
err_out:
if (sta)
sta_info_put(sta);
return ret;
}
static int ieee80211_ioctl_siwgenie(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
if (local->user_space_mlme)
return -EOPNOTSUPP;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
int ret = ieee80211_sta_set_extra_ie(dev, extra, data->length);
if (ret)
return ret;
sdata->u.sta.auto_bssid_sel = 0;
ieee80211_sta_req_auth(dev, &sdata->u.sta);
return 0;
}
if (sdata->type == IEEE80211_IF_TYPE_AP) {
kfree(sdata->u.ap.generic_elem);
sdata->u.ap.generic_elem = kmalloc(data->length, GFP_KERNEL);
if (!sdata->u.ap.generic_elem)
return -ENOMEM;
memcpy(sdata->u.ap.generic_elem, extra, data->length);
sdata->u.ap.generic_elem_len = data->length;
return ieee80211_if_config(dev);
}
return -EOPNOTSUPP;
}
static int ieee80211_ioctl_set_radio_enabled(struct net_device *dev,
int val)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_conf *conf = &local->hw.conf;
conf->radio_enabled = val;
return ieee80211_hw_config(wdev_priv(dev->ieee80211_ptr));
}
static int ieee80211_ioctl_giwname(struct net_device *dev,
struct iw_request_info *info,
char *name, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
switch (local->hw.conf.phymode) {
case MODE_IEEE80211A:
strcpy(name, "IEEE 802.11a");
break;
case MODE_IEEE80211B:
strcpy(name, "IEEE 802.11b");
break;
case MODE_IEEE80211G:
strcpy(name, "IEEE 802.11g");
break;
case MODE_ATHEROS_TURBO:
strcpy(name, "5GHz Turbo");
break;
default:
strcpy(name, "IEEE 802.11");
break;
}
return 0;
}
static int ieee80211_ioctl_giwrange(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct iw_range *range = (struct iw_range *) extra;
struct ieee80211_hw_mode *mode = NULL;
int c = 0;
data->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = 21;
range->retry_capa = IW_RETRY_LIMIT;
range->retry_flags = IW_RETRY_LIMIT;
range->min_retry = 0;
range->max_retry = 255;
range->min_rts = 0;
range->max_rts = 2347;
range->min_frag = 256;
range->max_frag = 2346;
range->encoding_size[0] = 5;
range->encoding_size[1] = 13;
range->num_encoding_sizes = 2;
range->max_encoding_tokens = NUM_DEFAULT_KEYS;
range->max_qual.qual = local->hw.max_signal;
range->max_qual.level = local->hw.max_rssi;
range->max_qual.noise = local->hw.max_noise;
range->max_qual.updated = local->wstats_flags;
range->avg_qual.qual = local->hw.max_signal/2;
range->avg_qual.level = 0;
range->avg_qual.noise = 0;
range->avg_qual.updated = local->wstats_flags;
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
list_for_each_entry(mode, &local->modes_list, list) {
int i = 0;
if (!(local->enabled_modes & (1 << mode->mode)) ||
(local->hw_modes & local->enabled_modes &
(1 << MODE_IEEE80211G) && mode->mode == MODE_IEEE80211B))
continue;
while (i < mode->num_channels && c < IW_MAX_FREQUENCIES) {
struct ieee80211_channel *chan = &mode->channels[i];
if (chan->flag & IEEE80211_CHAN_W_SCAN) {
range->freq[c].i = chan->chan;
range->freq[c].m = chan->freq * 100000;
range->freq[c].e = 1;
c++;
}
i++;
}
}
range->num_channels = c;
range->num_frequency = c;
IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
return 0;
}
static int ieee80211_ioctl_siwmode(struct net_device *dev,
struct iw_request_info *info,
__u32 *mode, char *extra)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
int type;
if (sdata->type == IEEE80211_IF_TYPE_VLAN)
return -EOPNOTSUPP;
switch (*mode) {
case IW_MODE_INFRA:
type = IEEE80211_IF_TYPE_STA;
break;
case IW_MODE_ADHOC:
type = IEEE80211_IF_TYPE_IBSS;
break;
case IW_MODE_MONITOR:
type = IEEE80211_IF_TYPE_MNTR;
break;
default:
return -EINVAL;
}
if (type == sdata->type)
return 0;
if (netif_running(dev))
return -EBUSY;
ieee80211_if_reinit(dev);
ieee80211_if_set_type(dev, type);
return 0;
}
static int ieee80211_ioctl_giwmode(struct net_device *dev,
struct iw_request_info *info,
__u32 *mode, char *extra)
{
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
switch (sdata->type) {
case IEEE80211_IF_TYPE_AP:
*mode = IW_MODE_MASTER;
break;
case IEEE80211_IF_TYPE_STA:
*mode = IW_MODE_INFRA;
break;
case IEEE80211_IF_TYPE_IBSS:
*mode = IW_MODE_ADHOC;
break;
case IEEE80211_IF_TYPE_MNTR:
*mode = IW_MODE_MONITOR;
break;
case IEEE80211_IF_TYPE_WDS:
*mode = IW_MODE_REPEAT;
break;
case IEEE80211_IF_TYPE_VLAN:
*mode = IW_MODE_SECOND; /* FIXME */
break;
default:
*mode = IW_MODE_AUTO;
break;
}
return 0;
}
int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq)
{
struct ieee80211_hw_mode *mode;
int c, set = 0;
int ret = -EINVAL;
list_for_each_entry(mode, &local->modes_list, list) {
if (!(local->enabled_modes & (1 << mode->mode)))
continue;
for (c = 0; c < mode->num_channels; c++) {
struct ieee80211_channel *chan = &mode->channels[c];
if (chan->flag & IEEE80211_CHAN_W_SCAN &&
((chan->chan == channel) || (chan->freq == freq))) {
/* Use next_mode as the mode preference to
* resolve non-unique channel numbers. */
if (set && mode->mode != local->next_mode)
continue;
local->oper_channel = chan;
local->oper_hw_mode = mode;
set++;
}
}
}
if (set) {
if (local->sta_scanning)
ret = 0;
else
ret = ieee80211_hw_config(local);
rate_control_clear(local);
}
return ret;
}
static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA)
sdata->u.sta.auto_channel_sel = 0;
/* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
if (freq->e == 0) {
if (freq->m < 0) {
if (sdata->type == IEEE80211_IF_TYPE_STA)
sdata->u.sta.auto_channel_sel = 1;
return 0;
} else
return ieee80211_set_channel(local, freq->m, -1);
} else {
int i, div = 1000000;
for (i = 0; i < freq->e; i++)
div /= 10;
if (div > 0)
return ieee80211_set_channel(local, -1, freq->m / div);
else
return -EINVAL;
}
}
static int ieee80211_ioctl_giwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
/* TODO: in station mode (Managed/Ad-hoc) might need to poll low-level
* driver for the current channel with firmware-based management */
freq->m = local->hw.conf.freq;
freq->e = 6;
return 0;
}
static int ieee80211_ioctl_siwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata;
size_t len = data->length;
/* iwconfig uses nul termination in SSID.. */
if (len > 0 && ssid[len - 1] == '\0')
len--;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
int ret;
if (local->user_space_mlme) {
if (len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
memcpy(sdata->u.sta.ssid, ssid, len);
sdata->u.sta.ssid_len = len;
return 0;
}
sdata->u.sta.auto_ssid_sel = !data->flags;
ret = ieee80211_sta_set_ssid(dev, ssid, len);
if (ret)
return ret;
ieee80211_sta_req_auth(dev, &sdata->u.sta);
return 0;
}
if (sdata->type == IEEE80211_IF_TYPE_AP) {
memcpy(sdata->u.ap.ssid, ssid, len);
memset(sdata->u.ap.ssid + len, 0,
IEEE80211_MAX_SSID_LEN - len);
sdata->u.ap.ssid_len = len;
return ieee80211_if_config(dev);
}
return -EOPNOTSUPP;
}
static int ieee80211_ioctl_giwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
size_t len;
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
int res = ieee80211_sta_get_ssid(dev, ssid, &len);
if (res == 0) {
data->length = len;
data->flags = 1;
} else
data->flags = 0;
return res;
}
if (sdata->type == IEEE80211_IF_TYPE_AP) {
len = sdata->u.ap.ssid_len;
if (len > IW_ESSID_MAX_SIZE)
len = IW_ESSID_MAX_SIZE;
memcpy(ssid, sdata->u.ap.ssid, len);
data->length = len;
data->flags = 1;
return 0;
}
return -EOPNOTSUPP;
}
static int ieee80211_ioctl_siwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
int ret;
if (local->user_space_mlme) {
memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
ETH_ALEN);
return 0;
}
if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) {
sdata->u.sta.auto_bssid_sel = 1;
sdata->u.sta.auto_channel_sel = 1;
} else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
sdata->u.sta.auto_bssid_sel = 1;
else
sdata->u.sta.auto_bssid_sel = 0;
ret = ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data);
if (ret)
return ret;
ieee80211_sta_req_auth(dev, &sdata->u.sta);
return 0;
} else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
ETH_ALEN) == 0)
return 0;
return ieee80211_if_update_wds(dev, (u8 *) &ap_addr->sa_data);
}
return -EOPNOTSUPP;
}
static int ieee80211_ioctl_giwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
ap_addr->sa_family = ARPHRD_ETHER;
memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN);
return 0;
} else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
ap_addr->sa_family = ARPHRD_ETHER;
memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
return 0;
}
return -EOPNOTSUPP;
}
static int ieee80211_ioctl_siwscan(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
u8 *ssid = NULL;
size_t ssid_len = 0;
if (!netif_running(dev))
return -ENETDOWN;
switch (sdata->type) {
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
ssid = sdata->u.sta.ssid;
ssid_len = sdata->u.sta.ssid_len;
}
break;
case IEEE80211_IF_TYPE_AP:
if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
ssid = sdata->u.ap.ssid;
ssid_len = sdata->u.ap.ssid_len;
}
break;
default:
return -EOPNOTSUPP;
}
return ieee80211_sta_req_scan(dev, ssid, ssid_len);
}
static int ieee80211_ioctl_giwscan(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
{
int res;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
if (local->sta_scanning)
return -EAGAIN;
res = ieee80211_sta_scan_results(dev, extra, data->length);
if (res >= 0) {
data->length = res;
return 0;
}
data->length = 0;
return res;
}
static int ieee80211_ioctl_siwrate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rate, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_hw_mode *mode;
int i;
u32 target_rate = rate->value / 100000;
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (!sdata->bss)
return -ENODEV;
mode = local->oper_hw_mode;
/* target_rate = -1, rate->fixed = 0 means auto only, so use all rates
* target_rate = X, rate->fixed = 1 means only rate X
* target_rate = X, rate->fixed = 0 means all rates <= X */
sdata->bss->max_ratectrl_rateidx = -1;
sdata->bss->force_unicast_rateidx = -1;
if (rate->value < 0)
return 0;
for (i=0; i< mode->num_rates; i++) {
struct ieee80211_rate *rates = &mode->rates[i];
int this_rate = rates->rate;
if (mode->mode == MODE_ATHEROS_TURBO ||
mode->mode == MODE_ATHEROS_TURBOG)
this_rate *= 2;
if (target_rate == this_rate) {
sdata->bss->max_ratectrl_rateidx = i;
if (rate->fixed)
sdata->bss->force_unicast_rateidx = i;
break;
}
}
return 0;
}
static int ieee80211_ioctl_giwrate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rate, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA)
sta = sta_info_get(local, sdata->u.sta.bssid);
else
return -EOPNOTSUPP;
if (!sta)
return -ENODEV;
if (sta->txrate < local->oper_hw_mode->num_rates)
rate->value = local->oper_hw_mode->rates[sta->txrate].rate * 100000;
else
rate->value = 0;
sta_info_put(sta);
return 0;
}
static int ieee80211_ioctl_siwrts(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rts, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
if (rts->disabled)
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD)
return -EINVAL;
else
local->rts_threshold = rts->value;
/* If the wlan card performs RTS/CTS in hardware/firmware,
* configure it here */
if (local->ops->set_rts_threshold)
local->ops->set_rts_threshold(local_to_hw(local),
local->rts_threshold);
return 0;
}
static int ieee80211_ioctl_giwrts(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rts, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
rts->value = local->rts_threshold;
rts->disabled = (rts->value >= IEEE80211_MAX_RTS_THRESHOLD);
rts->fixed = 1;
return 0;
}
static int ieee80211_ioctl_siwfrag(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *frag, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
if (frag->disabled)
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
else if (frag->value < 256 ||
frag->value > IEEE80211_MAX_FRAG_THRESHOLD)
return -EINVAL;
else {
/* Fragment length must be even, so strip LSB. */
local->fragmentation_threshold = frag->value & ~0x1;
}
/* If the wlan card performs fragmentation in hardware/firmware,
* configure it here */
if (local->ops->set_frag_threshold)
local->ops->set_frag_threshold(
local_to_hw(local),
local->fragmentation_threshold);
return 0;
}
static int ieee80211_ioctl_giwfrag(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *frag, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
frag->value = local->fragmentation_threshold;
frag->disabled = (frag->value >= IEEE80211_MAX_RTS_THRESHOLD);
frag->fixed = 1;
return 0;
}
static int ieee80211_ioctl_siwretry(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *retry, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
if (retry->disabled ||
(retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
return -EINVAL;
if (retry->flags & IW_RETRY_MAX)
local->long_retry_limit = retry->value;
else if (retry->flags & IW_RETRY_MIN)
local->short_retry_limit = retry->value;
else {
local->long_retry_limit = retry->value;
local->short_retry_limit = retry->value;
}
if (local->ops->set_retry_limit) {
return local->ops->set_retry_limit(
local_to_hw(local),
local->short_retry_limit,
local->long_retry_limit);
}
return 0;
}
static int ieee80211_ioctl_giwretry(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *retry, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
retry->disabled = 0;
if (retry->flags == 0 || retry->flags & IW_RETRY_MIN) {
/* first return min value, iwconfig will ask max value
* later if needed */
retry->flags |= IW_RETRY_LIMIT;
retry->value = local->short_retry_limit;
if (local->long_retry_limit != local->short_retry_limit)
retry->flags |= IW_RETRY_MIN;
return 0;
}
if (retry->flags & IW_RETRY_MAX) {
retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
retry->value = local->long_retry_limit;
}
return 0;
}
static void ieee80211_key_enable_hwaccel(struct ieee80211_local *local,
struct ieee80211_key *key)
{
struct ieee80211_key_conf *keyconf;
u8 addr[ETH_ALEN];
if (!key || key->alg != ALG_WEP || !key->force_sw_encrypt ||
(local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP))
return;
memset(addr, 0xff, ETH_ALEN);
keyconf = ieee80211_key_data2conf(local, key);
if (keyconf && local->ops->set_key &&
local->ops->set_key(local_to_hw(local),
SET_KEY, addr, keyconf, 0) == 0) {
key->force_sw_encrypt =
!!(keyconf->flags & IEEE80211_KEY_FORCE_SW_ENCRYPT);
key->hw_key_idx = keyconf->hw_key_idx;
}
kfree(keyconf);
}
static void ieee80211_key_disable_hwaccel(struct ieee80211_local *local,
struct ieee80211_key *key)
{
struct ieee80211_key_conf *keyconf;
u8 addr[ETH_ALEN];
if (!key || key->alg != ALG_WEP || key->force_sw_encrypt ||
(local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP))
return;
memset(addr, 0xff, ETH_ALEN);
keyconf = ieee80211_key_data2conf(local, key);
if (keyconf && local->ops->set_key)
local->ops->set_key(local_to_hw(local), DISABLE_KEY,
addr, keyconf, 0);
kfree(keyconf);
key->force_sw_encrypt = 1;
}
static int ieee80211_ioctl_default_wep_only(struct ieee80211_local *local,
int value)
{
int i;
struct ieee80211_sub_if_data *sdata;
local->default_wep_only = value;
read_lock(&local->sub_if_lock);
list_for_each_entry(sdata, &local->sub_if_list, list)
for (i = 0; i < NUM_DEFAULT_KEYS; i++)
if (value)
ieee80211_key_enable_hwaccel(local,
sdata->keys[i]);
else
ieee80211_key_disable_hwaccel(local,
sdata->keys[i]);
read_unlock(&local->sub_if_lock);
return 0;
}
void ieee80211_update_default_wep_only(struct ieee80211_local *local)
{
int i = 0;
struct ieee80211_sub_if_data *sdata;
read_lock(&local->sub_if_lock);
list_for_each_entry(sdata, &local->sub_if_list, list) {
if (sdata->dev == local->mdev)
continue;
/* If there is an AP interface then depend on userspace to
set default_wep_only correctly. */
if (sdata->type == IEEE80211_IF_TYPE_AP) {
read_unlock(&local->sub_if_lock);
return;
}
i++;
}
read_unlock(&local->sub_if_lock);
if (i <= 1)
ieee80211_ioctl_default_wep_only(local, 1);
else
ieee80211_ioctl_default_wep_only(local, 0);
}
static int ieee80211_ioctl_prism2_param(struct net_device *dev,
struct iw_request_info *info,
void *wrqu, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata;
int *i = (int *) extra;
int param = *i;
int value = *(i + 1);
int ret = 0;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
switch (param) {
case PRISM2_PARAM_IEEE_802_1X:
if (local->ops->set_ieee8021x)
ret = local->ops->set_ieee8021x(local_to_hw(local),
value);
if (ret)
printk(KERN_DEBUG "%s: failed to set IEEE 802.1X (%d) "
"for low-level driver\n", dev->name, value);
else
sdata->ieee802_1x = value;
break;
case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES:
if (sdata->type != IEEE80211_IF_TYPE_AP)
ret = -ENOENT;
else
sdata->use_protection = value;
break;
case PRISM2_PARAM_PREAMBLE:
if (sdata->type != IEEE80211_IF_TYPE_AP)
ret = -ENOENT;
else
sdata->short_preamble = value;
break;
case PRISM2_PARAM_STAT_TIME:
if (!local->stat_time && value) {
local->stat_timer.expires = jiffies + HZ * value / 100;
add_timer(&local->stat_timer);
} else if (local->stat_time && !value) {
del_timer_sync(&local->stat_timer);
}
local->stat_time = value;
break;
case PRISM2_PARAM_SHORT_SLOT_TIME:
if (value)
local->hw.conf.flags |= IEEE80211_CONF_SHORT_SLOT_TIME;
else
local->hw.conf.flags &= ~IEEE80211_CONF_SHORT_SLOT_TIME;
if (ieee80211_hw_config(local))
ret = -EINVAL;
break;
case PRISM2_PARAM_NEXT_MODE:
local->next_mode = value;
break;
case PRISM2_PARAM_RADIO_ENABLED:
ret = ieee80211_ioctl_set_radio_enabled(dev, value);
break;
case PRISM2_PARAM_ANTENNA_MODE:
local->hw.conf.antenna_mode = value;
if (ieee80211_hw_config(local))
ret = -EINVAL;
break;
case PRISM2_PARAM_STA_ANTENNA_SEL:
local->sta_antenna_sel = value;
break;
case PRISM2_PARAM_TX_POWER_REDUCTION:
if (value < 0)
ret = -EINVAL;
else
local->hw.conf.tx_power_reduction = value;
break;
case PRISM2_PARAM_KEY_TX_RX_THRESHOLD:
local->key_tx_rx_threshold = value;
break;
case PRISM2_PARAM_DEFAULT_WEP_ONLY:
ret = ieee80211_ioctl_default_wep_only(local, value);
break;
case PRISM2_PARAM_WIFI_WME_NOACK_TEST:
local->wifi_wme_noack_test = value;
break;
case PRISM2_PARAM_SCAN_FLAGS:
local->scan_flags = value;
break;
case PRISM2_PARAM_MIXED_CELL:
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS)
ret = -EINVAL;
else
sdata->u.sta.mixed_cell = !!value;
break;
case PRISM2_PARAM_HW_MODES:
local->enabled_modes = value;
break;
case PRISM2_PARAM_CREATE_IBSS:
if (sdata->type != IEEE80211_IF_TYPE_IBSS)
ret = -EINVAL;
else
sdata->u.sta.create_ibss = !!value;
break;
case PRISM2_PARAM_WMM_ENABLED:
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS)
ret = -EINVAL;
else
sdata->u.sta.wmm_enabled = !!value;
break;
case PRISM2_PARAM_RADAR_DETECT:
local->hw.conf.radar_detect = value;
break;
case PRISM2_PARAM_SPECTRUM_MGMT:
local->hw.conf.spect_mgmt = value;
break;
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
}
static int ieee80211_ioctl_get_prism2_param(struct net_device *dev,
struct iw_request_info *info,
void *wrqu, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata;
int *param = (int *) extra;
int ret = 0;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
switch (*param) {
case PRISM2_PARAM_IEEE_802_1X:
*param = sdata->ieee802_1x;
break;
case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES:
*param = sdata->use_protection;
break;
case PRISM2_PARAM_PREAMBLE:
*param = sdata->short_preamble;
break;
case PRISM2_PARAM_STAT_TIME:
*param = local->stat_time;
break;
case PRISM2_PARAM_SHORT_SLOT_TIME:
*param = !!(local->hw.conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME);
break;
case PRISM2_PARAM_NEXT_MODE:
*param = local->next_mode;
break;
case PRISM2_PARAM_ANTENNA_MODE:
*param = local->hw.conf.antenna_mode;
break;
case PRISM2_PARAM_STA_ANTENNA_SEL:
*param = local->sta_antenna_sel;
break;
case PRISM2_PARAM_TX_POWER_REDUCTION:
*param = local->hw.conf.tx_power_reduction;
break;
case PRISM2_PARAM_KEY_TX_RX_THRESHOLD:
*param = local->key_tx_rx_threshold;
break;
case PRISM2_PARAM_DEFAULT_WEP_ONLY:
*param = local->default_wep_only;
break;
case PRISM2_PARAM_WIFI_WME_NOACK_TEST:
*param = local->wifi_wme_noack_test;
break;
case PRISM2_PARAM_SCAN_FLAGS:
*param = local->scan_flags;
break;
case PRISM2_PARAM_HW_MODES:
*param = local->enabled_modes;
break;
case PRISM2_PARAM_CREATE_IBSS:
if (sdata->type != IEEE80211_IF_TYPE_IBSS)
ret = -EINVAL;
else
*param = !!sdata->u.sta.create_ibss;
break;
case PRISM2_PARAM_MIXED_CELL:
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS)
ret = -EINVAL;
else
*param = !!sdata->u.sta.mixed_cell;
break;
case PRISM2_PARAM_WMM_ENABLED:
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS)
ret = -EINVAL;
else
*param = !!sdata->u.sta.wmm_enabled;
break;
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
}
static int ieee80211_ioctl_siwmlme(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
{
struct ieee80211_sub_if_data *sdata;
struct iw_mlme *mlme = (struct iw_mlme *) extra;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS)
return -EINVAL;
switch (mlme->cmd) {
case IW_MLME_DEAUTH:
/* TODO: mlme->addr.sa_data */
return ieee80211_sta_deauthenticate(dev, mlme->reason_code);
case IW_MLME_DISASSOC:
/* TODO: mlme->addr.sa_data */
return ieee80211_sta_disassociate(dev, mlme->reason_code);
default:
return -EOPNOTSUPP;
}
}
static int ieee80211_ioctl_siwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq, char *keybuf)
{
struct ieee80211_sub_if_data *sdata;
int idx, i, alg = ALG_WEP;
u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
idx = erq->flags & IW_ENCODE_INDEX;
if (idx == 0) {
if (sdata->default_key)
for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
if (sdata->default_key == sdata->keys[i]) {
idx = i;
break;
}
}
} else if (idx < 1 || idx > 4)
return -EINVAL;
else
idx--;
if (erq->flags & IW_ENCODE_DISABLED)
alg = ALG_NONE;
else if (erq->length == 0) {
/* No key data - just set the default TX key index */
if (sdata->default_key != sdata->keys[idx]) {
ieee80211_debugfs_key_remove_default(sdata);
sdata->default_key = sdata->keys[idx];
if (sdata->default_key)
ieee80211_debugfs_key_add_default(sdata);
}
return 0;
}
return ieee80211_set_encryption(
dev, bcaddr,
idx, alg,
!sdata->default_key,
keybuf, erq->length);
}
static int ieee80211_ioctl_giwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq, char *key)
{
struct ieee80211_sub_if_data *sdata;
int idx, i;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
idx = erq->flags & IW_ENCODE_INDEX;
if (idx < 1 || idx > 4) {
idx = -1;
if (!sdata->default_key)
idx = 0;
else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
if (sdata->default_key == sdata->keys[i]) {
idx = i;
break;
}
}
if (idx < 0)
return -EINVAL;
} else
idx--;
erq->flags = idx + 1;
if (!sdata->keys[idx]) {
erq->length = 0;
erq->flags |= IW_ENCODE_DISABLED;
return 0;
}
memcpy(key, sdata->keys[idx]->key,
min((int)erq->length, sdata->keys[idx]->keylen));
erq->length = sdata->keys[idx]->keylen;
erq->flags |= IW_ENCODE_ENABLED;
return 0;
}
static int ieee80211_ioctl_siwauth(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *data, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
int ret = 0;
switch (data->flags & IW_AUTH_INDEX) {
case IW_AUTH_WPA_VERSION:
case IW_AUTH_CIPHER_PAIRWISE:
case IW_AUTH_CIPHER_GROUP:
case IW_AUTH_WPA_ENABLED:
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
break;
case IW_AUTH_KEY_MGMT:
if (sdata->type != IEEE80211_IF_TYPE_STA)
ret = -EINVAL;
else {
/*
* TODO: sdata->u.sta.key_mgmt does not match with WE18
* value completely; could consider modifying this to
* be closer to WE18. For now, this value is not really
* used for anything else than Privacy matching, so the
* current code here should be more or less OK.
*/
if (data->value & IW_AUTH_KEY_MGMT_802_1X) {
sdata->u.sta.key_mgmt =
IEEE80211_KEY_MGMT_WPA_EAP;
} else if (data->value & IW_AUTH_KEY_MGMT_PSK) {
sdata->u.sta.key_mgmt =
IEEE80211_KEY_MGMT_WPA_PSK;
} else {
sdata->u.sta.key_mgmt =
IEEE80211_KEY_MGMT_NONE;
}
}
break;
case IW_AUTH_80211_AUTH_ALG:
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS)
sdata->u.sta.auth_algs = data->value;
else
ret = -EOPNOTSUPP;
break;
case IW_AUTH_PRIVACY_INVOKED:
if (local->ops->set_privacy_invoked)
ret = local->ops->set_privacy_invoked(
local_to_hw(local), data->value);
break;
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
}
/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */
static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct iw_statistics *wstats = &local->wstats;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta = NULL;
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS)
sta = sta_info_get(local, sdata->u.sta.bssid);
if (!sta) {
wstats->discard.fragment = 0;
wstats->discard.misc = 0;
wstats->qual.qual = 0;
wstats->qual.level = 0;
wstats->qual.noise = 0;
wstats->qual.updated = IW_QUAL_ALL_INVALID;
} else {
wstats->qual.level = sta->last_rssi;
wstats->qual.qual = sta->last_signal;
wstats->qual.noise = sta->last_noise;
wstats->qual.updated = local->wstats_flags;
sta_info_put(sta);
}
return wstats;
}
static int ieee80211_ioctl_giwauth(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *data, char *extra)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
int ret = 0;
switch (data->flags & IW_AUTH_INDEX) {
case IW_AUTH_80211_AUTH_ALG:
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS)
data->value = sdata->u.sta.auth_algs;
else
ret = -EOPNOTSUPP;
break;
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
}
static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq, char *extra)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
int alg, idx, i;
switch (ext->alg) {
case IW_ENCODE_ALG_NONE:
alg = ALG_NONE;
break;
case IW_ENCODE_ALG_WEP:
alg = ALG_WEP;
break;
case IW_ENCODE_ALG_TKIP:
alg = ALG_TKIP;
break;
case IW_ENCODE_ALG_CCMP:
alg = ALG_CCMP;
break;
default:
return -EOPNOTSUPP;
}
if (erq->flags & IW_ENCODE_DISABLED)
alg = ALG_NONE;
idx = erq->flags & IW_ENCODE_INDEX;
if (idx < 1 || idx > 4) {
idx = -1;
if (!sdata->default_key)
idx = 0;
else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
if (sdata->default_key == sdata->keys[i]) {
idx = i;
break;
}
}
if (idx < 0)
return -EINVAL;
} else
idx--;
return ieee80211_set_encryption(dev, ext->addr.sa_data, idx, alg,
ext->ext_flags &
IW_ENCODE_EXT_SET_TX_KEY,
ext->key, ext->key_len);
}
static const struct iw_priv_args ieee80211_ioctl_priv[] = {
{ PRISM2_IOCTL_PRISM2_PARAM,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "param" },
{ PRISM2_IOCTL_GET_PRISM2_PARAM,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_param" },
};
/* Structures to export the Wireless Handlers */
static const iw_handler ieee80211_handler[] =
{
(iw_handler) NULL, /* SIOCSIWCOMMIT */
(iw_handler) ieee80211_ioctl_giwname, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */
(iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */
(iw_handler) ieee80211_ioctl_siwmode, /* SIOCSIWMODE */
(iw_handler) ieee80211_ioctl_giwmode, /* SIOCGIWMODE */
(iw_handler) NULL, /* SIOCSIWSENS */
(iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
(iw_handler) ieee80211_ioctl_giwrange, /* SIOCGIWRANGE */
(iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
(iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
(iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
(iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
iw_handler_set_spy, /* SIOCSIWSPY */
iw_handler_get_spy, /* SIOCGIWSPY */
iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
(iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */
(iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */
(iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */
(iw_handler) NULL, /* SIOCGIWAPLIST */
(iw_handler) ieee80211_ioctl_siwscan, /* SIOCSIWSCAN */
(iw_handler) ieee80211_ioctl_giwscan, /* SIOCGIWSCAN */
(iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */
(iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */
(iw_handler) NULL, /* SIOCSIWNICKN */
(iw_handler) NULL, /* SIOCGIWNICKN */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */
(iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */
(iw_handler) ieee80211_ioctl_siwrts, /* SIOCSIWRTS */
(iw_handler) ieee80211_ioctl_giwrts, /* SIOCGIWRTS */
(iw_handler) ieee80211_ioctl_siwfrag, /* SIOCSIWFRAG */
(iw_handler) ieee80211_ioctl_giwfrag, /* SIOCGIWFRAG */
(iw_handler) NULL, /* SIOCSIWTXPOW */
(iw_handler) NULL, /* SIOCGIWTXPOW */
(iw_handler) ieee80211_ioctl_siwretry, /* SIOCSIWRETRY */
(iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */
(iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */
(iw_handler) ieee80211_ioctl_giwencode, /* SIOCGIWENCODE */
(iw_handler) NULL, /* SIOCSIWPOWER */
(iw_handler) NULL, /* SIOCGIWPOWER */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */
(iw_handler) NULL, /* SIOCGIWGENIE */
(iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */
(iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */
(iw_handler) ieee80211_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */
(iw_handler) NULL, /* SIOCGIWENCODEEXT */
(iw_handler) NULL, /* SIOCSIWPMKSA */
(iw_handler) NULL, /* -- hole -- */
};
static const iw_handler ieee80211_private_handler[] =
{ /* SIOCIWFIRSTPRIV + */
(iw_handler) ieee80211_ioctl_prism2_param, /* 0 */
(iw_handler) ieee80211_ioctl_get_prism2_param, /* 1 */
};
const struct iw_handler_def ieee80211_iw_handler_def =
{
.num_standard = ARRAY_SIZE(ieee80211_handler),
.num_private = ARRAY_SIZE(ieee80211_private_handler),
.num_private_args = ARRAY_SIZE(ieee80211_ioctl_priv),
.standard = (iw_handler *) ieee80211_handler,
.private = (iw_handler *) ieee80211_private_handler,
.private_args = (struct iw_priv_args *) ieee80211_ioctl_priv,
.get_wireless_stats = ieee80211_get_wireless_stats,
};