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

Commit e0f114e8 authored by Christian Lamparter's avatar Christian Lamparter Committed by John W. Linville
Browse files

p54: re-enable power save feature



This patch re-enables p54's power save features and adds a workaround
which temporarily alters the device's power state in order to allow
ps-polls to be sent and buffered data to be retrieved during psm.

(Incorporates patch originally posted as "p54: fix beacon template dtim
IE corruption". -- JWL)

Signed-off-by: default avatarChristian Lamparter <chunkeey@web.de>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 0a2b8bb2
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -585,7 +585,8 @@ int p54_set_ps(struct p54_common *priv)
	unsigned int i;
	u16 mode;

	if (priv->hw->conf.flags & IEEE80211_CONF_PS)
	if (priv->hw->conf.flags & IEEE80211_CONF_PS &&
	    !priv->powersave_override)
		mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM |
		       P54_PSM_CHECKSUM | P54_PSM_MCBC;
	else
@@ -607,8 +608,8 @@ int p54_set_ps(struct p54_common *priv)

	psm->beacon_rssi_skip_max = 200;
	psm->rssi_delta_threshold = 0;
	psm->nr = 10;
	psm->exclude[0] = 0;
	psm->nr = 1;
	psm->exclude[0] = WLAN_EID_TIM;

	p54_tx(priv, skb);
	return 0;
+3 −0
Original line number Diff line number Diff line
@@ -548,4 +548,7 @@ int p54_upload_key(struct p54_common *priv, u8 algo, int slot,
int p54_download_eeprom(struct p54_common *priv, void *buf,
			u16 offset, u16 len);

/* utility */
u8 *p54_find_ie(struct sk_buff *skb, u8 ie);

#endif /* LMAC_H */
+48 −29
Original line number Diff line number Diff line
@@ -65,51 +65,64 @@ static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
	return p54_update_beacon_tim(priv, sta->aid, set);
}

static int p54_beacon_format_ie_tim(struct sk_buff *skb)
u8 *p54_find_ie(struct sk_buff *skb, u8 ie)
{
	/*
	 * the good excuse for this mess is ... the firmware.
	 * The dummy TIM MUST be at the end of the beacon frame,
	 * because it'll be overwritten!
	 */

	struct ieee80211_mgmt *mgmt = (void *)skb->data;
	u8 *pos, *end;

	if (skb->len <= sizeof(mgmt))
		return -EINVAL;
		return NULL;

	pos = (u8 *)mgmt->u.beacon.variable;
	end = skb->data + skb->len;
	while (pos < end) {
		if (pos + 2 + pos[1] > end)
			return -EINVAL;
			return NULL;

		if (pos[0] == ie)
			return pos;

		pos += 2 + pos[1];
	}
	return NULL;
}

static int p54_beacon_format_ie_tim(struct sk_buff *skb)
{
	/*
	 * the good excuse for this mess is ... the firmware.
	 * The dummy TIM MUST be at the end of the beacon frame,
	 * because it'll be overwritten!
	 */
	u8 *tim;
	u8 dtim_len;
	u8 dtim_period;
	u8 *next;

	tim = p54_find_ie(skb, WLAN_EID_TIM);
	if (!tim)
		return 0;

		if (pos[0] == WLAN_EID_TIM) {
			u8 dtim_len = pos[1];
			u8 dtim_period = pos[3];
			u8 *next = pos + 2 + dtim_len;
	dtim_len = tim[1];
	dtim_period = tim[3];
	next = tim + 2 + dtim_len;

	if (dtim_len < 3)
		return -EINVAL;

			memmove(pos, next, end - next);
	memmove(tim, next, skb_tail_pointer(skb) - next);
	tim = skb_tail_pointer(skb) - (dtim_len + 2);

	/* add the dummy at the end */
	tim[0] = WLAN_EID_TIM;
	tim[1] = 3;
	tim[2] = 0;
	tim[3] = dtim_period;
	tim[4] = 0;

	if (dtim_len > 3)
		skb_trim(skb, skb->len - (dtim_len - 3));

			pos = end - (dtim_len + 2);

			/* add the dummy at the end */
			pos[0] = WLAN_EID_TIM;
			pos[1] = 3;
			pos[2] = 0;
			pos[3] = dtim_period;
			pos[4] = 0;
			return 0;
		}
		pos += 2 + pos[1];
	}
	return 0;
}

@@ -384,6 +397,9 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev,
			priv->wakeup_timer = info->beacon_int *
					     info->dtim_period * 5;
			p54_setup_mac(priv);
		} else {
			priv->wakeup_timer = 500;
			priv->aid = 0;
		}
	}

@@ -517,6 +533,9 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
	skb_queue_head_init(&priv->tx_pending);
	dev->flags = IEEE80211_HW_RX_INCLUDES_FCS |
		     IEEE80211_HW_SIGNAL_DBM |
		     IEEE80211_HW_SUPPORTS_PS |
		     IEEE80211_HW_PS_NULLFUNC_STACK |
		     IEEE80211_HW_BEACON_FILTER |
		     IEEE80211_HW_NOISE_DBM;

	dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+1 −0
Original line number Diff line number Diff line
@@ -208,6 +208,7 @@ struct p54_common {
	u32 tsf_low32, tsf_high32;
	u32 basic_rate_mask;
	u16 aid;
	bool powersave_override;
	__le32 beacon_req_id;

	/* cryptographic engine information */
+42 −0
Original line number Diff line number Diff line
@@ -288,6 +288,45 @@ static int p54_rssi_to_dbm(struct p54_common *priv, int rssi)
			 priv->rssical_db[band].add) / 4;
}

/*
 * Even if the firmware is capable of dealing with incoming traffic,
 * while dozing, we have to prepared in case mac80211 uses PS-POLL
 * to retrieve outstanding frames from our AP.
 * (see comment in net/mac80211/mlme.c @ line 1993)
 */
static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
{
	struct ieee80211_hdr *hdr = (void *) skb->data;
	struct ieee80211_tim_ie *tim_ie;
	u8 *tim;
	u8 tim_len;
	bool new_psm;

	/* only beacons have a TIM IE */
	if (!ieee80211_is_beacon(hdr->frame_control))
		return;

	if (!priv->aid)
		return;

	/* only consider beacons from the associated BSSID */
	if (compare_ether_addr(hdr->addr3, priv->bssid))
		return;

	tim = p54_find_ie(skb, WLAN_EID_TIM);
	if (!tim)
		return;

	tim_len = tim[1];
	tim_ie = (struct ieee80211_tim_ie *) &tim[2];

	new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid);
	if (new_psm != priv->powersave_override) {
		priv->powersave_override = new_psm;
		p54_set_ps(priv);
	}
}

static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
{
	struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data;
@@ -340,6 +379,9 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)

	skb_pull(skb, header_len);
	skb_trim(skb, le16_to_cpu(hdr->len));
	if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS))
		p54_pspoll_workaround(priv, skb);

	ieee80211_rx_irqsafe(priv->hw, skb);

	queue_delayed_work(priv->hw->workqueue, &priv->work,