162306a36Sopenharmony_ci/* Wireless extensions support. 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * See copyright notice in main.c 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/slab.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/if_arp.h> 862306a36Sopenharmony_ci#include <linux/wireless.h> 962306a36Sopenharmony_ci#include <linux/ieee80211.h> 1062306a36Sopenharmony_ci#include <linux/etherdevice.h> 1162306a36Sopenharmony_ci#include <net/iw_handler.h> 1262306a36Sopenharmony_ci#include <net/cfg80211.h> 1362306a36Sopenharmony_ci#include <net/cfg80211-wext.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "hermes.h" 1662306a36Sopenharmony_ci#include "hermes_rid.h" 1762306a36Sopenharmony_ci#include "orinoco.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "hw.h" 2062306a36Sopenharmony_ci#include "mic.h" 2162306a36Sopenharmony_ci#include "scan.h" 2262306a36Sopenharmony_ci#include "main.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "wext.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MAX_RID_LEN 1024 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Helper routine to record keys 2962306a36Sopenharmony_ci * It is called under orinoco_lock so it may not sleep */ 3062306a36Sopenharmony_cistatic int orinoco_set_key(struct orinoco_private *priv, int index, 3162306a36Sopenharmony_ci enum orinoco_alg alg, const u8 *key, int key_len, 3262306a36Sopenharmony_ci const u8 *seq, int seq_len) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci kfree_sensitive(priv->keys[index].key); 3562306a36Sopenharmony_ci kfree_sensitive(priv->keys[index].seq); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (key_len) { 3862306a36Sopenharmony_ci priv->keys[index].key = kzalloc(key_len, GFP_ATOMIC); 3962306a36Sopenharmony_ci if (!priv->keys[index].key) 4062306a36Sopenharmony_ci goto nomem; 4162306a36Sopenharmony_ci } else 4262306a36Sopenharmony_ci priv->keys[index].key = NULL; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (seq_len) { 4562306a36Sopenharmony_ci priv->keys[index].seq = kzalloc(seq_len, GFP_ATOMIC); 4662306a36Sopenharmony_ci if (!priv->keys[index].seq) 4762306a36Sopenharmony_ci goto free_key; 4862306a36Sopenharmony_ci } else 4962306a36Sopenharmony_ci priv->keys[index].seq = NULL; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci priv->keys[index].key_len = key_len; 5262306a36Sopenharmony_ci priv->keys[index].seq_len = seq_len; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (key_len) 5562306a36Sopenharmony_ci memcpy((void *)priv->keys[index].key, key, key_len); 5662306a36Sopenharmony_ci if (seq_len) 5762306a36Sopenharmony_ci memcpy((void *)priv->keys[index].seq, seq, seq_len); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci switch (alg) { 6062306a36Sopenharmony_ci case ORINOCO_ALG_TKIP: 6162306a36Sopenharmony_ci priv->keys[index].cipher = WLAN_CIPHER_SUITE_TKIP; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci case ORINOCO_ALG_WEP: 6562306a36Sopenharmony_ci priv->keys[index].cipher = (key_len > SMALL_KEY_SIZE) ? 6662306a36Sopenharmony_ci WLAN_CIPHER_SUITE_WEP104 : WLAN_CIPHER_SUITE_WEP40; 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci case ORINOCO_ALG_NONE: 7062306a36Sopenharmony_ci default: 7162306a36Sopenharmony_ci priv->keys[index].cipher = 0; 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cifree_key: 7862306a36Sopenharmony_ci kfree(priv->keys[index].key); 7962306a36Sopenharmony_ci priv->keys[index].key = NULL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cinomem: 8262306a36Sopenharmony_ci priv->keys[index].key_len = 0; 8362306a36Sopenharmony_ci priv->keys[index].seq_len = 0; 8462306a36Sopenharmony_ci priv->keys[index].cipher = 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return -ENOMEM; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 9262306a36Sopenharmony_ci struct hermes *hw = &priv->hw; 9362306a36Sopenharmony_ci struct iw_statistics *wstats = &priv->wstats; 9462306a36Sopenharmony_ci int err; 9562306a36Sopenharmony_ci unsigned long flags; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!netif_device_present(dev)) { 9862306a36Sopenharmony_ci printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", 9962306a36Sopenharmony_ci dev->name); 10062306a36Sopenharmony_ci return NULL; /* FIXME: Can we do better than this? */ 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* If busy, return the old stats. Returning NULL may cause 10462306a36Sopenharmony_ci * the interface to disappear from /proc/net/wireless */ 10562306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 10662306a36Sopenharmony_ci return wstats; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* We can't really wait for the tallies inquiry command to 10962306a36Sopenharmony_ci * complete, so we just use the previous results and trigger 11062306a36Sopenharmony_ci * a new tallies inquiry command for next time - Jean II */ 11162306a36Sopenharmony_ci /* FIXME: Really we should wait for the inquiry to come back - 11262306a36Sopenharmony_ci * as it is the stats we give don't make a whole lot of sense. 11362306a36Sopenharmony_ci * Unfortunately, it's not clear how to do that within the 11462306a36Sopenharmony_ci * wireless extensions framework: I think we're in user 11562306a36Sopenharmony_ci * context, but a lock seems to be held by the time we get in 11662306a36Sopenharmony_ci * here so we're not safe to sleep here. */ 11762306a36Sopenharmony_ci hermes_inquire(hw, HERMES_INQ_TALLIES); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (priv->iw_mode == NL80211_IFTYPE_ADHOC) { 12062306a36Sopenharmony_ci memset(&wstats->qual, 0, sizeof(wstats->qual)); 12162306a36Sopenharmony_ci /* If a spy address is defined, we report stats of the 12262306a36Sopenharmony_ci * first spy address - Jean II */ 12362306a36Sopenharmony_ci if (SPY_NUMBER(priv)) { 12462306a36Sopenharmony_ci wstats->qual.qual = priv->spy_data.spy_stat[0].qual; 12562306a36Sopenharmony_ci wstats->qual.level = priv->spy_data.spy_stat[0].level; 12662306a36Sopenharmony_ci wstats->qual.noise = priv->spy_data.spy_stat[0].noise; 12762306a36Sopenharmony_ci wstats->qual.updated = 12862306a36Sopenharmony_ci priv->spy_data.spy_stat[0].updated; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci } else { 13162306a36Sopenharmony_ci struct { 13262306a36Sopenharmony_ci __le16 qual, signal, noise, unused; 13362306a36Sopenharmony_ci } __packed cq; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci err = HERMES_READ_RECORD(hw, USER_BAP, 13662306a36Sopenharmony_ci HERMES_RID_COMMSQUALITY, &cq); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (!err) { 13962306a36Sopenharmony_ci wstats->qual.qual = (int)le16_to_cpu(cq.qual); 14062306a36Sopenharmony_ci wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; 14162306a36Sopenharmony_ci wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; 14262306a36Sopenharmony_ci wstats->qual.updated = 14362306a36Sopenharmony_ci IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 14862306a36Sopenharmony_ci return wstats; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/********************************************************************/ 15262306a36Sopenharmony_ci/* Wireless extensions */ 15362306a36Sopenharmony_ci/********************************************************************/ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int orinoco_ioctl_setwap(struct net_device *dev, 15662306a36Sopenharmony_ci struct iw_request_info *info, 15762306a36Sopenharmony_ci union iwreq_data *wrqu, 15862306a36Sopenharmony_ci char *extra) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct sockaddr *ap_addr = &wrqu->ap_addr; 16162306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 16262306a36Sopenharmony_ci int err = -EINPROGRESS; /* Call commit handler */ 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 16662306a36Sopenharmony_ci return -EBUSY; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Enable automatic roaming - no sanity checks are needed */ 16962306a36Sopenharmony_ci if (is_zero_ether_addr(ap_addr->sa_data) || 17062306a36Sopenharmony_ci is_broadcast_ether_addr(ap_addr->sa_data)) { 17162306a36Sopenharmony_ci priv->bssid_fixed = 0; 17262306a36Sopenharmony_ci eth_zero_addr(priv->desired_bssid); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* "off" means keep existing connection */ 17562306a36Sopenharmony_ci if (ap_addr->sa_data[0] == 0) { 17662306a36Sopenharmony_ci __orinoco_hw_set_wap(priv); 17762306a36Sopenharmony_ci err = 0; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { 18362306a36Sopenharmony_ci printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " 18462306a36Sopenharmony_ci "support manual roaming\n", 18562306a36Sopenharmony_ci dev->name); 18662306a36Sopenharmony_ci err = -EOPNOTSUPP; 18762306a36Sopenharmony_ci goto out; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (priv->iw_mode != NL80211_IFTYPE_STATION) { 19162306a36Sopenharmony_ci printk(KERN_WARNING "%s: Manual roaming supported only in " 19262306a36Sopenharmony_ci "managed mode\n", dev->name); 19362306a36Sopenharmony_ci err = -EOPNOTSUPP; 19462306a36Sopenharmony_ci goto out; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Intersil firmware hangs without Desired ESSID */ 19862306a36Sopenharmony_ci if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && 19962306a36Sopenharmony_ci strlen(priv->desired_essid) == 0) { 20062306a36Sopenharmony_ci printk(KERN_WARNING "%s: Desired ESSID must be set for " 20162306a36Sopenharmony_ci "manual roaming\n", dev->name); 20262306a36Sopenharmony_ci err = -EOPNOTSUPP; 20362306a36Sopenharmony_ci goto out; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Finally, enable manual roaming */ 20762306a36Sopenharmony_ci priv->bssid_fixed = 1; 20862306a36Sopenharmony_ci memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci out: 21162306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 21262306a36Sopenharmony_ci return err; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int orinoco_ioctl_getwap(struct net_device *dev, 21662306a36Sopenharmony_ci struct iw_request_info *info, 21762306a36Sopenharmony_ci union iwreq_data *wrqu, 21862306a36Sopenharmony_ci char *extra) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct sockaddr *ap_addr = &wrqu->ap_addr; 22162306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci int err = 0; 22462306a36Sopenharmony_ci unsigned long flags; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 22762306a36Sopenharmony_ci return -EBUSY; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ap_addr->sa_family = ARPHRD_ETHER; 23062306a36Sopenharmony_ci err = orinoco_hw_get_current_bssid(priv, ap_addr->sa_data); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return err; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int orinoco_ioctl_setiwencode(struct net_device *dev, 23862306a36Sopenharmony_ci struct iw_request_info *info, 23962306a36Sopenharmony_ci union iwreq_data *wrqu, 24062306a36Sopenharmony_ci char *keybuf) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct iw_point *erq = &wrqu->encoding; 24362306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 24462306a36Sopenharmony_ci int index = (erq->flags & IW_ENCODE_INDEX) - 1; 24562306a36Sopenharmony_ci int setindex = priv->tx_key; 24662306a36Sopenharmony_ci enum orinoco_alg encode_alg = priv->encode_alg; 24762306a36Sopenharmony_ci int restricted = priv->wep_restrict; 24862306a36Sopenharmony_ci int err = -EINPROGRESS; /* Call commit handler */ 24962306a36Sopenharmony_ci unsigned long flags; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!priv->has_wep) 25262306a36Sopenharmony_ci return -EOPNOTSUPP; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (erq->pointer) { 25562306a36Sopenharmony_ci /* We actually have a key to set - check its length */ 25662306a36Sopenharmony_ci if (erq->length > LARGE_KEY_SIZE) 25762306a36Sopenharmony_ci return -E2BIG; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep) 26062306a36Sopenharmony_ci return -E2BIG; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 26462306a36Sopenharmony_ci return -EBUSY; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Clear any TKIP key we have */ 26762306a36Sopenharmony_ci if ((priv->has_wpa) && (priv->encode_alg == ORINOCO_ALG_TKIP)) 26862306a36Sopenharmony_ci (void) orinoco_clear_tkip_key(priv, setindex); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (erq->length > 0) { 27162306a36Sopenharmony_ci if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) 27262306a36Sopenharmony_ci index = priv->tx_key; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Switch on WEP if off */ 27562306a36Sopenharmony_ci if (encode_alg != ORINOCO_ALG_WEP) { 27662306a36Sopenharmony_ci setindex = index; 27762306a36Sopenharmony_ci encode_alg = ORINOCO_ALG_WEP; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } else { 28062306a36Sopenharmony_ci /* Important note : if the user do "iwconfig eth0 enc off", 28162306a36Sopenharmony_ci * we will arrive there with an index of -1. This is valid 28262306a36Sopenharmony_ci * but need to be taken care off... Jean II */ 28362306a36Sopenharmony_ci if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { 28462306a36Sopenharmony_ci if ((index != -1) || (erq->flags == 0)) { 28562306a36Sopenharmony_ci err = -EINVAL; 28662306a36Sopenharmony_ci goto out; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci /* Set the index : Check that the key is valid */ 29062306a36Sopenharmony_ci if (priv->keys[index].key_len == 0) { 29162306a36Sopenharmony_ci err = -EINVAL; 29262306a36Sopenharmony_ci goto out; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci setindex = index; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (erq->flags & IW_ENCODE_DISABLED) 29962306a36Sopenharmony_ci encode_alg = ORINOCO_ALG_NONE; 30062306a36Sopenharmony_ci if (erq->flags & IW_ENCODE_OPEN) 30162306a36Sopenharmony_ci restricted = 0; 30262306a36Sopenharmony_ci if (erq->flags & IW_ENCODE_RESTRICTED) 30362306a36Sopenharmony_ci restricted = 1; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (erq->pointer && erq->length > 0) { 30662306a36Sopenharmony_ci err = orinoco_set_key(priv, index, ORINOCO_ALG_WEP, keybuf, 30762306a36Sopenharmony_ci erq->length, NULL, 0); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci priv->tx_key = setindex; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Try fast key change if connected and only keys are changed */ 31262306a36Sopenharmony_ci if ((priv->encode_alg == encode_alg) && 31362306a36Sopenharmony_ci (priv->wep_restrict == restricted) && 31462306a36Sopenharmony_ci netif_carrier_ok(dev)) { 31562306a36Sopenharmony_ci err = __orinoco_hw_setup_wepkeys(priv); 31662306a36Sopenharmony_ci /* No need to commit if successful */ 31762306a36Sopenharmony_ci goto out; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci priv->encode_alg = encode_alg; 32162306a36Sopenharmony_ci priv->wep_restrict = restricted; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci out: 32462306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return err; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int orinoco_ioctl_getiwencode(struct net_device *dev, 33062306a36Sopenharmony_ci struct iw_request_info *info, 33162306a36Sopenharmony_ci union iwreq_data *wrqu, 33262306a36Sopenharmony_ci char *keybuf) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct iw_point *erq = &wrqu->encoding; 33562306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 33662306a36Sopenharmony_ci int index = (erq->flags & IW_ENCODE_INDEX) - 1; 33762306a36Sopenharmony_ci unsigned long flags; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (!priv->has_wep) 34062306a36Sopenharmony_ci return -EOPNOTSUPP; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 34362306a36Sopenharmony_ci return -EBUSY; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) 34662306a36Sopenharmony_ci index = priv->tx_key; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci erq->flags = 0; 34962306a36Sopenharmony_ci if (!priv->encode_alg) 35062306a36Sopenharmony_ci erq->flags |= IW_ENCODE_DISABLED; 35162306a36Sopenharmony_ci erq->flags |= index + 1; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (priv->wep_restrict) 35462306a36Sopenharmony_ci erq->flags |= IW_ENCODE_RESTRICTED; 35562306a36Sopenharmony_ci else 35662306a36Sopenharmony_ci erq->flags |= IW_ENCODE_OPEN; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci erq->length = priv->keys[index].key_len; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci memcpy(keybuf, priv->keys[index].key, erq->length); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int orinoco_ioctl_setessid(struct net_device *dev, 36762306a36Sopenharmony_ci struct iw_request_info *info, 36862306a36Sopenharmony_ci union iwreq_data *wrqu, 36962306a36Sopenharmony_ci char *essidbuf) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct iw_point *erq = &wrqu->essid; 37262306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 37362306a36Sopenharmony_ci unsigned long flags; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it 37662306a36Sopenharmony_ci * anyway... - Jean II */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Hum... Should not use Wireless Extension constant (may change), 37962306a36Sopenharmony_ci * should use our own... - Jean II */ 38062306a36Sopenharmony_ci if (erq->length > IW_ESSID_MAX_SIZE) 38162306a36Sopenharmony_ci return -E2BIG; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 38462306a36Sopenharmony_ci return -EBUSY; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ 38762306a36Sopenharmony_ci memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* If not ANY, get the new ESSID */ 39062306a36Sopenharmony_ci if (erq->flags) 39162306a36Sopenharmony_ci memcpy(priv->desired_essid, essidbuf, erq->length); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int orinoco_ioctl_getessid(struct net_device *dev, 39962306a36Sopenharmony_ci struct iw_request_info *info, 40062306a36Sopenharmony_ci union iwreq_data *wrqu, 40162306a36Sopenharmony_ci char *essidbuf) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct iw_point *erq = &wrqu->essid; 40462306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 40562306a36Sopenharmony_ci int active; 40662306a36Sopenharmony_ci int err = 0; 40762306a36Sopenharmony_ci unsigned long flags; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (netif_running(dev)) { 41062306a36Sopenharmony_ci err = orinoco_hw_get_essid(priv, &active, essidbuf); 41162306a36Sopenharmony_ci if (err < 0) 41262306a36Sopenharmony_ci return err; 41362306a36Sopenharmony_ci erq->length = err; 41462306a36Sopenharmony_ci } else { 41562306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 41662306a36Sopenharmony_ci return -EBUSY; 41762306a36Sopenharmony_ci memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); 41862306a36Sopenharmony_ci erq->length = strlen(priv->desired_essid); 41962306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci erq->flags = 1; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int orinoco_ioctl_setfreq(struct net_device *dev, 42862306a36Sopenharmony_ci struct iw_request_info *info, 42962306a36Sopenharmony_ci union iwreq_data *wrqu, 43062306a36Sopenharmony_ci char *extra) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct iw_freq *frq = &wrqu->freq; 43362306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 43462306a36Sopenharmony_ci int chan = -1; 43562306a36Sopenharmony_ci unsigned long flags; 43662306a36Sopenharmony_ci int err = -EINPROGRESS; /* Call commit handler */ 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* In infrastructure mode the AP sets the channel */ 43962306a36Sopenharmony_ci if (priv->iw_mode == NL80211_IFTYPE_STATION) 44062306a36Sopenharmony_ci return -EBUSY; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if ((frq->e == 0) && (frq->m <= 1000)) { 44362306a36Sopenharmony_ci /* Setting by channel number */ 44462306a36Sopenharmony_ci chan = frq->m; 44562306a36Sopenharmony_ci } else { 44662306a36Sopenharmony_ci /* Setting by frequency */ 44762306a36Sopenharmony_ci int denom = 1; 44862306a36Sopenharmony_ci int i; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Calculate denominator to rescale to MHz */ 45162306a36Sopenharmony_ci for (i = 0; i < (6 - frq->e); i++) 45262306a36Sopenharmony_ci denom *= 10; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci chan = ieee80211_frequency_to_channel(frq->m / denom); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if ((chan < 1) || (chan > NUM_CHANNELS) || 45862306a36Sopenharmony_ci !(priv->channel_mask & (1 << (chan - 1)))) 45962306a36Sopenharmony_ci return -EINVAL; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 46262306a36Sopenharmony_ci return -EBUSY; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci priv->channel = chan; 46562306a36Sopenharmony_ci if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { 46662306a36Sopenharmony_ci /* Fast channel change - no commit if successful */ 46762306a36Sopenharmony_ci struct hermes *hw = &priv->hw; 46862306a36Sopenharmony_ci err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | 46962306a36Sopenharmony_ci HERMES_TEST_SET_CHANNEL, 47062306a36Sopenharmony_ci chan, NULL); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return err; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int orinoco_ioctl_getfreq(struct net_device *dev, 47862306a36Sopenharmony_ci struct iw_request_info *info, 47962306a36Sopenharmony_ci union iwreq_data *wrqu, 48062306a36Sopenharmony_ci char *extra) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct iw_freq *frq = &wrqu->freq; 48362306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 48462306a36Sopenharmony_ci int tmp; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Locking done in there */ 48762306a36Sopenharmony_ci tmp = orinoco_hw_get_freq(priv); 48862306a36Sopenharmony_ci if (tmp < 0) 48962306a36Sopenharmony_ci return tmp; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci frq->m = tmp * 100000; 49262306a36Sopenharmony_ci frq->e = 1; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int orinoco_ioctl_getsens(struct net_device *dev, 49862306a36Sopenharmony_ci struct iw_request_info *info, 49962306a36Sopenharmony_ci union iwreq_data *wrqu, 50062306a36Sopenharmony_ci char *extra) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct iw_param *srq = &wrqu->sens; 50362306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 50462306a36Sopenharmony_ci struct hermes *hw = &priv->hw; 50562306a36Sopenharmony_ci u16 val; 50662306a36Sopenharmony_ci int err; 50762306a36Sopenharmony_ci unsigned long flags; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (!priv->has_sensitivity) 51062306a36Sopenharmony_ci return -EOPNOTSUPP; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 51362306a36Sopenharmony_ci return -EBUSY; 51462306a36Sopenharmony_ci err = hermes_read_wordrec(hw, USER_BAP, 51562306a36Sopenharmony_ci HERMES_RID_CNFSYSTEMSCALE, &val); 51662306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (err) 51962306a36Sopenharmony_ci return err; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci srq->value = val; 52262306a36Sopenharmony_ci srq->fixed = 0; /* auto */ 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int orinoco_ioctl_setsens(struct net_device *dev, 52862306a36Sopenharmony_ci struct iw_request_info *info, 52962306a36Sopenharmony_ci union iwreq_data *wrqu, 53062306a36Sopenharmony_ci char *extra) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct iw_param *srq = &wrqu->sens; 53362306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 53462306a36Sopenharmony_ci int val = srq->value; 53562306a36Sopenharmony_ci unsigned long flags; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!priv->has_sensitivity) 53862306a36Sopenharmony_ci return -EOPNOTSUPP; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if ((val < 1) || (val > 3)) 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 54462306a36Sopenharmony_ci return -EBUSY; 54562306a36Sopenharmony_ci priv->ap_density = val; 54662306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int orinoco_ioctl_setrate(struct net_device *dev, 55262306a36Sopenharmony_ci struct iw_request_info *info, 55362306a36Sopenharmony_ci union iwreq_data *wrqu, 55462306a36Sopenharmony_ci char *extra) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct iw_param *rrq = &wrqu->bitrate; 55762306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 55862306a36Sopenharmony_ci int ratemode; 55962306a36Sopenharmony_ci int bitrate; /* 100s of kilobits */ 56062306a36Sopenharmony_ci unsigned long flags; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* As the user space doesn't know our highest rate, it uses -1 56362306a36Sopenharmony_ci * to ask us to set the highest rate. Test it using "iwconfig 56462306a36Sopenharmony_ci * ethX rate auto" - Jean II */ 56562306a36Sopenharmony_ci if (rrq->value == -1) 56662306a36Sopenharmony_ci bitrate = 110; 56762306a36Sopenharmony_ci else { 56862306a36Sopenharmony_ci if (rrq->value % 100000) 56962306a36Sopenharmony_ci return -EINVAL; 57062306a36Sopenharmony_ci bitrate = rrq->value / 100000; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (ratemode == -1) 57662306a36Sopenharmony_ci return -EINVAL; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 57962306a36Sopenharmony_ci return -EBUSY; 58062306a36Sopenharmony_ci priv->bitratemode = ratemode; 58162306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return -EINPROGRESS; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int orinoco_ioctl_getrate(struct net_device *dev, 58762306a36Sopenharmony_ci struct iw_request_info *info, 58862306a36Sopenharmony_ci union iwreq_data *wrqu, 58962306a36Sopenharmony_ci char *extra) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct iw_param *rrq = &wrqu->bitrate; 59262306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 59362306a36Sopenharmony_ci int err = 0; 59462306a36Sopenharmony_ci int bitrate, automatic; 59562306a36Sopenharmony_ci unsigned long flags; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 59862306a36Sopenharmony_ci return -EBUSY; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* If the interface is running we try to find more about the 60362306a36Sopenharmony_ci current mode */ 60462306a36Sopenharmony_ci if (netif_running(dev)) { 60562306a36Sopenharmony_ci int act_bitrate; 60662306a36Sopenharmony_ci int lerr; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Ignore errors if we can't get the actual bitrate */ 60962306a36Sopenharmony_ci lerr = orinoco_hw_get_act_bitrate(priv, &act_bitrate); 61062306a36Sopenharmony_ci if (!lerr) 61162306a36Sopenharmony_ci bitrate = act_bitrate; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci rrq->value = bitrate; 61762306a36Sopenharmony_ci rrq->fixed = !automatic; 61862306a36Sopenharmony_ci rrq->disabled = 0; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return err; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int orinoco_ioctl_setpower(struct net_device *dev, 62462306a36Sopenharmony_ci struct iw_request_info *info, 62562306a36Sopenharmony_ci union iwreq_data *wrqu, 62662306a36Sopenharmony_ci char *extra) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct iw_param *prq = &wrqu->power; 62962306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 63062306a36Sopenharmony_ci int err = -EINPROGRESS; /* Call commit handler */ 63162306a36Sopenharmony_ci unsigned long flags; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 63462306a36Sopenharmony_ci return -EBUSY; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (prq->disabled) { 63762306a36Sopenharmony_ci priv->pm_on = 0; 63862306a36Sopenharmony_ci } else { 63962306a36Sopenharmony_ci switch (prq->flags & IW_POWER_MODE) { 64062306a36Sopenharmony_ci case IW_POWER_UNICAST_R: 64162306a36Sopenharmony_ci priv->pm_mcast = 0; 64262306a36Sopenharmony_ci priv->pm_on = 1; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case IW_POWER_ALL_R: 64562306a36Sopenharmony_ci priv->pm_mcast = 1; 64662306a36Sopenharmony_ci priv->pm_on = 1; 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci case IW_POWER_ON: 64962306a36Sopenharmony_ci /* No flags : but we may have a value - Jean II */ 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci default: 65262306a36Sopenharmony_ci err = -EINVAL; 65362306a36Sopenharmony_ci goto out; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (prq->flags & IW_POWER_TIMEOUT) { 65762306a36Sopenharmony_ci priv->pm_on = 1; 65862306a36Sopenharmony_ci priv->pm_timeout = prq->value / 1000; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci if (prq->flags & IW_POWER_PERIOD) { 66162306a36Sopenharmony_ci priv->pm_on = 1; 66262306a36Sopenharmony_ci priv->pm_period = prq->value / 1000; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci /* It's valid to not have a value if we are just toggling 66562306a36Sopenharmony_ci * the flags... Jean II */ 66662306a36Sopenharmony_ci if (!priv->pm_on) { 66762306a36Sopenharmony_ci err = -EINVAL; 66862306a36Sopenharmony_ci goto out; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci out: 67362306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci return err; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic int orinoco_ioctl_getpower(struct net_device *dev, 67962306a36Sopenharmony_ci struct iw_request_info *info, 68062306a36Sopenharmony_ci union iwreq_data *wrqu, 68162306a36Sopenharmony_ci char *extra) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct iw_param *prq = &wrqu->power; 68462306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 68562306a36Sopenharmony_ci struct hermes *hw = &priv->hw; 68662306a36Sopenharmony_ci int err = 0; 68762306a36Sopenharmony_ci u16 enable, period, timeout, mcast; 68862306a36Sopenharmony_ci unsigned long flags; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 69162306a36Sopenharmony_ci return -EBUSY; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci err = hermes_read_wordrec(hw, USER_BAP, 69462306a36Sopenharmony_ci HERMES_RID_CNFPMENABLED, &enable); 69562306a36Sopenharmony_ci if (err) 69662306a36Sopenharmony_ci goto out; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci err = hermes_read_wordrec(hw, USER_BAP, 69962306a36Sopenharmony_ci HERMES_RID_CNFMAXSLEEPDURATION, &period); 70062306a36Sopenharmony_ci if (err) 70162306a36Sopenharmony_ci goto out; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci err = hermes_read_wordrec(hw, USER_BAP, 70462306a36Sopenharmony_ci HERMES_RID_CNFPMHOLDOVERDURATION, &timeout); 70562306a36Sopenharmony_ci if (err) 70662306a36Sopenharmony_ci goto out; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci err = hermes_read_wordrec(hw, USER_BAP, 70962306a36Sopenharmony_ci HERMES_RID_CNFMULTICASTRECEIVE, &mcast); 71062306a36Sopenharmony_ci if (err) 71162306a36Sopenharmony_ci goto out; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci prq->disabled = !enable; 71462306a36Sopenharmony_ci /* Note : by default, display the period */ 71562306a36Sopenharmony_ci if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { 71662306a36Sopenharmony_ci prq->flags = IW_POWER_TIMEOUT; 71762306a36Sopenharmony_ci prq->value = timeout * 1000; 71862306a36Sopenharmony_ci } else { 71962306a36Sopenharmony_ci prq->flags = IW_POWER_PERIOD; 72062306a36Sopenharmony_ci prq->value = period * 1000; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci if (mcast) 72362306a36Sopenharmony_ci prq->flags |= IW_POWER_ALL_R; 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci prq->flags |= IW_POWER_UNICAST_R; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci out: 72862306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return err; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic int orinoco_ioctl_set_encodeext(struct net_device *dev, 73462306a36Sopenharmony_ci struct iw_request_info *info, 73562306a36Sopenharmony_ci union iwreq_data *wrqu, 73662306a36Sopenharmony_ci char *extra) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 73962306a36Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 74062306a36Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 74162306a36Sopenharmony_ci int idx, alg = ext->alg, set_key = 1; 74262306a36Sopenharmony_ci unsigned long flags; 74362306a36Sopenharmony_ci int err = -EINVAL; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 74662306a36Sopenharmony_ci return -EBUSY; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Determine and validate the key index */ 74962306a36Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 75062306a36Sopenharmony_ci if (idx) { 75162306a36Sopenharmony_ci if ((idx < 1) || (idx > 4)) 75262306a36Sopenharmony_ci goto out; 75362306a36Sopenharmony_ci idx--; 75462306a36Sopenharmony_ci } else 75562306a36Sopenharmony_ci idx = priv->tx_key; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (encoding->flags & IW_ENCODE_DISABLED) 75862306a36Sopenharmony_ci alg = IW_ENCODE_ALG_NONE; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { 76162306a36Sopenharmony_ci /* Clear any TKIP TX key we had */ 76262306a36Sopenharmony_ci (void) orinoco_clear_tkip_key(priv, priv->tx_key); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { 76662306a36Sopenharmony_ci priv->tx_key = idx; 76762306a36Sopenharmony_ci set_key = ((alg == IW_ENCODE_ALG_TKIP) || 76862306a36Sopenharmony_ci (ext->key_len > 0)) ? 1 : 0; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (set_key) { 77262306a36Sopenharmony_ci /* Set the requested key first */ 77362306a36Sopenharmony_ci switch (alg) { 77462306a36Sopenharmony_ci case IW_ENCODE_ALG_NONE: 77562306a36Sopenharmony_ci priv->encode_alg = ORINOCO_ALG_NONE; 77662306a36Sopenharmony_ci err = orinoco_set_key(priv, idx, ORINOCO_ALG_NONE, 77762306a36Sopenharmony_ci NULL, 0, NULL, 0); 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci case IW_ENCODE_ALG_WEP: 78162306a36Sopenharmony_ci if (ext->key_len <= 0) 78262306a36Sopenharmony_ci goto out; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci priv->encode_alg = ORINOCO_ALG_WEP; 78562306a36Sopenharmony_ci err = orinoco_set_key(priv, idx, ORINOCO_ALG_WEP, 78662306a36Sopenharmony_ci ext->key, ext->key_len, NULL, 0); 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci case IW_ENCODE_ALG_TKIP: 79062306a36Sopenharmony_ci { 79162306a36Sopenharmony_ci u8 *tkip_iv = NULL; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!priv->has_wpa || 79462306a36Sopenharmony_ci (ext->key_len > sizeof(struct orinoco_tkip_key))) 79562306a36Sopenharmony_ci goto out; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci priv->encode_alg = ORINOCO_ALG_TKIP; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) 80062306a36Sopenharmony_ci tkip_iv = &ext->rx_seq[0]; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci err = orinoco_set_key(priv, idx, ORINOCO_ALG_TKIP, 80362306a36Sopenharmony_ci ext->key, ext->key_len, tkip_iv, 80462306a36Sopenharmony_ci ORINOCO_SEQ_LEN); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci err = __orinoco_hw_set_tkip_key(priv, idx, 80762306a36Sopenharmony_ci ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, 80862306a36Sopenharmony_ci priv->keys[idx].key, priv->keys[idx].key_len, 80962306a36Sopenharmony_ci tkip_iv, ORINOCO_SEQ_LEN, NULL, 0); 81062306a36Sopenharmony_ci if (err) 81162306a36Sopenharmony_ci printk(KERN_ERR "%s: Error %d setting TKIP key" 81262306a36Sopenharmony_ci "\n", dev->name, err); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci goto out; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci default: 81762306a36Sopenharmony_ci goto out; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci err = -EINPROGRESS; 82162306a36Sopenharmony_ci out: 82262306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci return err; 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int orinoco_ioctl_get_encodeext(struct net_device *dev, 82862306a36Sopenharmony_ci struct iw_request_info *info, 82962306a36Sopenharmony_ci union iwreq_data *wrqu, 83062306a36Sopenharmony_ci char *extra) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 83362306a36Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 83462306a36Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 83562306a36Sopenharmony_ci int idx, max_key_len; 83662306a36Sopenharmony_ci unsigned long flags; 83762306a36Sopenharmony_ci int err; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 84062306a36Sopenharmony_ci return -EBUSY; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci err = -EINVAL; 84362306a36Sopenharmony_ci max_key_len = encoding->length - sizeof(*ext); 84462306a36Sopenharmony_ci if (max_key_len < 0) 84562306a36Sopenharmony_ci goto out; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 84862306a36Sopenharmony_ci if (idx) { 84962306a36Sopenharmony_ci if ((idx < 1) || (idx > 4)) 85062306a36Sopenharmony_ci goto out; 85162306a36Sopenharmony_ci idx--; 85262306a36Sopenharmony_ci } else 85362306a36Sopenharmony_ci idx = priv->tx_key; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci encoding->flags = idx + 1; 85662306a36Sopenharmony_ci memset(ext, 0, sizeof(*ext)); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci switch (priv->encode_alg) { 85962306a36Sopenharmony_ci case ORINOCO_ALG_NONE: 86062306a36Sopenharmony_ci ext->alg = IW_ENCODE_ALG_NONE; 86162306a36Sopenharmony_ci ext->key_len = 0; 86262306a36Sopenharmony_ci encoding->flags |= IW_ENCODE_DISABLED; 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci case ORINOCO_ALG_WEP: 86562306a36Sopenharmony_ci ext->alg = IW_ENCODE_ALG_WEP; 86662306a36Sopenharmony_ci ext->key_len = min(priv->keys[idx].key_len, max_key_len); 86762306a36Sopenharmony_ci memcpy(ext->key, priv->keys[idx].key, ext->key_len); 86862306a36Sopenharmony_ci encoding->flags |= IW_ENCODE_ENABLED; 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case ORINOCO_ALG_TKIP: 87162306a36Sopenharmony_ci ext->alg = IW_ENCODE_ALG_TKIP; 87262306a36Sopenharmony_ci ext->key_len = min(priv->keys[idx].key_len, max_key_len); 87362306a36Sopenharmony_ci memcpy(ext->key, priv->keys[idx].key, ext->key_len); 87462306a36Sopenharmony_ci encoding->flags |= IW_ENCODE_ENABLED; 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci err = 0; 87962306a36Sopenharmony_ci out: 88062306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return err; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int orinoco_ioctl_set_auth(struct net_device *dev, 88662306a36Sopenharmony_ci struct iw_request_info *info, 88762306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 89062306a36Sopenharmony_ci struct hermes *hw = &priv->hw; 89162306a36Sopenharmony_ci struct iw_param *param = &wrqu->param; 89262306a36Sopenharmony_ci unsigned long flags; 89362306a36Sopenharmony_ci int ret = -EINPROGRESS; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 89662306a36Sopenharmony_ci return -EBUSY; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 89962306a36Sopenharmony_ci case IW_AUTH_WPA_VERSION: 90062306a36Sopenharmony_ci case IW_AUTH_CIPHER_PAIRWISE: 90162306a36Sopenharmony_ci case IW_AUTH_CIPHER_GROUP: 90262306a36Sopenharmony_ci case IW_AUTH_RX_UNENCRYPTED_EAPOL: 90362306a36Sopenharmony_ci case IW_AUTH_PRIVACY_INVOKED: 90462306a36Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 90562306a36Sopenharmony_ci /* 90662306a36Sopenharmony_ci * orinoco does not use these parameters 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci case IW_AUTH_MFP: 91162306a36Sopenharmony_ci /* Management Frame Protection not supported. 91262306a36Sopenharmony_ci * Only fail if set to required. 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_ci if (param->value == IW_AUTH_MFP_REQUIRED) 91562306a36Sopenharmony_ci ret = -EINVAL; 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci case IW_AUTH_KEY_MGMT: 91962306a36Sopenharmony_ci /* wl_lkm implies value 2 == PSK for Hermes I 92062306a36Sopenharmony_ci * which ties in with WEXT 92162306a36Sopenharmony_ci * no other hints tho :( 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci priv->key_mgmt = param->value; 92462306a36Sopenharmony_ci break; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci case IW_AUTH_TKIP_COUNTERMEASURES: 92762306a36Sopenharmony_ci /* When countermeasures are enabled, shut down the 92862306a36Sopenharmony_ci * card; when disabled, re-enable the card. This must 92962306a36Sopenharmony_ci * take effect immediately. 93062306a36Sopenharmony_ci * 93162306a36Sopenharmony_ci * TODO: Make sure that the EAPOL message is getting 93262306a36Sopenharmony_ci * out before card disabled 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci if (param->value) { 93562306a36Sopenharmony_ci priv->tkip_cm_active = 1; 93662306a36Sopenharmony_ci ret = hermes_disable_port(hw, 0); 93762306a36Sopenharmony_ci } else { 93862306a36Sopenharmony_ci priv->tkip_cm_active = 0; 93962306a36Sopenharmony_ci ret = hermes_enable_port(hw, 0); 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: 94462306a36Sopenharmony_ci if (param->value & IW_AUTH_ALG_SHARED_KEY) 94562306a36Sopenharmony_ci priv->wep_restrict = 1; 94662306a36Sopenharmony_ci else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) 94762306a36Sopenharmony_ci priv->wep_restrict = 0; 94862306a36Sopenharmony_ci else 94962306a36Sopenharmony_ci ret = -EINVAL; 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 95362306a36Sopenharmony_ci if (priv->has_wpa) { 95462306a36Sopenharmony_ci priv->wpa_enabled = param->value ? 1 : 0; 95562306a36Sopenharmony_ci } else { 95662306a36Sopenharmony_ci if (param->value) 95762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 95862306a36Sopenharmony_ci /* else silently accept disable of WPA */ 95962306a36Sopenharmony_ci priv->wpa_enabled = 0; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci break; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci default: 96462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 96862306a36Sopenharmony_ci return ret; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic int orinoco_ioctl_get_auth(struct net_device *dev, 97262306a36Sopenharmony_ci struct iw_request_info *info, 97362306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 97662306a36Sopenharmony_ci struct iw_param *param = &wrqu->param; 97762306a36Sopenharmony_ci unsigned long flags; 97862306a36Sopenharmony_ci int ret = 0; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 98162306a36Sopenharmony_ci return -EBUSY; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 98462306a36Sopenharmony_ci case IW_AUTH_KEY_MGMT: 98562306a36Sopenharmony_ci param->value = priv->key_mgmt; 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci case IW_AUTH_TKIP_COUNTERMEASURES: 98962306a36Sopenharmony_ci param->value = priv->tkip_cm_active; 99062306a36Sopenharmony_ci break; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: 99362306a36Sopenharmony_ci if (priv->wep_restrict) 99462306a36Sopenharmony_ci param->value = IW_AUTH_ALG_SHARED_KEY; 99562306a36Sopenharmony_ci else 99662306a36Sopenharmony_ci param->value = IW_AUTH_ALG_OPEN_SYSTEM; 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 100062306a36Sopenharmony_ci param->value = priv->wpa_enabled; 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci default: 100462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 100862306a36Sopenharmony_ci return ret; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int orinoco_ioctl_set_genie(struct net_device *dev, 101262306a36Sopenharmony_ci struct iw_request_info *info, 101362306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 101662306a36Sopenharmony_ci u8 *buf; 101762306a36Sopenharmony_ci unsigned long flags; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* cut off at IEEE80211_MAX_DATA_LEN */ 102062306a36Sopenharmony_ci if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || 102162306a36Sopenharmony_ci (wrqu->data.length && (extra == NULL))) 102262306a36Sopenharmony_ci return -EINVAL; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (wrqu->data.length) { 102562306a36Sopenharmony_ci buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); 102662306a36Sopenharmony_ci if (buf == NULL) 102762306a36Sopenharmony_ci return -ENOMEM; 102862306a36Sopenharmony_ci } else 102962306a36Sopenharmony_ci buf = NULL; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) { 103262306a36Sopenharmony_ci kfree(buf); 103362306a36Sopenharmony_ci return -EBUSY; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci kfree(priv->wpa_ie); 103762306a36Sopenharmony_ci priv->wpa_ie = buf; 103862306a36Sopenharmony_ci priv->wpa_ie_len = wrqu->data.length; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (priv->wpa_ie) { 104162306a36Sopenharmony_ci /* Looks like wl_lkm wants to check the auth alg, and 104262306a36Sopenharmony_ci * somehow pass it to the firmware. 104362306a36Sopenharmony_ci * Instead it just calls the key mgmt rid 104462306a36Sopenharmony_ci * - we do this in set auth. 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 104962306a36Sopenharmony_ci return 0; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic int orinoco_ioctl_get_genie(struct net_device *dev, 105362306a36Sopenharmony_ci struct iw_request_info *info, 105462306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 105762306a36Sopenharmony_ci unsigned long flags; 105862306a36Sopenharmony_ci int err = 0; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 106162306a36Sopenharmony_ci return -EBUSY; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { 106462306a36Sopenharmony_ci wrqu->data.length = 0; 106562306a36Sopenharmony_ci goto out; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (wrqu->data.length < priv->wpa_ie_len) { 106962306a36Sopenharmony_ci err = -E2BIG; 107062306a36Sopenharmony_ci goto out; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci wrqu->data.length = priv->wpa_ie_len; 107462306a36Sopenharmony_ci memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ciout: 107762306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 107862306a36Sopenharmony_ci return err; 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic int orinoco_ioctl_set_mlme(struct net_device *dev, 108262306a36Sopenharmony_ci struct iw_request_info *info, 108362306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 108662306a36Sopenharmony_ci struct iw_mlme *mlme = (struct iw_mlme *)extra; 108762306a36Sopenharmony_ci unsigned long flags; 108862306a36Sopenharmony_ci int ret = 0; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 109162306a36Sopenharmony_ci return -EBUSY; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci switch (mlme->cmd) { 109462306a36Sopenharmony_ci case IW_MLME_DEAUTH: 109562306a36Sopenharmony_ci /* silently ignore */ 109662306a36Sopenharmony_ci break; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci case IW_MLME_DISASSOC: 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci ret = orinoco_hw_disassociate(priv, mlme->addr.sa_data, 110162306a36Sopenharmony_ci mlme->reason_code); 110262306a36Sopenharmony_ci break; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci default: 110562306a36Sopenharmony_ci ret = -EOPNOTSUPP; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 110962306a36Sopenharmony_ci return ret; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic int orinoco_ioctl_reset(struct net_device *dev, 111362306a36Sopenharmony_ci struct iw_request_info *info, 111462306a36Sopenharmony_ci union iwreq_data *wrqu, 111562306a36Sopenharmony_ci char *extra) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 112062306a36Sopenharmony_ci return -EPERM; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { 112362306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* Firmware reset */ 112662306a36Sopenharmony_ci orinoco_reset(&priv->reset_work); 112762306a36Sopenharmony_ci } else { 112862306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci schedule_work(&priv->reset_work); 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci return 0; 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic int orinoco_ioctl_setibssport(struct net_device *dev, 113762306a36Sopenharmony_ci struct iw_request_info *info, 113862306a36Sopenharmony_ci union iwreq_data *wrqu, 113962306a36Sopenharmony_ci char *extra) 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 114362306a36Sopenharmony_ci int val = *((int *) extra); 114462306a36Sopenharmony_ci unsigned long flags; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 114762306a36Sopenharmony_ci return -EBUSY; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci priv->ibss_port = val; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci /* Actually update the mode we are using */ 115262306a36Sopenharmony_ci set_port_type(priv); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 115562306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic int orinoco_ioctl_getibssport(struct net_device *dev, 115962306a36Sopenharmony_ci struct iw_request_info *info, 116062306a36Sopenharmony_ci union iwreq_data *wrqu, 116162306a36Sopenharmony_ci char *extra) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 116462306a36Sopenharmony_ci int *val = (int *) extra; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci *val = priv->ibss_port; 116762306a36Sopenharmony_ci return 0; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic int orinoco_ioctl_setport3(struct net_device *dev, 117162306a36Sopenharmony_ci struct iw_request_info *info, 117262306a36Sopenharmony_ci union iwreq_data *wrqu, 117362306a36Sopenharmony_ci char *extra) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 117662306a36Sopenharmony_ci int val = *((int *) extra); 117762306a36Sopenharmony_ci int err = 0; 117862306a36Sopenharmony_ci unsigned long flags; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 118162306a36Sopenharmony_ci return -EBUSY; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci switch (val) { 118462306a36Sopenharmony_ci case 0: /* Try to do IEEE ad-hoc mode */ 118562306a36Sopenharmony_ci if (!priv->has_ibss) { 118662306a36Sopenharmony_ci err = -EINVAL; 118762306a36Sopenharmony_ci break; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci priv->prefer_port3 = 0; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci break; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci case 1: /* Try to do Lucent proprietary ad-hoc mode */ 119462306a36Sopenharmony_ci if (!priv->has_port3) { 119562306a36Sopenharmony_ci err = -EINVAL; 119662306a36Sopenharmony_ci break; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci priv->prefer_port3 = 1; 119962306a36Sopenharmony_ci break; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci default: 120262306a36Sopenharmony_ci err = -EINVAL; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (!err) { 120662306a36Sopenharmony_ci /* Actually update the mode we are using */ 120762306a36Sopenharmony_ci set_port_type(priv); 120862306a36Sopenharmony_ci err = -EINPROGRESS; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return err; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int orinoco_ioctl_getport3(struct net_device *dev, 121762306a36Sopenharmony_ci struct iw_request_info *info, 121862306a36Sopenharmony_ci union iwreq_data *wrqu, 121962306a36Sopenharmony_ci char *extra) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 122262306a36Sopenharmony_ci int *val = (int *) extra; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci *val = priv->prefer_port3; 122562306a36Sopenharmony_ci return 0; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_cistatic int orinoco_ioctl_setpreamble(struct net_device *dev, 122962306a36Sopenharmony_ci struct iw_request_info *info, 123062306a36Sopenharmony_ci union iwreq_data *wrqu, 123162306a36Sopenharmony_ci char *extra) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 123462306a36Sopenharmony_ci unsigned long flags; 123562306a36Sopenharmony_ci int val; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (!priv->has_preamble) 123862306a36Sopenharmony_ci return -EOPNOTSUPP; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* 802.11b has recently defined some short preamble. 124162306a36Sopenharmony_ci * Basically, the Phy header has been reduced in size. 124262306a36Sopenharmony_ci * This increase performance, especially at high rates 124362306a36Sopenharmony_ci * (the preamble is transmitted at 1Mb/s), unfortunately 124462306a36Sopenharmony_ci * this give compatibility troubles... - Jean II */ 124562306a36Sopenharmony_ci val = *((int *) extra); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 124862306a36Sopenharmony_ci return -EBUSY; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (val) 125162306a36Sopenharmony_ci priv->preamble = 1; 125262306a36Sopenharmony_ci else 125362306a36Sopenharmony_ci priv->preamble = 0; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic int orinoco_ioctl_getpreamble(struct net_device *dev, 126162306a36Sopenharmony_ci struct iw_request_info *info, 126262306a36Sopenharmony_ci union iwreq_data *wrqu, 126362306a36Sopenharmony_ci char *extra) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 126662306a36Sopenharmony_ci int *val = (int *) extra; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (!priv->has_preamble) 126962306a36Sopenharmony_ci return -EOPNOTSUPP; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci *val = priv->preamble; 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci/* ioctl interface to hermes_read_ltv() 127662306a36Sopenharmony_ci * To use with iwpriv, pass the RID as the token argument, e.g. 127762306a36Sopenharmony_ci * iwpriv get_rid [0xfc00] 127862306a36Sopenharmony_ci * At least Wireless Tools 25 is required to use iwpriv. 127962306a36Sopenharmony_ci * For Wireless Tools 25 and 26 append "dummy" are the end. */ 128062306a36Sopenharmony_cistatic int orinoco_ioctl_getrid(struct net_device *dev, 128162306a36Sopenharmony_ci struct iw_request_info *info, 128262306a36Sopenharmony_ci union iwreq_data *wrqu, 128362306a36Sopenharmony_ci char *extra) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct iw_point *data = &wrqu->data; 128662306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 128762306a36Sopenharmony_ci struct hermes *hw = &priv->hw; 128862306a36Sopenharmony_ci int rid = data->flags; 128962306a36Sopenharmony_ci u16 length; 129062306a36Sopenharmony_ci int err; 129162306a36Sopenharmony_ci unsigned long flags; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* It's a "get" function, but we don't want users to access the 129462306a36Sopenharmony_ci * WEP key and other raw firmware data */ 129562306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 129662306a36Sopenharmony_ci return -EPERM; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (rid < 0xfc00 || rid > 0xffff) 129962306a36Sopenharmony_ci return -EINVAL; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 130262306a36Sopenharmony_ci return -EBUSY; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, 130562306a36Sopenharmony_ci extra); 130662306a36Sopenharmony_ci if (err) 130762306a36Sopenharmony_ci goto out; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), 131062306a36Sopenharmony_ci MAX_RID_LEN); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci out: 131362306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 131462306a36Sopenharmony_ci return err; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci/* Commit handler, called after set operations */ 131962306a36Sopenharmony_cistatic int orinoco_ioctl_commit(struct net_device *dev, 132062306a36Sopenharmony_ci struct iw_request_info *info, 132162306a36Sopenharmony_ci union iwreq_data *wrqu, 132262306a36Sopenharmony_ci char *extra) 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci struct orinoco_private *priv = ndev_priv(dev); 132562306a36Sopenharmony_ci unsigned long flags; 132662306a36Sopenharmony_ci int err = 0; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (!priv->open) 132962306a36Sopenharmony_ci return 0; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (orinoco_lock(priv, &flags) != 0) 133262306a36Sopenharmony_ci return err; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci err = orinoco_commit(priv); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci orinoco_unlock(priv, &flags); 133762306a36Sopenharmony_ci return err; 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic const struct iw_priv_args orinoco_privtab[] = { 134162306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, 134262306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, 134362306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 134462306a36Sopenharmony_ci 0, "set_port3" }, 134562306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 134662306a36Sopenharmony_ci "get_port3" }, 134762306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 134862306a36Sopenharmony_ci 0, "set_preamble" }, 134962306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 135062306a36Sopenharmony_ci "get_preamble" }, 135162306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 135262306a36Sopenharmony_ci 0, "set_ibssport" }, 135362306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 135462306a36Sopenharmony_ci "get_ibssport" }, 135562306a36Sopenharmony_ci { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, 135662306a36Sopenharmony_ci "get_rid" }, 135762306a36Sopenharmony_ci}; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/* 136162306a36Sopenharmony_ci * Structures to export the Wireless Handlers 136262306a36Sopenharmony_ci */ 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_cistatic const iw_handler orinoco_handler[] = { 136562306a36Sopenharmony_ci IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), 136662306a36Sopenharmony_ci IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), 136762306a36Sopenharmony_ci IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), 136862306a36Sopenharmony_ci IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), 136962306a36Sopenharmony_ci IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), 137062306a36Sopenharmony_ci IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), 137162306a36Sopenharmony_ci IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), 137262306a36Sopenharmony_ci IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), 137362306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), 137462306a36Sopenharmony_ci IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), 137562306a36Sopenharmony_ci IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), 137662306a36Sopenharmony_ci IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), 137762306a36Sopenharmony_ci IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), 137862306a36Sopenharmony_ci IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), 137962306a36Sopenharmony_ci IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), 138062306a36Sopenharmony_ci IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), 138162306a36Sopenharmony_ci IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), 138262306a36Sopenharmony_ci IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), 138362306a36Sopenharmony_ci IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), 138462306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), 138562306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), 138662306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRTS, cfg80211_wext_siwrts), 138762306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRTS, cfg80211_wext_giwrts), 138862306a36Sopenharmony_ci IW_HANDLER(SIOCSIWFRAG, cfg80211_wext_siwfrag), 138962306a36Sopenharmony_ci IW_HANDLER(SIOCGIWFRAG, cfg80211_wext_giwfrag), 139062306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRETRY, cfg80211_wext_giwretry), 139162306a36Sopenharmony_ci IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), 139262306a36Sopenharmony_ci IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), 139362306a36Sopenharmony_ci IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), 139462306a36Sopenharmony_ci IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), 139562306a36Sopenharmony_ci IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), 139662306a36Sopenharmony_ci IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), 139762306a36Sopenharmony_ci IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), 139862306a36Sopenharmony_ci IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), 139962306a36Sopenharmony_ci IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), 140062306a36Sopenharmony_ci IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), 140162306a36Sopenharmony_ci IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), 140262306a36Sopenharmony_ci}; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci/* 140662306a36Sopenharmony_ci Added typecasting since we no longer use iwreq_data -- Moustafa 140762306a36Sopenharmony_ci */ 140862306a36Sopenharmony_cistatic const iw_handler orinoco_private_handler[] = { 140962306a36Sopenharmony_ci [0] = orinoco_ioctl_reset, 141062306a36Sopenharmony_ci [1] = orinoco_ioctl_reset, 141162306a36Sopenharmony_ci [2] = orinoco_ioctl_setport3, 141262306a36Sopenharmony_ci [3] = orinoco_ioctl_getport3, 141362306a36Sopenharmony_ci [4] = orinoco_ioctl_setpreamble, 141462306a36Sopenharmony_ci [5] = orinoco_ioctl_getpreamble, 141562306a36Sopenharmony_ci [6] = orinoco_ioctl_setibssport, 141662306a36Sopenharmony_ci [7] = orinoco_ioctl_getibssport, 141762306a36Sopenharmony_ci [9] = orinoco_ioctl_getrid, 141862306a36Sopenharmony_ci}; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ciconst struct iw_handler_def orinoco_handler_def = { 142162306a36Sopenharmony_ci .num_standard = ARRAY_SIZE(orinoco_handler), 142262306a36Sopenharmony_ci .num_private = ARRAY_SIZE(orinoco_private_handler), 142362306a36Sopenharmony_ci .num_private_args = ARRAY_SIZE(orinoco_privtab), 142462306a36Sopenharmony_ci .standard = orinoco_handler, 142562306a36Sopenharmony_ci .private = orinoco_private_handler, 142662306a36Sopenharmony_ci .private_args = orinoco_privtab, 142762306a36Sopenharmony_ci .get_wireless_stats = orinoco_get_wireless_stats, 142862306a36Sopenharmony_ci}; 1429