[RFC v2 21/99] NAN: Add potential availability entries

Andrei Otcheretianski andrei.otcheretianski at intel.com
Tue Dec 23 03:51:25 PST 2025


From: Ilan Peer <ilan.peer at intel.com>

Add support for adding potential availability entries to
the availability attribute.

The potential availability entries are constructed using the
channels supported by the upper layer. These channels are
retrieved using a callback that is expected to provide
the supported channels and their preference for a given
map.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/nan/nan.h      |  47 ++++++++++
 src/nan/nan_util.c | 218 ++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 252 insertions(+), 13 deletions(-)

diff --git a/src/nan/nan.h b/src/nan/nan.h
index 530bf83890..1555bad484 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -274,6 +274,29 @@ struct nan_ndp_action_notif_params {
 	size_t ssi_len;
 };
 
+/**
+ * struct nan_channel_info - Channel information for NAN channel selection
+ * @op_class: Operating class
+ * @channel: Control channel index
+ * @pref: Channel Preference (higher is preferred). Valid values are 0-3.
+ */
+struct nan_channel_info {
+	u8 op_class;
+	u8 channel;
+	u8 pref;
+};
+
+/**
+ * struct nan_channels - Array of channel information entries
+ *
+ * @n_chans: Number of channel information entries
+ * @chans: Array of channel information. Sorted by preference.
+ */
+struct nan_channels {
+	size_t n_chans;
+	struct nan_channel_info *chans;
+};
+
 struct nan_config {
 	void *cb_ctx;
 
@@ -336,6 +359,30 @@ struct nan_config {
 	void (*ndp_disconnected)(void *ctx, struct nan_ndp_id *ndp_id,
 				 const u8 *local_ndi, const u8 *peer_ndi,
 				 enum nan_reason reason);
+
+	/**
+	 * get_chans - Get the prioritized allowed channel information to be
+	 * used for building the potential availability entries associated with
+	 * the given map id.
+	 *
+	 * @ctx: Callback context from cb_ctx
+	 * @map_id: Map ID of the availability attribute for which
+	 *     the channels are requested.
+	 * @chans: Pointer to a nan_channels structure that should be filled
+	 *     with the prioritized frequencies. On successful return the
+	 *     channels should be sorted having the higher priority channels
+	 *     first.
+	 * Returns 0 on success, -1 on failure.
+	 *
+	 * Note: the callback is responsible for allocating chans->chans as
+	 * needed. The caller (the NAN module) is responsible for freeing the
+	 * memory allocated for the chans->chans.
+	 *
+	 * Note: The callback should add all channels that are considered valid
+	 * for use by the NAN module for the given map.
+	 */
+	int (*get_chans)(void *ctx, u8 map_id, struct nan_channels *chans);
+
 };
 
 struct nan_data * nan_init(const struct nan_config *cfg);
diff --git a/src/nan/nan_util.c b/src/nan/nan_util.c
index 57414cdc37..cf37545683 100644
--- a/src/nan/nan_util.c
+++ b/src/nan/nan_util.c
@@ -383,10 +383,7 @@ static u16 nan_add_avail_entry(struct nan_data *nan,
 
 	len_ptr = wpabuf_put(buf, 2);
 
-	/*
-	 * TODO: Need to also add potential entries as otherwise the peer would
-	 * not be able to counter
-	 */
+	/* Potential availability entries are handled separately */
 	if (type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED &&
 	    type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND) {
 		wpa_printf(MSG_DEBUG,
@@ -494,6 +491,152 @@ int nan_get_chan_bm(struct nan_data *nan, struct nan_sched_chan *chan,
 }
 
 
+static void nan_add_pot_avail_entry(struct nan_data *nan,
+				    struct nan_chan_entry *entries,
+				    size_t n_entries, u8 pref,
+				    struct wpabuf *buf)
+{
+	u16 ctrl;
+	u8 chan_ctrl;
+	size_t i;
+	u8 nss = BITS(nan->cfg->dev_capa.n_antennas, NAN_DEV_CAPA_RX_ANT_MASK,
+		      NAN_DEV_CAPA_RX_ANT_POS);
+
+	wpa_printf(MSG_DEBUG, "NAN: Adding potential entry: n_entries=%zu",
+		   n_entries);
+
+	if (!n_entries)
+		return;
+
+	/* The number of channel entries can be too big for the buffer */
+	if (wpabuf_tailroom(buf) < 2 + 2 + 1 + n_entries * 4) {
+		n_entries = (wpabuf_tailroom(buf) - 5) / 4;
+
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Not enough space to add potential entries, reduce to %zu",
+			   n_entries);
+	}
+
+	/*
+	 * ctrl (2) + chan control (1) + n_entries * (nan_chan_entry without
+	 * the aux bitmap).
+	 */
+	wpabuf_put_le16(buf, 3 + n_entries * 4);
+
+	ctrl = NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL;
+	ctrl |= NAN_AVAIL_ENTRY_DEF_UTIL << NAN_AVAIL_ENTRY_CTRL_UTIL_POS;
+	ctrl |= nss << NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS;
+	ctrl |= pref << NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_POS;
+	wpabuf_put_le16(buf, ctrl);
+
+	/* Add all channel entries */
+	chan_ctrl = NAN_BAND_CHAN_CTRL_TYPE;
+	chan_ctrl |= n_entries << NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS;
+	wpabuf_put_u8(buf, chan_ctrl);
+
+	for (i = 0; i < n_entries; i++) {
+		struct nan_chan_entry *cur = &entries[i];
+
+		wpabuf_put_u8(buf, cur->op_class);
+		wpabuf_put_le16(buf, cur->chan_bitmap);
+		wpabuf_put_u8(buf, 0);
+	}
+}
+
+
+static void
+nan_build_pot_avail_entry_with_chans(struct nan_data *nan,
+				     struct nan_channels *pot_chans,
+				     struct wpabuf *buf,
+				     u8 map_id)
+{
+	struct nan_chan_entry chan_entries[global_op_class_size];
+	size_t i, n_entries;
+
+	os_memset(chan_entries, 0, sizeof(chan_entries));
+	n_entries = 0;
+
+	wpa_printf(MSG_DEBUG, "NAN: Adding potential entries: n_chans=%zu",
+		   pot_chans->n_chans);
+
+	for (i = 0; i < pot_chans->n_chans; i++) {
+		struct nan_channel_info *chan = &pot_chans->chans[i];
+		struct nan_chan_entry *cur;
+		u16 cbm = 0;
+		size_t j;
+		int ret;
+
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Channel: op_class=%u, chan=%u, pref=%u",
+			   chan->op_class, chan->channel, chan->pref);
+
+		if ((i > 0) && (pot_chans->chans[i - 1].pref != chan->pref)) {
+			nan_add_pot_avail_entry(nan, chan_entries, n_entries,
+						pot_chans->chans[i - 1].pref,
+						buf);
+
+			os_memset(chan_entries, 0, sizeof(chan_entries));
+			n_entries = 0;
+		}
+
+		ret = nan_chan_to_chan_idx_map(nan, chan->op_class,
+					       chan->channel, &cbm);
+		if (ret)
+			continue;
+
+		/* Try to find and entry that matches the operating class */
+		for (j = 0, cur = NULL; j < n_entries; j++) {
+			cur = &chan_entries[j];
+
+			if (!cur->op_class ||
+			    cur->op_class == chan->op_class)
+				break;
+		}
+
+		if (!n_entries)
+			cur = &chan_entries[n_entries++];
+		else if (j == n_entries && n_entries < global_op_class_size)
+			cur = &chan_entries[n_entries++];
+		else if (!cur)
+			continue;
+
+		cur->op_class = chan->op_class;
+		cur->chan_bitmap |= cbm;
+	}
+
+	if (n_entries)
+		nan_add_pot_avail_entry(nan, chan_entries, n_entries,
+					pot_chans->chans[i - 1].pref, buf);
+
+	wpa_printf(MSG_DEBUG, "NAN: Added potential entries: done");
+}
+
+
+static void nan_build_pot_avail_entry(struct nan_data *nan, struct wpabuf *buf,
+				      u8 map_id)
+{
+	struct nan_channels pot_chans;
+
+	os_memset(&pot_chans, 0, sizeof(pot_chans));
+
+	if (nan->cfg->get_chans(nan->cfg->cb_ctx, map_id, &pot_chans) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to get channels. Not adding potential");
+		return;
+	}
+
+	if (pot_chans.n_chans != 0)
+		nan_build_pot_avail_entry_with_chans(nan, &pot_chans,
+						     buf, map_id);
+	else
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No channels available. Not adding potential: map_id=%u",
+			   map_id);
+
+	os_free(pot_chans.chans);
+}
+
+
 /**
  * nan_add_avail_attrs - Add NAN availability attributes
  * @nan: NAN module context from nan_init()
@@ -518,6 +661,7 @@ int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id,
 			struct wpabuf *buf)
 {
 	u8 last_map_id = NAN_INVALID_MAP_ID;
+	u32 handled_map_ids = 0;
 	u8 *len_ptr = NULL;
 	u8 i;
 
@@ -568,12 +712,14 @@ int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id,
 					   "NAN: Add avail attr done: map_id=%u",
 					   last_map_id);
 
+				nan_build_pot_avail_entry(nan, buf,
+							  last_map_id);
 				WPA_PUT_LE16(len_ptr,
 					     (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
 			}
 
 			last_map_id = chan->map_id;
-			map_ids_bitmap &= ~BIT(last_map_id);
+			handled_map_ids |= BIT(last_map_id);
 
 			wpa_printf(MSG_DEBUG, "NAN: Add avail attr map_id=%u",
 				   last_map_id);
@@ -588,9 +734,11 @@ int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id,
 			 * The spec states that this bit should be set if the
 			 * committed changed or if conditional is included. Set
 			 * it anyway, as it is not known what information the
-			 * peer has on our schedule.
+			 * peer has on our schedule. Similarly, always set the
+			 * potential changed bit.
 			 */
-			ctrl |= NAN_AVAIL_CTRL_COMMITTED_CHANGED;
+			ctrl |= NAN_AVAIL_CTRL_COMMITTED_CHANGED |
+				NAN_AVAIL_CTRL_POTENTIAL_CHANGED;
 			wpabuf_put_le16(buf, ctrl);
 		}
 
@@ -607,16 +755,60 @@ int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id,
 					    op_class, chan_bm, 0, buf);
 	}
 
-	if (last_map_id == NAN_INVALID_MAP_ID) {
+	if (last_map_id != NAN_INVALID_MAP_ID) {
+		nan_build_pot_avail_entry(nan, buf, last_map_id);
+		WPA_PUT_LE16(len_ptr, (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
+
+		wpa_printf(MSG_DEBUG, "NAN: Add avail attr done: map_id=%u",
+			   last_map_id);
+	} else {
 		wpa_printf(MSG_DEBUG,
-			   "NAN: No valid availability entries added");
-		return -1;
+			   "NAN: No committed/conditional entries were added");
 	}
 
-	wpa_printf(MSG_DEBUG, "NAN: Add avail attr done: map_id=%u",
-		   last_map_id);
+	/*
+	 * Add NAN availability attributes with a single potential availability
+	 * entry for map IDs that are not included in the schedule
+	 */
+	map_ids_bitmap &= ~handled_map_ids;
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Add avail attrs for remaining map IDs: bitmap=0x%x",
+		   map_ids_bitmap);
 
-	WPA_PUT_LE16(len_ptr, (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
+	while (map_ids_bitmap) {
+		struct nan_channels pot_chans;
+
+		u8 map_id = ffs(map_ids_bitmap) - 1;
+		u16 ctrl = map_id << NAN_AVAIL_CTRL_MAP_ID_POS |
+			  NAN_AVAIL_CTRL_POTENTIAL_CHANGED;
+
+		map_ids_bitmap &= ~BIT(map_id);
+
+		wpa_printf(MSG_DEBUG, "NAN: Add avail attr for map_id=%u",
+			   map_id);
+
+		os_memset(&pot_chans, 0, sizeof(pot_chans));
+
+		if (nan->cfg->get_chans(nan->cfg->cb_ctx, map_id,
+					&pot_chans) < 0 ||
+		    !pot_chans.chans) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: No channels available. Not adding potential: map_id=%u",
+				   map_id);
+			continue;
+		}
+
+		wpabuf_put_u8(buf, NAN_ATTR_NAN_AVAILABILITY);
+		len_ptr = wpabuf_put(buf, 2);
+		wpabuf_put_u8(buf, sequence_id);
+		wpabuf_put_le16(buf, ctrl);
+
+		nan_build_pot_avail_entry_with_chans(nan, &pot_chans, buf,
+						     map_id);
+		os_free(pot_chans.chans);
+
+		WPA_PUT_LE16(len_ptr, (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
+	}
 
 	return 0;
 }
-- 
2.49.0




More information about the Hostap mailing list