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

Commit e5b900d2 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

mac80211: allow drivers to request DTIM period



Some features require knowing the DTIM period
before associating. This implements the ability
to wait for a beacon in mac80211 before assoc
to provide this value. It is optional since
most likely not all drivers will need this.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent d28232b4
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -194,7 +194,9 @@ enum ieee80211_bss_change {
 *	if the hardware cannot handle this it must set the
 *	if the hardware cannot handle this it must set the
 *	IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
 *	IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
 * @dtim_period: num of beacons before the next DTIM, for beaconing,
 * @dtim_period: num of beacons before the next DTIM, for beaconing,
 *	not valid in station mode (cf. hw conf ps_dtim_period)
 *	valid in station mode only while @assoc is true and if also
 *	requested by %IEEE80211_HW_NEED_DTIM_PERIOD (cf. also hw conf
 *	@ps_dtim_period)
 * @timestamp: beacon timestamp
 * @timestamp: beacon timestamp
 * @beacon_int: beacon interval
 * @beacon_int: beacon interval
 * @assoc_capability: capabilities taken from assoc resp
 * @assoc_capability: capabilities taken from assoc resp
@@ -1027,6 +1029,9 @@ enum ieee80211_tkip_key_type {
 *	connection quality related parameters, such as the RSSI level and
 *	connection quality related parameters, such as the RSSI level and
 *	provide notifications if configured trigger levels are reached.
 *	provide notifications if configured trigger levels are reached.
 *
 *
 * @IEEE80211_HW_NEED_DTIM_PERIOD:
 *	This device needs to know the DTIM period for the BSS before
 *	associating.
 */
 */
enum ieee80211_hw_flags {
enum ieee80211_hw_flags {
	IEEE80211_HW_HAS_RATE_CONTROL			= 1<<0,
	IEEE80211_HW_HAS_RATE_CONTROL			= 1<<0,
@@ -1036,7 +1041,7 @@ enum ieee80211_hw_flags {
	IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE	= 1<<4,
	IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE	= 1<<4,
	IEEE80211_HW_SIGNAL_UNSPEC			= 1<<5,
	IEEE80211_HW_SIGNAL_UNSPEC			= 1<<5,
	IEEE80211_HW_SIGNAL_DBM				= 1<<6,
	IEEE80211_HW_SIGNAL_DBM				= 1<<6,
	/* use this hole */
	IEEE80211_HW_NEED_DTIM_PERIOD			= 1<<7,
	IEEE80211_HW_SPECTRUM_MGMT			= 1<<8,
	IEEE80211_HW_SPECTRUM_MGMT			= 1<<8,
	IEEE80211_HW_AMPDU_AGGREGATION			= 1<<9,
	IEEE80211_HW_AMPDU_AGGREGATION			= 1<<9,
	IEEE80211_HW_SUPPORTS_PS			= 1<<10,
	IEEE80211_HW_SUPPORTS_PS			= 1<<10,
+1 −0
Original line number Original line Diff line number Diff line
@@ -238,6 +238,7 @@ enum ieee80211_work_type {
	IEEE80211_WORK_ABORT,
	IEEE80211_WORK_ABORT,
	IEEE80211_WORK_DIRECT_PROBE,
	IEEE80211_WORK_DIRECT_PROBE,
	IEEE80211_WORK_AUTH,
	IEEE80211_WORK_AUTH,
	IEEE80211_WORK_ASSOC_BEACON_WAIT,
	IEEE80211_WORK_ASSOC,
	IEEE80211_WORK_ASSOC,
	IEEE80211_WORK_REMAIN_ON_CHANNEL,
	IEEE80211_WORK_REMAIN_ON_CHANNEL,
};
};
+29 −3
Original line number Original line Diff line number Diff line
@@ -870,6 +870,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,


	ieee80211_led_assoc(local, 1);
	ieee80211_led_assoc(local, 1);


	if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
		bss_conf->dtim_period = bss->dtim_period;
	else
		bss_conf->dtim_period = 0;

	bss_conf->assoc = 1;
	bss_conf->assoc = 1;
	/*
	/*
	 * For now just always ask the driver to update the basic rateset
	 * For now just always ask the driver to update the basic rateset
@@ -1751,7 +1756,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
			if (wk->sdata != sdata)
			if (wk->sdata != sdata)
				continue;
				continue;


			if (wk->type != IEEE80211_WORK_ASSOC)
			if (wk->type != IEEE80211_WORK_ASSOC &&
			    wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
				continue;
				continue;


			if (memcmp(mgmt->bssid, wk->filter_ta, ETH_ALEN))
			if (memcmp(mgmt->bssid, wk->filter_ta, ETH_ALEN))
@@ -2086,6 +2092,8 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
						  struct sk_buff *skb)
						  struct sk_buff *skb)
{
{
	struct ieee80211_mgmt *mgmt;
	struct ieee80211_mgmt *mgmt;
	struct ieee80211_rx_status *rx_status;
	struct ieee802_11_elems elems;
	u16 status;
	u16 status;


	if (!skb) {
	if (!skb) {
@@ -2093,6 +2101,19 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
		return WORK_DONE_DESTROY;
		return WORK_DONE_DESTROY;
	}
	}


	if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) {
		mutex_lock(&wk->sdata->u.mgd.mtx);
		rx_status = (void *) skb->cb;
		ieee802_11_parse_elems(skb->data + 24 + 12, skb->len - 24 - 12, &elems);
		ieee80211_rx_bss_info(wk->sdata, (void *)skb->data, skb->len, rx_status,
				      &elems, true);
		mutex_unlock(&wk->sdata->u.mgd.mtx);

		wk->type = IEEE80211_WORK_ASSOC;
		/* not really done yet */
		return WORK_DONE_REQUEUE;
	}

	mgmt = (void *)skb->data;
	mgmt = (void *)skb->data;
	status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
	status = le16_to_cpu(mgmt->u.assoc_resp.status_code);


@@ -2206,10 +2227,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
	if (req->prev_bssid)
	if (req->prev_bssid)
		memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
		memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);


	wk->type = IEEE80211_WORK_ASSOC;
	wk->chan = req->bss->channel;
	wk->chan = req->bss->channel;
	wk->sdata = sdata;
	wk->sdata = sdata;
	wk->done = ieee80211_assoc_done;
	wk->done = ieee80211_assoc_done;
	if (!bss->dtim_period &&
	    sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
		wk->type = IEEE80211_WORK_ASSOC_BEACON_WAIT;
	else
		wk->type = IEEE80211_WORK_ASSOC;


	if (req->use_mfp) {
	if (req->use_mfp) {
		ifmgd->mfp = IEEE80211_MFP_REQUIRED;
		ifmgd->mfp = IEEE80211_MFP_REQUIRED;
@@ -2257,7 +2282,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,


			if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
			if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
			    wk->type != IEEE80211_WORK_AUTH &&
			    wk->type != IEEE80211_WORK_AUTH &&
			    wk->type != IEEE80211_WORK_ASSOC)
			    wk->type != IEEE80211_WORK_ASSOC &&
			    wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
				continue;
				continue;


			if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
			if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
+4 −0
Original line number Original line Diff line number Diff line
@@ -114,6 +114,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
		bss->dtim_period = tim_ie->dtim_period;
		bss->dtim_period = tim_ie->dtim_period;
	}
	}


	/* If the beacon had no TIM IE, or it was invalid, use 1 */
	if (beacon && !bss->dtim_period)
		bss->dtim_period = 1;

	/* replace old supported rates if we get new values */
	/* replace old supported rates if we get new values */
	srlen = 0;
	srlen = 0;
	if (elems->supp_rates) {
	if (elems->supp_rates) {
+43 −0
Original line number Original line Diff line number Diff line
@@ -560,6 +560,22 @@ ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
	return WORK_ACT_TIMEOUT;
	return WORK_ACT_TIMEOUT;
}
}


static enum work_action __must_check
ieee80211_assoc_beacon_wait(struct ieee80211_work *wk)
{
	if (wk->started)
		return WORK_ACT_TIMEOUT;

	/*
	 * Wait up to one beacon interval ...
	 * should this be more if we miss one?
	 */
	printk(KERN_DEBUG "%s: waiting for beacon from %pM\n",
	       wk->sdata->name, wk->filter_ta);
	wk->timeout = TU_TO_EXP_TIME(wk->assoc.bss->beacon_interval);
	return WORK_ACT_NONE;
}

static void ieee80211_auth_challenge(struct ieee80211_work *wk,
static void ieee80211_auth_challenge(struct ieee80211_work *wk,
				     struct ieee80211_mgmt *mgmt,
				     struct ieee80211_mgmt *mgmt,
				     size_t len)
				     size_t len)
@@ -709,6 +725,25 @@ ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
	return WORK_ACT_DONE;
	return WORK_ACT_DONE;
}
}


static enum work_action __must_check
ieee80211_rx_mgmt_beacon(struct ieee80211_work *wk,
			 struct ieee80211_mgmt *mgmt, size_t len)
{
	struct ieee80211_sub_if_data *sdata = wk->sdata;
	struct ieee80211_local *local = sdata->local;

	ASSERT_WORK_MTX(local);

	if (wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
		return WORK_ACT_MISMATCH;

	if (len < 24 + 12)
		return WORK_ACT_NONE;

	printk(KERN_DEBUG "%s: beacon received\n", sdata->name);
	return WORK_ACT_DONE;
}

static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
					  struct sk_buff *skb)
					  struct sk_buff *skb)
{
{
@@ -731,6 +766,7 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
		case IEEE80211_WORK_DIRECT_PROBE:
		case IEEE80211_WORK_DIRECT_PROBE:
		case IEEE80211_WORK_AUTH:
		case IEEE80211_WORK_AUTH:
		case IEEE80211_WORK_ASSOC:
		case IEEE80211_WORK_ASSOC:
		case IEEE80211_WORK_ASSOC_BEACON_WAIT:
			bssid = wk->filter_ta;
			bssid = wk->filter_ta;
			break;
			break;
		default:
		default:
@@ -745,6 +781,9 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
			continue;
			continue;


		switch (fc & IEEE80211_FCTL_STYPE) {
		switch (fc & IEEE80211_FCTL_STYPE) {
		case IEEE80211_STYPE_BEACON:
			rma = ieee80211_rx_mgmt_beacon(wk, mgmt, skb->len);
			break;
		case IEEE80211_STYPE_PROBE_RESP:
		case IEEE80211_STYPE_PROBE_RESP:
			rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len,
			rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len,
							   rx_status);
							   rx_status);
@@ -916,6 +955,9 @@ static void ieee80211_work_work(struct work_struct *work)
		case IEEE80211_WORK_REMAIN_ON_CHANNEL:
		case IEEE80211_WORK_REMAIN_ON_CHANNEL:
			rma = ieee80211_remain_on_channel_timeout(wk);
			rma = ieee80211_remain_on_channel_timeout(wk);
			break;
			break;
		case IEEE80211_WORK_ASSOC_BEACON_WAIT:
			rma = ieee80211_assoc_beacon_wait(wk);
			break;
		}
		}


		wk->started = started;
		wk->started = started;
@@ -1065,6 +1107,7 @@ ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
		case IEEE80211_STYPE_PROBE_RESP:
		case IEEE80211_STYPE_PROBE_RESP:
		case IEEE80211_STYPE_ASSOC_RESP:
		case IEEE80211_STYPE_ASSOC_RESP:
		case IEEE80211_STYPE_REASSOC_RESP:
		case IEEE80211_STYPE_REASSOC_RESP:
		case IEEE80211_STYPE_BEACON:
			skb_queue_tail(&local->work_skb_queue, skb);
			skb_queue_tail(&local->work_skb_queue, skb);
			ieee80211_queue_work(&local->hw, &local->work_work);
			ieee80211_queue_work(&local->hw, &local->work_work);
			return RX_QUEUED;
			return RX_QUEUED;