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

Commit 1e4dcd01 authored by Juuso Oikarinen's avatar Juuso Oikarinen Committed by John W. Linville
Browse files

mac80211: Add support for connection monitor in hardware



This patch is based on a RFC patch by Kalle Valo.

The wl1271 has a feature which handles the connection monitor logic
in hardware, basically sending periodically nullfunc frames and reporting
to the host if AP is lost, after attempting to recover by sending
probe-requests to the AP.

Add support to mac80211 by adding a new flag IEEE80211_HW_CONNECTION_MONITOR
which prevents conn_mon_timer from triggering during idle periods, and
prevents sending probe-requests to the AP if beacon-loss is indicated by the
hardware.

Cc: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: default avatarJuuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 43ead78a
Loading
Loading
Loading
Loading
+22 −2
Original line number Diff line number Diff line
@@ -954,6 +954,11 @@ enum ieee80211_tkip_key_type {
 *	Hardware can provide ack status reports of Tx frames to
 *	the stack.
 *
 * @IEEE80211_HW_CONNECTION_MONITOR:
 *      The hardware performs its own connection monitoring, including
 *      periodic keep-alives to the AP and probing the AP on beacon loss.
 *      When this flag is set, signaling beacon-loss will cause an immediate
 *      change to disassociated state.
 */
enum ieee80211_hw_flags {
	IEEE80211_HW_HAS_RATE_CONTROL			= 1<<0,
@@ -975,6 +980,7 @@ enum ieee80211_hw_flags {
	IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS		= 1<<16,
	IEEE80211_HW_SUPPORTS_UAPSD			= 1<<17,
	IEEE80211_HW_REPORTS_TX_ACK_STATUS		= 1<<18,
	IEEE80211_HW_CONNECTION_MONITOR			= 1<<19,
};

/**
@@ -2364,12 +2370,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
 *
 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
 *
 * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and
 * IEEE80211_CONF_PS is set, the driver needs to inform whenever the
 * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and
 * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the
 * hardware is not receiving beacons with this function.
 */
void ieee80211_beacon_loss(struct ieee80211_vif *vif);

/**
 * ieee80211_connection_loss - inform hardware has lost connection to the AP
 *
 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
 *
 * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING, and
 * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver
 * needs to inform if the connection to the AP has been lost.
 *
 * This function will cause immediate change to disassociated state,
 * without connection recovery attempts.
 */
void ieee80211_connection_loss(struct ieee80211_vif *vif);

/* Rate control API */

/**
+2 −2
Original line number Diff line number Diff line
@@ -327,7 +327,7 @@ struct ieee80211_if_managed {
	struct work_struct work;
	struct work_struct monitor_work;
	struct work_struct chswitch_work;
	struct work_struct beacon_loss_work;
	struct work_struct beacon_connection_loss_work;

	unsigned long probe_timeout;
	int probe_send_count;
@@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
			     int powersave);
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
			     struct ieee80211_hdr *hdr);
void ieee80211_beacon_loss_work(struct work_struct *work);
void ieee80211_beacon_connection_loss_work(struct work_struct *work);

void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
				     enum queue_stop_reason reason);
+1 −1
Original line number Diff line number Diff line
@@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev)
		cancel_work_sync(&sdata->u.mgd.work);
		cancel_work_sync(&sdata->u.mgd.chswitch_work);
		cancel_work_sync(&sdata->u.mgd.monitor_work);
		cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
		cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);

		/*
		 * When we get here, the interface is marked down.
+57 −7
Original line number Diff line number Diff line
@@ -851,6 +851,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
	if (is_multicast_ether_addr(hdr->addr1))
		return;

	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
		return;

	mod_timer(&sdata->u.mgd.conn_mon_timer,
		  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
}
@@ -928,23 +931,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
	mutex_unlock(&ifmgd->mtx);
}

void ieee80211_beacon_loss_work(struct work_struct *work)
static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
{
	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
	struct ieee80211_local *local = sdata->local;
	u8 bssid[ETH_ALEN];

	mutex_lock(&ifmgd->mtx);
	if (!ifmgd->associated) {
		mutex_unlock(&ifmgd->mtx);
		return;
	}

	memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);

	printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);

	ieee80211_set_disassoc(sdata);
	ieee80211_recalc_idle(local);
	mutex_unlock(&ifmgd->mtx);
	/*
	 * must be outside lock due to cfg80211,
	 * but that's not a problem.
	 */
	ieee80211_send_deauth_disassoc(sdata, bssid,
				       IEEE80211_STYPE_DEAUTH,
				       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
				       NULL);
}

void ieee80211_beacon_connection_loss_work(struct work_struct *work)
{
	struct ieee80211_sub_if_data *sdata =
		container_of(work, struct ieee80211_sub_if_data,
			     u.mgd.beacon_loss_work);
			     u.mgd.beacon_connection_loss_work);

	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
		__ieee80211_connection_loss(sdata);
	else
		ieee80211_mgd_probe_ap(sdata, true);
}

void ieee80211_beacon_loss(struct ieee80211_vif *vif)
{
	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
	struct ieee80211_hw *hw = &sdata->local->hw;

	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
	WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
}
EXPORT_SYMBOL(ieee80211_beacon_loss);

void ieee80211_connection_loss(struct ieee80211_vif *vif)
{
	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
	struct ieee80211_hw *hw = &sdata->local->hw;

	WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
}
EXPORT_SYMBOL(ieee80211_connection_loss);


static enum rx_mgmt_action __must_check
ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
			 struct ieee80211_mgmt *mgmt, size_t len)
@@ -1634,7 +1682,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
	if (local->quiescing)
		return;

	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
	ieee80211_queue_work(&sdata->local->hw,
			     &sdata->u.mgd.beacon_connection_loss_work);
}

static void ieee80211_sta_conn_mon_timer(unsigned long data)
@@ -1686,7 +1735,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
	 */

	cancel_work_sync(&ifmgd->work);
	cancel_work_sync(&ifmgd->beacon_loss_work);
	cancel_work_sync(&ifmgd->beacon_connection_loss_work);
	if (del_timer_sync(&ifmgd->timer))
		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);

@@ -1720,7 +1769,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
	INIT_WORK(&ifmgd->work, ieee80211_sta_work);
	INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
	INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
	INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
	INIT_WORK(&ifmgd->beacon_connection_loss_work,
		  ieee80211_beacon_connection_loss_work);
	setup_timer(&ifmgd->timer, ieee80211_sta_timer,
		    (unsigned long) sdata);
	setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,