/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */
#ifdef in_atomic
/* Get kernel_locked() for in_atomic() */
#include <linux/smp_lock.h>
#endif
#include <linux/ethtool.h>
static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev)
{
struct hostap_interface *iface = dev->priv;
local_info_t *local = iface->local;
struct iw_statistics *wstats;
#if WIRELESS_EXT > 16
/* Why are we doing that ? Jean II */
if (iface->type != HOSTAP_INTERFACE_MAIN)
return NULL;
#endif /* WIRELESS_EXT > 16 */
wstats = &local->wstats;
wstats->status = 0;
wstats->discard.code =
local->comm_tallies.rx_discards_wep_undecryptable;
wstats->discard.misc =
local->comm_tallies.rx_fcs_errors +
local->comm_tallies.rx_discards_no_buffer +
local->comm_tallies.tx_discards_wrong_sa;
wstats->discard.retries =
local->comm_tallies.tx_retry_limit_exceeded;
wstats->discard.fragment =
local->comm_tallies.rx_message_in_bad_msg_fragments;
if (local->iw_mode != IW_MODE_MASTER &&
local->iw_mode != IW_MODE_REPEAT) {
int update = 1;
#ifdef in_atomic
/* RID reading might sleep and it must not be called in
* interrupt context or while atomic. However, this
* function seems to be called while atomic (at least in Linux
* 2.5.59). Update signal quality values only if in suitable
* context. Otherwise, previous values read from tick timer
* will be used. */
if (in_atomic())
update = 0;
#endif /* in_atomic */
if (update && prism2_update_comms_qual(dev) == 0)
wstats->qual.updated = IW_QUAL_ALL_UPDATED |
IW_QUAL_DBM;
wstats->qual.qual = local->comms_qual;
wstats->qual.level = local->avg_signal;
wstats->qual.noise = local->avg_noise;
} else {
wstats->qual.qual = 0;
wstats->qual.level = 0;
wstats->qual.noise = 0;
wstats->qual.updated = IW_QUAL_ALL_INVALID;
}
return wstats;
}
static int prism2_get_datarates(struct net_device *dev, u8 *rates)
{
struct hostap_interface *iface = dev->priv;
local_info_t *local = iface->local;
u8 buf[12];
int len;
u16 val;
len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf,
sizeof(buf), 0);
if (len < 2)
return 0;
val = le16_to_cpu(*(u16 *) buf); /* string length */
if (len - 2 < val || val > 10)
return 0;
memcpy(rates, buf + 2, val);
return val;
}
static int prism2_get_name(struct net_device *dev,
struct iw_request_info *info,
char *name, char *extra)
{
u8 rates[10];
int len, i, over2 = 0;
len = prism2_get_datarates(dev, rates);
for (i = 0; i < len; i++) {
if (rates[i] == 0x0b || rates[i] == 0x16) {
over2 = 1;
break;
}
}
strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS");
return 0;
}
static void prism2_crypt_delayed_deinit(local_info_t *local,
struct prism2_crypt_data **crypt)
{
struct prism2_crypt_data *tmp;
unsigned long flags;
tmp = *crypt;
*crypt = NULL;
if (tmp == NULL)
return;
/* must not run ops->deinit() while there may be pending encrypt or
* decrypt operations. Use a list of delayed deinits to avoid needing
* locking. */
spin_lock_irqsave(&local->lock, flags);
list_add(&tmp->list, &local->crypt_deinit_list);
if (!timer_pending(&local->crypt_deinit_timer)) {
local->crypt_deinit_timer.expires = jiffies + HZ;
add_timer(&local->crypt_deinit_timer);
}
spin_unlock_irqrestore(&local->lock, flags);
}
static int prism2_ioctl_siwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq, char *keybuf)
{
struct hostap_interface *iface = dev->priv;
local_info_t *local = iface->local;
int i;
struct prism2_crypt_data **crypt;
i = erq->flags & IW_ENCODE_INDEX;
if (i < 1 || i > 4)
i = local->tx_keyidx;
else
i--;
if (i < 0 || i >= WEP_KEYS)
return -EINVAL;
crypt = &local->crypt[i];
if (erq->flags & IW_ENCODE_DISABLED) {
if (*crypt)
prism2_crypt_delayed_deinit(local, crypt);
goto done;
}
if (*crypt != NULL && (*crypt)->ops != NULL &&
strcmp((*crypt)->ops->name, "WEP") != 0) {
/* changing to use WEP; deinit previously used algorithm */
prism2_crypt_delayed_deinit(local, crypt);
}
if (*crypt == NULL) {
struct prism2_crypt_data *new_crypt;
/* take WEP into use */
new_crypt = (struct prism2_crypt_data *)
kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL);
if (new_crypt == NULL)
return -ENOMEM;
memset(new_crypt, 0, sizeof(struct prism2_crypt_data));
new_crypt->ops = hostap_get_crypto_ops("WEP");
if (!new_crypt->ops) {
request_module("hostap_crypt_wep");
new_crypt->ops = hostap_get_crypto_ops("WEP");
}
if (new_crypt->ops)
new_crypt->priv = new_crypt->ops->init(i);
if (!new_crypt->ops || !new_crypt->priv) {
kfree(new_crypt);
new_crypt = NULL;
printk(KERN_WARNING "%s: could not initialize WEP: "
"load module hostap_crypt_wep.o\n",
dev->name);
return -EOPNOTSUPP;
}
*crypt = new_crypt;
}
if (erq->length > 0) {
int len = erq->length <= 5 ? 5 : 13;
int first = 1, j;
if (len > erq->length)
memset(keybuf + erq->length, 0, len - erq->length);
(*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv);
for (j = 0; j < WEP_KEYS; j++) {
if (j != i && local->crypt[j]) {
first = 0;
break;
}
}
if (first)
local->tx_keyidx = i;
} else {
/* No key data - just set the default TX key index */
local->tx_keyidx = i;
}
done:
local->open_wep = erq->flags & IW_ENCODE_OPEN;
if (hostap_set_encryption(local)) {
printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name);
return -EINVAL;
}
/* Do not reset port0 if card is in Managed mode since resetting will
* generate new IEEE 802.11 authentication which may end up in looping
* with IEEE 802.1X. Prism2 documentation seem to require port reset
* after WEP configuration. However, keys are apparently changed at
* least in Managed mode. */
if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) {
printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
return -EINVAL;
}
return 0;
}
static int prism2_ioctl_giwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq, char *key)
{
struct hostap_interface *iface = dev->priv;
local_info_t *local = iface->local;
int i, len;
u16 val;
struct prism2_crypt_data *crypt;
i = erq->flags & IW_ENCODE_INDEX;
if (i < 1 || i > 4)
i = local->tx_keyidx;
else
i--;
if (i < 0 || i >= WEP_KEYS)
return -EINVAL;
crypt = local->crypt[i];
erq->flags = i + 1;
if (crypt == NULL || crypt->ops == NULL) {
erq->length = 0;
erq->flags |= IW_ENCODE_DISABLED;
return 0;
}
if (strcmp(crypt->ops->name, "WEP") != 0) {
/* only WEP is supported with wireless extensions, so just
* report that encryption is used */
erq->length = 0;
erq->flags |= IW_ENCODE_ENABLED;
return 0;
}
/* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show
* the keys from driver buffer */
len = crypt->ops->get_key(key, WEP_KEY_LEN, NULL, crypt->priv);
erq->length = (len >= 0 ? len : 0);
if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0)
{
printk("CNFWEPFLAGS reading failed\n");
return -EOPNOTSUPP;
}
le16_to_cpus(&val);
if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED)
erq->flags |= IW_ENCODE_ENABLED;
else
erq->flags |= IW_ENCODE_DISABLED;
if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED)
erq->flags |= IW_ENCODE_RESTRICTED;
else
erq->flags |= IW_ENCODE_OPEN;
return 0;
}
#if WIRELESS_EXT <= 15
static int prism2_ioctl_giwspy(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *srq, char *extra)
{
struct hostap_interface *iface = dev->priv;
local_info_t *local = iface->local;
struct sockaddr addr[IW_MAX_SPY];
struct iw_quality qual[IW_MAX_SPY];
srq->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_SPY, 0);
memcpy(extra, &addr, sizeof(addr[0]) * srq->length);
memcpy(extra + sizeof(addr[0]) * srq->length, &qual,
sizeof(qual[0]