Commit 1e5023c7 authored by John W. Linville's avatar John W. Linville
Browse files

Merge branch 'for-linville' of git://github.com/kvalo/ath6kl

parents dfbebe14 ff7e6867
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -30,3 +30,12 @@ config ATH6KL_DEBUG
	depends on ATH6KL
	---help---
	  Enables debug support

config ATH6KL_REGDOMAIN
	bool "Atheros ath6kl regdomain support"
	depends on ATH6KL
	depends on CFG80211_CERTIFICATION_ONUS
	---help---
	  Enabling this makes it possible to change the regdomain in
	  the firmware. This can be only enabled if regulatory requirements
	  are taken into account.
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ ath6kl_core-y += main.o
ath6kl_core-y += txrx.o
ath6kl_core-y += wmi.o
ath6kl_core-y += core.o
ath6kl_core-y += recovery.o
ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o

obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o
+244 −126
Original line number Diff line number Diff line
@@ -147,15 +147,15 @@ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
{
	struct ath6kl *ar = vif->ar;

	if (ar->state != ATH6KL_STATE_SCHED_SCAN)
	if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags))
		return false;

	del_timer_sync(&vif->sched_scan_timer);

	ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
					   ATH6KL_HOST_MODE_AWAKE);
	if (ar->state == ATH6KL_STATE_RECOVERY)
		return true;

	ar->state = ATH6KL_STATE_ON;
	ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, false);

	return true;
}
@@ -369,17 +369,13 @@ static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
{
	switch (type) {
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
		*nw_type = INFRA_NETWORK;
		break;
	case NL80211_IFTYPE_ADHOC:
		*nw_type = ADHOC_NETWORK;
		break;
	case NL80211_IFTYPE_AP:
		*nw_type = AP_NETWORK;
		break;
	case NL80211_IFTYPE_P2P_CLIENT:
		*nw_type = INFRA_NETWORK;
		break;
	case NL80211_IFTYPE_P2P_GO:
		*nw_type = AP_NETWORK;
		break;
@@ -1031,14 +1027,6 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy,

	vif->scan_req = request;

	if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
		     ar->fw_capabilities)) {
		/*
		 * If capable of doing P2P mgmt operations using
		 * station interface, send additional information like
		 * supported rates to advertise and xmit rates for
		 * probe requests
		 */
	ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
				       WMI_LONG_SCAN, force_fg_scan,
				       false, 0,
@@ -1046,15 +1034,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy,
				       n_channels, channels,
				       request->no_cck,
				       request->rates);
	} else {
		ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
						WMI_LONG_SCAN, force_fg_scan,
						false, 0,
						ATH6KL_FG_SCAN_INTERVAL,
						n_channels, channels);
	}
	if (ret) {
		ath6kl_err("wmi_startscan_cmd failed\n");
		ath6kl_err("failed to start scan: %d\n", ret);
		vif->scan_req = NULL;
	}

@@ -1888,7 +1869,7 @@ static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
			  struct cfg80211_wowlan *wow, u32 *filter)
{
	int ret, pos;
	u8 mask[WOW_MASK_SIZE];
	u8 mask[WOW_PATTERN_SIZE];
	u16 i;

	/* Configure the patterns that we received from the user. */
@@ -2106,33 +2087,16 @@ static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
	return ret;
}

static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif,
				  struct cfg80211_wowlan *wow, u32 *filter)
{
	struct ath6kl *ar = vif->ar;
	struct in_device *in_dev;
	struct in_ifaddr *ifa;
	struct ath6kl_vif *vif;
	int ret;
	u32 filter = 0;
	u16 i, bmiss_time;
	u8 index = 0;
	__be32 ips[MAX_IP_ADDRS];

	/* The FW currently can't support multi-vif WoW properly. */
	if (ar->num_vif > 1)
		return -EIO;

	vif = ath6kl_vif_first(ar);
	if (!vif)
		return -EIO;

	if (!ath6kl_cfg80211_ready(vif))
		return -EIO;

	if (!test_bit(CONNECTED, &vif->flags))
		return -ENOTCONN;

	if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
		return -EINVAL;
	u8 index = 0;

	if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
	    test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
@@ -2154,7 +2118,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
	 * the user.
	 */
	if (wow)
		ret = ath6kl_wow_usr(ar, vif, wow, &filter);
		ret = ath6kl_wow_usr(ar, vif, wow, filter);
	else if (vif->nw_type == AP_NETWORK)
		ret = ath6kl_wow_ap(ar, vif);
	else
@@ -2189,12 +2153,10 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
			return ret;
	}

	ar->state = ATH6KL_STATE_SUSPENDING;

	/* Setup own IP addr for ARP agent. */
	in_dev = __in_dev_get_rtnl(vif->ndev);
	if (!in_dev)
		goto skip_arp;
		return 0;

	ifa = in_dev->ifa_list;
	memset(&ips, 0, sizeof(ips));
@@ -2217,41 +2179,61 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
		return ret;
	}

skip_arp:
	ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
					  ATH6KL_WOW_MODE_ENABLE,
					  filter,
					  WOW_HOST_REQ_DELAY);
	if (ret)
	return ret;

	ret = ath6kl_cfg80211_host_sleep(ar, vif);
	if (ret)
		return ret;

	return 0;
}

static int ath6kl_wow_resume(struct ath6kl *ar)
static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
{
	struct ath6kl_vif *vif;
	int ret;
	struct ath6kl_vif *first_vif, *vif;
	int ret = 0;
	u32 filter = 0;
	bool connected = false;

	vif = ath6kl_vif_first(ar);
	if (!vif)
	/* enter / leave wow suspend on first vif always */
	first_vif = ath6kl_vif_first(ar);
	if (WARN_ON(unlikely(!first_vif)) ||
	    !ath6kl_cfg80211_ready(first_vif))
		return -EIO;

	ar->state = ATH6KL_STATE_RESUMING;
	if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
		return -EINVAL;

	ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
						 ATH6KL_HOST_MODE_AWAKE);
	if (ret) {
		ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
			    ret);
		ar->state = ATH6KL_STATE_WOW;
	/* install filters for each connected vif */
	spin_lock_bh(&ar->list_lock);
	list_for_each_entry(vif, &ar->vif_list, list) {
		if (!test_bit(CONNECTED, &vif->flags) ||
		    !ath6kl_cfg80211_ready(vif))
			continue;
		connected = true;

		ret = ath6kl_wow_suspend_vif(vif, wow, &filter);
		if (ret)
			break;
	}
	spin_unlock_bh(&ar->list_lock);

	if (!connected)
		return -ENOTCONN;
	else if (ret)
		return ret;

	ar->state = ATH6KL_STATE_SUSPENDING;

	ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, first_vif->fw_vif_idx,
					  ATH6KL_WOW_MODE_ENABLE,
					  filter,
					  WOW_HOST_REQ_DELAY);
	if (ret)
		return ret;

	return ath6kl_cfg80211_host_sleep(ar, first_vif);
}

static int ath6kl_wow_resume_vif(struct ath6kl_vif *vif)
{
	struct ath6kl *ar = vif->ar;
	int ret;

	if (vif->nw_type != AP_NETWORK) {
		ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
						0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
@@ -2269,8 +2251,6 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
			return ret;
	}

	ar->state = ATH6KL_STATE_ON;

	if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
	    test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
		     ar->fw_capabilities)) {
@@ -2285,6 +2265,48 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
	return 0;
}

static int ath6kl_wow_resume(struct ath6kl *ar)
{
	struct ath6kl_vif *vif;
	int ret;

	vif = ath6kl_vif_first(ar);
	if (WARN_ON(unlikely(!vif)) ||
	    !ath6kl_cfg80211_ready(vif))
		return -EIO;

	ar->state = ATH6KL_STATE_RESUMING;

	ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
						 ATH6KL_HOST_MODE_AWAKE);
	if (ret) {
		ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
			    ret);
		goto cleanup;
	}

	spin_lock_bh(&ar->list_lock);
	list_for_each_entry(vif, &ar->vif_list, list) {
		if (!test_bit(CONNECTED, &vif->flags) ||
		    !ath6kl_cfg80211_ready(vif))
			continue;
		ret = ath6kl_wow_resume_vif(vif);
		if (ret)
			break;
	}
	spin_unlock_bh(&ar->list_lock);

	if (ret)
		goto cleanup;

	ar->state = ATH6KL_STATE_ON;
	return 0;

cleanup:
	ar->state = ATH6KL_STATE_WOW;
	return ret;
}

static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
{
	struct ath6kl_vif *vif;
@@ -2421,13 +2443,6 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,

		break;

	case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
		/*
		 * Nothing needed for schedule scan, firmware is already in
		 * wow mode and sleeping most of the time.
		 */
		break;

	default:
		break;
	}
@@ -2475,9 +2490,6 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
		}
		break;

	case ATH6KL_STATE_SCHED_SCAN:
		break;

	default:
		break;
	}
@@ -2494,14 +2506,23 @@ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
{
	struct ath6kl *ar = wiphy_priv(wiphy);

	ath6kl_recovery_suspend(ar);

	return ath6kl_hif_suspend(ar, wow);
}

static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
{
	struct ath6kl *ar = wiphy_priv(wiphy);
	int err;

	err = ath6kl_hif_resume(ar);
	if (err)
		return err;

	return ath6kl_hif_resume(ar);
	ath6kl_recovery_resume(ar);

	return 0;
}

/*
@@ -2738,6 +2759,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
	int res;
	int i, ret;
	u16 rsn_capab = 0;
	int inactivity_timeout = 0;

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);

@@ -2874,8 +2896,15 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
	}

	if (info->inactivity_timeout) {

		inactivity_timeout = info->inactivity_timeout;

		if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS)
			inactivity_timeout = DIV_ROUND_UP(inactivity_timeout,
							  60);

		res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
						  info->inactivity_timeout);
						  inactivity_timeout);
		if (res < 0)
			return res;
	}
@@ -2897,6 +2926,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
					    WLAN_EID_RSN, WMI_RSN_IE_CAPB,
					    (const u8 *) &rsn_capab,
					    sizeof(rsn_capab));
		vif->rsn_capab = rsn_capab;
		if (res < 0)
			return res;
	}
@@ -3210,7 +3240,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
	struct ath6kl *ar = ath6kl_priv(dev);
	struct ath6kl_vif *vif = netdev_priv(dev);
	u16 interval;
	int ret;
	int ret, rssi_thold;

	if (ar->state != ATH6KL_STATE_ON)
		return -EIO;
@@ -3218,10 +3248,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
	if (vif->sme_state != SME_DISCONNECTED)
		return -EBUSY;

	/* The FW currently can't support multi-vif WoW properly. */
	if (ar->num_vif > 1)
		return -EIO;

	ath6kl_cfg80211_scan_complete_event(vif, true);

	ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
@@ -3243,6 +3269,23 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
			return ret;
	}

	if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
		     ar->fw_capabilities)) {
		if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
			rssi_thold = 0;
		else if (request->rssi_thold < -127)
			rssi_thold = -127;
		else
			rssi_thold = request->rssi_thold;

		ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
						     rssi_thold);
		if (ret) {
			ath6kl_err("failed to set RSSI threshold for scan\n");
			return ret;
		}
	}

	/* fw uses seconds, also make sure that it's >0 */
	interval = max_t(u16, 1, request->interval / 1000);

@@ -3250,15 +3293,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
				  interval, interval,
				  vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);

	ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
					  ATH6KL_WOW_MODE_ENABLE,
					  WOW_FILTER_SSID,
					  WOW_HOST_REQ_DELAY);
	if (ret) {
		ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
		return ret;
	}

	/* this also clears IE in fw if it's not set */
	ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
				       WMI_FRAME_PROBE_REQ,
@@ -3269,17 +3303,13 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
		return ret;
	}

	ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
						 ATH6KL_HOST_MODE_ASLEEP);
	if (ret) {
		ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
			    ret);
	ret = ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, true);
	if (ret)
		return ret;
	}

	ar->state = ATH6KL_STATE_SCHED_SCAN;
	set_bit(SCHED_SCANNING, &vif->flags);

	return ret;
	return 0;
}

static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
@@ -3308,6 +3338,27 @@ static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
					   mask);
}

static int ath6kl_cfg80211_set_txe_config(struct wiphy *wiphy,
					  struct net_device *dev,
					  u32 rate, u32 pkts, u32 intvl)
{
	struct ath6kl *ar = ath6kl_priv(dev);
	struct ath6kl_vif *vif = netdev_priv(dev);

	if (vif->nw_type != INFRA_NETWORK ||
	    !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ar->fw_capabilities))
		return -EOPNOTSUPP;

	if (vif->sme_state != SME_CONNECTED)
		return -ENOTCONN;

	/* save this since the firmware won't report the interval */
	vif->txe_intvl = intvl;

	return ath6kl_wmi_set_txe_notify(ar->wmi, vif->fw_vif_idx,
					 rate, pkts, intvl);
}

static const struct ieee80211_txrx_stypes
ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
	[NL80211_IFTYPE_STATION] = {
@@ -3374,6 +3425,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
	.sched_scan_start = ath6kl_cfg80211_sscan_start,
	.sched_scan_stop = ath6kl_cfg80211_sscan_stop,
	.set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
	.set_cqm_txe_config = ath6kl_cfg80211_set_txe_config,
};

void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
@@ -3394,16 +3446,22 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
		break;
	}

	if (test_bit(CONNECTED, &vif->flags) ||
	    test_bit(CONNECT_PEND, &vif->flags))
	if (vif->ar->state != ATH6KL_STATE_RECOVERY &&
	    (test_bit(CONNECTED, &vif->flags) ||
	    test_bit(CONNECT_PEND, &vif->flags)))
		ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);

	vif->sme_state = SME_DISCONNECTED;
	clear_bit(CONNECTED, &vif->flags);
	clear_bit(CONNECT_PEND, &vif->flags);

	/* Stop netdev queues, needed during recovery */
	netif_stop_queue(vif->ndev);
	netif_carrier_off(vif->ndev);

	/* disable scanning */
	if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
	if (vif->ar->state != ATH6KL_STATE_RECOVERY &&
	    ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
				      0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
		ath6kl_warn("failed to disable scan during stop\n");

@@ -3415,7 +3473,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
	struct ath6kl_vif *vif;

	vif = ath6kl_vif_first(ar);
	if (!vif) {
	if (!vif && ar->state != ATH6KL_STATE_RECOVERY) {
		/* save the current power mode before enabling power save */
		ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;

@@ -3433,6 +3491,56 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
		ath6kl_cfg80211_stop(vif);
}

static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
				      struct regulatory_request *request)
{
	struct ath6kl *ar = wiphy_priv(wiphy);
	u32 rates[IEEE80211_NUM_BANDS];
	int ret, i;

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
		   "cfg reg_notify %c%c%s%s initiator %d hint_type %d\n",
		   request->alpha2[0], request->alpha2[1],
		   request->intersect ? " intersect" : "",
		   request->processed ? " processed" : "",
		   request->initiator, request->user_reg_hint_type);

	/*
	 * As firmware is not able intersect regdoms, we can only listen to
	 * cellular hints.
	 */
	if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
		return -EOPNOTSUPP;

	ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2);
	if (ret) {
		ath6kl_err("failed to set regdomain: %d\n", ret);
		return ret;
	}

	/*
	 * Firmware will apply the regdomain change only after a scan is
	 * issued and it will send a WMI_REGDOMAIN_EVENTID when it has been
	 * changed.
	 */

	for (i = 0; i < IEEE80211_NUM_BANDS; i++)
		if (wiphy->bands[i])
			rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;


	ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false,
				       false, 0, ATH6KL_FG_SCAN_INTERVAL,
				       0, NULL, false, rates);
	if (ret) {
		ath6kl_err("failed to start scan for a regdomain change: %d\n",
			   ret);
		return ret;
	}

	return 0;
}

static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
{
	vif->aggr_cntxt = aggr_init(vif);
@@ -3505,9 +3613,13 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
	vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true;

	memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
	if (fw_vif_idx != 0)
	if (fw_vif_idx != 0) {
		ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
				     0x2;
		if (test_bit(ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR,
			     ar->fw_capabilities))
			ndev->dev_addr[4] ^= 0x80;
	}

	init_netdev(ndev);

@@ -3561,6 +3673,12 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
					  BIT(NL80211_IFTYPE_P2P_CLIENT);
	}

	if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) &&
	    test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities)) {
		wiphy->reg_notifier = ath6kl_cfg80211_reg_notify;
		ar->wiphy->features |= NL80211_FEATURE_CELL_BASE_REG_HINTS;
	}

	/* max num of ssids that can be probed during scanning */
	wiphy->max_scan_ssids = MAX_PROBED_SSIDS;

@@ -3606,7 +3724,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
		ath6kl_band_5ghz.ht_cap.ht_supported = false;
	}

	if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) {
	if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) {
		ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
		ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
		ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
@@ -3645,7 +3763,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
			    WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
			    WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;

	if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
	if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities))
		ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;

	if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
+0 −1
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ enum ath6kl_cfg_suspend_mode {
	ATH6KL_CFG_SUSPEND_DEEPSLEEP,
	ATH6KL_CFG_SUSPEND_CUTPOWER,
	ATH6KL_CFG_SUSPEND_WOW,
	ATH6KL_CFG_SUSPEND_SCHED_SCAN,
};

struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
+21 −0
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ static unsigned int wow_mode;
static unsigned int uart_debug;
static unsigned int ath6kl_p2p;
static unsigned int testmode;
static unsigned int recovery_enable;
static unsigned int heart_beat_poll;

module_param(debug_mask, uint, 0644);
module_param(suspend_mode, uint, 0644);
@@ -40,6 +42,12 @@ module_param(wow_mode, uint, 0644);
module_param(uart_debug, uint, 0644);
module_param(ath6kl_p2p, uint, 0644);
module_param(testmode, uint, 0644);
module_param(recovery_enable, uint, 0644);
module_param(heart_beat_poll, uint, 0644);
MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error");
MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic"   \
		 "polling. This also specifies the polling interval in"  \
		 "msecs. Set reocvery_enable for this to be effective");

void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
{
@@ -202,6 +210,17 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
		   __func__, wdev->netdev->name, wdev->netdev, ar);

	ar->fw_recovery.enable = !!recovery_enable;
	if (!ar->fw_recovery.enable)
		return ret;

	if (heart_beat_poll &&
	    test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL,
		     ar->fw_capabilities))
		ar->fw_recovery.hb_poll = heart_beat_poll;

	ath6kl_recovery_init(ar);

	return ret;

err_rxbuf_cleanup:
@@ -291,6 +310,8 @@ void ath6kl_core_cleanup(struct ath6kl *ar)
{
	ath6kl_hif_power_off(ar);

	ath6kl_recovery_cleanup(ar);

	destroy_workqueue(ar->ath6kl_wq);

	if (ar->htc_target)
Loading