465e7417b1
Signed-off-by: Moritz Muehlenhoff <jmm@debian.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1780 lines
42 KiB
C
1780 lines
42 KiB
C
/* src/p80211/p80211wext.c
|
|
*
|
|
* Glue code to make linux-wlan-ng a happy wireless extension camper.
|
|
*
|
|
* original author: Reyk Floeter <reyk@synack.de>
|
|
* Completely re-written by Solomon Peachy <solomon@linux-wlan.com>
|
|
*
|
|
* Copyright (C) 2002 AbsoluteValue Systems, Inc. All Rights Reserved.
|
|
* --------------------------------------------------------------------
|
|
*
|
|
* linux-wlan
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU Public License version 2 (the "GPL"), in which
|
|
* case the provisions of the GPL are applicable instead of the
|
|
* above. If you wish to allow the use of your version of this file
|
|
* only under the terms of the GPL and not to allow others to use
|
|
* your version of this file under the MPL, indicate your decision
|
|
* by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this
|
|
* file under either the MPL or the GPL.
|
|
*
|
|
* --------------------------------------------------------------------
|
|
*/
|
|
|
|
/*================================================================*/
|
|
/* System Includes */
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/wireless.h>
|
|
#include <net/iw_handler.h>
|
|
#include <linux/if_arp.h>
|
|
#include <asm/bitops.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/byteorder.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/bitops.h>
|
|
|
|
/*================================================================*/
|
|
/* Project Includes */
|
|
|
|
#include "p80211types.h"
|
|
#include "p80211hdr.h"
|
|
#include "p80211conv.h"
|
|
#include "p80211mgmt.h"
|
|
#include "p80211msg.h"
|
|
#include "p80211metastruct.h"
|
|
#include "p80211metadef.h"
|
|
#include "p80211netdev.h"
|
|
#include "p80211ioctl.h"
|
|
#include "p80211req.h"
|
|
|
|
static int p80211wext_giwrate(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rrq, char *extra);
|
|
static int p80211wext_giwessid(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *data, char *essid);
|
|
|
|
static u8 p80211_mhz_to_channel(u16 mhz)
|
|
{
|
|
if (mhz >= 5000)
|
|
return (mhz - 5000) / 5;
|
|
|
|
if (mhz == 2482)
|
|
return 14;
|
|
|
|
if (mhz >= 2407)
|
|
return (mhz - 2407) / 5;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u16 p80211_channel_to_mhz(u8 ch, int dot11a)
|
|
{
|
|
|
|
if (ch == 0)
|
|
return 0;
|
|
if (ch > 200)
|
|
return 0;
|
|
|
|
/* 5G */
|
|
if (dot11a)
|
|
return 5000 + (5 * ch);
|
|
|
|
/* 2.4G */
|
|
if (ch == 14)
|
|
return 2484;
|
|
|
|
if ((ch < 14) && (ch > 0))
|
|
return 2407 + (5 * ch);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* taken from orinoco.c ;-) */
|
|
static const long p80211wext_channel_freq[] = {
|
|
2412, 2417, 2422, 2427, 2432, 2437, 2442,
|
|
2447, 2452, 2457, 2462, 2467, 2472, 2484
|
|
};
|
|
|
|
#define NUM_CHANNELS ARRAY_SIZE(p80211wext_channel_freq)
|
|
|
|
/* steal a spare bit to store the shared/opensystems state.
|
|
should default to open if not set */
|
|
#define HOSTWEP_SHAREDKEY BIT(3)
|
|
|
|
static int qual_as_percent(int snr)
|
|
{
|
|
if (snr <= 0)
|
|
return 0;
|
|
if (snr <= 40)
|
|
return snr * 5 / 2;
|
|
return 100;
|
|
}
|
|
|
|
static int p80211wext_dorequest(wlandevice_t *wlandev, u32 did, u32 data)
|
|
{
|
|
p80211msg_dot11req_mibset_t msg;
|
|
p80211item_uint32_t mibitem;
|
|
int result;
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
mibitem.did = did;
|
|
mibitem.data = data;
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int p80211wext_autojoin(wlandevice_t *wlandev)
|
|
{
|
|
p80211msg_lnxreq_autojoin_t msg;
|
|
struct iw_point data;
|
|
char ssid[IW_ESSID_MAX_SIZE];
|
|
|
|
int result;
|
|
int err = 0;
|
|
|
|
/* Get ESSID */
|
|
result = p80211wext_giwessid(wlandev->netdev, NULL, &data, ssid);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
if (wlandev->hostwep & HOSTWEP_SHAREDKEY)
|
|
msg.authtype.data = P80211ENUM_authalg_sharedkey;
|
|
else
|
|
msg.authtype.data = P80211ENUM_authalg_opensystem;
|
|
|
|
msg.msgcode = DIDmsg_lnxreq_autojoin;
|
|
|
|
/* Trim the last '\0' to fit the SSID format */
|
|
|
|
if (data.length && ssid[data.length - 1] == '\0')
|
|
data.length = data.length - 1;
|
|
|
|
memcpy(msg.ssid.data.data, ssid, data.length);
|
|
msg.ssid.data.len = data.length;
|
|
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
/* called by /proc/net/wireless */
|
|
struct iw_statistics *p80211wext_get_wireless_stats(netdevice_t * dev)
|
|
{
|
|
p80211msg_lnxreq_commsquality_t quality;
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
struct iw_statistics *wstats = &wlandev->wstats;
|
|
int retval;
|
|
|
|
/* Check */
|
|
if ((wlandev == NULL) || (wlandev->msdstate != WLAN_MSD_RUNNING))
|
|
return NULL;
|
|
|
|
/* XXX Only valid in station mode */
|
|
wstats->status = 0;
|
|
|
|
/* build request message */
|
|
quality.msgcode = DIDmsg_lnxreq_commsquality;
|
|
quality.dbm.data = P80211ENUM_truth_true;
|
|
quality.dbm.status = P80211ENUM_msgitem_status_data_ok;
|
|
|
|
/* send message to nsd */
|
|
if (wlandev->mlmerequest == NULL)
|
|
return NULL;
|
|
|
|
retval = wlandev->mlmerequest(wlandev, (p80211msg_t *)&quality);
|
|
|
|
wstats->qual.qual = qual_as_percent(quality.link.data); /* overall link quality */
|
|
wstats->qual.level = quality.level.data; /* instant signal level */
|
|
wstats->qual.noise = quality.noise.data; /* instant noise level */
|
|
|
|
wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
|
|
wstats->discard.code = wlandev->rx.decrypt_err;
|
|
wstats->discard.nwid = 0;
|
|
wstats->discard.misc = 0;
|
|
|
|
wstats->discard.fragment = 0; /* incomplete fragments */
|
|
wstats->discard.retries = 0; /* tx retries. */
|
|
wstats->miss.beacon = 0;
|
|
|
|
return wstats;
|
|
}
|
|
|
|
static int p80211wext_giwname(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
char *name, char *extra)
|
|
{
|
|
struct iw_param rate;
|
|
int result;
|
|
int err = 0;
|
|
|
|
result = p80211wext_giwrate(dev, NULL, &rate, NULL);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
switch (rate.value) {
|
|
case 1000000:
|
|
case 2000000:
|
|
strcpy(name, "IEEE 802.11-DS");
|
|
break;
|
|
case 5500000:
|
|
case 11000000:
|
|
strcpy(name, "IEEE 802.11-b");
|
|
break;
|
|
}
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwfreq(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_freq *freq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibget;
|
|
mibitem.did = DIDmib_dot11phy_dot11PhyDSSSTable_dot11CurrentChannel;
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
if (mibitem.data > NUM_CHANNELS) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
/* convert into frequency instead of a channel */
|
|
freq->e = 1;
|
|
freq->m = p80211_channel_to_mhz(mibitem.data, 0) * 100000;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_siwfreq(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_freq *freq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
mibitem.did = DIDmib_dot11phy_dot11PhyDSSSTable_dot11CurrentChannel;
|
|
mibitem.status = P80211ENUM_msgitem_status_data_ok;
|
|
|
|
if ((freq->e == 0) && (freq->m <= 1000))
|
|
mibitem.data = freq->m;
|
|
else
|
|
mibitem.data = p80211_mhz_to_channel(freq->m);
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwmode(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
__u32 *mode, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
|
|
switch (wlandev->macmode) {
|
|
case WLAN_MACMODE_IBSS_STA:
|
|
*mode = IW_MODE_ADHOC;
|
|
break;
|
|
case WLAN_MACMODE_ESS_STA:
|
|
*mode = IW_MODE_INFRA;
|
|
break;
|
|
case WLAN_MACMODE_ESS_AP:
|
|
*mode = IW_MODE_MASTER;
|
|
break;
|
|
default:
|
|
/* Not set yet. */
|
|
*mode = IW_MODE_AUTO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int p80211wext_siwmode(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
__u32 *mode, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA &&
|
|
*mode != IW_MODE_MASTER) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
/* Operation mode is the same with current mode */
|
|
if (*mode == wlandev->macmode)
|
|
goto exit;
|
|
|
|
switch (*mode) {
|
|
case IW_MODE_ADHOC:
|
|
wlandev->macmode = WLAN_MACMODE_IBSS_STA;
|
|
break;
|
|
case IW_MODE_INFRA:
|
|
wlandev->macmode = WLAN_MACMODE_ESS_STA;
|
|
break;
|
|
case IW_MODE_MASTER:
|
|
wlandev->macmode = WLAN_MACMODE_ESS_AP;
|
|
break;
|
|
default:
|
|
/* Not set yet. */
|
|
printk(KERN_INFO "Operation mode: %d not support\n", *mode);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
/* Set Operation mode to the PORT TYPE RID */
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
mibitem.did = DIDmib_p2_p2Static_p2CnfPortType;
|
|
mibitem.data = (*mode == IW_MODE_ADHOC) ? 0 : 1;
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result)
|
|
err = -EFAULT;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwrange(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *data, char *extra)
|
|
{
|
|
struct iw_range *range = (struct iw_range *)extra;
|
|
int i, val;
|
|
|
|
/* for backward compatability set size and zero everything we don't understand */
|
|
data->length = sizeof(*range);
|
|
memset(range, 0, sizeof(*range));
|
|
|
|
range->txpower_capa = IW_TXPOW_DBM;
|
|
/* XXX what about min/max_pmp, min/max_pmt, etc. */
|
|
|
|
range->we_version_compiled = WIRELESS_EXT;
|
|
range->we_version_source = 13;
|
|
|
|
range->retry_capa = IW_RETRY_LIMIT;
|
|
range->retry_flags = IW_RETRY_LIMIT;
|
|
range->min_retry = 0;
|
|
range->max_retry = 255;
|
|
|
|
range->event_capa[0] = (IW_EVENT_CAPA_K_0 | /* mode/freq/ssid */
|
|
IW_EVENT_CAPA_MASK(SIOCGIWAP) |
|
|
IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
|
|
range->event_capa[1] = IW_EVENT_CAPA_K_1; /* encode */
|
|
range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVQUAL) |
|
|
IW_EVENT_CAPA_MASK(IWEVCUSTOM));
|
|
|
|
range->num_channels = NUM_CHANNELS;
|
|
|
|
/* XXX need to filter against the regulatory domain &| active set */
|
|
val = 0;
|
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
|
range->freq[val].i = i + 1;
|
|
range->freq[val].m = p80211wext_channel_freq[i] * 100000;
|
|
range->freq[val].e = 1;
|
|
val++;
|
|
}
|
|
|
|
range->num_frequency = val;
|
|
|
|
/* Max of /proc/net/wireless */
|
|
range->max_qual.qual = 100;
|
|
range->max_qual.level = 0;
|
|
range->max_qual.noise = 0;
|
|
range->sensitivity = 3;
|
|
/* XXX these need to be nsd-specific! */
|
|
|
|
range->min_rts = 0;
|
|
range->max_rts = 2347;
|
|
range->min_frag = 256;
|
|
range->max_frag = 2346;
|
|
|
|
range->max_encoding_tokens = NUM_WEPKEYS;
|
|
range->num_encoding_sizes = 2;
|
|
range->encoding_size[0] = 5;
|
|
range->encoding_size[1] = 13;
|
|
|
|
/* XXX what about num_bitrates/throughput? */
|
|
range->num_bitrates = 0;
|
|
|
|
/* estimated max throughput */
|
|
/* XXX need to cap it if we're running at ~2Mbps.. */
|
|
range->throughput = 5500000;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int p80211wext_giwap(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct sockaddr *ap_addr, char *extra)
|
|
{
|
|
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
|
|
memcpy(ap_addr->sa_data, wlandev->bssid, WLAN_BSSID_LEN);
|
|
ap_addr->sa_family = ARPHRD_ETHER;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int p80211wext_giwencode(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *erq, char *key)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
int err = 0;
|
|
int i;
|
|
|
|
i = (erq->flags & IW_ENCODE_INDEX) - 1;
|
|
erq->flags = 0;
|
|
|
|
if (wlandev->hostwep & HOSTWEP_PRIVACYINVOKED)
|
|
erq->flags |= IW_ENCODE_ENABLED;
|
|
else
|
|
erq->flags |= IW_ENCODE_DISABLED;
|
|
|
|
if (wlandev->hostwep & HOSTWEP_EXCLUDEUNENCRYPTED)
|
|
erq->flags |= IW_ENCODE_RESTRICTED;
|
|
else
|
|
erq->flags |= IW_ENCODE_OPEN;
|
|
|
|
i = (erq->flags & IW_ENCODE_INDEX) - 1;
|
|
|
|
if (i == -1)
|
|
i = wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK;
|
|
|
|
if ((i < 0) || (i >= NUM_WEPKEYS)) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
erq->flags |= i + 1;
|
|
|
|
/* copy the key from the driver cache as the keys are read-only MIBs */
|
|
erq->length = wlandev->wep_keylens[i];
|
|
memcpy(key, wlandev->wep_keys[i], erq->length);
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_siwencode(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *erq, char *key)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
p80211item_pstr32_t pstr;
|
|
|
|
int err = 0;
|
|
int result = 0;
|
|
int i;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
/* Check the Key index first. */
|
|
if ((i = (erq->flags & IW_ENCODE_INDEX))) {
|
|
|
|
if ((i < 1) || (i > NUM_WEPKEYS)) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
} else
|
|
i--;
|
|
|
|
/* Set current key number only if no keys are given */
|
|
if (erq->flags & IW_ENCODE_NOKEY) {
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID,
|
|
i);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
/* Use defaultkey if no Key Index */
|
|
i = wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK;
|
|
}
|
|
|
|
/* Check if there is no key information in the iwconfig request */
|
|
if ((erq->flags & IW_ENCODE_NOKEY) == 0) {
|
|
|
|
/*------------------------------------------------------------
|
|
* If there is WEP Key for setting, check the Key Information
|
|
* and then set it to the firmware.
|
|
-------------------------------------------------------------*/
|
|
|
|
if (erq->length > 0) {
|
|
|
|
/* copy the key from the driver cache as the keys are read-only MIBs */
|
|
wlandev->wep_keylens[i] = erq->length;
|
|
memcpy(wlandev->wep_keys[i], key, erq->length);
|
|
|
|
/* Prepare data struture for p80211req_dorequest. */
|
|
memcpy(pstr.data.data, key, erq->length);
|
|
pstr.data.len = erq->length;
|
|
|
|
switch (i) {
|
|
case 0:
|
|
pstr.did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0;
|
|
break;
|
|
|
|
case 1:
|
|
pstr.did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1;
|
|
break;
|
|
|
|
case 2:
|
|
pstr.did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2;
|
|
break;
|
|
|
|
case 3:
|
|
pstr.did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3;
|
|
break;
|
|
|
|
default:
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
memcpy(&msg.mibattribute.data, &pstr, sizeof(pstr));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Check the PrivacyInvoked flag */
|
|
if (erq->flags & IW_ENCODE_DISABLED) {
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked,
|
|
P80211ENUM_truth_false);
|
|
} else {
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked,
|
|
P80211ENUM_truth_true);
|
|
}
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
/* The security mode may be open or restricted, and its meaning
|
|
depends on the card used. With most cards, in open mode no
|
|
authentication is used and the card may also accept non-
|
|
encrypted sessions, whereas in restricted mode only encrypted
|
|
sessions are accepted and the card will use authentication if
|
|
available.
|
|
*/
|
|
if (erq->flags & IW_ENCODE_RESTRICTED) {
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted,
|
|
P80211ENUM_truth_true);
|
|
} else if (erq->flags & IW_ENCODE_OPEN) {
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted,
|
|
P80211ENUM_truth_false);
|
|
}
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwessid(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *data, char *essid)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
|
|
if (wlandev->ssid.len) {
|
|
data->length = wlandev->ssid.len;
|
|
data->flags = 1;
|
|
memcpy(essid, wlandev->ssid.data, data->length);
|
|
essid[data->length] = 0;
|
|
#if (WIRELESS_EXT < 21)
|
|
data->length++;
|
|
#endif
|
|
} else {
|
|
memset(essid, 0, sizeof(wlandev->ssid.data));
|
|
data->length = 0;
|
|
data->flags = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int p80211wext_siwessid(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *data, char *essid)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211msg_lnxreq_autojoin_t msg;
|
|
|
|
int result;
|
|
int err = 0;
|
|
int length = data->length;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
if (wlandev->hostwep & HOSTWEP_SHAREDKEY)
|
|
msg.authtype.data = P80211ENUM_authalg_sharedkey;
|
|
else
|
|
msg.authtype.data = P80211ENUM_authalg_opensystem;
|
|
|
|
msg.msgcode = DIDmsg_lnxreq_autojoin;
|
|
|
|
#if (WIRELESS_EXT < 21)
|
|
if (length)
|
|
length--;
|
|
#endif
|
|
|
|
/* Trim the last '\0' to fit the SSID format */
|
|
if (length && essid[length - 1] == '\0')
|
|
length--;
|
|
|
|
memcpy(msg.ssid.data.data, essid, length);
|
|
msg.ssid.data.len = length;
|
|
|
|
pr_debug("autojoin_ssid for %s \n", essid);
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
pr_debug("autojoin_ssid %d\n", result);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_siwcommit(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *data, char *essid)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
int err = 0;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
/* Auto Join */
|
|
err = p80211wext_autojoin(wlandev);
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwrate(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rrq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibget;
|
|
mibitem.did = DIDmib_p2_p2MAC_p2CurrentTxRate;
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
rrq->fixed = 0; /* can it change? */
|
|
rrq->disabled = 0;
|
|
rrq->value = 0;
|
|
|
|
#define HFA384x_RATEBIT_1 ((u16)1)
|
|
#define HFA384x_RATEBIT_2 ((u16)2)
|
|
#define HFA384x_RATEBIT_5dot5 ((u16)4)
|
|
#define HFA384x_RATEBIT_11 ((u16)8)
|
|
|
|
switch (mibitem.data) {
|
|
case HFA384x_RATEBIT_1:
|
|
rrq->value = 1000000;
|
|
break;
|
|
case HFA384x_RATEBIT_2:
|
|
rrq->value = 2000000;
|
|
break;
|
|
case HFA384x_RATEBIT_5dot5:
|
|
rrq->value = 5500000;
|
|
break;
|
|
case HFA384x_RATEBIT_11:
|
|
rrq->value = 11000000;
|
|
break;
|
|
default:
|
|
err = -EINVAL;
|
|
}
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwrts(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rts, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibget;
|
|
mibitem.did = DIDmib_dot11mac_dot11OperationTable_dot11RTSThreshold;
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
rts->value = mibitem.data;
|
|
rts->disabled = (rts->value == 2347);
|
|
rts->fixed = 1;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_siwrts(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rts, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibget;
|
|
mibitem.did = DIDmib_dot11mac_dot11OperationTable_dot11RTSThreshold;
|
|
if (rts->disabled)
|
|
mibitem.data = 2347;
|
|
else
|
|
mibitem.data = rts->value;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwfrag(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *frag, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibget;
|
|
mibitem.did =
|
|
DIDmib_dot11mac_dot11OperationTable_dot11FragmentationThreshold;
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
frag->value = mibitem.data;
|
|
frag->disabled = (frag->value == 2346);
|
|
frag->fixed = 1;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_siwfrag(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *frag, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
mibitem.did =
|
|
DIDmib_dot11mac_dot11OperationTable_dot11FragmentationThreshold;
|
|
|
|
if (frag->disabled)
|
|
mibitem.data = 2346;
|
|
else
|
|
mibitem.data = frag->value;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
#ifndef IW_RETRY_LONG
|
|
#define IW_RETRY_LONG IW_RETRY_MAX
|
|
#endif
|
|
|
|
#ifndef IW_RETRY_SHORT
|
|
#define IW_RETRY_SHORT IW_RETRY_MIN
|
|
#endif
|
|
|
|
static int p80211wext_giwretry(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rrq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
u16 shortretry, longretry, lifetime;
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibget;
|
|
mibitem.did = DIDmib_dot11mac_dot11OperationTable_dot11ShortRetryLimit;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
shortretry = mibitem.data;
|
|
|
|
mibitem.did = DIDmib_dot11mac_dot11OperationTable_dot11LongRetryLimit;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
longretry = mibitem.data;
|
|
|
|
mibitem.did =
|
|
DIDmib_dot11mac_dot11OperationTable_dot11MaxTransmitMSDULifetime;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
lifetime = mibitem.data;
|
|
|
|
rrq->disabled = 0;
|
|
|
|
if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
|
|
rrq->flags = IW_RETRY_LIFETIME;
|
|
rrq->value = lifetime * 1024;
|
|
} else {
|
|
if (rrq->flags & IW_RETRY_LONG) {
|
|
rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
|
|
rrq->value = longretry;
|
|
} else {
|
|
rrq->flags = IW_RETRY_LIMIT;
|
|
rrq->value = shortretry;
|
|
if (shortretry != longretry)
|
|
rrq->flags |= IW_RETRY_SHORT;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
|
|
}
|
|
|
|
static int p80211wext_siwretry(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rrq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
if (rrq->disabled) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
|
|
if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
|
|
mibitem.did =
|
|
DIDmib_dot11mac_dot11OperationTable_dot11MaxTransmitMSDULifetime;
|
|
mibitem.data = rrq->value /= 1024;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
} else {
|
|
if (rrq->flags & IW_RETRY_LONG) {
|
|
mibitem.did =
|
|
DIDmib_dot11mac_dot11OperationTable_dot11LongRetryLimit;
|
|
mibitem.data = rrq->value;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem,
|
|
sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (rrq->flags & IW_RETRY_SHORT) {
|
|
mibitem.did =
|
|
DIDmib_dot11mac_dot11OperationTable_dot11ShortRetryLimit;
|
|
mibitem.data = rrq->value;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem,
|
|
sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
|
|
}
|
|
|
|
static int p80211wext_siwtxpow(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rrq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
if (!wlan_wext_write) {
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
mibitem.did =
|
|
DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel;
|
|
if (rrq->fixed == 0)
|
|
mibitem.data = 30;
|
|
else
|
|
mibitem.data = rrq->value;
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_giwtxpow(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *rrq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211item_uint32_t mibitem;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
int result;
|
|
int err = 0;
|
|
|
|
msg.msgcode = DIDmsg_dot11req_mibget;
|
|
mibitem.did =
|
|
DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel;
|
|
|
|
memcpy(&msg.mibattribute.data, &mibitem, sizeof(mibitem));
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
|
|
if (result) {
|
|
err = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(&mibitem, &msg.mibattribute.data, sizeof(mibitem));
|
|
|
|
/* XXX handle OFF by setting disabled = 1; */
|
|
|
|
rrq->flags = 0; /* IW_TXPOW_DBM; */
|
|
rrq->disabled = 0;
|
|
rrq->fixed = 0;
|
|
rrq->value = mibitem.data;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_siwspy(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *srq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
struct sockaddr address[IW_MAX_SPY];
|
|
int number = srq->length;
|
|
int i;
|
|
|
|
/* Copy the data from the input buffer */
|
|
memcpy(address, extra, sizeof(struct sockaddr) * number);
|
|
|
|
wlandev->spy_number = 0;
|
|
|
|
if (number > 0) {
|
|
|
|
/* extract the addresses */
|
|
for (i = 0; i < number; i++) {
|
|
|
|
memcpy(wlandev->spy_address[i], address[i].sa_data,
|
|
ETH_ALEN);
|
|
}
|
|
|
|
/* reset stats */
|
|
memset(wlandev->spy_stat, 0,
|
|
sizeof(struct iw_quality) * IW_MAX_SPY);
|
|
|
|
/* set number of addresses */
|
|
wlandev->spy_number = number;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* jkriegl: from orinoco, modified */
|
|
static int p80211wext_giwspy(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *srq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
|
|
struct sockaddr address[IW_MAX_SPY];
|
|
struct iw_quality spy_stat[IW_MAX_SPY];
|
|
int number;
|
|
int i;
|
|
|
|
number = wlandev->spy_number;
|
|
|
|
if (number > 0) {
|
|
|
|
/* populate address and spy struct's */
|
|
for (i = 0; i < number; i++) {
|
|
memcpy(address[i].sa_data, wlandev->spy_address[i],
|
|
ETH_ALEN);
|
|
address[i].sa_family = AF_UNIX;
|
|
memcpy(&spy_stat[i], &wlandev->spy_stat[i],
|
|
sizeof(struct iw_quality));
|
|
}
|
|
|
|
/* reset update flag */
|
|
for (i = 0; i < number; i++)
|
|
wlandev->spy_stat[i].updated = 0;
|
|
}
|
|
|
|
/* push stuff to user space */
|
|
srq->length = number;
|
|
memcpy(extra, address, sizeof(struct sockaddr) * number);
|
|
memcpy(extra + sizeof(struct sockaddr) * number, spy_stat,
|
|
sizeof(struct iw_quality) * number);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int prism2_result2err(int prism2_result)
|
|
{
|
|
int err = 0;
|
|
|
|
switch (prism2_result) {
|
|
case P80211ENUM_resultcode_invalid_parameters:
|
|
err = -EINVAL;
|
|
break;
|
|
case P80211ENUM_resultcode_implementation_failure:
|
|
err = -EIO;
|
|
break;
|
|
case P80211ENUM_resultcode_not_supported:
|
|
err = -EOPNOTSUPP;
|
|
break;
|
|
default:
|
|
err = 0;
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int p80211wext_siwscan(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *srq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211msg_dot11req_scan_t msg;
|
|
int result;
|
|
int err = 0;
|
|
int i = 0;
|
|
|
|
if (wlandev->macmode == WLAN_MACMODE_ESS_AP) {
|
|
printk(KERN_ERR "Can't scan in AP mode\n");
|
|
err = (-EOPNOTSUPP);
|
|
goto exit;
|
|
}
|
|
|
|
memset(&msg, 0x00, sizeof(p80211msg_dot11req_scan_t));
|
|
msg.msgcode = DIDmsg_dot11req_scan;
|
|
msg.bsstype.data = P80211ENUM_bsstype_any;
|
|
|
|
memset(&(msg.bssid.data), 0xFF, sizeof(p80211item_pstr6_t));
|
|
msg.bssid.data.len = 6;
|
|
|
|
msg.scantype.data = P80211ENUM_scantype_active;
|
|
msg.probedelay.data = 0;
|
|
|
|
for (i = 1; i <= 14; i++)
|
|
msg.channellist.data.data[i - 1] = i;
|
|
msg.channellist.data.len = 14;
|
|
|
|
msg.maxchanneltime.data = 250;
|
|
msg.minchanneltime.data = 200;
|
|
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
if (result)
|
|
err = prism2_result2err(msg.resultcode.data);
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
/* Helper to translate scan into Wireless Extensions scan results.
|
|
* Inspired by the prism54 code, which was in turn inspired by the
|
|
* airo driver code.
|
|
*/
|
|
static char *wext_translate_bss(struct iw_request_info *info, char *current_ev,
|
|
char *end_buf,
|
|
p80211msg_dot11req_scan_results_t *bss)
|
|
{
|
|
struct iw_event iwe; /* Temporary buffer */
|
|
|
|
/* The first entry must be the MAC address */
|
|
memcpy(iwe.u.ap_addr.sa_data, bss->bssid.data.data, WLAN_BSSID_LEN);
|
|
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
|
|
iwe.cmd = SIOCGIWAP;
|
|
current_ev =
|
|
iwe_stream_add_event(info, current_ev, end_buf, &iwe,
|
|
IW_EV_ADDR_LEN);
|
|
|
|
/* The following entries will be displayed in the same order we give them */
|
|
|
|
/* The ESSID. */
|
|
if (bss->ssid.data.len > 0) {
|
|
char essid[IW_ESSID_MAX_SIZE + 1];
|
|
int size;
|
|
|
|
size =
|
|
min_t(unsigned short, IW_ESSID_MAX_SIZE,
|
|
bss->ssid.data.len);
|
|
memset(&essid, 0, sizeof(essid));
|
|
memcpy(&essid, bss->ssid.data.data, size);
|
|
pr_debug(" essid size = %d\n", size);
|
|
iwe.u.data.length = size;
|
|
iwe.u.data.flags = 1;
|
|
iwe.cmd = SIOCGIWESSID;
|
|
current_ev =
|
|
iwe_stream_add_point(info, current_ev, end_buf, &iwe,
|
|
&essid[0]);
|
|
pr_debug(" essid size OK.\n");
|
|
}
|
|
|
|
switch (bss->bsstype.data) {
|
|
case P80211ENUM_bsstype_infrastructure:
|
|
iwe.u.mode = IW_MODE_MASTER;
|
|
break;
|
|
|
|
case P80211ENUM_bsstype_independent:
|
|
iwe.u.mode = IW_MODE_ADHOC;
|
|
break;
|
|
|
|
default:
|
|
iwe.u.mode = 0;
|
|
break;
|
|
}
|
|
iwe.cmd = SIOCGIWMODE;
|
|
if (iwe.u.mode)
|
|
current_ev =
|
|
iwe_stream_add_event(info, current_ev, end_buf, &iwe,
|
|
IW_EV_UINT_LEN);
|
|
|
|
/* Encryption capability */
|
|
if (bss->privacy.data == P80211ENUM_truth_true)
|
|
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
|
|
else
|
|
iwe.u.data.flags = IW_ENCODE_DISABLED;
|
|
iwe.u.data.length = 0;
|
|
iwe.cmd = SIOCGIWENCODE;
|
|
current_ev =
|
|
iwe_stream_add_point(info, current_ev, end_buf, &iwe, NULL);
|
|
|
|
/* Add frequency. (short) bss->channel is the frequency in MHz */
|
|
iwe.u.freq.m = bss->dschannel.data;
|
|
iwe.u.freq.e = 0;
|
|
iwe.cmd = SIOCGIWFREQ;
|
|
current_ev =
|
|
iwe_stream_add_event(info, current_ev, end_buf, &iwe,
|
|
IW_EV_FREQ_LEN);
|
|
|
|
/* Add quality statistics */
|
|
iwe.u.qual.level = bss->signal.data;
|
|
iwe.u.qual.noise = bss->noise.data;
|
|
/* do a simple SNR for quality */
|
|
iwe.u.qual.qual = qual_as_percent(bss->signal.data - bss->noise.data);
|
|
iwe.cmd = IWEVQUAL;
|
|
current_ev =
|
|
iwe_stream_add_event(info, current_ev, end_buf, &iwe,
|
|
IW_EV_QUAL_LEN);
|
|
|
|
return current_ev;
|
|
}
|
|
|
|
static int p80211wext_giwscan(netdevice_t *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *srq, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
p80211msg_dot11req_scan_results_t msg;
|
|
int result = 0;
|
|
int err = 0;
|
|
int i = 0;
|
|
int scan_good = 0;
|
|
char *current_ev = extra;
|
|
|
|
/* Since wireless tools doesn't really have a way of passing how
|
|
* many scan results results there were back here, keep grabbing them
|
|
* until we fail.
|
|
*/
|
|
do {
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msgcode = DIDmsg_dot11req_scan_results;
|
|
msg.bssindex.data = i;
|
|
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
if ((result != 0) ||
|
|
(msg.resultcode.data != P80211ENUM_resultcode_success)) {
|
|
break;
|
|
}
|
|
|
|
current_ev =
|
|
wext_translate_bss(info, current_ev,
|
|
extra + IW_SCAN_MAX_DATA, &msg);
|
|
scan_good = 1;
|
|
i++;
|
|
} while (i < IW_MAX_AP);
|
|
|
|
srq->length = (current_ev - extra);
|
|
srq->flags = 0; /* todo */
|
|
|
|
if (result && !scan_good)
|
|
err = prism2_result2err(msg.resultcode.data);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* extra wireless extensions stuff to support NetworkManager (I hope) */
|
|
|
|
/* SIOCSIWENCODEEXT */
|
|
static int p80211wext_set_encodeext(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *wrqu, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
|
|
p80211msg_dot11req_mibset_t msg;
|
|
p80211item_pstr32_t *pstr;
|
|
|
|
int result = 0;
|
|
struct iw_point *encoding = &wrqu->encoding;
|
|
int idx = encoding->flags & IW_ENCODE_INDEX;
|
|
|
|
pr_debug("set_encode_ext flags[%d] alg[%d] keylen[%d]\n",
|
|
ext->ext_flags, (int)ext->alg, (int)ext->key_len);
|
|
|
|
if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
|
|
/* set default key ? I'm not sure if this the the correct thing to do here */
|
|
|
|
if (idx) {
|
|
if (idx < 1 || idx > NUM_WEPKEYS)
|
|
return -EINVAL;
|
|
else
|
|
idx--;
|
|
}
|
|
pr_debug("setting default key (%d)\n", idx);
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID,
|
|
idx);
|
|
if (result)
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
|
|
if (!(ext->alg & IW_ENCODE_ALG_WEP)) {
|
|
pr_debug("asked to set a non wep key :(");
|
|
return -EINVAL;
|
|
}
|
|
if (idx) {
|
|
if (idx < 1 || idx > NUM_WEPKEYS)
|
|
return -EINVAL;
|
|
else
|
|
idx--;
|
|
}
|
|
pr_debug("Set WEP key (%d)\n", idx);
|
|
wlandev->wep_keylens[idx] = ext->key_len;
|
|
memcpy(wlandev->wep_keys[idx], ext->key, ext->key_len);
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
pstr = (p80211item_pstr32_t *)&msg.mibattribute.data;
|
|
memcpy(pstr->data.data, ext->key, ext->key_len);
|
|
pstr->data.len = ext->key_len;
|
|
switch (idx) {
|
|
case 0:
|
|
pstr->did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0;
|
|
break;
|
|
case 1:
|
|
pstr->did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1;
|
|
break;
|
|
case 2:
|
|
pstr->did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2;
|
|
break;
|
|
case 3:
|
|
pstr->did =
|
|
DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
msg.msgcode = DIDmsg_dot11req_mibset;
|
|
result = p80211req_dorequest(wlandev, (u8 *)&msg);
|
|
pr_debug("result (%d)\n", result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* SIOCGIWENCODEEXT */
|
|
static int p80211wext_get_encodeext(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *wrqu, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
|
|
|
|
struct iw_point *encoding = &wrqu->encoding;
|
|
int result = 0;
|
|
int max_len;
|
|
int idx;
|
|
|
|
pr_debug("get_encode_ext flags[%d] alg[%d] keylen[%d]\n",
|
|
ext->ext_flags, (int)ext->alg, (int)ext->key_len);
|
|
|
|
max_len = encoding->length - sizeof(*ext);
|
|
if (max_len <= 0) {
|
|
pr_debug("get_encodeext max_len [%d] invalid\n",
|
|
max_len);
|
|
result = -EINVAL;
|
|
goto exit;
|
|
}
|
|
idx = encoding->flags & IW_ENCODE_INDEX;
|
|
|
|
pr_debug("get_encode_ext index [%d]\n", idx);
|
|
|
|
if (idx) {
|
|
if (idx < 1 || idx > NUM_WEPKEYS) {
|
|
printk(KERN_DEBUG
|
|
"get_encode_ext invalid key index [%d]\n", idx);
|
|
result = -EINVAL;
|
|
goto exit;
|
|
}
|
|
idx--;
|
|
} else {
|
|
/* default key ? not sure what to do */
|
|
/* will just use key[0] for now ! FIX ME */
|
|
}
|
|
|
|
encoding->flags = idx + 1;
|
|
memset(ext, 0, sizeof(*ext));
|
|
|
|
ext->alg = IW_ENCODE_ALG_WEP;
|
|
ext->key_len = wlandev->wep_keylens[idx];
|
|
memcpy(ext->key, wlandev->wep_keys[idx], ext->key_len);
|
|
|
|
encoding->flags |= IW_ENCODE_ENABLED;
|
|
exit:
|
|
return result;
|
|
}
|
|
|
|
/* SIOCSIWAUTH */
|
|
static int p80211_wext_set_iwauth(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *wrqu, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
struct iw_param *param = &wrqu->param;
|
|
int result = 0;
|
|
|
|
pr_debug("set_iwauth flags[%d]\n",
|
|
(int)param->flags & IW_AUTH_INDEX);
|
|
|
|
switch (param->flags & IW_AUTH_INDEX) {
|
|
case IW_AUTH_DROP_UNENCRYPTED:
|
|
pr_debug("drop_unencrypted %d\n", param->value);
|
|
if (param->value)
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted,
|
|
P80211ENUM_truth_true);
|
|
else
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted,
|
|
P80211ENUM_truth_false);
|
|
break;
|
|
|
|
case IW_AUTH_PRIVACY_INVOKED:
|
|
pr_debug("privacy invoked %d\n", param->value);
|
|
if (param->value)
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked,
|
|
P80211ENUM_truth_true);
|
|
else
|
|
result =
|
|
p80211wext_dorequest(wlandev,
|
|
DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked,
|
|
P80211ENUM_truth_false);
|
|
|
|
break;
|
|
|
|
case IW_AUTH_80211_AUTH_ALG:
|
|
if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
|
|
pr_debug("set open_system\n");
|
|
wlandev->hostwep &= ~HOSTWEP_SHAREDKEY;
|
|
} else if (param->value & IW_AUTH_ALG_SHARED_KEY) {
|
|
pr_debug("set shared key\n");
|
|
wlandev->hostwep |= HOSTWEP_SHAREDKEY;
|
|
} else {
|
|
/* don't know what to do know */
|
|
pr_debug("unknown AUTH_ALG (%d)\n",
|
|
param->value);
|
|
result = -EINVAL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* SIOCSIWAUTH */
|
|
static int p80211_wext_get_iwauth(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *wrqu, char *extra)
|
|
{
|
|
wlandevice_t *wlandev = dev->ml_priv;
|
|
struct iw_param *param = &wrqu->param;
|
|
int result = 0;
|
|
|
|
pr_debug("get_iwauth flags[%d]\n",
|
|
(int)param->flags & IW_AUTH_INDEX);
|
|
|
|
switch (param->flags & IW_AUTH_INDEX) {
|
|
case IW_AUTH_DROP_UNENCRYPTED:
|
|
param->value =
|
|
wlandev->hostwep & HOSTWEP_EXCLUDEUNENCRYPTED ? 1 : 0;
|
|
break;
|
|
|
|
case IW_AUTH_PRIVACY_INVOKED:
|
|
param->value =
|
|
wlandev->hostwep & HOSTWEP_PRIVACYINVOKED ? 1 : 0;
|
|
break;
|
|
|
|
case IW_AUTH_80211_AUTH_ALG:
|
|
param->value =
|
|
wlandev->
|
|
hostwep & HOSTWEP_SHAREDKEY ? IW_AUTH_ALG_SHARED_KEY :
|
|
IW_AUTH_ALG_OPEN_SYSTEM;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static iw_handler p80211wext_handlers[] = {
|
|
(iw_handler) p80211wext_siwcommit, /* SIOCSIWCOMMIT */
|
|
(iw_handler) p80211wext_giwname, /* SIOCGIWNAME */
|
|
(iw_handler) NULL, /* SIOCSIWNWID */
|
|
(iw_handler) NULL, /* SIOCGIWNWID */
|
|
(iw_handler) p80211wext_siwfreq, /* SIOCSIWFREQ */
|
|
(iw_handler) p80211wext_giwfreq, /* SIOCGIWFREQ */
|
|
(iw_handler) p80211wext_siwmode, /* SIOCSIWMODE */
|
|
(iw_handler) p80211wext_giwmode, /* SIOCGIWMODE */
|
|
(iw_handler) NULL, /* SIOCSIWSENS */
|
|
(iw_handler) NULL, /* SIOCGIWSENS */
|
|
(iw_handler) NULL, /* not used *//* SIOCSIWRANGE */
|
|
(iw_handler) p80211wext_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) p80211wext_siwspy, /* SIOCSIWSPY */
|
|
(iw_handler) p80211wext_giwspy, /* SIOCGIWSPY */
|
|
(iw_handler) NULL, /* -- hole -- */
|
|
(iw_handler) NULL, /* -- hole -- */
|
|
(iw_handler) NULL, /* SIOCSIWAP */
|
|
(iw_handler) p80211wext_giwap, /* SIOCGIWAP */
|
|
(iw_handler) NULL, /* -- hole -- */
|
|
(iw_handler) NULL, /* SIOCGIWAPLIST */
|
|
(iw_handler) p80211wext_siwscan, /* SIOCSIWSCAN */
|
|
(iw_handler) p80211wext_giwscan, /* SIOCGIWSCAN */
|
|
(iw_handler) p80211wext_siwessid, /* SIOCSIWESSID */
|
|
(iw_handler) p80211wext_giwessid, /* SIOCGIWESSID */
|
|
(iw_handler) NULL, /* SIOCSIWNICKN */
|
|
(iw_handler) p80211wext_giwessid, /* SIOCGIWNICKN */
|
|
(iw_handler) NULL, /* -- hole -- */
|
|
(iw_handler) NULL, /* -- hole -- */
|
|
(iw_handler) NULL, /* SIOCSIWRATE */
|
|
(iw_handler) p80211wext_giwrate, /* SIOCGIWRATE */
|
|
(iw_handler) p80211wext_siwrts, /* SIOCSIWRTS */
|
|
(iw_handler) p80211wext_giwrts, /* SIOCGIWRTS */
|
|
(iw_handler) p80211wext_siwfrag, /* SIOCSIWFRAG */
|
|
(iw_handler) p80211wext_giwfrag, /* SIOCGIWFRAG */
|
|
(iw_handler) p80211wext_siwtxpow, /* SIOCSIWTXPOW */
|
|
(iw_handler) p80211wext_giwtxpow, /* SIOCGIWTXPOW */
|
|
(iw_handler) p80211wext_siwretry, /* SIOCSIWRETRY */
|
|
(iw_handler) p80211wext_giwretry, /* SIOCGIWRETRY */
|
|
(iw_handler) p80211wext_siwencode, /* SIOCSIWENCODE */
|
|
(iw_handler) p80211wext_giwencode, /* SIOCGIWENCODE */
|
|
(iw_handler) NULL, /* SIOCSIWPOWER */
|
|
(iw_handler) NULL, /* SIOCGIWPOWER */
|
|
/* WPA operations */
|
|
(iw_handler) NULL, /* -- hole -- */
|
|
(iw_handler) NULL, /* -- hole -- */
|
|
(iw_handler) NULL, /* SIOCSIWGENIE set generic IE */
|
|
(iw_handler) NULL, /* SIOCGIWGENIE get generic IE */
|
|
(iw_handler) p80211_wext_set_iwauth, /* SIOCSIWAUTH set authentication mode params */
|
|
(iw_handler) p80211_wext_get_iwauth, /* SIOCGIWAUTH get authentication mode params */
|
|
|
|
(iw_handler) p80211wext_set_encodeext, /* SIOCSIWENCODEEXT set encoding token & mode */
|
|
(iw_handler) p80211wext_get_encodeext, /* SIOCGIWENCODEEXT get encoding token & mode */
|
|
(iw_handler) NULL, /* SIOCSIWPMKSA PMKSA cache operation */
|
|
};
|
|
|
|
struct iw_handler_def p80211wext_handler_def = {
|
|
.num_standard = ARRAY_SIZE(p80211wext_handlers),
|
|
.num_private = 0,
|
|
.num_private_args = 0,
|
|
.standard = p80211wext_handlers,
|
|
.private = NULL,
|
|
.private_args = NULL,
|
|
.get_wireless_stats = p80211wext_get_wireless_stats
|
|
};
|
|
|
|
int p80211wext_event_associated(wlandevice_t *wlandev, int assoc)
|
|
{
|
|
union iwreq_data data;
|
|
|
|
/* Send the association state first */
|
|
data.ap_addr.sa_family = ARPHRD_ETHER;
|
|
if (assoc)
|
|
memcpy(data.ap_addr.sa_data, wlandev->bssid, ETH_ALEN);
|
|
else
|
|
memset(data.ap_addr.sa_data, 0, ETH_ALEN);
|
|
|
|
if (wlan_wext_write)
|
|
wireless_send_event(wlandev->netdev, SIOCGIWAP, &data, NULL);
|
|
|
|
if (!assoc)
|
|
goto done;
|
|
|
|
/* XXX send association data, like IEs, etc etc. */
|
|
|
|
done:
|
|
return 0;
|
|
}
|