Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42774986 authored by John W. Linville's avatar John W. Linville
Browse files
parents 9077f218 b622d992
Loading
Loading
Loading
Loading
+26 −2
Original line number Diff line number Diff line
@@ -1328,10 +1328,9 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
		/* get data from A-MPDU parameters field */
		acx->ampdu_max_length = ht_cap->ampdu_factor;
		acx->ampdu_min_spacing = ht_cap->ampdu_density;

		memcpy(acx->mac_address, mac_address, ETH_ALEN);
	}

	memcpy(acx->mac_address, mac_address, ETH_ALEN);
	acx->ht_capabilites = cpu_to_le32(ht_capabilites);

	ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx));
@@ -1542,3 +1541,28 @@ int wl1271_acx_config_ps(struct wl1271 *wl)
	kfree(config_ps);
	return ret;
}

int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
{
	struct wl1271_acx_inconnection_sta *acx = NULL;
	int ret;

	wl1271_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr);

	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
	if (!acx)
		return -ENOMEM;

	memcpy(acx->addr, addr, ETH_ALEN);

	ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST,
				   acx, sizeof(*acx));
	if (ret < 0) {
		wl1271_warning("acx set inconnaction sta failed: %d", ret);
		goto out;
	}

out:
	kfree(acx);
	return ret;
}
+9 −0
Original line number Diff line number Diff line
@@ -1155,6 +1155,13 @@ struct wl1271_acx_config_ps {
	__le32 null_data_rate;
} __packed;

struct wl1271_acx_inconnection_sta {
	struct acx_header header;

	u8 addr[ETH_ALEN];
	u8 padding1[2];
} __packed;

enum {
	ACX_WAKE_UP_CONDITIONS      = 0x0002,
	ACX_MEM_CFG                 = 0x0003,
@@ -1215,6 +1222,7 @@ enum {
	ACX_GEN_FW_CMD              = 0x0070,
	ACX_HOST_IF_CFG_BITMAP      = 0x0071,
	ACX_MAX_TX_FAILURE          = 0x0072,
	ACX_UPDATE_INCONNECTION_STA_LIST = 0x0073,
	DOT11_RX_MSDU_LIFE_TIME     = 0x1004,
	DOT11_CUR_TX_PWR            = 0x100D,
	DOT11_RX_DOT11_MODE         = 0x1012,
@@ -1290,5 +1298,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
int wl1271_acx_max_tx_retry(struct wl1271 *wl);
int wl1271_acx_config_ps(struct wl1271 *wl);
int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);

#endif /* __WL1271_ACX_H__ */
+1 −1
Original line number Diff line number Diff line
@@ -483,7 +483,7 @@ static void wl1271_check_ba_support(struct wl1271 *wl)
static int wl1271_set_ba_policies(struct wl1271 *wl)
{
	u8 tid_index;
	u8 ret = 0;
	int ret = 0;

	/* Reset the BA RX indicators */
	wl->ba_rx_bitmap = 0;
+118 −29
Original line number Diff line number Diff line
@@ -482,6 +482,10 @@ static int wl1271_plt_init(struct wl1271 *wl)
	if (ret < 0)
		goto out_free_memmap;

	ret = wl1271_acx_sta_mem_cfg(wl);
	if (ret < 0)
		goto out_free_memmap;

	/* Default fragmentation threshold */
	ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
	if (ret < 0)
@@ -533,6 +537,57 @@ static int wl1271_plt_init(struct wl1271 *wl)
	return ret;
}

static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks)
{
	bool fw_ps;

	/* only regulate station links */
	if (hlid < WL1271_AP_STA_HLID_START)
		return;

	fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);

	/*
	 * Wake up from high level PS if the STA is asleep with too little
	 * blocks in FW or if the STA is awake.
	 */
	if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS)
		wl1271_ps_link_end(wl, hlid);

	/* Start high-level PS if the STA is asleep with enough blocks in FW */
	else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS)
		wl1271_ps_link_start(wl, hlid, true);
}

static void wl1271_irq_update_links_status(struct wl1271 *wl,
				       struct wl1271_fw_ap_status *status)
{
	u32 cur_fw_ps_map;
	u8 hlid;

	cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
	if (wl->ap_fw_ps_map != cur_fw_ps_map) {
		wl1271_debug(DEBUG_PSM,
			     "link ps prev 0x%x cur 0x%x changed 0x%x",
			     wl->ap_fw_ps_map, cur_fw_ps_map,
			     wl->ap_fw_ps_map ^ cur_fw_ps_map);

		wl->ap_fw_ps_map = cur_fw_ps_map;
	}

	for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) {
		u8 cnt = status->tx_lnk_free_blks[hlid] -
			wl->links[hlid].prev_freed_blks;

		wl->links[hlid].prev_freed_blks =
			status->tx_lnk_free_blks[hlid];
		wl->links[hlid].allocated_blks -= cnt;

		wl1271_irq_ps_regulate_link(wl, hlid,
					    wl->links[hlid].allocated_blks);
	}
}

static void wl1271_fw_status(struct wl1271 *wl,
			     struct wl1271_fw_full_status *full_status)
{
@@ -570,6 +625,10 @@ static void wl1271_fw_status(struct wl1271 *wl,
	if (total)
		clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);

	/* for AP update num of allocated TX blocks per link and ps status */
	if (wl->bss_type == BSS_TYPE_AP_BSS)
		wl1271_irq_update_links_status(wl, &full_status->ap);

	/* update the host-chipset time offset */
	getnstimeofday(&ts);
	wl->time_offset = (timespec_to_ns(&ts) >> 10) -
@@ -980,14 +1039,32 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
	struct wl1271 *wl = hw->priv;
	unsigned long flags;
	int q;
	u8 hlid = 0;

	spin_lock_irqsave(&wl->wl_lock, flags);
	wl->tx_queue_count++;

	/*
	 * The workqueue is slow to process the tx_queue and we need stop
	 * the queue here, otherwise the queue will get too long.
	 */
	if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
		wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
		ieee80211_stop_queues(wl->hw);
		set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
	}

	spin_unlock_irqrestore(&wl->wl_lock, flags);

	/* queue the packet */
	q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
	if (wl->bss_type == BSS_TYPE_AP_BSS) {
		hlid = wl1271_tx_get_hlid(skb);
		wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
		skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
	} else {
		skb_queue_tail(&wl->tx_queue[q], skb);
	}

	/*
	 * The chip specific setup must run before the first TX packet -
@@ -997,19 +1074,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
	if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
		ieee80211_queue_work(wl->hw, &wl->tx_work);

	/*
	 * The workqueue is slow to process the tx_queue and we need stop
	 * the queue here, otherwise the queue will get too long.
	 */
	if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
		wl1271_debug(DEBUG_TX, "op_tx: stopping queues");

		spin_lock_irqsave(&wl->wl_lock, flags);
		ieee80211_stop_queues(wl->hw);
		set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
		spin_unlock_irqrestore(&wl->wl_lock, flags);
	}

	return NETDEV_TX_OK;
}

@@ -1221,6 +1285,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
	wl->filters = 0;
	wl1271_free_ap_keys(wl);
	memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
	wl->ap_fw_ps_map = 0;
	wl->ap_ps_map = 0;

	for (i = 0; i < NUM_TX_QUEUES; i++)
		wl->tx_blocks_freed[i] = 0;
@@ -2218,6 +2284,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
	u32 sta_rate_set = 0;
	int ret;
	struct ieee80211_sta *sta;
	bool sta_exists = false;
	struct ieee80211_sta_ht_cap sta_ht_cap;

	if (is_ibss) {
		ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
@@ -2289,16 +2357,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
		if (sta->ht_cap.ht_supported)
			sta_rate_set |=
			    (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
		sta_ht_cap = sta->ht_cap;
		sta_exists = true;
	}
	rcu_read_unlock();

	if (sta_exists) {
		/* handle new association with HT and HT information change */
		if ((changed & BSS_CHANGED_HT) &&
		    (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
			ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap,
			ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
							     true);
			if (ret < 0) {
				wl1271_warning("Set ht cap true failed %d",
					       ret);
				rcu_read_unlock();
				goto out;
			}
			ret = wl1271_acx_set_ht_information(wl,
@@ -2306,23 +2378,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
			if (ret < 0) {
				wl1271_warning("Set ht information failed %d",
					       ret);
				rcu_read_unlock();
				goto out;
			}
		}
		/* handle new association without HT and disassociation */
		else if (changed & BSS_CHANGED_ASSOC) {
			ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap,
			ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
							     false);
			if (ret < 0) {
				wl1271_warning("Set ht cap false failed %d",
					       ret);
				rcu_read_unlock();
				goto out;
			}
		}
	}
	rcu_read_unlock();

	if ((changed & BSS_CHANGED_ASSOC)) {
		if (bss_conf->assoc) {
@@ -2612,7 +2681,7 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
	return 0;
}

static int wl1271_allocate_hlid(struct wl1271 *wl,
static int wl1271_allocate_sta(struct wl1271 *wl,
			     struct ieee80211_sta *sta,
			     u8 *hlid)
{
@@ -2626,18 +2695,25 @@ static int wl1271_allocate_hlid(struct wl1271 *wl,
	}

	wl_sta = (struct wl1271_station *)sta->drv_priv;

	__set_bit(id, wl->ap_hlid_map);
	wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
	*hlid = wl_sta->hlid;
	memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
	return 0;
}

static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid)
static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
{
	int id = hlid - WL1271_AP_STA_HLID_START;

	if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
		return;

	__clear_bit(id, wl->ap_hlid_map);
	memset(wl->links[hlid].addr, 0, ETH_ALEN);
	wl1271_tx_reset_link_queues(wl, hlid);
	__clear_bit(hlid, &wl->ap_ps_map);
	__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
}

static int wl1271_op_sta_add(struct ieee80211_hw *hw,
@@ -2658,13 +2734,13 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,

	wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);

	ret = wl1271_allocate_hlid(wl, sta, &hlid);
	ret = wl1271_allocate_sta(wl, sta, &hlid);
	if (ret < 0)
		goto out;

	ret = wl1271_ps_elp_wakeup(wl, false);
	if (ret < 0)
		goto out;
		goto out_free_sta;

	ret = wl1271_cmd_add_sta(wl, sta, hlid);
	if (ret < 0)
@@ -2673,6 +2749,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
out_sleep:
	wl1271_ps_elp_sleep(wl);

out_free_sta:
	if (ret < 0)
		wl1271_free_sta(wl, hlid);

out:
	mutex_unlock(&wl->mutex);
	return ret;
@@ -2709,7 +2789,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
	if (ret < 0)
		goto out_sleep;

	wl1271_free_hlid(wl, wl_sta->hlid);
	wl1271_free_sta(wl, wl_sta->hlid);

out_sleep:
	wl1271_ps_elp_sleep(wl);
@@ -3212,7 +3292,9 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
		IEEE80211_HW_SUPPORTS_UAPSD |
		IEEE80211_HW_HAS_RATE_CONTROL |
		IEEE80211_HW_CONNECTION_MONITOR |
		IEEE80211_HW_SUPPORTS_CQM_RSSI;
		IEEE80211_HW_SUPPORTS_CQM_RSSI |
		IEEE80211_HW_REPORTS_TX_ACK_STATUS |
		IEEE80211_HW_AP_LINK_PS;

	wl->hw->wiphy->cipher_suites = cipher_suites;
	wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -3264,7 +3346,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
	struct ieee80211_hw *hw;
	struct platform_device *plat_dev = NULL;
	struct wl1271 *wl;
	int i, ret;
	int i, j, ret;
	unsigned int order;

	hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
@@ -3292,6 +3374,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
	for (i = 0; i < NUM_TX_QUEUES; i++)
		skb_queue_head_init(&wl->tx_queue[i]);

	for (i = 0; i < NUM_TX_QUEUES; i++)
		for (j = 0; j < AP_MAX_LINKS; j++)
			skb_queue_head_init(&wl->links[j].tx_queue[i]);

	INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
	INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
	INIT_WORK(&wl->irq_work, wl1271_irq_work);
@@ -3317,6 +3403,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
	wl->bss_type = MAX_BSS_TYPE;
	wl->set_bss_type = MAX_BSS_TYPE;
	wl->fw_bss_type = MAX_BSS_TYPE;
	wl->last_tx_hlid = 0;
	wl->ap_ps_map = 0;
	wl->ap_fw_ps_map = 0;

	memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
	for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
@@ -3412,5 +3501,5 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(debug_level, "wl12xx debugging level");

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
+78 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "reg.h"
#include "ps.h"
#include "io.h"
#include "tx.h"

#define WL1271_WAKEUP_TIMEOUT 500

@@ -173,4 +174,81 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
	return ret;
}

static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
{
	int i, filtered = 0;
	struct sk_buff *skb;
	struct ieee80211_tx_info *info;
	unsigned long flags;

	/* filter all frames currently the low level queus for this hlid */
	for (i = 0; i < NUM_TX_QUEUES; i++) {
		while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
			info = IEEE80211_SKB_CB(skb);
			info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
			info->status.rates[0].idx = -1;
			ieee80211_tx_status(wl->hw, skb);
			filtered++;
		}
	}

	spin_lock_irqsave(&wl->wl_lock, flags);
	wl->tx_queue_count -= filtered;
	spin_unlock_irqrestore(&wl->wl_lock, flags);

	wl1271_handle_tx_low_watermark(wl);
}

void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
{
	struct ieee80211_sta *sta;

	if (test_bit(hlid, &wl->ap_ps_map))
		return;

	wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d "
		     "clean_queues %d", hlid, wl->links[hlid].allocated_blks,
		     clean_queues);

	rcu_read_lock();
	sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
	if (!sta) {
		wl1271_error("could not find sta %pM for starting ps",
			     wl->links[hlid].addr);
		rcu_read_unlock();
		return;
	}

	ieee80211_sta_ps_transition_ni(sta, true);
	rcu_read_unlock();

	/* do we want to filter all frames from this link's queues? */
	if (clean_queues)
		wl1271_ps_filter_frames(wl, hlid);

	__set_bit(hlid, &wl->ap_ps_map);
}

void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid)
{
	struct ieee80211_sta *sta;

	if (!test_bit(hlid, &wl->ap_ps_map))
		return;

	wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid);

	__clear_bit(hlid, &wl->ap_ps_map);

	rcu_read_lock();
	sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
	if (!sta) {
		wl1271_error("could not find sta %pM for ending ps",
			     wl->links[hlid].addr);
		goto end;
	}

	ieee80211_sta_ps_transition_ni(sta, false);
end:
	rcu_read_unlock();
}
Loading