162306a36Sopenharmony_ci/* cfg80211 support
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * See copyright notice in main.c
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/ieee80211.h>
662306a36Sopenharmony_ci#include <net/cfg80211.h>
762306a36Sopenharmony_ci#include "hw.h"
862306a36Sopenharmony_ci#include "main.h"
962306a36Sopenharmony_ci#include "orinoco.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "cfg.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* Supported bitrates. Must agree with hw.c */
1462306a36Sopenharmony_cistatic struct ieee80211_rate orinoco_rates[] = {
1562306a36Sopenharmony_ci	{ .bitrate = 10 },
1662306a36Sopenharmony_ci	{ .bitrate = 20 },
1762306a36Sopenharmony_ci	{ .bitrate = 55 },
1862306a36Sopenharmony_ci	{ .bitrate = 110 },
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Called after orinoco_private is allocated. */
2462306a36Sopenharmony_civoid orinoco_wiphy_init(struct wiphy *wiphy)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct orinoco_private *priv = wiphy_priv(wiphy);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	wiphy->privid = orinoco_wiphy_privid;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	set_wiphy_dev(wiphy, priv->dev);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Called after firmware is initialised */
3462306a36Sopenharmony_ciint orinoco_wiphy_register(struct wiphy *wiphy)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct orinoco_private *priv = wiphy_priv(wiphy);
3762306a36Sopenharmony_ci	int i, channels = 0;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
4062306a36Sopenharmony_ci		wiphy->max_scan_ssids = 1;
4162306a36Sopenharmony_ci	else
4262306a36Sopenharmony_ci		wiphy->max_scan_ssids = 0;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* TODO: should we set if we only have demo ad-hoc?
4762306a36Sopenharmony_ci	 *       (priv->has_port3)
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	if (priv->has_ibss)
5062306a36Sopenharmony_ci		wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!priv->broken_monitor || force_monitor)
5362306a36Sopenharmony_ci		wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	priv->band.bitrates = orinoco_rates;
5662306a36Sopenharmony_ci	priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* Only support channels allowed by the card EEPROM */
5962306a36Sopenharmony_ci	for (i = 0; i < NUM_CHANNELS; i++) {
6062306a36Sopenharmony_ci		if (priv->channel_mask & (1 << i)) {
6162306a36Sopenharmony_ci			priv->channels[i].center_freq =
6262306a36Sopenharmony_ci				ieee80211_channel_to_frequency(i + 1,
6362306a36Sopenharmony_ci							   NL80211_BAND_2GHZ);
6462306a36Sopenharmony_ci			channels++;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci	priv->band.channels = priv->channels;
6862306a36Sopenharmony_ci	priv->band.n_channels = channels;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
7162306a36Sopenharmony_ci	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	i = 0;
7462306a36Sopenharmony_ci	if (priv->has_wep) {
7562306a36Sopenharmony_ci		priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40;
7662306a36Sopenharmony_ci		i++;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		if (priv->has_big_wep) {
7962306a36Sopenharmony_ci			priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104;
8062306a36Sopenharmony_ci			i++;
8162306a36Sopenharmony_ci		}
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci	if (priv->has_wpa) {
8462306a36Sopenharmony_ci		priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP;
8562306a36Sopenharmony_ci		i++;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	wiphy->cipher_suites = priv->cipher_suites;
8862306a36Sopenharmony_ci	wiphy->n_cipher_suites = i;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	wiphy->rts_threshold = priv->rts_thresh;
9162306a36Sopenharmony_ci	if (!priv->has_mwo)
9262306a36Sopenharmony_ci		wiphy->frag_threshold = priv->frag_thresh + 1;
9362306a36Sopenharmony_ci	wiphy->retry_short = priv->short_retry_limit;
9462306a36Sopenharmony_ci	wiphy->retry_long = priv->long_retry_limit;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return wiphy_register(wiphy);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev,
10062306a36Sopenharmony_ci			      enum nl80211_iftype type,
10162306a36Sopenharmony_ci			      struct vif_params *params)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct orinoco_private *priv = wiphy_priv(wiphy);
10462306a36Sopenharmony_ci	int err = 0;
10562306a36Sopenharmony_ci	unsigned long lock;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (orinoco_lock(priv, &lock) != 0)
10862306a36Sopenharmony_ci		return -EBUSY;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	switch (type) {
11162306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
11262306a36Sopenharmony_ci		if (!priv->has_ibss && !priv->has_port3)
11362306a36Sopenharmony_ci			err = -EINVAL;
11462306a36Sopenharmony_ci		break;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
11762306a36Sopenharmony_ci		break;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
12062306a36Sopenharmony_ci		if (priv->broken_monitor && !force_monitor) {
12162306a36Sopenharmony_ci			wiphy_warn(wiphy,
12262306a36Sopenharmony_ci				   "Monitor mode support is buggy in this firmware, not enabling\n");
12362306a36Sopenharmony_ci			err = -EINVAL;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	default:
12862306a36Sopenharmony_ci		err = -EINVAL;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (!err) {
13262306a36Sopenharmony_ci		priv->iw_mode = type;
13362306a36Sopenharmony_ci		set_port_type(priv);
13462306a36Sopenharmony_ci		err = orinoco_commit(priv);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	orinoco_unlock(priv, &lock);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return err;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int orinoco_scan(struct wiphy *wiphy,
14362306a36Sopenharmony_ci			struct cfg80211_scan_request *request)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct orinoco_private *priv = wiphy_priv(wiphy);
14662306a36Sopenharmony_ci	int err;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (!request)
14962306a36Sopenharmony_ci		return -EINVAL;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (priv->scan_request && priv->scan_request != request)
15262306a36Sopenharmony_ci		return -EBUSY;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	priv->scan_request = request;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	err = orinoco_hw_trigger_scan(priv, request->ssids);
15762306a36Sopenharmony_ci	/* On error the we aren't processing the request */
15862306a36Sopenharmony_ci	if (err)
15962306a36Sopenharmony_ci		priv->scan_request = NULL;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return err;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int orinoco_set_monitor_channel(struct wiphy *wiphy,
16562306a36Sopenharmony_ci				       struct cfg80211_chan_def *chandef)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct orinoco_private *priv = wiphy_priv(wiphy);
16862306a36Sopenharmony_ci	int err = 0;
16962306a36Sopenharmony_ci	unsigned long flags;
17062306a36Sopenharmony_ci	int channel;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (!chandef->chan)
17362306a36Sopenharmony_ci		return -EINVAL;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT)
17662306a36Sopenharmony_ci		return -EINVAL;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (chandef->chan->band != NL80211_BAND_2GHZ)
17962306a36Sopenharmony_ci		return -EINVAL;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	channel = ieee80211_frequency_to_channel(chandef->chan->center_freq);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if ((channel < 1) || (channel > NUM_CHANNELS) ||
18462306a36Sopenharmony_ci	     !(priv->channel_mask & (1 << (channel - 1))))
18562306a36Sopenharmony_ci		return -EINVAL;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
18862306a36Sopenharmony_ci		return -EBUSY;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	priv->channel = channel;
19162306a36Sopenharmony_ci	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
19262306a36Sopenharmony_ci		/* Fast channel change - no commit if successful */
19362306a36Sopenharmony_ci		struct hermes *hw = &priv->hw;
19462306a36Sopenharmony_ci		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
19562306a36Sopenharmony_ci					    HERMES_TEST_SET_CHANNEL,
19662306a36Sopenharmony_ci					channel, NULL);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return err;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct orinoco_private *priv = wiphy_priv(wiphy);
20662306a36Sopenharmony_ci	int frag_value = -1;
20762306a36Sopenharmony_ci	int rts_value = -1;
20862306a36Sopenharmony_ci	int err = 0;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_SHORT) {
21162306a36Sopenharmony_ci		/* Setting short retry not supported */
21262306a36Sopenharmony_ci		err = -EINVAL;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_LONG) {
21662306a36Sopenharmony_ci		/* Setting long retry not supported */
21762306a36Sopenharmony_ci		err = -EINVAL;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
22162306a36Sopenharmony_ci		/* Set fragmentation */
22262306a36Sopenharmony_ci		if (priv->has_mwo) {
22362306a36Sopenharmony_ci			if (wiphy->frag_threshold == -1)
22462306a36Sopenharmony_ci				frag_value = 0;
22562306a36Sopenharmony_ci			else {
22662306a36Sopenharmony_ci				printk(KERN_WARNING "%s: Fixed fragmentation "
22762306a36Sopenharmony_ci				       "is not supported on this firmware. "
22862306a36Sopenharmony_ci				       "Using MWO robust instead.\n",
22962306a36Sopenharmony_ci				       priv->ndev->name);
23062306a36Sopenharmony_ci				frag_value = 1;
23162306a36Sopenharmony_ci			}
23262306a36Sopenharmony_ci		} else {
23362306a36Sopenharmony_ci			if (wiphy->frag_threshold == -1)
23462306a36Sopenharmony_ci				frag_value = 2346;
23562306a36Sopenharmony_ci			else if ((wiphy->frag_threshold < 257) ||
23662306a36Sopenharmony_ci				 (wiphy->frag_threshold > 2347))
23762306a36Sopenharmony_ci				err = -EINVAL;
23862306a36Sopenharmony_ci			else
23962306a36Sopenharmony_ci				/* cfg80211 value is 257-2347 (odd only)
24062306a36Sopenharmony_ci				 * orinoco rid has range 256-2346 (even only) */
24162306a36Sopenharmony_ci				frag_value = wiphy->frag_threshold & ~0x1;
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
24662306a36Sopenharmony_ci		/* Set RTS.
24762306a36Sopenharmony_ci		 *
24862306a36Sopenharmony_ci		 * Prism documentation suggests default of 2432,
24962306a36Sopenharmony_ci		 * and a range of 0-3000.
25062306a36Sopenharmony_ci		 *
25162306a36Sopenharmony_ci		 * Current implementation uses 2347 as the default and
25262306a36Sopenharmony_ci		 * the upper limit.
25362306a36Sopenharmony_ci		 */
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		if (wiphy->rts_threshold == -1)
25662306a36Sopenharmony_ci			rts_value = 2347;
25762306a36Sopenharmony_ci		else if (wiphy->rts_threshold > 2347)
25862306a36Sopenharmony_ci			err = -EINVAL;
25962306a36Sopenharmony_ci		else
26062306a36Sopenharmony_ci			rts_value = wiphy->rts_threshold;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (!err) {
26462306a36Sopenharmony_ci		unsigned long flags;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		if (orinoco_lock(priv, &flags) != 0)
26762306a36Sopenharmony_ci			return -EBUSY;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		if (frag_value >= 0) {
27062306a36Sopenharmony_ci			if (priv->has_mwo)
27162306a36Sopenharmony_ci				priv->mwo_robust = frag_value;
27262306a36Sopenharmony_ci			else
27362306a36Sopenharmony_ci				priv->frag_thresh = frag_value;
27462306a36Sopenharmony_ci		}
27562306a36Sopenharmony_ci		if (rts_value >= 0)
27662306a36Sopenharmony_ci			priv->rts_thresh = rts_value;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		err = orinoco_commit(priv);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		orinoco_unlock(priv, &flags);
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return err;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ciconst struct cfg80211_ops orinoco_cfg_ops = {
28762306a36Sopenharmony_ci	.change_virtual_intf = orinoco_change_vif,
28862306a36Sopenharmony_ci	.set_monitor_channel = orinoco_set_monitor_channel,
28962306a36Sopenharmony_ci	.scan = orinoco_scan,
29062306a36Sopenharmony_ci	.set_wiphy_params = orinoco_set_wiphy_params,
29162306a36Sopenharmony_ci};
292