--- /dev/null
+Add an optional background scanning threshold triggered by low rssi
+(useful for passing updated scan results to the supplicant ahead of
+time, before losing connectivity entirely)
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+Index: madwifi-trunk-r3776/net80211/ieee80211_ioctl.h
+===================================================================
+--- madwifi-trunk-r3776.orig/net80211/ieee80211_ioctl.h 2008-07-17 03:07:53.000000000 +0200
++++ madwifi-trunk-r3776/net80211/ieee80211_ioctl.h 2008-07-17 04:03:30.000000000 +0200
+@@ -655,6 +655,7 @@
+ IEEE80211_PARAM_MINRATE = 84, /* Minimum rate (by table index) */
+ IEEE80211_PARAM_PROTMODE_RSSI = 85, /* RSSI Threshold for enabling protection mode */
+ IEEE80211_PARAM_PROTMODE_TIMEOUT = 86, /* Timeout for expiring protection mode */
++ IEEE80211_PARAM_BGSCAN_THRESH = 87, /* bg scan rssi threshold */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+Index: madwifi-trunk-r3776/net80211/ieee80211_var.h
+===================================================================
+--- madwifi-trunk-r3776.orig/net80211/ieee80211_var.h 2008-07-17 02:56:18.000000000 +0200
++++ madwifi-trunk-r3776/net80211/ieee80211_var.h 2008-07-17 04:03:07.000000000 +0200
+@@ -92,6 +92,8 @@
+ #define IEEE80211_BGSCAN_IDLE_MIN 100 /* min idle time (ms) */
+ #define IEEE80211_BGSCAN_IDLE_DEFAULT 250 /* default idle time (ms) */
+
++#define IEEE80211_BGSCAN_TRIGGER_INTVL 20 /* min trigger interval for thresh based bgscan (secs) */
++
+ #define IEEE80211_COVERAGE_CLASS_MAX 31 /* max coverage class */
+ #define IEEE80211_REGCLASSIDS_MAX 10 /* max regclass id list */
+
+@@ -229,6 +231,9 @@
+ u_int8_t iv_nickname[IEEE80211_NWID_LEN];
+ u_int iv_bgscanidle; /* bg scan idle threshold */
+ u_int iv_bgscanintvl; /* bg scan min interval */
++ u_int iv_bgscanthr; /* bg scan rssi threshold */
++ u_int iv_bgscantrintvl; /* bg scan trigger interval */
++ unsigned long iv_bgscanthr_next; /* last trigger for bgscan */
+ u_int iv_scanvalid; /* scan cache valid threshold */
+ struct ieee80211_roam iv_roam; /* sta-mode roaming state */
+
+@@ -612,6 +617,7 @@
+ #define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: use software beacon timer */
+ #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */
+ #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */
++#define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */
+
+ #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
+ #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
+Index: madwifi-trunk-r3776/net80211/ieee80211_wireless.c
+===================================================================
+--- madwifi-trunk-r3776.orig/net80211/ieee80211_wireless.c 2008-07-17 03:08:16.000000000 +0200
++++ madwifi-trunk-r3776/net80211/ieee80211_wireless.c 2008-07-17 04:03:07.000000000 +0200
+@@ -2778,6 +2778,9 @@
+ else
+ retv = EINVAL;
+ break;
++ case IEEE80211_PARAM_BGSCAN_THRESH:
++ vap->iv_bgscanthr = value;
++ break;
+ case IEEE80211_PARAM_MCAST_RATE:
+ /* units are in KILObits per second */
+ if (value >= 256 && value <= 54000)
+@@ -3181,6 +3184,9 @@
+ case IEEE80211_PARAM_BGSCAN_INTERVAL:
+ param[0] = vap->iv_bgscanintvl / HZ; /* seconds */
+ break;
++ case IEEE80211_PARAM_BGSCAN_THRESH:
++ param[0] = vap->iv_bgscanthr; /* rssi */
++ break;
+ case IEEE80211_PARAM_MCAST_RATE:
+ param[0] = vap->iv_mcast_rate; /* seconds */
+ break;
+@@ -5704,6 +5710,10 @@
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bgscanintvl" },
+ { IEEE80211_PARAM_BGSCAN_INTERVAL,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bgscanintvl" },
++ { IEEE80211_PARAM_BGSCAN_THRESH,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bgscanthr" },
++ { IEEE80211_PARAM_BGSCAN_THRESH,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bgscanthr" },
+ { IEEE80211_PARAM_MCAST_RATE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcast_rate" },
+ { IEEE80211_PARAM_MCAST_RATE,
+Index: madwifi-trunk-r3776/net80211/ieee80211_input.c
+===================================================================
+--- madwifi-trunk-r3776.orig/net80211/ieee80211_input.c 2008-07-17 02:56:18.000000000 +0200
++++ madwifi-trunk-r3776/net80211/ieee80211_input.c 2008-07-17 04:03:07.000000000 +0200
+@@ -2984,8 +2984,10 @@
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+
++ vap->iv_bgscantrintvl = (vap->iv_bgscantrintvl + 1) % 4;
+ return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
+- time_after(jiffies, ic->ic_lastdata + vap->iv_bgscanidle));
++ (((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN_THR) && !vap->iv_bgscantrintvl) ||
++ time_after(jiffies, ic->ic_lastdata + vap->iv_bgscanidle)));
+ }
+
+ static __inline int
+@@ -3229,6 +3231,23 @@
+ /* record tsf of last beacon */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
++
++ /* When rssi is low, start doing bgscans more frequently to allow
++ * the supplicant to make a better switching decision */
++ if ((rssi < vap->iv_bgscanthr) &&
++ (!vap->iv_bgscanthr_next ||
++ !time_before(jiffies, vap->iv_bgscanthr_next)) &&
++ !(ic->ic_flags & IEEE80211_F_SCAN)) {
++ int ret;
++
++ ic->ic_lastdata = 0;
++ ic->ic_lastscan = 0;
++ ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN_THR;
++ ret = ieee80211_bg_scan(vap);
++ if (ret)
++ vap->iv_bgscanthr_next = jiffies + msecs_to_jiffies(IEEE80211_BGSCAN_TRIGGER_INTVL * 1000);
++ }
++
+ if (ni->ni_intval != scan.bintval) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "beacon interval divergence: "
+Index: madwifi-trunk-r3776/net80211/ieee80211_scan.c
+===================================================================
+--- madwifi-trunk-r3776.orig/net80211/ieee80211_scan.c 2008-07-17 03:07:53.000000000 +0200
++++ madwifi-trunk-r3776/net80211/ieee80211_scan.c 2008-07-17 04:03:07.000000000 +0200
+@@ -793,7 +793,7 @@
+ ieee80211_sta_pwrsave(vap, 0);
+ if (ss->ss_next >= ss->ss_last) {
+ ieee80211_notify_scan_done(vap);
+- ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
++ ic->ic_flags_ext &= ~(IEEE80211_FEXT_BGSCAN|IEEE80211_FEXT_BGSCAN_THR);
+ }
+ }
+ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL;