12be45b66SKalle Valo /* Wireless extensions support.
22be45b66SKalle Valo *
32be45b66SKalle Valo * See copyright notice in main.c
42be45b66SKalle Valo */
52be45b66SKalle Valo #include <linux/slab.h>
62be45b66SKalle Valo #include <linux/kernel.h>
72be45b66SKalle Valo #include <linux/if_arp.h>
82be45b66SKalle Valo #include <linux/wireless.h>
92be45b66SKalle Valo #include <linux/ieee80211.h>
102be45b66SKalle Valo #include <linux/etherdevice.h>
112be45b66SKalle Valo #include <net/iw_handler.h>
122be45b66SKalle Valo #include <net/cfg80211.h>
132be45b66SKalle Valo #include <net/cfg80211-wext.h>
142be45b66SKalle Valo
152be45b66SKalle Valo #include "hermes.h"
162be45b66SKalle Valo #include "hermes_rid.h"
172be45b66SKalle Valo #include "orinoco.h"
182be45b66SKalle Valo
192be45b66SKalle Valo #include "hw.h"
202be45b66SKalle Valo #include "mic.h"
212be45b66SKalle Valo #include "scan.h"
222be45b66SKalle Valo #include "main.h"
232be45b66SKalle Valo
242be45b66SKalle Valo #include "wext.h"
252be45b66SKalle Valo
262be45b66SKalle Valo #define MAX_RID_LEN 1024
272be45b66SKalle Valo
282be45b66SKalle Valo /* Helper routine to record keys
292be45b66SKalle Valo * It is called under orinoco_lock so it may not sleep */
orinoco_set_key(struct orinoco_private * priv,int index,enum orinoco_alg alg,const u8 * key,int key_len,const u8 * seq,int seq_len)302be45b66SKalle Valo static int orinoco_set_key(struct orinoco_private *priv, int index,
312be45b66SKalle Valo enum orinoco_alg alg, const u8 *key, int key_len,
322be45b66SKalle Valo const u8 *seq, int seq_len)
332be45b66SKalle Valo {
34453431a5SWaiman Long kfree_sensitive(priv->keys[index].key);
35453431a5SWaiman Long kfree_sensitive(priv->keys[index].seq);
362be45b66SKalle Valo
372be45b66SKalle Valo if (key_len) {
382be45b66SKalle Valo priv->keys[index].key = kzalloc(key_len, GFP_ATOMIC);
392be45b66SKalle Valo if (!priv->keys[index].key)
402be45b66SKalle Valo goto nomem;
412be45b66SKalle Valo } else
422be45b66SKalle Valo priv->keys[index].key = NULL;
432be45b66SKalle Valo
442be45b66SKalle Valo if (seq_len) {
452be45b66SKalle Valo priv->keys[index].seq = kzalloc(seq_len, GFP_ATOMIC);
462be45b66SKalle Valo if (!priv->keys[index].seq)
472be45b66SKalle Valo goto free_key;
482be45b66SKalle Valo } else
492be45b66SKalle Valo priv->keys[index].seq = NULL;
502be45b66SKalle Valo
512be45b66SKalle Valo priv->keys[index].key_len = key_len;
522be45b66SKalle Valo priv->keys[index].seq_len = seq_len;
532be45b66SKalle Valo
542be45b66SKalle Valo if (key_len)
552be45b66SKalle Valo memcpy((void *)priv->keys[index].key, key, key_len);
562be45b66SKalle Valo if (seq_len)
572be45b66SKalle Valo memcpy((void *)priv->keys[index].seq, seq, seq_len);
582be45b66SKalle Valo
592be45b66SKalle Valo switch (alg) {
602be45b66SKalle Valo case ORINOCO_ALG_TKIP:
612be45b66SKalle Valo priv->keys[index].cipher = WLAN_CIPHER_SUITE_TKIP;
622be45b66SKalle Valo break;
632be45b66SKalle Valo
642be45b66SKalle Valo case ORINOCO_ALG_WEP:
652be45b66SKalle Valo priv->keys[index].cipher = (key_len > SMALL_KEY_SIZE) ?
662be45b66SKalle Valo WLAN_CIPHER_SUITE_WEP104 : WLAN_CIPHER_SUITE_WEP40;
672be45b66SKalle Valo break;
682be45b66SKalle Valo
692be45b66SKalle Valo case ORINOCO_ALG_NONE:
702be45b66SKalle Valo default:
712be45b66SKalle Valo priv->keys[index].cipher = 0;
722be45b66SKalle Valo break;
732be45b66SKalle Valo }
742be45b66SKalle Valo
752be45b66SKalle Valo return 0;
762be45b66SKalle Valo
772be45b66SKalle Valo free_key:
782be45b66SKalle Valo kfree(priv->keys[index].key);
792be45b66SKalle Valo priv->keys[index].key = NULL;
802be45b66SKalle Valo
812be45b66SKalle Valo nomem:
822be45b66SKalle Valo priv->keys[index].key_len = 0;
832be45b66SKalle Valo priv->keys[index].seq_len = 0;
842be45b66SKalle Valo priv->keys[index].cipher = 0;
852be45b66SKalle Valo
862be45b66SKalle Valo return -ENOMEM;
872be45b66SKalle Valo }
882be45b66SKalle Valo
orinoco_get_wireless_stats(struct net_device * dev)892be45b66SKalle Valo static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev)
902be45b66SKalle Valo {
912be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
922be45b66SKalle Valo struct hermes *hw = &priv->hw;
932be45b66SKalle Valo struct iw_statistics *wstats = &priv->wstats;
942be45b66SKalle Valo int err;
952be45b66SKalle Valo unsigned long flags;
962be45b66SKalle Valo
972be45b66SKalle Valo if (!netif_device_present(dev)) {
982be45b66SKalle Valo printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
992be45b66SKalle Valo dev->name);
1002be45b66SKalle Valo return NULL; /* FIXME: Can we do better than this? */
1012be45b66SKalle Valo }
1022be45b66SKalle Valo
1032be45b66SKalle Valo /* If busy, return the old stats. Returning NULL may cause
1042be45b66SKalle Valo * the interface to disappear from /proc/net/wireless */
1052be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
1062be45b66SKalle Valo return wstats;
1072be45b66SKalle Valo
1082be45b66SKalle Valo /* We can't really wait for the tallies inquiry command to
1092be45b66SKalle Valo * complete, so we just use the previous results and trigger
1102be45b66SKalle Valo * a new tallies inquiry command for next time - Jean II */
1112be45b66SKalle Valo /* FIXME: Really we should wait for the inquiry to come back -
1122be45b66SKalle Valo * as it is the stats we give don't make a whole lot of sense.
1132be45b66SKalle Valo * Unfortunately, it's not clear how to do that within the
1142be45b66SKalle Valo * wireless extensions framework: I think we're in user
1152be45b66SKalle Valo * context, but a lock seems to be held by the time we get in
1162be45b66SKalle Valo * here so we're not safe to sleep here. */
1172be45b66SKalle Valo hermes_inquire(hw, HERMES_INQ_TALLIES);
1182be45b66SKalle Valo
1192be45b66SKalle Valo if (priv->iw_mode == NL80211_IFTYPE_ADHOC) {
1202be45b66SKalle Valo memset(&wstats->qual, 0, sizeof(wstats->qual));
1212be45b66SKalle Valo /* If a spy address is defined, we report stats of the
1222be45b66SKalle Valo * first spy address - Jean II */
1232be45b66SKalle Valo if (SPY_NUMBER(priv)) {
1242be45b66SKalle Valo wstats->qual.qual = priv->spy_data.spy_stat[0].qual;
1252be45b66SKalle Valo wstats->qual.level = priv->spy_data.spy_stat[0].level;
1262be45b66SKalle Valo wstats->qual.noise = priv->spy_data.spy_stat[0].noise;
1272be45b66SKalle Valo wstats->qual.updated =
1282be45b66SKalle Valo priv->spy_data.spy_stat[0].updated;
1292be45b66SKalle Valo }
1302be45b66SKalle Valo } else {
1312be45b66SKalle Valo struct {
1322be45b66SKalle Valo __le16 qual, signal, noise, unused;
1332be45b66SKalle Valo } __packed cq;
1342be45b66SKalle Valo
1352be45b66SKalle Valo err = HERMES_READ_RECORD(hw, USER_BAP,
1362be45b66SKalle Valo HERMES_RID_COMMSQUALITY, &cq);
1372be45b66SKalle Valo
1382be45b66SKalle Valo if (!err) {
1392be45b66SKalle Valo wstats->qual.qual = (int)le16_to_cpu(cq.qual);
1402be45b66SKalle Valo wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
1412be45b66SKalle Valo wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
1422be45b66SKalle Valo wstats->qual.updated =
1432be45b66SKalle Valo IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
1442be45b66SKalle Valo }
1452be45b66SKalle Valo }
1462be45b66SKalle Valo
1472be45b66SKalle Valo orinoco_unlock(priv, &flags);
1482be45b66SKalle Valo return wstats;
1492be45b66SKalle Valo }
1502be45b66SKalle Valo
1512be45b66SKalle Valo /********************************************************************/
1522be45b66SKalle Valo /* Wireless extensions */
1532be45b66SKalle Valo /********************************************************************/
1542be45b66SKalle Valo
orinoco_ioctl_setwap(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1552be45b66SKalle Valo static int orinoco_ioctl_setwap(struct net_device *dev,
1562be45b66SKalle Valo struct iw_request_info *info,
1572c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
1582be45b66SKalle Valo char *extra)
1592be45b66SKalle Valo {
1602c0e077dSGustavo A. R. Silva struct sockaddr *ap_addr = &wrqu->ap_addr;
1612be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
1622be45b66SKalle Valo int err = -EINPROGRESS; /* Call commit handler */
1632be45b66SKalle Valo unsigned long flags;
1642be45b66SKalle Valo
1652be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
1662be45b66SKalle Valo return -EBUSY;
1672be45b66SKalle Valo
1682be45b66SKalle Valo /* Enable automatic roaming - no sanity checks are needed */
1692be45b66SKalle Valo if (is_zero_ether_addr(ap_addr->sa_data) ||
1702be45b66SKalle Valo is_broadcast_ether_addr(ap_addr->sa_data)) {
1712be45b66SKalle Valo priv->bssid_fixed = 0;
1722be45b66SKalle Valo eth_zero_addr(priv->desired_bssid);
1732be45b66SKalle Valo
1742be45b66SKalle Valo /* "off" means keep existing connection */
1752be45b66SKalle Valo if (ap_addr->sa_data[0] == 0) {
1762be45b66SKalle Valo __orinoco_hw_set_wap(priv);
1772be45b66SKalle Valo err = 0;
1782be45b66SKalle Valo }
1792be45b66SKalle Valo goto out;
1802be45b66SKalle Valo }
1812be45b66SKalle Valo
1822be45b66SKalle Valo if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
1832be45b66SKalle Valo printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
1842be45b66SKalle Valo "support manual roaming\n",
1852be45b66SKalle Valo dev->name);
1862be45b66SKalle Valo err = -EOPNOTSUPP;
1872be45b66SKalle Valo goto out;
1882be45b66SKalle Valo }
1892be45b66SKalle Valo
1902be45b66SKalle Valo if (priv->iw_mode != NL80211_IFTYPE_STATION) {
1912be45b66SKalle Valo printk(KERN_WARNING "%s: Manual roaming supported only in "
1922be45b66SKalle Valo "managed mode\n", dev->name);
1932be45b66SKalle Valo err = -EOPNOTSUPP;
1942be45b66SKalle Valo goto out;
1952be45b66SKalle Valo }
1962be45b66SKalle Valo
1972be45b66SKalle Valo /* Intersil firmware hangs without Desired ESSID */
1982be45b66SKalle Valo if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
1992be45b66SKalle Valo strlen(priv->desired_essid) == 0) {
2002be45b66SKalle Valo printk(KERN_WARNING "%s: Desired ESSID must be set for "
2012be45b66SKalle Valo "manual roaming\n", dev->name);
2022be45b66SKalle Valo err = -EOPNOTSUPP;
2032be45b66SKalle Valo goto out;
2042be45b66SKalle Valo }
2052be45b66SKalle Valo
2062be45b66SKalle Valo /* Finally, enable manual roaming */
2072be45b66SKalle Valo priv->bssid_fixed = 1;
2082be45b66SKalle Valo memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
2092be45b66SKalle Valo
2102be45b66SKalle Valo out:
2112be45b66SKalle Valo orinoco_unlock(priv, &flags);
2122be45b66SKalle Valo return err;
2132be45b66SKalle Valo }
2142be45b66SKalle Valo
orinoco_ioctl_getwap(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)2152be45b66SKalle Valo static int orinoco_ioctl_getwap(struct net_device *dev,
2162be45b66SKalle Valo struct iw_request_info *info,
2172c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
2182be45b66SKalle Valo char *extra)
2192be45b66SKalle Valo {
2202c0e077dSGustavo A. R. Silva struct sockaddr *ap_addr = &wrqu->ap_addr;
2212be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
2222be45b66SKalle Valo
2232be45b66SKalle Valo int err = 0;
2242be45b66SKalle Valo unsigned long flags;
2252be45b66SKalle Valo
2262be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
2272be45b66SKalle Valo return -EBUSY;
2282be45b66SKalle Valo
2292be45b66SKalle Valo ap_addr->sa_family = ARPHRD_ETHER;
2302be45b66SKalle Valo err = orinoco_hw_get_current_bssid(priv, ap_addr->sa_data);
2312be45b66SKalle Valo
2322be45b66SKalle Valo orinoco_unlock(priv, &flags);
2332be45b66SKalle Valo
2342be45b66SKalle Valo return err;
2352be45b66SKalle Valo }
2362be45b66SKalle Valo
orinoco_ioctl_setiwencode(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * keybuf)2372be45b66SKalle Valo static int orinoco_ioctl_setiwencode(struct net_device *dev,
2382be45b66SKalle Valo struct iw_request_info *info,
2392c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
2402be45b66SKalle Valo char *keybuf)
2412be45b66SKalle Valo {
2422c0e077dSGustavo A. R. Silva struct iw_point *erq = &wrqu->encoding;
2432be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
2442be45b66SKalle Valo int index = (erq->flags & IW_ENCODE_INDEX) - 1;
2452be45b66SKalle Valo int setindex = priv->tx_key;
2462be45b66SKalle Valo enum orinoco_alg encode_alg = priv->encode_alg;
2472be45b66SKalle Valo int restricted = priv->wep_restrict;
2482be45b66SKalle Valo int err = -EINPROGRESS; /* Call commit handler */
2492be45b66SKalle Valo unsigned long flags;
2502be45b66SKalle Valo
2512be45b66SKalle Valo if (!priv->has_wep)
2522be45b66SKalle Valo return -EOPNOTSUPP;
2532be45b66SKalle Valo
2542be45b66SKalle Valo if (erq->pointer) {
2552be45b66SKalle Valo /* We actually have a key to set - check its length */
2562be45b66SKalle Valo if (erq->length > LARGE_KEY_SIZE)
2572be45b66SKalle Valo return -E2BIG;
2582be45b66SKalle Valo
2592be45b66SKalle Valo if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep)
2602be45b66SKalle Valo return -E2BIG;
2612be45b66SKalle Valo }
2622be45b66SKalle Valo
2632be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
2642be45b66SKalle Valo return -EBUSY;
2652be45b66SKalle Valo
2662be45b66SKalle Valo /* Clear any TKIP key we have */
2672be45b66SKalle Valo if ((priv->has_wpa) && (priv->encode_alg == ORINOCO_ALG_TKIP))
2682be45b66SKalle Valo (void) orinoco_clear_tkip_key(priv, setindex);
2692be45b66SKalle Valo
2702be45b66SKalle Valo if (erq->length > 0) {
2712be45b66SKalle Valo if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
2722be45b66SKalle Valo index = priv->tx_key;
2732be45b66SKalle Valo
2742be45b66SKalle Valo /* Switch on WEP if off */
2752be45b66SKalle Valo if (encode_alg != ORINOCO_ALG_WEP) {
2762be45b66SKalle Valo setindex = index;
2772be45b66SKalle Valo encode_alg = ORINOCO_ALG_WEP;
2782be45b66SKalle Valo }
2792be45b66SKalle Valo } else {
2802be45b66SKalle Valo /* Important note : if the user do "iwconfig eth0 enc off",
2812be45b66SKalle Valo * we will arrive there with an index of -1. This is valid
2822be45b66SKalle Valo * but need to be taken care off... Jean II */
2832be45b66SKalle Valo if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
2842be45b66SKalle Valo if ((index != -1) || (erq->flags == 0)) {
2852be45b66SKalle Valo err = -EINVAL;
2862be45b66SKalle Valo goto out;
2872be45b66SKalle Valo }
2882be45b66SKalle Valo } else {
2892be45b66SKalle Valo /* Set the index : Check that the key is valid */
2902be45b66SKalle Valo if (priv->keys[index].key_len == 0) {
2912be45b66SKalle Valo err = -EINVAL;
2922be45b66SKalle Valo goto out;
2932be45b66SKalle Valo }
2942be45b66SKalle Valo setindex = index;
2952be45b66SKalle Valo }
2962be45b66SKalle Valo }
2972be45b66SKalle Valo
2982be45b66SKalle Valo if (erq->flags & IW_ENCODE_DISABLED)
2992be45b66SKalle Valo encode_alg = ORINOCO_ALG_NONE;
3002be45b66SKalle Valo if (erq->flags & IW_ENCODE_OPEN)
3012be45b66SKalle Valo restricted = 0;
3022be45b66SKalle Valo if (erq->flags & IW_ENCODE_RESTRICTED)
3032be45b66SKalle Valo restricted = 1;
3042be45b66SKalle Valo
3052be45b66SKalle Valo if (erq->pointer && erq->length > 0) {
3062be45b66SKalle Valo err = orinoco_set_key(priv, index, ORINOCO_ALG_WEP, keybuf,
3072be45b66SKalle Valo erq->length, NULL, 0);
3082be45b66SKalle Valo }
3092be45b66SKalle Valo priv->tx_key = setindex;
3102be45b66SKalle Valo
3112be45b66SKalle Valo /* Try fast key change if connected and only keys are changed */
3122be45b66SKalle Valo if ((priv->encode_alg == encode_alg) &&
3132be45b66SKalle Valo (priv->wep_restrict == restricted) &&
3142be45b66SKalle Valo netif_carrier_ok(dev)) {
3152be45b66SKalle Valo err = __orinoco_hw_setup_wepkeys(priv);
3162be45b66SKalle Valo /* No need to commit if successful */
3172be45b66SKalle Valo goto out;
3182be45b66SKalle Valo }
3192be45b66SKalle Valo
3202be45b66SKalle Valo priv->encode_alg = encode_alg;
3212be45b66SKalle Valo priv->wep_restrict = restricted;
3222be45b66SKalle Valo
3232be45b66SKalle Valo out:
3242be45b66SKalle Valo orinoco_unlock(priv, &flags);
3252be45b66SKalle Valo
3262be45b66SKalle Valo return err;
3272be45b66SKalle Valo }
3282be45b66SKalle Valo
orinoco_ioctl_getiwencode(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * keybuf)3292be45b66SKalle Valo static int orinoco_ioctl_getiwencode(struct net_device *dev,
3302be45b66SKalle Valo struct iw_request_info *info,
3312c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
3322be45b66SKalle Valo char *keybuf)
3332be45b66SKalle Valo {
3342c0e077dSGustavo A. R. Silva struct iw_point *erq = &wrqu->encoding;
3352be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
3362be45b66SKalle Valo int index = (erq->flags & IW_ENCODE_INDEX) - 1;
3372be45b66SKalle Valo unsigned long flags;
3382be45b66SKalle Valo
3392be45b66SKalle Valo if (!priv->has_wep)
3402be45b66SKalle Valo return -EOPNOTSUPP;
3412be45b66SKalle Valo
3422be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
3432be45b66SKalle Valo return -EBUSY;
3442be45b66SKalle Valo
3452be45b66SKalle Valo if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
3462be45b66SKalle Valo index = priv->tx_key;
3472be45b66SKalle Valo
3482be45b66SKalle Valo erq->flags = 0;
3492be45b66SKalle Valo if (!priv->encode_alg)
3502be45b66SKalle Valo erq->flags |= IW_ENCODE_DISABLED;
3512be45b66SKalle Valo erq->flags |= index + 1;
3522be45b66SKalle Valo
3532be45b66SKalle Valo if (priv->wep_restrict)
3542be45b66SKalle Valo erq->flags |= IW_ENCODE_RESTRICTED;
3552be45b66SKalle Valo else
3562be45b66SKalle Valo erq->flags |= IW_ENCODE_OPEN;
3572be45b66SKalle Valo
3582be45b66SKalle Valo erq->length = priv->keys[index].key_len;
3592be45b66SKalle Valo
3602be45b66SKalle Valo memcpy(keybuf, priv->keys[index].key, erq->length);
3612be45b66SKalle Valo
3622be45b66SKalle Valo orinoco_unlock(priv, &flags);
3632be45b66SKalle Valo return 0;
3642be45b66SKalle Valo }
3652be45b66SKalle Valo
orinoco_ioctl_setessid(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * essidbuf)3662be45b66SKalle Valo static int orinoco_ioctl_setessid(struct net_device *dev,
3672be45b66SKalle Valo struct iw_request_info *info,
3682c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
3692be45b66SKalle Valo char *essidbuf)
3702be45b66SKalle Valo {
3712c0e077dSGustavo A. R. Silva struct iw_point *erq = &wrqu->essid;
3722be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
3732be45b66SKalle Valo unsigned long flags;
3742be45b66SKalle Valo
3752be45b66SKalle Valo /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
3762be45b66SKalle Valo * anyway... - Jean II */
3772be45b66SKalle Valo
3782be45b66SKalle Valo /* Hum... Should not use Wireless Extension constant (may change),
3792be45b66SKalle Valo * should use our own... - Jean II */
3802be45b66SKalle Valo if (erq->length > IW_ESSID_MAX_SIZE)
3812be45b66SKalle Valo return -E2BIG;
3822be45b66SKalle Valo
3832be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
3842be45b66SKalle Valo return -EBUSY;
3852be45b66SKalle Valo
3862be45b66SKalle Valo /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */
3872be45b66SKalle Valo memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
3882be45b66SKalle Valo
3892be45b66SKalle Valo /* If not ANY, get the new ESSID */
3902be45b66SKalle Valo if (erq->flags)
3912be45b66SKalle Valo memcpy(priv->desired_essid, essidbuf, erq->length);
3922be45b66SKalle Valo
3932be45b66SKalle Valo orinoco_unlock(priv, &flags);
3942be45b66SKalle Valo
3952be45b66SKalle Valo return -EINPROGRESS; /* Call commit handler */
3962be45b66SKalle Valo }
3972be45b66SKalle Valo
orinoco_ioctl_getessid(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * essidbuf)3982be45b66SKalle Valo static int orinoco_ioctl_getessid(struct net_device *dev,
3992be45b66SKalle Valo struct iw_request_info *info,
4002c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
4012be45b66SKalle Valo char *essidbuf)
4022be45b66SKalle Valo {
4032c0e077dSGustavo A. R. Silva struct iw_point *erq = &wrqu->essid;
4042be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
4052be45b66SKalle Valo int active;
4062be45b66SKalle Valo int err = 0;
4072be45b66SKalle Valo unsigned long flags;
4082be45b66SKalle Valo
4092be45b66SKalle Valo if (netif_running(dev)) {
4102be45b66SKalle Valo err = orinoco_hw_get_essid(priv, &active, essidbuf);
4112be45b66SKalle Valo if (err < 0)
4122be45b66SKalle Valo return err;
4132be45b66SKalle Valo erq->length = err;
4142be45b66SKalle Valo } else {
4152be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
4162be45b66SKalle Valo return -EBUSY;
4172be45b66SKalle Valo memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE);
4182be45b66SKalle Valo erq->length = strlen(priv->desired_essid);
4192be45b66SKalle Valo orinoco_unlock(priv, &flags);
4202be45b66SKalle Valo }
4212be45b66SKalle Valo
4222be45b66SKalle Valo erq->flags = 1;
4232be45b66SKalle Valo
4242be45b66SKalle Valo return 0;
4252be45b66SKalle Valo }
4262be45b66SKalle Valo
orinoco_ioctl_setfreq(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)4272be45b66SKalle Valo static int orinoco_ioctl_setfreq(struct net_device *dev,
4282be45b66SKalle Valo struct iw_request_info *info,
4292c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
4302be45b66SKalle Valo char *extra)
4312be45b66SKalle Valo {
4322c0e077dSGustavo A. R. Silva struct iw_freq *frq = &wrqu->freq;
4332be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
4342be45b66SKalle Valo int chan = -1;
4352be45b66SKalle Valo unsigned long flags;
4362be45b66SKalle Valo int err = -EINPROGRESS; /* Call commit handler */
4372be45b66SKalle Valo
4382be45b66SKalle Valo /* In infrastructure mode the AP sets the channel */
4392be45b66SKalle Valo if (priv->iw_mode == NL80211_IFTYPE_STATION)
4402be45b66SKalle Valo return -EBUSY;
4412be45b66SKalle Valo
4422be45b66SKalle Valo if ((frq->e == 0) && (frq->m <= 1000)) {
4432be45b66SKalle Valo /* Setting by channel number */
4442be45b66SKalle Valo chan = frq->m;
4452be45b66SKalle Valo } else {
4462be45b66SKalle Valo /* Setting by frequency */
4472be45b66SKalle Valo int denom = 1;
4482be45b66SKalle Valo int i;
4492be45b66SKalle Valo
4502be45b66SKalle Valo /* Calculate denominator to rescale to MHz */
4512be45b66SKalle Valo for (i = 0; i < (6 - frq->e); i++)
4522be45b66SKalle Valo denom *= 10;
4532be45b66SKalle Valo
4542be45b66SKalle Valo chan = ieee80211_frequency_to_channel(frq->m / denom);
4552be45b66SKalle Valo }
4562be45b66SKalle Valo
4572be45b66SKalle Valo if ((chan < 1) || (chan > NUM_CHANNELS) ||
4582be45b66SKalle Valo !(priv->channel_mask & (1 << (chan - 1))))
4592be45b66SKalle Valo return -EINVAL;
4602be45b66SKalle Valo
4612be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
4622be45b66SKalle Valo return -EBUSY;
4632be45b66SKalle Valo
4642be45b66SKalle Valo priv->channel = chan;
4652be45b66SKalle Valo if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
4662be45b66SKalle Valo /* Fast channel change - no commit if successful */
4672be45b66SKalle Valo struct hermes *hw = &priv->hw;
4682be45b66SKalle Valo err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
4692be45b66SKalle Valo HERMES_TEST_SET_CHANNEL,
4702be45b66SKalle Valo chan, NULL);
4712be45b66SKalle Valo }
4722be45b66SKalle Valo orinoco_unlock(priv, &flags);
4732be45b66SKalle Valo
4742be45b66SKalle Valo return err;
4752be45b66SKalle Valo }
4762be45b66SKalle Valo
orinoco_ioctl_getfreq(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)4772be45b66SKalle Valo static int orinoco_ioctl_getfreq(struct net_device *dev,
4782be45b66SKalle Valo struct iw_request_info *info,
4792c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
4802be45b66SKalle Valo char *extra)
4812be45b66SKalle Valo {
4822c0e077dSGustavo A. R. Silva struct iw_freq *frq = &wrqu->freq;
4832be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
4842be45b66SKalle Valo int tmp;
4852be45b66SKalle Valo
4862be45b66SKalle Valo /* Locking done in there */
4872be45b66SKalle Valo tmp = orinoco_hw_get_freq(priv);
4882be45b66SKalle Valo if (tmp < 0)
4892be45b66SKalle Valo return tmp;
4902be45b66SKalle Valo
4912be45b66SKalle Valo frq->m = tmp * 100000;
4922be45b66SKalle Valo frq->e = 1;
4932be45b66SKalle Valo
4942be45b66SKalle Valo return 0;
4952be45b66SKalle Valo }
4962be45b66SKalle Valo
orinoco_ioctl_getsens(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)4972be45b66SKalle Valo static int orinoco_ioctl_getsens(struct net_device *dev,
4982be45b66SKalle Valo struct iw_request_info *info,
4992c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
5002be45b66SKalle Valo char *extra)
5012be45b66SKalle Valo {
5022c0e077dSGustavo A. R. Silva struct iw_param *srq = &wrqu->sens;
5032be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
5042be45b66SKalle Valo struct hermes *hw = &priv->hw;
5052be45b66SKalle Valo u16 val;
5062be45b66SKalle Valo int err;
5072be45b66SKalle Valo unsigned long flags;
5082be45b66SKalle Valo
5092be45b66SKalle Valo if (!priv->has_sensitivity)
5102be45b66SKalle Valo return -EOPNOTSUPP;
5112be45b66SKalle Valo
5122be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
5132be45b66SKalle Valo return -EBUSY;
5142be45b66SKalle Valo err = hermes_read_wordrec(hw, USER_BAP,
5152be45b66SKalle Valo HERMES_RID_CNFSYSTEMSCALE, &val);
5162be45b66SKalle Valo orinoco_unlock(priv, &flags);
5172be45b66SKalle Valo
5182be45b66SKalle Valo if (err)
5192be45b66SKalle Valo return err;
5202be45b66SKalle Valo
5212be45b66SKalle Valo srq->value = val;
5222be45b66SKalle Valo srq->fixed = 0; /* auto */
5232be45b66SKalle Valo
5242be45b66SKalle Valo return 0;
5252be45b66SKalle Valo }
5262be45b66SKalle Valo
orinoco_ioctl_setsens(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)5272be45b66SKalle Valo static int orinoco_ioctl_setsens(struct net_device *dev,
5282be45b66SKalle Valo struct iw_request_info *info,
5292c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
5302be45b66SKalle Valo char *extra)
5312be45b66SKalle Valo {
5322c0e077dSGustavo A. R. Silva struct iw_param *srq = &wrqu->sens;
5332be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
5342be45b66SKalle Valo int val = srq->value;
5352be45b66SKalle Valo unsigned long flags;
5362be45b66SKalle Valo
5372be45b66SKalle Valo if (!priv->has_sensitivity)
5382be45b66SKalle Valo return -EOPNOTSUPP;
5392be45b66SKalle Valo
5402be45b66SKalle Valo if ((val < 1) || (val > 3))
5412be45b66SKalle Valo return -EINVAL;
5422be45b66SKalle Valo
5432be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
5442be45b66SKalle Valo return -EBUSY;
5452be45b66SKalle Valo priv->ap_density = val;
5462be45b66SKalle Valo orinoco_unlock(priv, &flags);
5472be45b66SKalle Valo
5482be45b66SKalle Valo return -EINPROGRESS; /* Call commit handler */
5492be45b66SKalle Valo }
5502be45b66SKalle Valo
orinoco_ioctl_setrate(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)5512be45b66SKalle Valo static int orinoco_ioctl_setrate(struct net_device *dev,
5522be45b66SKalle Valo struct iw_request_info *info,
5532c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
5542be45b66SKalle Valo char *extra)
5552be45b66SKalle Valo {
5562c0e077dSGustavo A. R. Silva struct iw_param *rrq = &wrqu->bitrate;
5572be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
5582be45b66SKalle Valo int ratemode;
5592be45b66SKalle Valo int bitrate; /* 100s of kilobits */
5602be45b66SKalle Valo unsigned long flags;
5612be45b66SKalle Valo
5622be45b66SKalle Valo /* As the user space doesn't know our highest rate, it uses -1
5632be45b66SKalle Valo * to ask us to set the highest rate. Test it using "iwconfig
5642be45b66SKalle Valo * ethX rate auto" - Jean II */
5652be45b66SKalle Valo if (rrq->value == -1)
5662be45b66SKalle Valo bitrate = 110;
5672be45b66SKalle Valo else {
5682be45b66SKalle Valo if (rrq->value % 100000)
5692be45b66SKalle Valo return -EINVAL;
5702be45b66SKalle Valo bitrate = rrq->value / 100000;
5712be45b66SKalle Valo }
5722be45b66SKalle Valo
5732be45b66SKalle Valo ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed);
5742be45b66SKalle Valo
5752be45b66SKalle Valo if (ratemode == -1)
5762be45b66SKalle Valo return -EINVAL;
5772be45b66SKalle Valo
5782be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
5792be45b66SKalle Valo return -EBUSY;
5802be45b66SKalle Valo priv->bitratemode = ratemode;
5812be45b66SKalle Valo orinoco_unlock(priv, &flags);
5822be45b66SKalle Valo
5832be45b66SKalle Valo return -EINPROGRESS;
5842be45b66SKalle Valo }
5852be45b66SKalle Valo
orinoco_ioctl_getrate(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)5862be45b66SKalle Valo static int orinoco_ioctl_getrate(struct net_device *dev,
5872be45b66SKalle Valo struct iw_request_info *info,
5882c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
5892be45b66SKalle Valo char *extra)
5902be45b66SKalle Valo {
5912c0e077dSGustavo A. R. Silva struct iw_param *rrq = &wrqu->bitrate;
5922be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
5932be45b66SKalle Valo int err = 0;
5942be45b66SKalle Valo int bitrate, automatic;
5952be45b66SKalle Valo unsigned long flags;
5962be45b66SKalle Valo
5972be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
5982be45b66SKalle Valo return -EBUSY;
5992be45b66SKalle Valo
6002be45b66SKalle Valo orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic);
6012be45b66SKalle Valo
6022be45b66SKalle Valo /* If the interface is running we try to find more about the
6032be45b66SKalle Valo current mode */
6042be45b66SKalle Valo if (netif_running(dev)) {
6052be45b66SKalle Valo int act_bitrate;
6062be45b66SKalle Valo int lerr;
6072be45b66SKalle Valo
6082be45b66SKalle Valo /* Ignore errors if we can't get the actual bitrate */
6092be45b66SKalle Valo lerr = orinoco_hw_get_act_bitrate(priv, &act_bitrate);
6102be45b66SKalle Valo if (!lerr)
6112be45b66SKalle Valo bitrate = act_bitrate;
6122be45b66SKalle Valo }
6132be45b66SKalle Valo
6142be45b66SKalle Valo orinoco_unlock(priv, &flags);
6152be45b66SKalle Valo
6162be45b66SKalle Valo rrq->value = bitrate;
6172be45b66SKalle Valo rrq->fixed = !automatic;
6182be45b66SKalle Valo rrq->disabled = 0;
6192be45b66SKalle Valo
6202be45b66SKalle Valo return err;
6212be45b66SKalle Valo }
6222be45b66SKalle Valo
orinoco_ioctl_setpower(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)6232be45b66SKalle Valo static int orinoco_ioctl_setpower(struct net_device *dev,
6242be45b66SKalle Valo struct iw_request_info *info,
6252c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
6262be45b66SKalle Valo char *extra)
6272be45b66SKalle Valo {
6282c0e077dSGustavo A. R. Silva struct iw_param *prq = &wrqu->power;
6292be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
6302be45b66SKalle Valo int err = -EINPROGRESS; /* Call commit handler */
6312be45b66SKalle Valo unsigned long flags;
6322be45b66SKalle Valo
6332be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
6342be45b66SKalle Valo return -EBUSY;
6352be45b66SKalle Valo
6362be45b66SKalle Valo if (prq->disabled) {
6372be45b66SKalle Valo priv->pm_on = 0;
6382be45b66SKalle Valo } else {
6392be45b66SKalle Valo switch (prq->flags & IW_POWER_MODE) {
6402be45b66SKalle Valo case IW_POWER_UNICAST_R:
6412be45b66SKalle Valo priv->pm_mcast = 0;
6422be45b66SKalle Valo priv->pm_on = 1;
6432be45b66SKalle Valo break;
6442be45b66SKalle Valo case IW_POWER_ALL_R:
6452be45b66SKalle Valo priv->pm_mcast = 1;
6462be45b66SKalle Valo priv->pm_on = 1;
6472be45b66SKalle Valo break;
6482be45b66SKalle Valo case IW_POWER_ON:
6492be45b66SKalle Valo /* No flags : but we may have a value - Jean II */
6502be45b66SKalle Valo break;
6512be45b66SKalle Valo default:
6522be45b66SKalle Valo err = -EINVAL;
6532be45b66SKalle Valo goto out;
6542be45b66SKalle Valo }
6552be45b66SKalle Valo
6562be45b66SKalle Valo if (prq->flags & IW_POWER_TIMEOUT) {
6572be45b66SKalle Valo priv->pm_on = 1;
6582be45b66SKalle Valo priv->pm_timeout = prq->value / 1000;
6592be45b66SKalle Valo }
6602be45b66SKalle Valo if (prq->flags & IW_POWER_PERIOD) {
6612be45b66SKalle Valo priv->pm_on = 1;
6622be45b66SKalle Valo priv->pm_period = prq->value / 1000;
6632be45b66SKalle Valo }
6642be45b66SKalle Valo /* It's valid to not have a value if we are just toggling
6652be45b66SKalle Valo * the flags... Jean II */
6662be45b66SKalle Valo if (!priv->pm_on) {
6672be45b66SKalle Valo err = -EINVAL;
6682be45b66SKalle Valo goto out;
6692be45b66SKalle Valo }
6702be45b66SKalle Valo }
6712be45b66SKalle Valo
6722be45b66SKalle Valo out:
6732be45b66SKalle Valo orinoco_unlock(priv, &flags);
6742be45b66SKalle Valo
6752be45b66SKalle Valo return err;
6762be45b66SKalle Valo }
6772be45b66SKalle Valo
orinoco_ioctl_getpower(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)6782be45b66SKalle Valo static int orinoco_ioctl_getpower(struct net_device *dev,
6792be45b66SKalle Valo struct iw_request_info *info,
6802c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
6812be45b66SKalle Valo char *extra)
6822be45b66SKalle Valo {
6832c0e077dSGustavo A. R. Silva struct iw_param *prq = &wrqu->power;
6842be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
6852be45b66SKalle Valo struct hermes *hw = &priv->hw;
6862be45b66SKalle Valo int err = 0;
6872be45b66SKalle Valo u16 enable, period, timeout, mcast;
6882be45b66SKalle Valo unsigned long flags;
6892be45b66SKalle Valo
6902be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
6912be45b66SKalle Valo return -EBUSY;
6922be45b66SKalle Valo
6932be45b66SKalle Valo err = hermes_read_wordrec(hw, USER_BAP,
6942be45b66SKalle Valo HERMES_RID_CNFPMENABLED, &enable);
6952be45b66SKalle Valo if (err)
6962be45b66SKalle Valo goto out;
6972be45b66SKalle Valo
6982be45b66SKalle Valo err = hermes_read_wordrec(hw, USER_BAP,
6992be45b66SKalle Valo HERMES_RID_CNFMAXSLEEPDURATION, &period);
7002be45b66SKalle Valo if (err)
7012be45b66SKalle Valo goto out;
7022be45b66SKalle Valo
7032be45b66SKalle Valo err = hermes_read_wordrec(hw, USER_BAP,
7042be45b66SKalle Valo HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
7052be45b66SKalle Valo if (err)
7062be45b66SKalle Valo goto out;
7072be45b66SKalle Valo
7082be45b66SKalle Valo err = hermes_read_wordrec(hw, USER_BAP,
7092be45b66SKalle Valo HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
7102be45b66SKalle Valo if (err)
7112be45b66SKalle Valo goto out;
7122be45b66SKalle Valo
7132be45b66SKalle Valo prq->disabled = !enable;
7142be45b66SKalle Valo /* Note : by default, display the period */
7152be45b66SKalle Valo if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
7162be45b66SKalle Valo prq->flags = IW_POWER_TIMEOUT;
7172be45b66SKalle Valo prq->value = timeout * 1000;
7182be45b66SKalle Valo } else {
7192be45b66SKalle Valo prq->flags = IW_POWER_PERIOD;
7202be45b66SKalle Valo prq->value = period * 1000;
7212be45b66SKalle Valo }
7222be45b66SKalle Valo if (mcast)
7232be45b66SKalle Valo prq->flags |= IW_POWER_ALL_R;
7242be45b66SKalle Valo else
7252be45b66SKalle Valo prq->flags |= IW_POWER_UNICAST_R;
7262be45b66SKalle Valo
7272be45b66SKalle Valo out:
7282be45b66SKalle Valo orinoco_unlock(priv, &flags);
7292be45b66SKalle Valo
7302be45b66SKalle Valo return err;
7312be45b66SKalle Valo }
7322be45b66SKalle Valo
orinoco_ioctl_set_encodeext(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)7332be45b66SKalle Valo static int orinoco_ioctl_set_encodeext(struct net_device *dev,
7342be45b66SKalle Valo struct iw_request_info *info,
7352be45b66SKalle Valo union iwreq_data *wrqu,
7362be45b66SKalle Valo char *extra)
7372be45b66SKalle Valo {
7382be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
7392be45b66SKalle Valo struct iw_point *encoding = &wrqu->encoding;
7402be45b66SKalle Valo struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
7412be45b66SKalle Valo int idx, alg = ext->alg, set_key = 1;
7422be45b66SKalle Valo unsigned long flags;
7432be45b66SKalle Valo int err = -EINVAL;
7442be45b66SKalle Valo
7452be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
7462be45b66SKalle Valo return -EBUSY;
7472be45b66SKalle Valo
7482be45b66SKalle Valo /* Determine and validate the key index */
7492be45b66SKalle Valo idx = encoding->flags & IW_ENCODE_INDEX;
7502be45b66SKalle Valo if (idx) {
7512be45b66SKalle Valo if ((idx < 1) || (idx > 4))
7522be45b66SKalle Valo goto out;
7532be45b66SKalle Valo idx--;
7542be45b66SKalle Valo } else
7552be45b66SKalle Valo idx = priv->tx_key;
7562be45b66SKalle Valo
7572be45b66SKalle Valo if (encoding->flags & IW_ENCODE_DISABLED)
7582be45b66SKalle Valo alg = IW_ENCODE_ALG_NONE;
7592be45b66SKalle Valo
7602be45b66SKalle Valo if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) {
7612be45b66SKalle Valo /* Clear any TKIP TX key we had */
7622be45b66SKalle Valo (void) orinoco_clear_tkip_key(priv, priv->tx_key);
7632be45b66SKalle Valo }
7642be45b66SKalle Valo
7652be45b66SKalle Valo if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
7662be45b66SKalle Valo priv->tx_key = idx;
7672be45b66SKalle Valo set_key = ((alg == IW_ENCODE_ALG_TKIP) ||
7682be45b66SKalle Valo (ext->key_len > 0)) ? 1 : 0;
7692be45b66SKalle Valo }
7702be45b66SKalle Valo
7712be45b66SKalle Valo if (set_key) {
7722be45b66SKalle Valo /* Set the requested key first */
7732be45b66SKalle Valo switch (alg) {
7742be45b66SKalle Valo case IW_ENCODE_ALG_NONE:
7752be45b66SKalle Valo priv->encode_alg = ORINOCO_ALG_NONE;
7762be45b66SKalle Valo err = orinoco_set_key(priv, idx, ORINOCO_ALG_NONE,
7772be45b66SKalle Valo NULL, 0, NULL, 0);
7782be45b66SKalle Valo break;
7792be45b66SKalle Valo
7802be45b66SKalle Valo case IW_ENCODE_ALG_WEP:
7812be45b66SKalle Valo if (ext->key_len <= 0)
7822be45b66SKalle Valo goto out;
7832be45b66SKalle Valo
7842be45b66SKalle Valo priv->encode_alg = ORINOCO_ALG_WEP;
7852be45b66SKalle Valo err = orinoco_set_key(priv, idx, ORINOCO_ALG_WEP,
7862be45b66SKalle Valo ext->key, ext->key_len, NULL, 0);
7872be45b66SKalle Valo break;
7882be45b66SKalle Valo
7892be45b66SKalle Valo case IW_ENCODE_ALG_TKIP:
7902be45b66SKalle Valo {
7912be45b66SKalle Valo u8 *tkip_iv = NULL;
7922be45b66SKalle Valo
7932be45b66SKalle Valo if (!priv->has_wpa ||
7942be45b66SKalle Valo (ext->key_len > sizeof(struct orinoco_tkip_key)))
7952be45b66SKalle Valo goto out;
7962be45b66SKalle Valo
7972be45b66SKalle Valo priv->encode_alg = ORINOCO_ALG_TKIP;
7982be45b66SKalle Valo
7992be45b66SKalle Valo if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
8002be45b66SKalle Valo tkip_iv = &ext->rx_seq[0];
8012be45b66SKalle Valo
8022be45b66SKalle Valo err = orinoco_set_key(priv, idx, ORINOCO_ALG_TKIP,
8032be45b66SKalle Valo ext->key, ext->key_len, tkip_iv,
8042be45b66SKalle Valo ORINOCO_SEQ_LEN);
8052be45b66SKalle Valo
8062be45b66SKalle Valo err = __orinoco_hw_set_tkip_key(priv, idx,
8072be45b66SKalle Valo ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
80870ca8441SKees Cook priv->keys[idx].key, priv->keys[idx].key_len,
8092be45b66SKalle Valo tkip_iv, ORINOCO_SEQ_LEN, NULL, 0);
8102be45b66SKalle Valo if (err)
8112be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting TKIP key"
8122be45b66SKalle Valo "\n", dev->name, err);
8132be45b66SKalle Valo
8142be45b66SKalle Valo goto out;
8152be45b66SKalle Valo }
8162be45b66SKalle Valo default:
8172be45b66SKalle Valo goto out;
8182be45b66SKalle Valo }
8192be45b66SKalle Valo }
8202be45b66SKalle Valo err = -EINPROGRESS;
8212be45b66SKalle Valo out:
8222be45b66SKalle Valo orinoco_unlock(priv, &flags);
8232be45b66SKalle Valo
8242be45b66SKalle Valo return err;
8252be45b66SKalle Valo }
8262be45b66SKalle Valo
orinoco_ioctl_get_encodeext(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)8272be45b66SKalle Valo static int orinoco_ioctl_get_encodeext(struct net_device *dev,
8282be45b66SKalle Valo struct iw_request_info *info,
8292be45b66SKalle Valo union iwreq_data *wrqu,
8302be45b66SKalle Valo char *extra)
8312be45b66SKalle Valo {
8322be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
8332be45b66SKalle Valo struct iw_point *encoding = &wrqu->encoding;
8342be45b66SKalle Valo struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
8352be45b66SKalle Valo int idx, max_key_len;
8362be45b66SKalle Valo unsigned long flags;
8372be45b66SKalle Valo int err;
8382be45b66SKalle Valo
8392be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
8402be45b66SKalle Valo return -EBUSY;
8412be45b66SKalle Valo
8422be45b66SKalle Valo err = -EINVAL;
8432be45b66SKalle Valo max_key_len = encoding->length - sizeof(*ext);
8442be45b66SKalle Valo if (max_key_len < 0)
8452be45b66SKalle Valo goto out;
8462be45b66SKalle Valo
8472be45b66SKalle Valo idx = encoding->flags & IW_ENCODE_INDEX;
8482be45b66SKalle Valo if (idx) {
8492be45b66SKalle Valo if ((idx < 1) || (idx > 4))
8502be45b66SKalle Valo goto out;
8512be45b66SKalle Valo idx--;
8522be45b66SKalle Valo } else
8532be45b66SKalle Valo idx = priv->tx_key;
8542be45b66SKalle Valo
8552be45b66SKalle Valo encoding->flags = idx + 1;
8562be45b66SKalle Valo memset(ext, 0, sizeof(*ext));
8572be45b66SKalle Valo
8582be45b66SKalle Valo switch (priv->encode_alg) {
8592be45b66SKalle Valo case ORINOCO_ALG_NONE:
8602be45b66SKalle Valo ext->alg = IW_ENCODE_ALG_NONE;
8612be45b66SKalle Valo ext->key_len = 0;
8622be45b66SKalle Valo encoding->flags |= IW_ENCODE_DISABLED;
8632be45b66SKalle Valo break;
8642be45b66SKalle Valo case ORINOCO_ALG_WEP:
8652be45b66SKalle Valo ext->alg = IW_ENCODE_ALG_WEP;
8662be45b66SKalle Valo ext->key_len = min(priv->keys[idx].key_len, max_key_len);
8672be45b66SKalle Valo memcpy(ext->key, priv->keys[idx].key, ext->key_len);
8682be45b66SKalle Valo encoding->flags |= IW_ENCODE_ENABLED;
8692be45b66SKalle Valo break;
8702be45b66SKalle Valo case ORINOCO_ALG_TKIP:
8712be45b66SKalle Valo ext->alg = IW_ENCODE_ALG_TKIP;
8722be45b66SKalle Valo ext->key_len = min(priv->keys[idx].key_len, max_key_len);
8732be45b66SKalle Valo memcpy(ext->key, priv->keys[idx].key, ext->key_len);
8742be45b66SKalle Valo encoding->flags |= IW_ENCODE_ENABLED;
8752be45b66SKalle Valo break;
8762be45b66SKalle Valo }
8772be45b66SKalle Valo
8782be45b66SKalle Valo err = 0;
8792be45b66SKalle Valo out:
8802be45b66SKalle Valo orinoco_unlock(priv, &flags);
8812be45b66SKalle Valo
8822be45b66SKalle Valo return err;
8832be45b66SKalle Valo }
8842be45b66SKalle Valo
orinoco_ioctl_set_auth(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)8852be45b66SKalle Valo static int orinoco_ioctl_set_auth(struct net_device *dev,
8862be45b66SKalle Valo struct iw_request_info *info,
8872be45b66SKalle Valo union iwreq_data *wrqu, char *extra)
8882be45b66SKalle Valo {
8892be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
8902be45b66SKalle Valo struct hermes *hw = &priv->hw;
8912be45b66SKalle Valo struct iw_param *param = &wrqu->param;
8922be45b66SKalle Valo unsigned long flags;
8932be45b66SKalle Valo int ret = -EINPROGRESS;
8942be45b66SKalle Valo
8952be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
8962be45b66SKalle Valo return -EBUSY;
8972be45b66SKalle Valo
8982be45b66SKalle Valo switch (param->flags & IW_AUTH_INDEX) {
8992be45b66SKalle Valo case IW_AUTH_WPA_VERSION:
9002be45b66SKalle Valo case IW_AUTH_CIPHER_PAIRWISE:
9012be45b66SKalle Valo case IW_AUTH_CIPHER_GROUP:
9022be45b66SKalle Valo case IW_AUTH_RX_UNENCRYPTED_EAPOL:
9032be45b66SKalle Valo case IW_AUTH_PRIVACY_INVOKED:
9042be45b66SKalle Valo case IW_AUTH_DROP_UNENCRYPTED:
9052be45b66SKalle Valo /*
9062be45b66SKalle Valo * orinoco does not use these parameters
9072be45b66SKalle Valo */
9082be45b66SKalle Valo break;
9092be45b66SKalle Valo
9102be45b66SKalle Valo case IW_AUTH_MFP:
9112be45b66SKalle Valo /* Management Frame Protection not supported.
9122be45b66SKalle Valo * Only fail if set to required.
9132be45b66SKalle Valo */
9142be45b66SKalle Valo if (param->value == IW_AUTH_MFP_REQUIRED)
9152be45b66SKalle Valo ret = -EINVAL;
9162be45b66SKalle Valo break;
9172be45b66SKalle Valo
9182be45b66SKalle Valo case IW_AUTH_KEY_MGMT:
9192be45b66SKalle Valo /* wl_lkm implies value 2 == PSK for Hermes I
9202be45b66SKalle Valo * which ties in with WEXT
9212be45b66SKalle Valo * no other hints tho :(
9222be45b66SKalle Valo */
9232be45b66SKalle Valo priv->key_mgmt = param->value;
9242be45b66SKalle Valo break;
9252be45b66SKalle Valo
9262be45b66SKalle Valo case IW_AUTH_TKIP_COUNTERMEASURES:
9272be45b66SKalle Valo /* When countermeasures are enabled, shut down the
9282be45b66SKalle Valo * card; when disabled, re-enable the card. This must
9292be45b66SKalle Valo * take effect immediately.
9302be45b66SKalle Valo *
9312be45b66SKalle Valo * TODO: Make sure that the EAPOL message is getting
9322be45b66SKalle Valo * out before card disabled
9332be45b66SKalle Valo */
9342be45b66SKalle Valo if (param->value) {
9352be45b66SKalle Valo priv->tkip_cm_active = 1;
9362be45b66SKalle Valo ret = hermes_disable_port(hw, 0);
9372be45b66SKalle Valo } else {
9382be45b66SKalle Valo priv->tkip_cm_active = 0;
9392be45b66SKalle Valo ret = hermes_enable_port(hw, 0);
9402be45b66SKalle Valo }
9412be45b66SKalle Valo break;
9422be45b66SKalle Valo
9432be45b66SKalle Valo case IW_AUTH_80211_AUTH_ALG:
9442be45b66SKalle Valo if (param->value & IW_AUTH_ALG_SHARED_KEY)
9452be45b66SKalle Valo priv->wep_restrict = 1;
9462be45b66SKalle Valo else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
9472be45b66SKalle Valo priv->wep_restrict = 0;
9482be45b66SKalle Valo else
9492be45b66SKalle Valo ret = -EINVAL;
9502be45b66SKalle Valo break;
9512be45b66SKalle Valo
9522be45b66SKalle Valo case IW_AUTH_WPA_ENABLED:
9532be45b66SKalle Valo if (priv->has_wpa) {
9542be45b66SKalle Valo priv->wpa_enabled = param->value ? 1 : 0;
9552be45b66SKalle Valo } else {
9562be45b66SKalle Valo if (param->value)
9572be45b66SKalle Valo ret = -EOPNOTSUPP;
9582be45b66SKalle Valo /* else silently accept disable of WPA */
9592be45b66SKalle Valo priv->wpa_enabled = 0;
9602be45b66SKalle Valo }
9612be45b66SKalle Valo break;
9622be45b66SKalle Valo
9632be45b66SKalle Valo default:
9642be45b66SKalle Valo ret = -EOPNOTSUPP;
9652be45b66SKalle Valo }
9662be45b66SKalle Valo
9672be45b66SKalle Valo orinoco_unlock(priv, &flags);
9682be45b66SKalle Valo return ret;
9692be45b66SKalle Valo }
9702be45b66SKalle Valo
orinoco_ioctl_get_auth(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)9712be45b66SKalle Valo static int orinoco_ioctl_get_auth(struct net_device *dev,
9722be45b66SKalle Valo struct iw_request_info *info,
9732be45b66SKalle Valo union iwreq_data *wrqu, char *extra)
9742be45b66SKalle Valo {
9752be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
9762be45b66SKalle Valo struct iw_param *param = &wrqu->param;
9772be45b66SKalle Valo unsigned long flags;
9782be45b66SKalle Valo int ret = 0;
9792be45b66SKalle Valo
9802be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
9812be45b66SKalle Valo return -EBUSY;
9822be45b66SKalle Valo
9832be45b66SKalle Valo switch (param->flags & IW_AUTH_INDEX) {
9842be45b66SKalle Valo case IW_AUTH_KEY_MGMT:
9852be45b66SKalle Valo param->value = priv->key_mgmt;
9862be45b66SKalle Valo break;
9872be45b66SKalle Valo
9882be45b66SKalle Valo case IW_AUTH_TKIP_COUNTERMEASURES:
9892be45b66SKalle Valo param->value = priv->tkip_cm_active;
9902be45b66SKalle Valo break;
9912be45b66SKalle Valo
9922be45b66SKalle Valo case IW_AUTH_80211_AUTH_ALG:
9932be45b66SKalle Valo if (priv->wep_restrict)
9942be45b66SKalle Valo param->value = IW_AUTH_ALG_SHARED_KEY;
9952be45b66SKalle Valo else
9962be45b66SKalle Valo param->value = IW_AUTH_ALG_OPEN_SYSTEM;
9972be45b66SKalle Valo break;
9982be45b66SKalle Valo
9992be45b66SKalle Valo case IW_AUTH_WPA_ENABLED:
10002be45b66SKalle Valo param->value = priv->wpa_enabled;
10012be45b66SKalle Valo break;
10022be45b66SKalle Valo
10032be45b66SKalle Valo default:
10042be45b66SKalle Valo ret = -EOPNOTSUPP;
10052be45b66SKalle Valo }
10062be45b66SKalle Valo
10072be45b66SKalle Valo orinoco_unlock(priv, &flags);
10082be45b66SKalle Valo return ret;
10092be45b66SKalle Valo }
10102be45b66SKalle Valo
orinoco_ioctl_set_genie(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)10112be45b66SKalle Valo static int orinoco_ioctl_set_genie(struct net_device *dev,
10122be45b66SKalle Valo struct iw_request_info *info,
10132be45b66SKalle Valo union iwreq_data *wrqu, char *extra)
10142be45b66SKalle Valo {
10152be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
10162be45b66SKalle Valo u8 *buf;
10172be45b66SKalle Valo unsigned long flags;
10182be45b66SKalle Valo
10192be45b66SKalle Valo /* cut off at IEEE80211_MAX_DATA_LEN */
10202be45b66SKalle Valo if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) ||
10212be45b66SKalle Valo (wrqu->data.length && (extra == NULL)))
10222be45b66SKalle Valo return -EINVAL;
10232be45b66SKalle Valo
10242be45b66SKalle Valo if (wrqu->data.length) {
10252be45b66SKalle Valo buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
10262be45b66SKalle Valo if (buf == NULL)
10272be45b66SKalle Valo return -ENOMEM;
10282be45b66SKalle Valo } else
10292be45b66SKalle Valo buf = NULL;
10302be45b66SKalle Valo
10312be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0) {
10322be45b66SKalle Valo kfree(buf);
10332be45b66SKalle Valo return -EBUSY;
10342be45b66SKalle Valo }
10352be45b66SKalle Valo
10362be45b66SKalle Valo kfree(priv->wpa_ie);
10372be45b66SKalle Valo priv->wpa_ie = buf;
10382be45b66SKalle Valo priv->wpa_ie_len = wrqu->data.length;
10392be45b66SKalle Valo
10402be45b66SKalle Valo if (priv->wpa_ie) {
10412be45b66SKalle Valo /* Looks like wl_lkm wants to check the auth alg, and
10422be45b66SKalle Valo * somehow pass it to the firmware.
10432be45b66SKalle Valo * Instead it just calls the key mgmt rid
10442be45b66SKalle Valo * - we do this in set auth.
10452be45b66SKalle Valo */
10462be45b66SKalle Valo }
10472be45b66SKalle Valo
10482be45b66SKalle Valo orinoco_unlock(priv, &flags);
10492be45b66SKalle Valo return 0;
10502be45b66SKalle Valo }
10512be45b66SKalle Valo
orinoco_ioctl_get_genie(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)10522be45b66SKalle Valo static int orinoco_ioctl_get_genie(struct net_device *dev,
10532be45b66SKalle Valo struct iw_request_info *info,
10542be45b66SKalle Valo union iwreq_data *wrqu, char *extra)
10552be45b66SKalle Valo {
10562be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
10572be45b66SKalle Valo unsigned long flags;
10582be45b66SKalle Valo int err = 0;
10592be45b66SKalle Valo
10602be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
10612be45b66SKalle Valo return -EBUSY;
10622be45b66SKalle Valo
10632be45b66SKalle Valo if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) {
10642be45b66SKalle Valo wrqu->data.length = 0;
10652be45b66SKalle Valo goto out;
10662be45b66SKalle Valo }
10672be45b66SKalle Valo
10682be45b66SKalle Valo if (wrqu->data.length < priv->wpa_ie_len) {
10692be45b66SKalle Valo err = -E2BIG;
10702be45b66SKalle Valo goto out;
10712be45b66SKalle Valo }
10722be45b66SKalle Valo
10732be45b66SKalle Valo wrqu->data.length = priv->wpa_ie_len;
10742be45b66SKalle Valo memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
10752be45b66SKalle Valo
10762be45b66SKalle Valo out:
10772be45b66SKalle Valo orinoco_unlock(priv, &flags);
10782be45b66SKalle Valo return err;
10792be45b66SKalle Valo }
10802be45b66SKalle Valo
orinoco_ioctl_set_mlme(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)10812be45b66SKalle Valo static int orinoco_ioctl_set_mlme(struct net_device *dev,
10822be45b66SKalle Valo struct iw_request_info *info,
10832be45b66SKalle Valo union iwreq_data *wrqu, char *extra)
10842be45b66SKalle Valo {
10852be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
10862be45b66SKalle Valo struct iw_mlme *mlme = (struct iw_mlme *)extra;
10872be45b66SKalle Valo unsigned long flags;
10882be45b66SKalle Valo int ret = 0;
10892be45b66SKalle Valo
10902be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
10912be45b66SKalle Valo return -EBUSY;
10922be45b66SKalle Valo
10932be45b66SKalle Valo switch (mlme->cmd) {
10942be45b66SKalle Valo case IW_MLME_DEAUTH:
10952be45b66SKalle Valo /* silently ignore */
10962be45b66SKalle Valo break;
10972be45b66SKalle Valo
10982be45b66SKalle Valo case IW_MLME_DISASSOC:
10992be45b66SKalle Valo
11002be45b66SKalle Valo ret = orinoco_hw_disassociate(priv, mlme->addr.sa_data,
11012be45b66SKalle Valo mlme->reason_code);
11022be45b66SKalle Valo break;
11032be45b66SKalle Valo
11042be45b66SKalle Valo default:
11052be45b66SKalle Valo ret = -EOPNOTSUPP;
11062be45b66SKalle Valo }
11072be45b66SKalle Valo
11082be45b66SKalle Valo orinoco_unlock(priv, &flags);
11092be45b66SKalle Valo return ret;
11102be45b66SKalle Valo }
11112be45b66SKalle Valo
orinoco_ioctl_reset(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)11122be45b66SKalle Valo static int orinoco_ioctl_reset(struct net_device *dev,
11132be45b66SKalle Valo struct iw_request_info *info,
11142c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
11152be45b66SKalle Valo char *extra)
11162be45b66SKalle Valo {
11172be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
11182be45b66SKalle Valo
11192be45b66SKalle Valo if (!capable(CAP_NET_ADMIN))
11202be45b66SKalle Valo return -EPERM;
11212be45b66SKalle Valo
11222be45b66SKalle Valo if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) {
11232be45b66SKalle Valo printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
11242be45b66SKalle Valo
11252be45b66SKalle Valo /* Firmware reset */
11262be45b66SKalle Valo orinoco_reset(&priv->reset_work);
11272be45b66SKalle Valo } else {
11282be45b66SKalle Valo printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
11292be45b66SKalle Valo
11302be45b66SKalle Valo schedule_work(&priv->reset_work);
11312be45b66SKalle Valo }
11322be45b66SKalle Valo
11332be45b66SKalle Valo return 0;
11342be45b66SKalle Valo }
11352be45b66SKalle Valo
orinoco_ioctl_setibssport(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)11362be45b66SKalle Valo static int orinoco_ioctl_setibssport(struct net_device *dev,
11372be45b66SKalle Valo struct iw_request_info *info,
11382c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
11392be45b66SKalle Valo char *extra)
11402be45b66SKalle Valo
11412be45b66SKalle Valo {
11422be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
11432be45b66SKalle Valo int val = *((int *) extra);
11442be45b66SKalle Valo unsigned long flags;
11452be45b66SKalle Valo
11462be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
11472be45b66SKalle Valo return -EBUSY;
11482be45b66SKalle Valo
11492be45b66SKalle Valo priv->ibss_port = val;
11502be45b66SKalle Valo
11512be45b66SKalle Valo /* Actually update the mode we are using */
11522be45b66SKalle Valo set_port_type(priv);
11532be45b66SKalle Valo
11542be45b66SKalle Valo orinoco_unlock(priv, &flags);
11552be45b66SKalle Valo return -EINPROGRESS; /* Call commit handler */
11562be45b66SKalle Valo }
11572be45b66SKalle Valo
orinoco_ioctl_getibssport(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)11582be45b66SKalle Valo static int orinoco_ioctl_getibssport(struct net_device *dev,
11592be45b66SKalle Valo struct iw_request_info *info,
11602c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
11612be45b66SKalle Valo char *extra)
11622be45b66SKalle Valo {
11632be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
11642be45b66SKalle Valo int *val = (int *) extra;
11652be45b66SKalle Valo
11662be45b66SKalle Valo *val = priv->ibss_port;
11672be45b66SKalle Valo return 0;
11682be45b66SKalle Valo }
11692be45b66SKalle Valo
orinoco_ioctl_setport3(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)11702be45b66SKalle Valo static int orinoco_ioctl_setport3(struct net_device *dev,
11712be45b66SKalle Valo struct iw_request_info *info,
11722c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
11732be45b66SKalle Valo char *extra)
11742be45b66SKalle Valo {
11752be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
11762be45b66SKalle Valo int val = *((int *) extra);
11772be45b66SKalle Valo int err = 0;
11782be45b66SKalle Valo unsigned long flags;
11792be45b66SKalle Valo
11802be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
11812be45b66SKalle Valo return -EBUSY;
11822be45b66SKalle Valo
11832be45b66SKalle Valo switch (val) {
11842be45b66SKalle Valo case 0: /* Try to do IEEE ad-hoc mode */
11852be45b66SKalle Valo if (!priv->has_ibss) {
11862be45b66SKalle Valo err = -EINVAL;
11872be45b66SKalle Valo break;
11882be45b66SKalle Valo }
11892be45b66SKalle Valo priv->prefer_port3 = 0;
11902be45b66SKalle Valo
11912be45b66SKalle Valo break;
11922be45b66SKalle Valo
11932be45b66SKalle Valo case 1: /* Try to do Lucent proprietary ad-hoc mode */
11942be45b66SKalle Valo if (!priv->has_port3) {
11952be45b66SKalle Valo err = -EINVAL;
11962be45b66SKalle Valo break;
11972be45b66SKalle Valo }
11982be45b66SKalle Valo priv->prefer_port3 = 1;
11992be45b66SKalle Valo break;
12002be45b66SKalle Valo
12012be45b66SKalle Valo default:
12022be45b66SKalle Valo err = -EINVAL;
12032be45b66SKalle Valo }
12042be45b66SKalle Valo
12052be45b66SKalle Valo if (!err) {
12062be45b66SKalle Valo /* Actually update the mode we are using */
12072be45b66SKalle Valo set_port_type(priv);
12082be45b66SKalle Valo err = -EINPROGRESS;
12092be45b66SKalle Valo }
12102be45b66SKalle Valo
12112be45b66SKalle Valo orinoco_unlock(priv, &flags);
12122be45b66SKalle Valo
12132be45b66SKalle Valo return err;
12142be45b66SKalle Valo }
12152be45b66SKalle Valo
orinoco_ioctl_getport3(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)12162be45b66SKalle Valo static int orinoco_ioctl_getport3(struct net_device *dev,
12172be45b66SKalle Valo struct iw_request_info *info,
12182c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
12192be45b66SKalle Valo char *extra)
12202be45b66SKalle Valo {
12212be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
12222be45b66SKalle Valo int *val = (int *) extra;
12232be45b66SKalle Valo
12242be45b66SKalle Valo *val = priv->prefer_port3;
12252be45b66SKalle Valo return 0;
12262be45b66SKalle Valo }
12272be45b66SKalle Valo
orinoco_ioctl_setpreamble(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)12282be45b66SKalle Valo static int orinoco_ioctl_setpreamble(struct net_device *dev,
12292be45b66SKalle Valo struct iw_request_info *info,
12302c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
12312be45b66SKalle Valo char *extra)
12322be45b66SKalle Valo {
12332be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
12342be45b66SKalle Valo unsigned long flags;
12352be45b66SKalle Valo int val;
12362be45b66SKalle Valo
12372be45b66SKalle Valo if (!priv->has_preamble)
12382be45b66SKalle Valo return -EOPNOTSUPP;
12392be45b66SKalle Valo
12402be45b66SKalle Valo /* 802.11b has recently defined some short preamble.
12412be45b66SKalle Valo * Basically, the Phy header has been reduced in size.
12422be45b66SKalle Valo * This increase performance, especially at high rates
12432be45b66SKalle Valo * (the preamble is transmitted at 1Mb/s), unfortunately
12442be45b66SKalle Valo * this give compatibility troubles... - Jean II */
12452be45b66SKalle Valo val = *((int *) extra);
12462be45b66SKalle Valo
12472be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
12482be45b66SKalle Valo return -EBUSY;
12492be45b66SKalle Valo
12502be45b66SKalle Valo if (val)
12512be45b66SKalle Valo priv->preamble = 1;
12522be45b66SKalle Valo else
12532be45b66SKalle Valo priv->preamble = 0;
12542be45b66SKalle Valo
12552be45b66SKalle Valo orinoco_unlock(priv, &flags);
12562be45b66SKalle Valo
12572be45b66SKalle Valo return -EINPROGRESS; /* Call commit handler */
12582be45b66SKalle Valo }
12592be45b66SKalle Valo
orinoco_ioctl_getpreamble(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)12602be45b66SKalle Valo static int orinoco_ioctl_getpreamble(struct net_device *dev,
12612be45b66SKalle Valo struct iw_request_info *info,
12622c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
12632be45b66SKalle Valo char *extra)
12642be45b66SKalle Valo {
12652be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
12662be45b66SKalle Valo int *val = (int *) extra;
12672be45b66SKalle Valo
12682be45b66SKalle Valo if (!priv->has_preamble)
12692be45b66SKalle Valo return -EOPNOTSUPP;
12702be45b66SKalle Valo
12712be45b66SKalle Valo *val = priv->preamble;
12722be45b66SKalle Valo return 0;
12732be45b66SKalle Valo }
12742be45b66SKalle Valo
12752be45b66SKalle Valo /* ioctl interface to hermes_read_ltv()
12762be45b66SKalle Valo * To use with iwpriv, pass the RID as the token argument, e.g.
12772be45b66SKalle Valo * iwpriv get_rid [0xfc00]
12782be45b66SKalle Valo * At least Wireless Tools 25 is required to use iwpriv.
12792be45b66SKalle Valo * For Wireless Tools 25 and 26 append "dummy" are the end. */
orinoco_ioctl_getrid(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)12802be45b66SKalle Valo static int orinoco_ioctl_getrid(struct net_device *dev,
12812be45b66SKalle Valo struct iw_request_info *info,
12822c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
12832be45b66SKalle Valo char *extra)
12842be45b66SKalle Valo {
12852c0e077dSGustavo A. R. Silva struct iw_point *data = &wrqu->data;
12862be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
12872be45b66SKalle Valo struct hermes *hw = &priv->hw;
12882be45b66SKalle Valo int rid = data->flags;
12892be45b66SKalle Valo u16 length;
12902be45b66SKalle Valo int err;
12912be45b66SKalle Valo unsigned long flags;
12922be45b66SKalle Valo
12932be45b66SKalle Valo /* It's a "get" function, but we don't want users to access the
12942be45b66SKalle Valo * WEP key and other raw firmware data */
12952be45b66SKalle Valo if (!capable(CAP_NET_ADMIN))
12962be45b66SKalle Valo return -EPERM;
12972be45b66SKalle Valo
12982be45b66SKalle Valo if (rid < 0xfc00 || rid > 0xffff)
12992be45b66SKalle Valo return -EINVAL;
13002be45b66SKalle Valo
13012be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
13022be45b66SKalle Valo return -EBUSY;
13032be45b66SKalle Valo
13042be45b66SKalle Valo err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
13052be45b66SKalle Valo extra);
13062be45b66SKalle Valo if (err)
13072be45b66SKalle Valo goto out;
13082be45b66SKalle Valo
13092be45b66SKalle Valo data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length),
13102be45b66SKalle Valo MAX_RID_LEN);
13112be45b66SKalle Valo
13122be45b66SKalle Valo out:
13132be45b66SKalle Valo orinoco_unlock(priv, &flags);
13142be45b66SKalle Valo return err;
13152be45b66SKalle Valo }
13162be45b66SKalle Valo
13172be45b66SKalle Valo
13182be45b66SKalle Valo /* Commit handler, called after set operations */
orinoco_ioctl_commit(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)13192be45b66SKalle Valo static int orinoco_ioctl_commit(struct net_device *dev,
13202be45b66SKalle Valo struct iw_request_info *info,
13212c0e077dSGustavo A. R. Silva union iwreq_data *wrqu,
13222be45b66SKalle Valo char *extra)
13232be45b66SKalle Valo {
13242be45b66SKalle Valo struct orinoco_private *priv = ndev_priv(dev);
13252be45b66SKalle Valo unsigned long flags;
13262be45b66SKalle Valo int err = 0;
13272be45b66SKalle Valo
13282be45b66SKalle Valo if (!priv->open)
13292be45b66SKalle Valo return 0;
13302be45b66SKalle Valo
13312be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
13322be45b66SKalle Valo return err;
13332be45b66SKalle Valo
13342be45b66SKalle Valo err = orinoco_commit(priv);
13352be45b66SKalle Valo
13362be45b66SKalle Valo orinoco_unlock(priv, &flags);
13372be45b66SKalle Valo return err;
13382be45b66SKalle Valo }
13392be45b66SKalle Valo
13402be45b66SKalle Valo static const struct iw_priv_args orinoco_privtab[] = {
13412be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
13422be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
13432be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
13442be45b66SKalle Valo 0, "set_port3" },
13452be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
13462be45b66SKalle Valo "get_port3" },
13472be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
13482be45b66SKalle Valo 0, "set_preamble" },
13492be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
13502be45b66SKalle Valo "get_preamble" },
13512be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
13522be45b66SKalle Valo 0, "set_ibssport" },
13532be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
13542be45b66SKalle Valo "get_ibssport" },
13552be45b66SKalle Valo { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN,
13562be45b66SKalle Valo "get_rid" },
13572be45b66SKalle Valo };
13582be45b66SKalle Valo
13592be45b66SKalle Valo
13602be45b66SKalle Valo /*
13612be45b66SKalle Valo * Structures to export the Wireless Handlers
13622be45b66SKalle Valo */
13632be45b66SKalle Valo
13642be45b66SKalle Valo static const iw_handler orinoco_handler[] = {
13652c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
1366*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname),
13672c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
13682c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
1369*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode),
1370*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode),
13712c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
13722c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
1373*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange),
13742be45b66SKalle Valo IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
13752be45b66SKalle Valo IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
13762be45b66SKalle Valo IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
13772be45b66SKalle Valo IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
13782c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
13792c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
1380*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan),
1381*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan),
13822c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
13832c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
13842c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
13852c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
1386*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCSIWRTS, cfg80211_wext_siwrts),
1387*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCGIWRTS, cfg80211_wext_giwrts),
1388*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCSIWFRAG, cfg80211_wext_siwfrag),
1389*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCGIWFRAG, cfg80211_wext_giwfrag),
1390*02ae6a70SGustavo A. R. Silva IW_HANDLER(SIOCGIWRETRY, cfg80211_wext_giwretry),
13912c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
13922c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
13932c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
13942c0e077dSGustavo A. R. Silva IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
13952be45b66SKalle Valo IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
13962be45b66SKalle Valo IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
13972be45b66SKalle Valo IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
13982be45b66SKalle Valo IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
13992be45b66SKalle Valo IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
14002be45b66SKalle Valo IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
14012be45b66SKalle Valo IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
14022be45b66SKalle Valo };
14032be45b66SKalle Valo
14042be45b66SKalle Valo
14052be45b66SKalle Valo /*
14062be45b66SKalle Valo Added typecasting since we no longer use iwreq_data -- Moustafa
14072be45b66SKalle Valo */
14082be45b66SKalle Valo static const iw_handler orinoco_private_handler[] = {
14092c0e077dSGustavo A. R. Silva [0] = orinoco_ioctl_reset,
14102c0e077dSGustavo A. R. Silva [1] = orinoco_ioctl_reset,
14112c0e077dSGustavo A. R. Silva [2] = orinoco_ioctl_setport3,
14122c0e077dSGustavo A. R. Silva [3] = orinoco_ioctl_getport3,
14132c0e077dSGustavo A. R. Silva [4] = orinoco_ioctl_setpreamble,
14142c0e077dSGustavo A. R. Silva [5] = orinoco_ioctl_getpreamble,
14152c0e077dSGustavo A. R. Silva [6] = orinoco_ioctl_setibssport,
14162c0e077dSGustavo A. R. Silva [7] = orinoco_ioctl_getibssport,
14172c0e077dSGustavo A. R. Silva [9] = orinoco_ioctl_getrid,
14182be45b66SKalle Valo };
14192be45b66SKalle Valo
14202be45b66SKalle Valo const struct iw_handler_def orinoco_handler_def = {
14212be45b66SKalle Valo .num_standard = ARRAY_SIZE(orinoco_handler),
14222be45b66SKalle Valo .num_private = ARRAY_SIZE(orinoco_private_handler),
14232be45b66SKalle Valo .num_private_args = ARRAY_SIZE(orinoco_privtab),
14242be45b66SKalle Valo .standard = orinoco_handler,
14252be45b66SKalle Valo .private = orinoco_private_handler,
14262be45b66SKalle Valo .private_args = orinoco_privtab,
14272be45b66SKalle Valo .get_wireless_stats = orinoco_get_wireless_stats,
14282be45b66SKalle Valo };
1429