git: 5edde07c2aba - main - LinuxKPI: 802.11: implement cfg80211_{get,put}_bss

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Sat, 31 Dec 2022 02:33:00 UTC
The branch main has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=5edde07c2aba02d0f7d30bbb0f203ec3e920e273

commit 5edde07c2aba02d0f7d30bbb0f203ec3e920e273
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2022-12-31 01:18:16 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2022-12-31 01:18:16 +0000

    LinuxKPI: 802.11: implement cfg80211_{get,put}_bss
    
    Implement cfg80211_{get,put}_bss currently doing malloc/free bits,
    so hopefully the drivers get the calls right.
    
    cfg80211_get_bss() sets up a lookup structure which may also take a
    result (first hit wins) and calls ieee80211_scan_iterate() comparing
    the various values in the iterator funcion.  Some of the checks are
    partially pointless (as it seems the drivers are not interested in
    these parts [ANY] but we keep them for documentation purposes should
    futher values arise in the future).
    
    We currently only iterate over the first VAP which will do for now.
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      10 days
---
 sys/compat/linuxkpi/common/include/net/cfg80211.h |  51 +++---
 sys/compat/linuxkpi/common/src/linux_80211.c      | 194 +++++++++++++++++++---
 2 files changed, 199 insertions(+), 46 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index e6f1bd88309d..5f7755b44ba7 100644
--- a/sys/compat/linuxkpi/common/include/net/cfg80211.h
+++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h
@@ -1118,6 +1118,10 @@ uint32_t linuxkpi_ieee80211_channel_to_frequency(uint32_t, enum nl80211_band);
 uint32_t linuxkpi_ieee80211_frequency_to_channel(uint32_t, uint32_t);
 struct linuxkpi_ieee80211_channel *
     linuxkpi_ieee80211_get_channel(struct wiphy *, uint32_t);
+struct cfg80211_bss *linuxkpi_cfg80211_get_bss(struct wiphy *,
+    struct linuxkpi_ieee80211_channel *, const uint8_t *,
+    const uint8_t *, size_t, enum ieee80211_bss_type, enum ieee80211_privacy);
+void linuxkpi_cfg80211_put_bss(struct wiphy *, struct cfg80211_bss *);
 void linuxkpi_cfg80211_bss_flush(struct wiphy *);
 
 /* -------------------------------------------------------------------------- */
@@ -1189,6 +1193,32 @@ wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked,
 
 /* -------------------------------------------------------------------------- */
 
+static inline struct cfg80211_bss *
+cfg80211_get_bss(struct wiphy *wiphy, struct linuxkpi_ieee80211_channel *chan,
+    const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len,
+    enum ieee80211_bss_type bss_type, enum ieee80211_privacy privacy)
+{
+
+	return (linuxkpi_cfg80211_get_bss(wiphy, chan, bssid, ssid, ssid_len,
+	   bss_type, privacy));
+}
+
+static inline void
+cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss)
+{
+
+	linuxkpi_cfg80211_put_bss(wiphy, bss);
+}
+
+static inline void
+cfg80211_bss_flush(struct wiphy *wiphy)
+{
+
+	linuxkpi_cfg80211_bss_flush(wiphy);
+}
+
+/* -------------------------------------------------------------------------- */
+
 static __inline bool
 rfkill_blocked(int rfkill)		/* argument type? */
 {
@@ -1383,20 +1413,6 @@ freq_reg_info(struct wiphy *wiphy, uint32_t center_freq)
 	return (NULL);
 }
 
-static __inline struct cfg80211_bss *
-cfg80211_get_bss(struct wiphy *wiphy, struct linuxkpi_ieee80211_channel *chan,
-    const uint8_t *bssid, void *p, int x, uint32_t f1, uint32_t f2)
-{
-	TODO();
-	return (NULL);
-}
-
-static __inline void
-cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss)
-{
-	TODO();
-}
-
 static __inline void
 wiphy_apply_custom_regulatory(struct wiphy *wiphy,
     const struct linuxkpi_ieee80211_regdomain *regd)
@@ -1658,13 +1674,6 @@ ieee80211_get_hdrlen_from_skb(struct sk_buff *skb)
 	return (-1);
 }
 
-static __inline void
-cfg80211_bss_flush(struct wiphy *wiphy)
-{
-
-	linuxkpi_cfg80211_bss_flush(wiphy);
-}
-
 static __inline bool
 cfg80211_channel_is_psc(struct linuxkpi_ieee80211_channel *channel)
 {
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 949f9157fe35..3741996eff1c 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -544,31 +544,6 @@ linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq)
 	return (NULL);
 }
 
-void
-linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
-{
-	struct lkpi_hw *lhw;
-	struct ieee80211com *ic;
-	struct ieee80211vap *vap;
-
-	lhw = wiphy_priv(wiphy);
-	ic = lhw->ic;
-
-	/*
-	 * If we haven't called ieee80211_ifattach() yet
-	 * or there is no VAP, there are no scans to flush.
-	 */
-	if (ic == NULL ||
-	    (lhw->sc_flags & LKPI_MAC80211_DRV_STARTED) == 0)
-		return;
-
-	/* Should only happen on the current one? Not seen it late enough. */
-	IEEE80211_LOCK(ic);
-	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
-		ieee80211_scan_flush(vap);
-	IEEE80211_UNLOCK(ic);
-}
-
 #ifdef LKPI_80211_HW_CRYPTO
 static int
 _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
@@ -4586,6 +4561,175 @@ linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *vif)
 	ieee80211_beacon_miss(vap->iv_ic);
 }
 
+/* -------------------------------------------------------------------------- */
+
+struct lkpi_cfg80211_bss {
+	u_int refcnt;
+	struct cfg80211_bss bss;
+};
+
+struct lkpi_cfg80211_get_bss_iter_lookup {
+	struct wiphy *wiphy;
+	struct linuxkpi_ieee80211_channel *chan;
+	const uint8_t *bssid;
+	const uint8_t *ssid;
+	size_t ssid_len;
+	enum ieee80211_bss_type bss_type;
+	enum ieee80211_privacy privacy;
+
+	/*
+	 * Something to store a copy of the result as the net80211 scan cache
+	 * is not refoucnted so a scan entry might go away any time.
+	 */
+	bool match;
+	struct cfg80211_bss *bss;
+};
+
+static void
+lkpi_cfg80211_get_bss_iterf(void *arg, const struct ieee80211_scan_entry *se)
+{
+	struct lkpi_cfg80211_get_bss_iter_lookup *lookup;
+	size_t ielen;
+
+	lookup = arg;
+
+	/* Do not try to find another match. */
+	if (lookup->match)
+		return;
+
+	/* Nothing to store result. */
+	if (lookup->bss == NULL)
+		return;
+
+	if (lookup->privacy != IEEE80211_PRIVACY_ANY) {
+		/* if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) */
+		/* We have no idea what to compare to as the drivers only request ANY */
+		return;
+	}
+
+	if (lookup->bss_type != IEEE80211_BSS_TYPE_ANY) {
+		/* if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS)) */
+		/* We have no idea what to compare to as the drivers only request ANY */
+		return;
+	}
+
+	if (lookup->chan != NULL) {
+		struct linuxkpi_ieee80211_channel *chan;
+
+		chan = linuxkpi_ieee80211_get_channel(lookup->wiphy,
+		    se->se_chan->ic_freq);
+		if (chan == NULL || chan != lookup->chan)
+			return;
+	}
+
+	if (lookup->bssid && !IEEE80211_ADDR_EQ(lookup->bssid, se->se_bssid))
+		return;
+
+	if (lookup->ssid) {
+		if (lookup->ssid_len != se->se_ssid[1] ||
+		    se->se_ssid[1] == 0)
+			return;
+		if (memcmp(lookup->ssid, se->se_ssid+2, lookup->ssid_len) != 0)
+			return;
+	}
+
+	ielen = se->se_ies.len;
+
+	lookup->bss->ies = malloc(sizeof(*lookup->bss->ies) + ielen,
+	    M_LKPI80211, M_NOWAIT | M_ZERO);
+	if (lookup->bss->ies == NULL)
+		return;
+
+	lookup->bss->ies->data = (uint8_t *)lookup->bss->ies + sizeof(*lookup->bss->ies);
+	lookup->bss->ies->len = ielen;
+	if (ielen)
+		memcpy(lookup->bss->ies->data, se->se_ies.data, ielen);
+
+	lookup->match = true;
+}
+
+struct cfg80211_bss *
+linuxkpi_cfg80211_get_bss(struct wiphy *wiphy, struct linuxkpi_ieee80211_channel *chan,
+    const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len,
+    enum ieee80211_bss_type bss_type, enum ieee80211_privacy privacy)
+{
+	struct lkpi_cfg80211_bss *lbss;
+	struct lkpi_cfg80211_get_bss_iter_lookup lookup;
+	struct lkpi_hw *lhw;
+	struct ieee80211vap *vap;
+
+	lhw = wiphy_priv(wiphy);
+
+	/* Let's hope we can alloc. */
+	lbss = malloc(sizeof(*lbss), M_LKPI80211, M_NOWAIT | M_ZERO);
+	if (lbss == NULL) {
+		ic_printf(lhw->ic, "%s: alloc failed.\n", __func__);
+		return (NULL);
+	}
+
+	lookup.wiphy = wiphy;
+	lookup.chan = chan;
+	lookup.bssid = bssid;
+	lookup.ssid = ssid;
+	lookup.ssid_len = ssid_len;
+	lookup.bss_type = bss_type;
+	lookup.privacy = privacy;
+	lookup.match = false;
+	lookup.bss = &lbss->bss;
+
+	IMPROVE("Iterate over all VAPs comparing perm_addr and addresses?");
+	vap = TAILQ_FIRST(&lhw->ic->ic_vaps);
+	ieee80211_scan_iterate(vap, lkpi_cfg80211_get_bss_iterf, &lookup);
+	if (!lookup.match) {
+		free(lbss, M_LKPI80211);
+		return (NULL);
+	}
+
+	refcount_init(&lbss->refcnt, 1);
+	return (&lbss->bss);
+}
+
+void
+linuxkpi_cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss)
+{
+	struct lkpi_cfg80211_bss *lbss;
+
+	lbss = container_of(bss, struct lkpi_cfg80211_bss, bss);
+
+	/* Free everything again on refcount ... */
+	if (refcount_release(&lbss->refcnt)) {
+		free(lbss->bss.ies, M_LKPI80211);
+		free(lbss, M_LKPI80211);
+	}
+}
+
+void
+linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
+{
+	struct lkpi_hw *lhw;
+	struct ieee80211com *ic;
+	struct ieee80211vap *vap;
+
+	lhw = wiphy_priv(wiphy);
+	ic = lhw->ic;
+
+	/*
+	 * If we haven't called ieee80211_ifattach() yet
+	 * or there is no VAP, there are no scans to flush.
+	 */
+	if (ic == NULL ||
+	    (lhw->sc_flags & LKPI_MAC80211_DRV_STARTED) == 0)
+		return;
+
+	/* Should only happen on the current one? Not seen it late enough. */
+	IEEE80211_LOCK(ic);
+	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+		ieee80211_scan_flush(vap);
+	IEEE80211_UNLOCK(ic);
+}
+
+/* -------------------------------------------------------------------------- */
+
 MODULE_VERSION(linuxkpi_wlan, 1);
 MODULE_DEPEND(linuxkpi_wlan, linuxkpi, 1, 1, 1);
 MODULE_DEPEND(linuxkpi_wlan, wlan, 1, 1, 1);