diff options
author | Jouni Malinen <jkmaline@cc.hut.fi> | 2005-05-12 22:54:16 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-05-12 22:54:16 -0400 |
commit | ff1d2767d5a43c85f944e86a45284b721f66196c (patch) | |
tree | 91c1b6dd20bd772bc112c0012830678b46b69604 /drivers/net/wireless/hostap/hostap_ioctl.c | |
parent | 88d7bd8cb9eb8d64bf7997600b0d64f7834047c5 (diff) | |
download | linux-ff1d2767d5a43c85f944e86a45284b721f66196c.tar.gz linux-ff1d2767d5a43c85f944e86a45284b721f66196c.tar.bz2 linux-ff1d2767d5a43c85f944e86a45284b721f66196c.zip |
Add HostAP wireless driver.
Includes minor cleanups from Adrian Bunk <bunk@stusta.de>.
Diffstat (limited to 'drivers/net/wireless/hostap/hostap_ioctl.c')
-rw-r--r-- | drivers/net/wireless/hostap/hostap_ioctl.c | 4123 |
1 files changed, 4123 insertions, 0 deletions
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c new file mode 100644 index 000000000000..e545ac9c1b10 --- /dev/null +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -0,0 +1,4123 @@ +/* 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; + local_info_t *local; + struct iw_statistics *wstats; + + iface = netdev_priv(dev); + local = iface->local; + + /* Why are we doing that ? Jean II */ + if (iface->type != HOSTAP_INTERFACE_MAIN) + return NULL; + + 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 = 7; + + 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 = 0; + } + + return wstats; +} + + +static int prism2_get_datarates(struct net_device *dev, u8 *rates) +{ + struct hostap_interface *iface; + local_info_t *local; + u8 buf[12]; + int len; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + 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; + local_info_t *local; + int i; + struct prism2_crypt_data **crypt; + + iface = netdev_priv(dev); + local = iface->local; + + 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; + local_info_t *local; + int i, len; + u16 val; + struct prism2_crypt_data *crypt; + + iface = netdev_priv(dev); + local = iface->local; + + 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; +} + + +static int hostap_set_rate(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret, basic_rates; + + iface = netdev_priv(dev); + local = iface->local; + + basic_rates = local->basic_rates & local->tx_rate_control; + if (!basic_rates || basic_rates != local->basic_rates) { + printk(KERN_INFO "%s: updating basic rate set automatically " + "to match with the new supported rate set\n", + dev->name); + if (!basic_rates) + basic_rates = local->tx_rate_control; + + local->basic_rates = basic_rates; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + basic_rates)) + printk(KERN_WARNING "%s: failed to set " + "cnfBasicRates\n", dev->name); + } + + ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control) || + hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control) || + local->func->reset_port(dev)); + + if (ret) { + printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates " + "setting to 0x%x failed\n", + dev->name, local->tx_rate_control); + } + + /* Update TX rate configuration for all STAs based on new operational + * rate set. */ + hostap_update_rates(local); + + return ret; +} + + +static int prism2_ioctl_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (rrq->fixed) { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } else { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } + + return hostap_set_rate(dev); +} + + +static int prism2_ioctl_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + u16 val; + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) < + 0) + return -EINVAL; + + if ((val & 0x1) && (val > 1)) + rrq->fixed = 0; + else + rrq->fixed = 1; + + if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL && + !local->fw_tx_rate_control) { + /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in + * Host AP mode, so use the recorded TX rate of the last sent + * frame */ + rrq->value = local->ap->last_tx_rate > 0 ? + local->ap->last_tx_rate * 100000 : 11000000; + return 0; + } + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) < + 0) + return -EINVAL; + + switch (val) { + case HFA384X_RATES_1MBPS: + rrq->value = 1000000; + break; + case HFA384X_RATES_2MBPS: + rrq->value = 2000000; + break; + case HFA384X_RATES_5MBPS: + rrq->value = 5500000; + break; + case HFA384X_RATES_11MBPS: + rrq->value = 11000000; + break; + default: + /* should not happen */ + rrq->value = 11000000; + ret = -EINVAL; + break; + } + + return ret; +} + + +static int prism2_ioctl_siwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* Set the desired AP density */ + if (sens->value < 1 || sens->value > 3) + return -EINVAL; + + if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + /* Get the current AP density */ + if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) < + 0) + return -EINVAL; + + sens->value = __le16_to_cpu(val); + sens->fixed = 1; + + return 0; +} + + +/* Deprecated in new wireless extension API */ +static int prism2_ioctl_giwaplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + struct sockaddr *addr; + struct iw_quality *qual; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->iw_mode != IW_MODE_MASTER) { + printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported " + "in Host AP mode\n"); + data->length = 0; + return -EOPNOTSUPP; + } + + addr = kmalloc(sizeof(struct sockaddr) * IW_MAX_AP, GFP_KERNEL); + qual = kmalloc(sizeof(struct iw_quality) * IW_MAX_AP, GFP_KERNEL); + if (addr == NULL || qual == NULL) { + kfree(addr); + kfree(qual); + data->length = 0; + return -ENOMEM; + } + + data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); + + memcpy(extra, &addr, sizeof(struct sockaddr) * data->length); + data->flags = 1; /* has quality information */ + memcpy(extra + sizeof(struct sockaddr) * data->length, &qual, + sizeof(struct iw_quality) * data->length); + + kfree(addr); + kfree(qual); + + return 0; +} + + +static int prism2_ioctl_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (rts->disabled) + val = __constant_cpu_to_le16(2347); + else if (rts->value < 0 || rts->value > 2347) + return -EINVAL; + else + val = __cpu_to_le16(rts->value); + + if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) || + local->func->reset_port(dev)) + return -EINVAL; + + local->rts_threshold = rts->value; + + return 0; +} + +static int prism2_ioctl_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) < + 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2347); + rts->fixed = 1; + + return 0; +} + + +static int prism2_ioctl_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (rts->disabled) + val = __constant_cpu_to_le16(2346); + else if (rts->value < 256 || rts->value > 2346) + return -EINVAL; + else + val = __cpu_to_le16(rts->value & ~0x1); /* even numbers only */ + + local->fragm_threshold = rts->value & ~0x1; + if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val, + 2) + || local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + &val, 2, 1) < 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2346); + rts->fixed = 1; + + return 0; +} + + +#ifndef PRISM2_NO_STATION_MODES +static int hostap_join_ap(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_join_request req; + unsigned long flags; + int i; + struct hfa384x_scan_result *entry; + + iface = netdev_priv(dev); + local = iface->local; + + memcpy(req.bssid, local->preferred_ap, ETH_ALEN); + req.channel = 0; + + spin_lock_irqsave(&local->lock, flags); + for (i = 0; i < local->last_scan_results_count; i++) { + if (!local->last_scan_results) + break; + entry = &local->last_scan_results[i]; + if (memcmp(local->preferred_ap, entry->bssid, ETH_ALEN) == 0) { + req.channel = entry->chid; + break; + } + } + spin_unlock_irqrestore(&local->lock, flags); + + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest " MACSTR + " failed\n", + dev->name, MAC2STR(local->preferred_ap)); + return -1; + } + + printk(KERN_DEBUG "%s: Trying to join BSSID " MACSTR "\n", + dev->name, MAC2STR(local->preferred_ap)); + + return 0; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static int prism2_ioctl_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN); + + if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) { + struct hfa384x_scan_request scan_req; + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, + &scan_req, sizeof(scan_req))) { + printk(KERN_DEBUG "%s: ScanResults request failed - " + "preferred AP delayed to next unsolicited " + "scan\n", dev->name); + } + } else if (local->host_roaming == 2 && + local->iw_mode == IW_MODE_INFRA) { + if (hostap_join_ap(dev)) + return -EINVAL; + } else { + printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only " + "in Managed mode when host_roaming is enabled\n", + dev->name); + } + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + +static int prism2_ioctl_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + ap_addr->sa_family = ARPHRD_ETHER; + switch (iface->type) { + case HOSTAP_INTERFACE_AP: + memcpy(&ap_addr->sa_data, dev->dev_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_STA: + memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_WDS: + memcpy(&ap_addr->sa_data, iface->u.wds.remote_addr, ETH_ALEN); + break; + default: + if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID, + &ap_addr->sa_data, ETH_ALEN, 1) < 0) + return -EOPNOTSUPP; + + /* local->bssid is also updated in LinkStatus handler when in + * station mode */ + memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN); + break; + } + + return 0; +} + + +static int prism2_ioctl_siwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + memset(local->name, 0, sizeof(local->name)); + memcpy(local->name, nickname, data->length); + local->name_set = 1; + + if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface; + local_info_t *local; + int len; + char name[MAX_NAME_LEN + 3]; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME, + &name, MAX_NAME_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) name); + if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN) + return -EOPNOTSUPP; + + name[val + 2] = '\0'; + data->length = val + 1; + memcpy(nickname, name + 2, val + 1); + + return 0; +} + + +static int prism2_ioctl_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* freq => chan. */ + if (freq->e == 1 && + freq->m / 100000 >= freq_list[0] && + freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) { + int ch; + int fr = freq->m / 100000; + for (ch = 0; ch < FREQ_COUNT; ch++) { + if (fr == freq_list[ch]) { + freq->e = 0; + freq->m = ch + 1; + break; + } + } + } + + if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT || + !(local->channel_mask & (1 << (freq->m - 1)))) + return -EINVAL; + + local->channel = freq->m; /* channel is used in prism2_setup_rids() */ + if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) < + 0) + return -EINVAL; + + le16_to_cpus(&val); + if (val < 1 || val > FREQ_COUNT) + return -EINVAL; + + freq->m = freq_list[val - 1] * 100000; + freq->e = 1; + + return 0; +} + + +static void hostap_monitor_set_type(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return; + + if (local->monitor_type == PRISM2_MONITOR_PRISM || + local->monitor_type == PRISM2_MONITOR_CAPHDR) { + dev->type = ARPHRD_IEEE80211_PRISM; + dev->hard_header_parse = + hostap_80211_prism_header_parse; + } else { + dev->type = ARPHRD_IEEE80211; + dev->hard_header_parse = hostap_80211_header_parse; + } +} + + +static int prism2_ioctl_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + if (data->flags == 0) + ssid[0] = '\0'; /* ANY */ + + if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') { + /* Setting SSID to empty string seems to kill the card in + * Host AP mode */ + printk(KERN_DEBUG "%s: Host AP mode does not support " + "'Any' essid\n", dev->name); + return -EINVAL; + } + + memcpy(local->essid, ssid, data->length); + local->essid[data->length] = '\0'; + + if ((!local->fw_ap && + hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid)) + || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *essid) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + data->flags = 1; /* active */ + if (local->iw_mode == IW_MODE_MASTER) { + data->length = strlen(local->essid); + memcpy(essid, local->essid, IW_ESSID_MAX_SIZE); + } else { + int len; + char ssid[MAX_SSID_LEN + 2]; + memset(ssid, 0, sizeof(ssid)); + len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID, + &ssid, MAX_SSID_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) ssid); + if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) { + return -EOPNOTSUPP; + } + data->length = val; + memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE); + } + + return 0; +} + + +static int prism2_ioctl_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + struct iw_range *range = (struct iw_range *) extra; + u8 rates[10]; + u16 val; + int i, len, over2; + + iface = netdev_priv(dev); + local = iface->local; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + /* TODO: could fill num_txpower and txpower array with + * something; however, there are 128 different values.. */ + + range->txpower_capa = IW_TXPOW_DBM; + + if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC) + { + range->min_pmp = 1 * 1024; + range->max_pmp = 65535 * 1024; + range->min_pmt = 1 * 1024; + range->max_pmt = 1000 * 1024; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | + IW_POWER_UNICAST_R | IW_POWER_ALL_R; + } + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 18; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + + range->num_channels = FREQ_COUNT; + + val = 0; + for (i = 0; i < FREQ_COUNT; i++) { + if (local->channel_mask & (1 << i)) { + range->freq[val].i = i + 1; + range->freq[val].m = freq_list[i] * 100000; + range->freq[val].e = 1; + val++; + } + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { + range->max_qual.qual = 70; /* what is correct max? This was not + * documented exactly. At least + * 69 has been observed. */ + range->max_qual.level = 0; /* dB */ + range->max_qual.noise = 0; /* dB */ + + /* What would be suitable values for "average/typical" qual? */ + range->avg_qual.qual = 20; + range->avg_qual.level = -60; + range->avg_qual.noise = -95; + } else { + range->max_qual.qual = 92; /* 0 .. 92 */ + range->max_qual.level = 154; /* 27 .. 154 */ + range->max_qual.noise = 154; /* 27 .. 154 */ + } + range->sensitivity = 3; + + range->max_encoding_tokens = WEP_KEYS; + range->num_encoding_sizes = 2; + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + + over2 = 0; + len = prism2_get_datarates(dev, rates); + range->num_bitrates = 0; + for (i = 0; i < len; i++) { + if (range->num_bitrates < IW_MAX_BITRATES) { + range->bitrate[range->num_bitrates] = + rates[i] * 500000; + range->num_bitrates++; + } + if (rates[i] == 0x0b || rates[i] == 0x16) + over2 = 1; + } + /* estimated maximum TCP throughput values (bps) */ + range->throughput = over2 ? 5500000 : 1500000; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | + IW_EVENT_CAPA_MASK(IWEVCUSTOM) | + IW_EVENT_CAPA_MASK(IWEVREGISTERED) | + IW_EVENT_CAPA_MASK(IWEVEXPIRED)); + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + return 0; +} + + +static int hostap_monitor_mode_enable(local_info_t *local) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "Enabling monitor mode\n"); + hostap_monitor_set_type(local); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_PSEUDO_IBSS)) { + printk(KERN_DEBUG "Port type setting for monitor mode " + "failed\n"); + return -EOPNOTSUPP; + } + + /* Host decrypt is needed to get the IV and ICV fields; + * however, monitor mode seems to remove WEP flag from frame + * control field */ + if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, + HFA384X_WEPFLAGS_HOSTENCRYPT | + HFA384X_WEPFLAGS_HOSTDECRYPT)) { + printk(KERN_DEBUG "WEP flags setting failed\n"); + return -EOPNOTSUPP; + } + + if (local->func->reset_port(dev) || + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_MONITOR << 8), + 0, NULL, NULL)) { + printk(KERN_DEBUG "Setting monitor mode failed\n"); + return -EOPNOTSUPP; + } + + return 0; +} + + +static int hostap_monitor_mode_disable(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return -1; + + printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name); + dev->type = ARPHRD_ETHER; + dev->hard_header_parse = local->saved_eth_header_parse; + if (local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_STOP << 8), + 0, NULL, NULL)) + return -1; + return hostap_set_encryption(local); +} + + +static int prism2_ioctl_siwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int double_reset = 0; + |