Example #1
0
/*
 * CFG802.11 operation handler for association request.
 *
 * This function does not work when the current mode is set to Ad-Hoc, or
 * when there is already an association procedure going on. The given BSS
 * information is used to associate.
 */
static int
mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
			 struct cfg80211_connect_params *sme)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	int ret = 0;

	if (priv->assoc_request)
		return -EBUSY;

	if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
		wiphy_err(wiphy, "received infra assoc request "
				"when station is in ibss mode\n");
		goto done;
	}

	priv->assoc_request = -EINPROGRESS;

	wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n",
	       (char *) sme->ssid, sme->bssid);

	ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
				     priv->bss_mode, sme->channel, sme, 0);

	priv->assoc_request = 1;
done:
	priv->assoc_result = ret;
	queue_work(priv->workqueue, &priv->cfg_workqueue);
	return ret;
}
Example #2
0
/*
 * CFG802.11 operation handler to join an IBSS.
 *
 * This function does not work in any mode other than Ad-Hoc, or if
 * a join operation is already in progress.
 */
static int
mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
			   struct cfg80211_ibss_params *params)
{
	struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
	int ret = 0;

	if (priv->ibss_join_request)
		return -EBUSY;

	if (priv->bss_mode != NL80211_IFTYPE_ADHOC) {
		wiphy_err(wiphy, "request to join ibss received "
				"when station is not in ibss mode\n");
		goto done;
	}

	priv->ibss_join_request = -EINPROGRESS;

	wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n",
	       (char *) params->ssid, params->bssid);

	ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid,
				params->bssid, priv->bss_mode,
				params->channel, NULL, params->privacy);

	priv->ibss_join_request = 1;
done:
	priv->ibss_join_result = ret;
	queue_work(priv->workqueue, &priv->cfg_workqueue);
	return ret;
}
Example #3
0
/*
 * CFG802.11 operation handler for association request.
 *
 * This function does not work when the current mode is set to Ad-Hoc, or
 * when there is already an association procedure going on. The given BSS
 * information is used to associate.
 */
static int
mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
			 struct cfg80211_connect_params *sme)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	int ret = 0;

	if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
		wiphy_err(wiphy, "received infra assoc request "
				"when station is in ibss mode\n");
		goto done;
	}

	wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n",
		  (char *) sme->ssid, sme->bssid);

	ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
				     priv->bss_mode, sme->channel, sme, 0);
done:
	if (!ret) {
		cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0,
					NULL, 0, WLAN_STATUS_SUCCESS,
					GFP_KERNEL);
		dev_dbg(priv->adapter->dev,
			"info: associated to bssid %pM successfully\n",
			priv->cfg_bssid);
	} else {
		dev_dbg(priv->adapter->dev,
			"info: association to bssid %pM failed\n",
			priv->cfg_bssid);
		memset(priv->cfg_bssid, 0, ETH_ALEN);
	}

	return ret;
}
Example #4
0
/*
 * CFG802.11 operation handler to join an IBSS.
 *
 * This function does not work in any mode other than Ad-Hoc, or if
 * a join operation is already in progress.
 */
static int
mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
			   struct cfg80211_ibss_params *params)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	int ret = 0;

	if (priv->bss_mode != NL80211_IFTYPE_ADHOC) {
		wiphy_err(wiphy, "request to join ibss received "
				"when station is not in ibss mode\n");
		goto done;
	}

	wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n",
		  (char *) params->ssid, params->bssid);

	ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid,
				     params->bssid, priv->bss_mode,
				     params->channel, NULL, params->privacy);
done:
	if (!ret) {
		cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL);
		dev_dbg(priv->adapter->dev,
			"info: joined/created adhoc network with bssid"
			" %pM successfully\n", priv->cfg_bssid);
	} else {
		dev_dbg(priv->adapter->dev,
			"info: failed creating/joining adhoc network\n");
	}

	return ret;
}
Example #5
0
static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
{
	int ret, i;
	int tmo = 2000;

	switch (priv->join_status) {
	case CW1200_JOIN_STATUS_PRE_STA:
	case CW1200_JOIN_STATUS_JOINING:
		return -EBUSY;
	default:
		break;
	}

	wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
		  scan->type, scan->num_channels, scan->flags);

	for (i = 0; i < scan->num_channels; ++i)
		tmo += scan->ch[i].max_chan_time + 10;

	cancel_delayed_work_sync(&priv->clear_recent_scan_work);
	atomic_set(&priv->scan.in_progress, 1);
	atomic_set(&priv->recent_scan, 1);
	cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo));
	queue_delayed_work(priv->workqueue, &priv->scan.timeout,
			   msecs_to_jiffies(tmo));
	ret = wsm_scan(priv, scan);
	if (ret) {
		atomic_set(&priv->scan.in_progress, 0);
		cancel_delayed_work_sync(&priv->scan.timeout);
		cw1200_scan_restart_delayed(priv);
	}
	return ret;
}
static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
{
	if (priv->delayed_link_loss) {
		int tmo = priv->cqm_beacon_loss_count;

		if (priv->scan.direct_probe)
			tmo = 0;

		priv->delayed_link_loss = 0;
		/* Restart beacon loss timer and requeue
		   BSS loss work. */
		wiphy_dbg(priv->hw->wiphy,
				"[CQM] Requeue BSS loss in %d "
				"beacons.\n", tmo);
		spin_lock(&priv->bss_loss_lock);
		priv->bss_loss_status = CW1200_BSS_LOSS_NONE;
		spin_unlock(&priv->bss_loss_lock);
		cancel_delayed_work_sync(&priv->bss_loss_work);
		queue_delayed_work(priv->workqueue,
				&priv->bss_loss_work,
				tmo * HZ / 10);
	}

	/* FW bug: driver has to restart p2p-dev mode after scan. */
	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
		cw1200_enable_listening(priv);
		cw1200_update_filtering(priv);
	}

	if (priv->delayed_unjoin) {
		priv->delayed_unjoin = false;
		if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
			wsm_unlock_tx(priv);
	}
}
static void
brcms_ops_configure_filter(struct ieee80211_hw *hw,
			unsigned int changed_flags,
			unsigned int *total_flags, u64 multicast)
{
	struct brcms_info *wl = hw->priv;
	struct wiphy *wiphy = hw->wiphy;

	changed_flags &= MAC_FILTERS;
	*total_flags &= MAC_FILTERS;

	if (changed_flags & FIF_PROMISC_IN_BSS)
		wiphy_dbg(wiphy, "FIF_PROMISC_IN_BSS\n");
	if (changed_flags & FIF_ALLMULTI)
		wiphy_dbg(wiphy, "FIF_ALLMULTI\n");
	if (changed_flags & FIF_FCSFAIL)
		wiphy_dbg(wiphy, "FIF_FCSFAIL\n");
	if (changed_flags & FIF_CONTROL)
		wiphy_dbg(wiphy, "FIF_CONTROL\n");
	if (changed_flags & FIF_OTHER_BSS)
		wiphy_dbg(wiphy, "FIF_OTHER_BSS\n");
	if (changed_flags & FIF_PSPOLL)
		wiphy_dbg(wiphy, "FIF_PSPOLL\n");
	if (changed_flags & FIF_BCN_PRBRESP_PROMISC)
		wiphy_dbg(wiphy, "FIF_BCN_PRBRESP_PROMISC\n");

	spin_lock_bh(&wl->lock);
	brcms_c_mac_promisc(wl->wlc, *total_flags);
	spin_unlock_bh(&wl->lock);
	return;
}
Example #8
0
/*
 * CFG802.11 operation handler to change interface type.
 */
static int
mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
				     struct net_device *dev,
				     enum nl80211_iftype type, u32 *flags,
				     struct vif_params *params)
{
	int ret;
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);

	if (priv->bss_mode == type) {
		wiphy_warn(wiphy, "already set to required type\n");
		return 0;
	}

	priv->bss_mode = type;

	switch (type) {
	case NL80211_IFTYPE_ADHOC:
		dev->ieee80211_ptr->iftype = NL80211_IFTYPE_ADHOC;
		wiphy_dbg(wiphy, "info: setting interface type to adhoc\n");
		break;
	case NL80211_IFTYPE_STATION:
		dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
		wiphy_dbg(wiphy, "info: setting interface type to managed\n");
		break;
	case NL80211_IFTYPE_UNSPECIFIED:
		dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
		wiphy_dbg(wiphy, "info: setting interface type to auto\n");
		return 0;
	default:
		wiphy_err(wiphy, "unknown interface type: %d\n", type);
		return -EINVAL;
	}

	mwifiex_deauthenticate(priv, NULL);

	priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;

	ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE,
				    HostCmd_ACT_GEN_SET, 0, NULL);

	return ret;
}
Example #9
0
static int mwifiex_process_country_ie(struct mwifiex_private *priv,
				      struct cfg80211_bss *bss)
{
	const u8 *country_ie;
	u8 country_ie_len;
	struct mwifiex_802_11d_domain_reg *domain_info =
					&priv->adapter->domain_reg;

	rcu_read_lock();
	country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
	if (!country_ie) {
		rcu_read_unlock();
		return 0;
	}

	country_ie_len = country_ie[1];
	if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
		rcu_read_unlock();
		return 0;
	}

	if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
		rcu_read_unlock();
		wiphy_dbg(priv->wdev->wiphy,
			  "11D: skip setting domain info in FW\n");
		return 0;
	}
	memcpy(priv->adapter->country_code, &country_ie[2], 2);

	domain_info->country_code[0] = country_ie[2];
	domain_info->country_code[1] = country_ie[3];
	domain_info->country_code[2] = ' ';

	country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;

	domain_info->no_of_triplet =
		country_ie_len / sizeof(struct ieee80211_country_ie_triplet);

	memcpy((u8 *)domain_info->triplet,
	       &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);

	rcu_read_unlock();

	if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
			     HostCmd_ACT_GEN_SET, 0, NULL, false)) {
		wiphy_err(priv->adapter->wiphy,
			  "11D: setting domain info in FW\n");
		return -1;
	}

	mwifiex_dnld_txpwr_table(priv);

	return 0;
}
static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed)
{
	struct ieee80211_conf *conf = &hw->conf;
	struct brcms_info *wl = hw->priv;
	int err = 0;
	int new_int;
	struct wiphy *wiphy = hw->wiphy;

	spin_lock_bh(&wl->lock);
	if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
		brcms_c_set_beacon_listen_interval(wl->wlc,
						   conf->listen_interval);
	}
	if (changed & IEEE80211_CONF_CHANGE_MONITOR)
		wiphy_dbg(wiphy, "%s: change monitor mode: %s\n",
			  __func__, conf->flags & IEEE80211_CONF_MONITOR ?
			  "true" : "false");
	if (changed & IEEE80211_CONF_CHANGE_PS)
		wiphy_err(wiphy, "%s: change power-save mode: %s (implement)\n",
			  __func__, conf->flags & IEEE80211_CONF_PS ?
			  "true" : "false");

	if (changed & IEEE80211_CONF_CHANGE_POWER) {
		err = brcms_c_set_tx_power(wl->wlc, conf->power_level);
		if (err < 0) {
			wiphy_err(wiphy, "%s: Error setting power_level\n",
				  __func__);
			goto config_out;
		}
		new_int = brcms_c_get_tx_power(wl->wlc);
		if (new_int != conf->power_level)
			wiphy_err(wiphy, "%s: Power level req != actual, %d %d"
				  "\n", __func__, conf->power_level,
				  new_int);
	}
	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
		if (conf->channel_type == NL80211_CHAN_HT20 ||
		    conf->channel_type == NL80211_CHAN_NO_HT)
			err = brcms_c_set_channel(wl->wlc,
						  conf->channel->hw_value);
		else
			err = -ENOTSUPP;
	}
	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
		err = brcms_c_set_rate_limit(wl->wlc,
					     conf->short_frame_max_tx_count,
					     conf->long_frame_max_tx_count);

 config_out:
	spin_unlock_bh(&wl->lock);
	return err;
}
Example #11
0
static void cw1200_scan_complete(struct cw1200_common *priv)
{
	queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
	if (priv->scan.direct_probe) {
		wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
		cw1200_scan_restart_delayed(priv);
		priv->scan.direct_probe = 0;
		up(&priv->scan.lock);
		wsm_unlock_tx(priv);
	} else {
		cw1200_scan_work(&priv->scan.work);
	}
}
Example #12
0
/*
 * This function sets the RF channel.
 *
 * This function creates multiple IOCTL requests, populates them accordingly
 * and issues them to set the band/channel and frequency.
 */
static int
mwifiex_set_rf_channel(struct mwifiex_private *priv,
		       struct ieee80211_channel *chan,
		       enum nl80211_channel_type channel_type)
{
	struct mwifiex_chan_freq_power cfp;
	struct mwifiex_ds_band_cfg band_cfg;
	u32 config_bands = 0;
	struct wiphy *wiphy = priv->wdev->wiphy;

	if (chan) {
		memset(&band_cfg, 0, sizeof(band_cfg));
		/* Set appropriate bands */
		if (chan->band == IEEE80211_BAND_2GHZ)
			config_bands = BAND_B | BAND_G | BAND_GN;
		else
			config_bands = BAND_AN | BAND_A;
		if (priv->bss_mode == NL80211_IFTYPE_STATION
		    || priv->bss_mode == NL80211_IFTYPE_UNSPECIFIED) {
			band_cfg.config_bands = config_bands;
		} else if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
			band_cfg.config_bands = config_bands;
			band_cfg.adhoc_start_band = config_bands;
		}

		band_cfg.sec_chan_offset =
			mwifiex_cfg80211_channel_type_to_mwifiex_channels
			(channel_type);

		if (mwifiex_set_radio_band_cfg(priv, &band_cfg))
			return -EFAULT;

		mwifiex_send_domain_info_cmd_fw(wiphy);
	}

	wiphy_dbg(wiphy, "info: setting band %d, channel offset %d and "
		"mode %d\n", config_bands, band_cfg.sec_chan_offset,
		priv->bss_mode);
	if (!chan)
		return 0;

	memset(&cfp, 0, sizeof(cfp));
	cfp.freq = chan->center_freq;
	cfp.channel = ieee80211_frequency_to_channel(chan->center_freq);

	if (mwifiex_bss_set_channel(priv, &cfp))
		return -EFAULT;

	return mwifiex_drv_change_adhoc_chan(priv, cfp.channel);
}
Example #13
0
/*
 * CFG802.11 operation handler to leave an IBSS.
 *
 * This function does not work if a leave operation is
 * already in progress.
 */
static int
mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);

	wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n",
		  priv->cfg_bssid);
	if (mwifiex_deauthenticate(priv, NULL))
		return -EFAULT;

	memset(priv->cfg_bssid, 0, ETH_ALEN);

	return 0;
}
Example #14
0
/*
 * CFG802.11 operation handler to delete a network key.
 */
static int
mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
			 u8 key_index, bool pairwise, const u8 *mac_addr)
{
	struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);

	if (mwifiex_set_encode(priv, NULL, 0, key_index, 1)) {
		wiphy_err(wiphy, "deleting the crypto keys\n");
		return -EFAULT;
	}

	wiphy_dbg(wiphy, "info: crypto keys deleted\n");
	return 0;
}
Example #15
0
void __wiphy_dbg(struct wiphy *wiphy, bool print, const char *fmt, ...)
{
    struct va_format vaf = {
        .fmt = fmt,
    };
    va_list args;

    va_start(args, fmt);
    vaf.va = &args;

    if (print)
        wiphy_dbg(wiphy, "%pV", &vaf);
    trace_mac80211_dbg(&vaf);
    va_end(args);
}
Example #16
0
/*
 * CFG802.11 operation handler for disconnection request.
 *
 * This function does not work when there is already a disconnection
 * procedure going on.
 */
static int
mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
			    u16 reason_code)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);

	if (mwifiex_deauthenticate(priv, NULL))
		return -EFAULT;

	wiphy_dbg(wiphy, "info: successfully disconnected from %pM:"
		" reason code %d\n", priv->cfg_bssid, reason_code);

	memset(priv->cfg_bssid, 0, ETH_ALEN);

	return 0;
}
Example #17
0
/*
 * CFG802.11 operation handler for scan request.
 *
 * This function issues a scan request to the firmware based upon
 * the user specified scan configuration. On successfull completion,
 * it also informs the results.
 */
static int
mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
		      struct cfg80211_scan_request *request)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);

	wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);

	if (priv->scan_request && priv->scan_request != request)
		return -EBUSY;

	priv->scan_request = request;

	queue_work(priv->workqueue, &priv->cfg_workqueue);
	return 0;
}
Example #18
0
/*
 * CFG802.11 operation handler to set Power Save option.
 *
 * The timeout value, if provided, is currently ignored.
 */
static int
mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy,
				struct net_device *dev,
				bool enabled, int timeout)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	u32 ps_mode;

	if (timeout)
		wiphy_dbg(wiphy,
			  "info: ignore timeout value for IEEE Power Save\n");

	ps_mode = enabled;

	return mwifiex_drv_set_power(priv, &ps_mode);
}
Example #19
0
static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
{
	/* FW bug: driver has to restart p2p-dev mode after scan. */
	if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
		cw1200_enable_listening(priv);
		cw1200_update_filtering(priv);
	}

	if (priv->delayed_unjoin) {
		priv->delayed_unjoin = false;
		if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
			wsm_unlock_tx(priv);
	} else if (priv->delayed_link_loss) {
			wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
			priv->delayed_link_loss = 0;
			cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
	}
}
Example #20
0
/*
 * CFG802.11 operation handler to leave an IBSS.
 *
 * This function does not work if a leave operation is
 * already in progress.
 */
static int
mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
{
	struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);

	if (priv->disconnect)
		return -EBUSY;

	priv->disconnect = 1;

	wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n",
			priv->cfg_bssid);
	if (mwifiex_deauthenticate(priv, NULL))
		return -EFAULT;

	queue_work(priv->workqueue, &priv->cfg_workqueue);

	return 0;
}
Example #21
0
/*
 * CFG802.11 operation handler for disconnection request.
 *
 * This function does not work when there is already a disconnection
 * procedure going on.
 */
static int
mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
			    u16 reason_code)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);

	if (priv->disconnect)
		return -EBUSY;

	priv->disconnect = 1;
	if (mwifiex_deauthenticate(priv, NULL))
		return -EFAULT;

	wiphy_dbg(wiphy, "info: successfully disconnected from %pM:"
		" reason code %d\n", priv->cfg_bssid, reason_code);

	queue_work(priv->workqueue, &priv->cfg_workqueue);

	return 0;
}
Example #22
0
/*
 * CFG802.11 operation handler for scan request.
 *
 * This function issues a scan request to the firmware based upon
 * the user specified scan configuration. On successfull completion,
 * it also informs the results.
 */
static int
mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
		      struct cfg80211_scan_request *request)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	int i;
	struct ieee80211_channel *chan;

	wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);

	priv->scan_request = request;

	priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
				      GFP_KERNEL);
	if (!priv->user_scan_cfg) {
		dev_err(priv->adapter->dev, "failed to alloc scan_req\n");
		return -ENOMEM;
	}

	priv->user_scan_cfg->num_ssids = request->n_ssids;
	priv->user_scan_cfg->ssid_list = request->ssids;

	for (i = 0; i < request->n_channels; i++) {
		chan = request->channels[i];
		priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
		priv->user_scan_cfg->chan_list[i].radio_type = chan->band;

		if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
			priv->user_scan_cfg->chan_list[i].scan_type =
						MWIFIEX_SCAN_TYPE_PASSIVE;
		else
			priv->user_scan_cfg->chan_list[i].scan_type =
						MWIFIEX_SCAN_TYPE_ACTIVE;

		priv->user_scan_cfg->chan_list[i].scan_time = 0;
	}
	if (mwifiex_set_user_scan_ioctl(priv, priv->user_scan_cfg))
		return -EFAULT;

	return 0;
}
Example #23
0
/*
 * CFG802.11 regulatory domain callback function.
 *
 * This function is called when the regulatory domain is changed due to the
 * following reasons -
 *      - Set by driver
 *      - Set by system core
 *      - Set by user
 *      - Set bt Country IE
 */
static int mwifiex_reg_notifier(struct wiphy *wiphy,
				struct regulatory_request *request)
{
	struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);

	wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for %c%c\n",
		  request->alpha2[0], request->alpha2[1]);

	memcpy(adapter->country_code, request->alpha2, sizeof(request->alpha2));

	switch (request->initiator) {
	case NL80211_REGDOM_SET_BY_DRIVER:
	case NL80211_REGDOM_SET_BY_CORE:
	case NL80211_REGDOM_SET_BY_USER:
		break;
		/* Todo: apply driver specific changes in channel flags based
		   on the request initiator if necessary. */
	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
		break;
	}
	mwifiex_send_domain_info_cmd_fw(wiphy);

	return 0;
}
Example #24
0
void cw1200_scan_work(struct work_struct *work)
{
	struct cw1200_common *priv = container_of(work, struct cw1200_common,
							scan.work);
	struct ieee80211_channel **it;
	struct wsm_scan scan = {
		.type = WSM_SCAN_TYPE_FOREGROUND,
		.flags = WSM_SCAN_FLAG_SPLIT_METHOD,
	};
	bool first_run = (priv->scan.begin == priv->scan.curr &&
			  priv->scan.begin != priv->scan.end);
	int i;

	if (first_run) {
		/* Firmware gets crazy if scan request is sent
		 * when STA is joined but not yet associated.
		 * Force unjoin in this case.
		 */
		if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
			cw1200_join_timeout(&priv->join_timeout.work);
	}

	mutex_lock(&priv->conf_mutex);

	if (first_run) {
		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
		    !(priv->powersave_mode.mode & WSM_PSM_PS)) {
			struct wsm_set_pm pm = priv->powersave_mode;
			pm.mode = WSM_PSM_PS;
			cw1200_set_pm(priv, &pm);
		} else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
			/* FW bug: driver has to restart p2p-dev mode
			 * after scan
			 */
			cw1200_disable_listening(priv);
		}
	}

	if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
		struct cfg80211_scan_info info = {
			.aborted = priv->scan.status ? 1 : 0,
		};

		if (priv->scan.output_power != priv->output_power)
			wsm_set_output_power(priv, priv->output_power * 10);
		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
		    !(priv->powersave_mode.mode & WSM_PSM_PS))
			cw1200_set_pm(priv, &priv->powersave_mode);

		if (priv->scan.status < 0)
			wiphy_warn(priv->hw->wiphy,
				   "[SCAN] Scan failed (%d).\n",
				   priv->scan.status);
		else if (priv->scan.req)
			wiphy_dbg(priv->hw->wiphy,
				  "[SCAN] Scan completed.\n");
		else
			wiphy_dbg(priv->hw->wiphy,
				  "[SCAN] Scan canceled.\n");

		priv->scan.req = NULL;
		cw1200_scan_restart_delayed(priv);
		wsm_unlock_tx(priv);
		mutex_unlock(&priv->conf_mutex);
		ieee80211_scan_completed(priv->hw, &info);
		up(&priv->scan.lock);
		return;
	} else {
		struct ieee80211_channel *first = *priv->scan.curr;
		for (it = priv->scan.curr + 1, i = 1;
		     it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
		     ++it, ++i) {
			if ((*it)->band != first->band)
				break;
			if (((*it)->flags ^ first->flags) &
					IEEE80211_CHAN_NO_IR)
				break;
			if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
			    (*it)->max_power != first->max_power)
				break;
		}
		scan.band = first->band;

		if (priv->scan.req->no_cck)
			scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
		else
			scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
		scan.num_probes =
			(first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
		scan.num_ssids = priv->scan.n_ssids;
		scan.ssids = &priv->scan.ssids[0];
		scan.num_channels = it - priv->scan.curr;
		/* TODO: Is it optimal? */
		scan.probe_delay = 100;
		/* It is not stated in WSM specification, however
		 * FW team says that driver may not use FG scan
		 * when joined.
		 */
		if (priv->join_status == CW1200_JOIN_STATUS_STA) {
			scan.type = WSM_SCAN_TYPE_BACKGROUND;
			scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
		}
		scan.ch = kcalloc(it - priv->scan.curr,
				  sizeof(struct wsm_scan_ch),
				  GFP_KERNEL);
		if (!scan.ch) {
			priv->scan.status = -ENOMEM;
			goto fail;
		}
		for (i = 0; i < scan.num_channels; ++i) {
			scan.ch[i].number = priv->scan.curr[i]->hw_value;
			if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
				scan.ch[i].min_chan_time = 50;
				scan.ch[i].max_chan_time = 100;
			} else {
				scan.ch[i].min_chan_time = 10;
				scan.ch[i].max_chan_time = 25;
			}
		}
		if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
		    priv->scan.output_power != first->max_power) {
			priv->scan.output_power = first->max_power;
			wsm_set_output_power(priv,
					     priv->scan.output_power * 10);
		}
		priv->scan.status = cw1200_scan_start(priv, &scan);
		kfree(scan.ch);
		if (priv->scan.status)
			goto fail;
		priv->scan.curr = it;
	}
	mutex_unlock(&priv->conf_mutex);
	return;

fail:
	priv->scan.curr = priv->scan.end;
	mutex_unlock(&priv->conf_mutex);
	queue_work(priv->workqueue, &priv->scan.work);
	return;
}
Example #25
0
int cw1200_hw_scan(struct ieee80211_hw *hw,
		   struct ieee80211_vif *vif,
		   struct ieee80211_scan_request *hw_req)
{
	struct cw1200_common *priv = hw->priv;
	struct cfg80211_scan_request *req = &hw_req->req;
	struct wsm_template_frame frame = {
		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
	};
	int i, ret;

	if (!priv->vif)
		return -EINVAL;

	/* Scan when P2P_GO corrupt firmware MiniAP mode */
	if (priv->join_status == CW1200_JOIN_STATUS_AP)
		return -EOPNOTSUPP;

	if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
		req->n_ssids = 0;

	wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
		  req->n_ssids);

	if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
		return -EINVAL;

	/* will be unlocked in cw1200_scan_work() */
	down(&priv->scan.lock);
	mutex_lock(&priv->conf_mutex);

	frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
		req->ie_len);
	if (!frame.skb) {
		mutex_unlock(&priv->conf_mutex);
		up(&priv->scan.lock);
		return -ENOMEM;
	}

	if (req->ie_len)
		skb_put_data(frame.skb, req->ie, req->ie_len);

	ret = wsm_set_template_frame(priv, &frame);
	if (!ret) {
		/* Host want to be the probe responder. */
		ret = wsm_set_probe_responder(priv, true);
	}
	if (ret) {
		dev_kfree_skb(frame.skb);
		mutex_unlock(&priv->conf_mutex);
		up(&priv->scan.lock);
		return ret;
	}

	wsm_lock_tx(priv);

	BUG_ON(priv->scan.req);
	priv->scan.req = req;
	priv->scan.n_ssids = 0;
	priv->scan.status = 0;
	priv->scan.begin = &req->channels[0];
	priv->scan.curr = priv->scan.begin;
	priv->scan.end = &req->channels[req->n_channels];
	priv->scan.output_power = priv->output_power;

	for (i = 0; i < req->n_ssids; ++i) {
		struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
		memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
		dst->length = req->ssids[i].ssid_len;
		++priv->scan.n_ssids;
	}

	if (frame.skb)
		dev_kfree_skb(frame.skb);
	mutex_unlock(&priv->conf_mutex);
	queue_work(priv->workqueue, &priv->scan.work);
	return 0;
}
Example #26
0
/*
 * This function sets the RF channel.
 *
 * This function creates multiple IOCTL requests, populates them accordingly
 * and issues them to set the band/channel and frequency.
 */
static int
mwifiex_set_rf_channel(struct mwifiex_private *priv,
		       struct ieee80211_channel *chan,
		       enum nl80211_channel_type channel_type)
{
	struct mwifiex_chan_freq_power cfp;
	u32 config_bands = 0;
	struct wiphy *wiphy = priv->wdev->wiphy;
	struct mwifiex_adapter *adapter = priv->adapter;

	if (chan) {
		/* Set appropriate bands */
		if (chan->band == IEEE80211_BAND_2GHZ) {
			if (channel_type == NL80211_CHAN_NO_HT)
				if (priv->adapter->config_bands == BAND_B ||
				    priv->adapter->config_bands == BAND_G)
					config_bands =
						priv->adapter->config_bands;
				else
					config_bands = BAND_B | BAND_G;
			else
				config_bands = BAND_B | BAND_G | BAND_GN;
		} else {
			if (channel_type == NL80211_CHAN_NO_HT)
				config_bands = BAND_A;
			else
				config_bands = BAND_AN | BAND_A;
		}

		if (!((config_bands | adapter->fw_bands) &
						~adapter->fw_bands)) {
			adapter->config_bands = config_bands;
			if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
				adapter->adhoc_start_band = config_bands;
				if ((config_bands & BAND_GN) ||
				    (config_bands & BAND_AN))
					adapter->adhoc_11n_enabled = true;
				else
					adapter->adhoc_11n_enabled = false;
			}
		}
		adapter->sec_chan_offset =
			mwifiex_cfg80211_channel_type_to_sec_chan_offset
			(channel_type);
		adapter->channel_type = channel_type;

		mwifiex_send_domain_info_cmd_fw(wiphy);
	}

	wiphy_dbg(wiphy, "info: setting band %d, chan offset %d, mode %d\n",
		  config_bands, adapter->sec_chan_offset, priv->bss_mode);
	if (!chan)
		return 0;

	memset(&cfp, 0, sizeof(cfp));
	cfp.freq = chan->center_freq;
	cfp.channel = ieee80211_frequency_to_channel(chan->center_freq);

	if (mwifiex_bss_set_channel(priv, &cfp))
		return -EFAULT;

	if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
		return mwifiex_drv_change_adhoc_chan(priv, cfp.channel);
	else
		return mwifiex_uap_set_channel(priv, cfp.channel);
}
Example #27
0
void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
{
	struct carl9170_rsp *cmd = (void *) buf;
	struct ieee80211_vif *vif;

	if (carl9170_check_sequence(ar, cmd->hdr.seq))
		return;

	if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) {
		if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG))
			carl9170_cmd_callback(ar, len, buf);

		return;
	}

	if (unlikely(cmd->hdr.len != (len - 4))) {
		if (net_ratelimit()) {
			wiphy_err(ar->hw->wiphy, "FW: received over-/under"
				"sized event %x (%d, but should be %d).\n",
			       cmd->hdr.cmd, cmd->hdr.len, len - 4);

			print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE,
					     buf, len);
		}

		return;
	}

	/* hardware event handlers */
	switch (cmd->hdr.cmd) {
	case CARL9170_RSP_PRETBTT:
		/* pre-TBTT event */
		rcu_read_lock();
		vif = carl9170_get_main_vif(ar);

		if (!vif) {
			rcu_read_unlock();
			break;
		}

		switch (vif->type) {
		case NL80211_IFTYPE_STATION:
			carl9170_handle_ps(ar, cmd);
			break;

		case NL80211_IFTYPE_AP:
		case NL80211_IFTYPE_ADHOC:
			carl9170_update_beacon(ar, true);
			break;

		default:
			break;
		}
		rcu_read_unlock();

		break;


	case CARL9170_RSP_TXCOMP:
		/* TX status notification */
		carl9170_tx_process_status(ar, cmd);
		break;

	case CARL9170_RSP_BEACON_CONFIG:
		/*
		 * (IBSS) beacon send notification
		 * bytes: 04 c2 XX YY B4 B3 B2 B1
		 *
		 * XX always 80
		 * YY always 00
		 * B1-B4 "should" be the number of send out beacons.
		 */
		break;

	case CARL9170_RSP_ATIM:
		/* End of Atim Window */
		break;

	case CARL9170_RSP_WATCHDOG:
		/* Watchdog Interrupt */
		carl9170_restart(ar, CARL9170_RR_WATCHDOG);
		break;

	case CARL9170_RSP_TEXT:
		/* firmware debug */
		carl9170_dbg_message(ar, (char *)buf + 4, len - 4);
		break;

	case CARL9170_RSP_HEXDUMP:
		wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4);
		print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE,
				     (char *)buf + 4, len - 4);
		break;

	case CARL9170_RSP_RADAR:
		if (!net_ratelimit())
			break;

		wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this "
		       "incident to [email protected] !\n");
		break;

	case CARL9170_RSP_GPIO:
#ifdef CONFIG_CARL9170_WPC
		if (ar->wps.pbc) {
			bool state = !!(cmd->gpio.gpio & cpu_to_le32(
				AR9170_GPIO_PORT_WPS_BUTTON_PRESSED));

			if (state != ar->wps.pbc_state) {
				ar->wps.pbc_state = state;
				input_report_key(ar->wps.pbc, KEY_WPS_BUTTON,
						 state);
				input_sync(ar->wps.pbc);
			}
		}
#endif /* CONFIG_CARL9170_WPC */
		break;

	case CARL9170_RSP_BOOT:
		complete(&ar->fw_boot_wait);
		break;

	default:
		wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n",
			cmd->hdr.cmd);
		print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len);
		break;
	}
}
Example #28
0
/*
 * CFG802.11 operation handler for scan request.
 *
 * This function issues a scan request to the firmware based upon
 * the user specified scan configuration. On successfull completion,
 * it also informs the results.
 */
static int
mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
		      struct cfg80211_scan_request *request)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	int i;
	struct ieee80211_channel *chan;

	wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);

	priv->scan_request = request;

	priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
				      GFP_KERNEL);
	if (!priv->user_scan_cfg) {
		dev_err(priv->adapter->dev, "failed to alloc scan_req\n");
		return -ENOMEM;
	}

	priv->user_scan_cfg->num_ssids = request->n_ssids;
	priv->user_scan_cfg->ssid_list = request->ssids;

	if (request->ie && request->ie_len) {
		for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
			if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR)
				continue;
			priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN;
			memcpy(&priv->vs_ie[i].ie, request->ie,
			       request->ie_len);
			break;
		}
	}

	for (i = 0; i < min_t(u32, request->n_channels,
			      MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
		chan = request->channels[i];
		priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
		priv->user_scan_cfg->chan_list[i].radio_type = chan->band;

		if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
			priv->user_scan_cfg->chan_list[i].scan_type =
						MWIFIEX_SCAN_TYPE_PASSIVE;
		else
			priv->user_scan_cfg->chan_list[i].scan_type =
						MWIFIEX_SCAN_TYPE_ACTIVE;

		priv->user_scan_cfg->chan_list[i].scan_time = 0;
	}
	if (mwifiex_set_user_scan_ioctl(priv, priv->user_scan_cfg))
		return -EFAULT;

	if (request->ie && request->ie_len) {
		for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
			if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) {
				priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR;
				memset(&priv->vs_ie[i].ie, 0,
				       MWIFIEX_MAX_VSIE_LEN);
			}
		}
	}
	return 0;
}
Example #29
0
void cw1200_scan_work(struct work_struct *work)
{
	struct cw1200_common *priv = container_of(work, struct cw1200_common,
							scan.work);
	struct ieee80211_channel **it;
	struct wsm_scan scan = {
		.scanType = WSM_SCAN_TYPE_FOREGROUND,
		.scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD,
	};
	bool first_run = priv->scan.begin == priv->scan.curr &&
			priv->scan.begin != priv->scan.end;
	int i;

	mutex_lock(&priv->conf_mutex);

	if (first_run) {
		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
				!(priv->powersave_mode.pmMode & WSM_PSM_PS)) {
			struct wsm_set_pm pm = priv->powersave_mode;
			pm.pmMode = WSM_PSM_PS;
			cw1200_set_pm(priv, &pm);
		} else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
			/* FW bug: driver has to restart p2p-dev mode
			 * after scan */
			cw1200_disable_listening(priv);
		}
	}

	if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
		if (priv->scan.output_power != priv->output_power)
			WARN_ON(wsm_set_output_power(priv,
						priv->output_power * 10));
		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
				!(priv->powersave_mode.pmMode & WSM_PSM_PS))
			cw1200_set_pm(priv, &priv->powersave_mode);

		if (priv->scan.status < 0)
			wiphy_dbg(priv->hw->wiphy,
					"[SCAN] Scan failed (%d).\n",
					priv->scan.status);
		else if (priv->scan.req)
			wiphy_dbg(priv->hw->wiphy,
					"[SCAN] Scan completed.\n");
		else
			wiphy_dbg(priv->hw->wiphy,
					"[SCAN] Scan canceled.\n");

		priv->scan.req = NULL;
		cw1200_scan_restart_delayed(priv);
		wsm_unlock_tx(priv);
		mutex_unlock(&priv->conf_mutex);
		ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
		up(&priv->scan.lock);
		return;
	} else {
		struct ieee80211_channel *first = *priv->scan.curr;
		for (it = priv->scan.curr + 1, i = 1;
		     it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
		     ++it, ++i) {
			if ((*it)->band != first->band)
				break;
			if (((*it)->flags ^ first->flags) &
					IEEE80211_CHAN_PASSIVE_SCAN)
				break;
			if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
			    (*it)->max_power != first->max_power)
				break;
		}
		scan.band = first->band;

		if (priv->scan.req->no_cck)
			scan.maxTransmitRate = WSM_TRANSMIT_RATE_6;
		else
			scan.maxTransmitRate = WSM_TRANSMIT_RATE_1;
		/* TODO: Is it optimal? */
		scan.numOfProbeRequests =
			(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2;
		scan.numOfSSIDs = priv->scan.n_ssids;
		scan.ssids = &priv->scan.ssids[0];
		scan.numOfChannels = it - priv->scan.curr;
		scan.probeDelay = 100;
		/* It is not stated in WSM specification, however
		 * FW team says that driver may not use FG scan
		 * when joined. */
		if (priv->join_status == CW1200_JOIN_STATUS_STA) {
			scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
			scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
		}
		scan.ch = kzalloc(
			sizeof(struct wsm_scan_ch[it - priv->scan.curr]),
			GFP_KERNEL);
		if (!scan.ch) {
			priv->scan.status = -ENOMEM;
			goto fail;
		}
		for (i = 0; i < scan.numOfChannels; ++i) {
			scan.ch[i].number = priv->scan.curr[i]->hw_value;
			if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
				scan.ch[i].minChannelTime = 50;
				scan.ch[i].maxChannelTime = 100;
			}
			else {
				scan.ch[i].minChannelTime = 10;
				scan.ch[i].maxChannelTime = 25;
			}
		}
		if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
		    priv->scan.output_power != first->max_power) {
			priv->scan.output_power = first->max_power;
			WARN_ON(wsm_set_output_power(priv,
						priv->scan.output_power * 10));
		}
		priv->scan.status = cw1200_scan_start(priv, &scan);
		kfree(scan.ch);
		if (priv->scan.status)
			goto fail;
		priv->scan.curr = it;
	}
	mutex_unlock(&priv->conf_mutex);
	return;

fail:
	priv->scan.curr = priv->scan.end;
	mutex_unlock(&priv->conf_mutex);
	queue_work(priv->workqueue, &priv->scan.work);
	return;
}
Example #30
0
static int carl9170_rx_mac_status(struct ar9170 *ar,
	struct ar9170_rx_head *head, struct ar9170_rx_macstatus *mac,
	struct ieee80211_rx_status *status)
{
	struct ieee80211_channel *chan;
	u8 error, decrypt;

	BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
	BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4);

	error = mac->error;

	if (error & AR9170_RX_ERROR_WRONG_RA) {
		if (!ar->sniffer_enabled)
			return -EINVAL;
	}

	if (error & AR9170_RX_ERROR_PLCP) {
		if (!(ar->filter_state & FIF_PLCPFAIL))
			return -EINVAL;

		status->flag |= RX_FLAG_FAILED_PLCP_CRC;
	}

	if (error & AR9170_RX_ERROR_FCS) {
		ar->tx_fcs_errors++;

		if (!(ar->filter_state & FIF_FCSFAIL))
			return -EINVAL;

		status->flag |= RX_FLAG_FAILED_FCS_CRC;
	}

	decrypt = ar9170_get_decrypt_type(mac);
	if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
	    decrypt != AR9170_ENC_ALG_NONE) {
		if ((decrypt == AR9170_ENC_ALG_TKIP) &&
		    (error & AR9170_RX_ERROR_MMIC))
			status->flag |= RX_FLAG_MMIC_ERROR;

		status->flag |= RX_FLAG_DECRYPTED;
	}

	if (error & AR9170_RX_ERROR_DECRYPT && !ar->sniffer_enabled)
		return -ENODATA;

	error &= ~(AR9170_RX_ERROR_MMIC |
		   AR9170_RX_ERROR_FCS |
		   AR9170_RX_ERROR_WRONG_RA |
		   AR9170_RX_ERROR_DECRYPT |
		   AR9170_RX_ERROR_PLCP);

	/* drop any other error frames */
	if (unlikely(error)) {
		/* TODO: update netdevice's RX dropped/errors statistics */

		if (net_ratelimit())
			wiphy_dbg(ar->hw->wiphy, "received frame with "
			       "suspicious error code (%#x).\n", error);

		return -EINVAL;
	}

	chan = ar->channel;
	if (chan) {
		status->band = chan->band;
		status->freq = chan->center_freq;
	}

	switch (mac->status & AR9170_RX_STATUS_MODULATION) {
	case AR9170_RX_STATUS_MODULATION_CCK:
		if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
			status->flag |= RX_FLAG_SHORTPRE;
		switch (head->plcp[0]) {
		case AR9170_RX_PHY_RATE_CCK_1M:
			status->rate_idx = 0;
			break;
		case AR9170_RX_PHY_RATE_CCK_2M:
			status->rate_idx = 1;
			break;
		case AR9170_RX_PHY_RATE_CCK_5M:
			status->rate_idx = 2;
			break;
		case AR9170_RX_PHY_RATE_CCK_11M:
			status->rate_idx = 3;
			break;
		default:
			if (net_ratelimit()) {
				wiphy_err(ar->hw->wiphy, "invalid plcp cck "
				       "rate (%x).\n", head->plcp[0]);
			}

			return -EINVAL;
		}
		break;

	case AR9170_RX_STATUS_MODULATION_DUPOFDM:
	case AR9170_RX_STATUS_MODULATION_OFDM:
		switch (head->plcp[0] & 0xf) {
		case AR9170_TXRX_PHY_RATE_OFDM_6M:
			status->rate_idx = 0;
			break;
		case AR9170_TXRX_PHY_RATE_OFDM_9M:
			status->rate_idx = 1;
			break;
		case AR9170_TXRX_PHY_RATE_OFDM_12M:
			status->rate_idx = 2;
			break;
		case AR9170_TXRX_PHY_RATE_OFDM_18M:
			status->rate_idx = 3;
			break;
		case AR9170_TXRX_PHY_RATE_OFDM_24M:
			status->rate_idx = 4;
			break;
		case AR9170_TXRX_PHY_RATE_OFDM_36M:
			status->rate_idx = 5;
			break;
		case AR9170_TXRX_PHY_RATE_OFDM_48M:
			status->rate_idx = 6;
			break;
		case AR9170_TXRX_PHY_RATE_OFDM_54M:
			status->rate_idx = 7;
			break;
		default:
			if (net_ratelimit()) {
				wiphy_err(ar->hw->wiphy, "invalid plcp ofdm "
					"rate (%x).\n", head->plcp[0]);
			}

			return -EINVAL;
		}
		if (status->band == IEEE80211_BAND_2GHZ)
			status->rate_idx += 4;
		break;

	case AR9170_RX_STATUS_MODULATION_HT:
		if (head->plcp[3] & 0x80)
			status->flag |= RX_FLAG_40MHZ;
		if (head->plcp[6] & 0x80)
			status->flag |= RX_FLAG_SHORT_GI;

		status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f);
		status->flag |= RX_FLAG_HT;
		break;

	default:
		BUG();
		return -ENOSYS;
	}

	return 0;
}