[RFC 48/56] NAN: Add an API to get committed/conditional schedule of a peer

Andrei Otcheretianski andrei.otcheretianski at intel.com
Sun Dec 7 03:18:57 PST 2025


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

In addition add an API to dump the peer schedule into a buffer.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/nan/nan.c      | 267 +++++++++++++++++++++++++++++++++++++++++++++
 src/nan/nan.h      |  40 +++++++
 src/nan/nan_util.c | 109 ++++++++++++++++++
 3 files changed, 416 insertions(+)

diff --git a/src/nan/nan.c b/src/nan/nan.c
index eac97004a0..2aa76fd982 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -11,6 +11,7 @@
 #include "nan.h"
 #include "nan_i.h"
 #include "eloop.h"
+#include "common/ieee802_11_common.h"
 
 #define NAN_MAX_PEERS   32
 #define NAN_MAX_NAF_LEN 1024
@@ -1715,3 +1716,269 @@ int nan_peer_get_tk(struct nan_data *nan, const u8 *addr,
 
 	return nan_sec_get_tk(nan, peer, peer_ndi, local_ndi, tk, tk_len, csid);
 }
+
+
+static void nan_peer_get_committed_avail(struct nan_data *nan,
+					 struct nan_peer *peer,
+					 struct nan_peer_schedule *sched)
+{
+	struct nan_avail_entry *avail;
+
+	dl_list_for_each(avail, &peer->info.avail_entries,
+			 struct nan_avail_entry, list) {
+		struct nan_map *map;
+		struct nan_map_chan *chan;
+		struct nan_sched_chan schan;
+		const struct oper_class_map *op;
+		u8 chan_id;
+		bool committed;
+		int freq, bw, center_freq1, center_freq2, idx;
+		u8 i;
+
+		if (avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED &&
+		    avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND)
+			continue;
+
+		/*
+		 * This should not happen in practice as committed and
+		 * conditional entries should have only a single channel entry
+		 */
+		if (avail->n_band_chan != 1) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Skip availability entry with n_band_chan=%u",
+				   avail->n_band_chan);
+			continue;
+		}
+
+		/* Get all the channel parameters */
+		op = get_oper_class(NULL, avail->band_chan[0].u.chan.op_class);
+		if (!op) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Unknown operating class %u",
+				   avail->band_chan[0].u.chan.op_class);
+			continue;
+		}
+
+		idx = ffs(le_to_host16(avail->band_chan[0].u.chan.chan_bitmap)) - 1;
+		if (idx < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: No channel found in chan_bitmap 0x%04x for oper_class %u",
+				   le_to_host16(avail->band_chan[0].u.chan.chan_bitmap),
+				   avail->band_chan[0].u.chan.op_class);
+			continue;
+		}
+
+		chan_id = op_class_idx_to_chan(op, idx);
+		if (!chan_id) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: No channel found for oper_class %u idx %u",
+				   avail->band_chan[0].u.chan.op_class, idx);
+			continue;
+		}
+
+		freq = ieee80211_chan_to_freq(NULL,
+					      avail->band_chan[0].u.chan.op_class,
+					      chan_id);
+		bw = oper_class_bw_to_int(op);
+
+		center_freq2 = 0;
+		if (op->op_class < 128) {
+			center_freq1 = ieee80211_get_center_freq(freq, op->bw);
+		} else if (op->op_class > 130) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Missing support for op_class %u",
+				   op->op_class);
+			continue;
+		} else {
+			idx = ffs(avail->band_chan[0].u.chan.pri_chan_bitmap) - 1;
+			if (idx < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: No primary channel found in pri_chan_bitmap 0x%04x",
+					   le_to_host16(avail->band_chan[0].u.chan.pri_chan_bitmap));
+				continue;
+			}
+
+			center_freq1 = freq;
+			if (op->bw == BW80 || op->bw == BW80P80)
+				freq = freq - 30 + idx * 20;
+			else if (op->bw == BW160)
+				freq = freq - 70 + idx * 20;
+
+			/* TODO: Missing support for 80 + 80 */
+		}
+
+		committed =
+			(avail->type == NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED);
+
+		/* Find map ID entry if already exists */
+		for (i = 0; i < sched->n_maps; i++)
+			if (sched->maps[i].map_id == avail->map_id)
+				break;
+
+		map = &sched->maps[i];
+		if (i == sched->n_maps) {
+			if (sched->n_maps == NAN_MAX_MAPS) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Too many map entries in schedule");
+				continue;
+			}
+			sched->n_maps++;
+		}
+
+		map->map_id = avail->map_id;
+
+		os_memset(&schan, 0, sizeof(schan));
+
+		/* Find channel entry if already exists */
+		for (i = 0; i < map->n_chans; i++) {
+			if (map->chans[i].committed != committed)
+				continue;
+
+			if (map->chans[i].chan.freq == freq &&
+			    map->chans[i].chan.bandwidth == bw &&
+			    map->chans[i].chan.center_freq1 == center_freq1 &&
+			    map->chans[i].chan.center_freq2 == center_freq2)
+				break;
+		}
+
+		chan = &map->chans[i];
+		if (i == map->n_chans) {
+			if (map->n_chans == NAN_MAX_CHAN_ENTRIES) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Too many channel entries in schedule map_id=%u",
+					   map->map_id);
+				continue;
+			}
+			map->n_chans++;
+		}
+
+		chan->committed = committed;
+		chan->rx_nss = avail->rx_nss;
+		chan->chan.freq = freq;
+		chan->chan.bandwidth = bw;
+		chan->chan.center_freq1 = center_freq1;
+		chan->chan.center_freq2 = center_freq2;
+
+		os_memcpy(&chan->tbm, &avail->tbm, sizeof(avail->tbm));
+	}
+}
+
+
+static void nan_peer_set_sched(struct nan_data *nan, struct nan_peer *peer,
+			       struct nan_peer_schedule *sched,
+			       const u8 *sched_buf, size_t sched_buf_len,
+			       bool ndc)
+{
+	struct dl_list sched_entries;
+	struct nan_avail_entry *cur;
+	int ret;
+
+	if (!sched->n_maps)
+		return;
+
+	if (!sched_buf || !sched_buf_len)
+		return;
+
+	if (sched_buf_len < sizeof(struct nan_sched_entry)) {
+		wpa_printf(MSG_DEBUG, "NAN: Schedule buffer too short=%zu",
+			   sched_buf_len);
+		return;
+	}
+
+	/* Convert the schedule the availability entries */
+	ret = nan_sched_entries_to_avail_entries(nan,
+						 &sched_entries,
+						 (u8 *)sched_buf,
+						 sched_buf_len);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to parse peer schedule entries");
+		return;
+	}
+
+	/*
+	 * For each schedule entry find the corresponding map in the committed
+	 * schedule and store the copy of the time bitmap
+	 */
+	dl_list_for_each(cur, &sched_entries, struct nan_avail_entry, list) {
+		struct nan_map *map;
+		u8 i;
+
+		for (i = 0; i < sched->n_maps; i++) {
+			map = &sched->maps[i];
+
+			if (map->map_id == cur->map_id)
+				break;
+		}
+
+		if (i == sched->n_maps) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: No map entry found for map_id=%u in peer schedule",
+				   cur->map_id);
+			continue;
+		}
+
+		if (ndc)
+			os_memcpy(&map->ndc, &cur->tbm, sizeof(cur->tbm));
+		else
+			os_memcpy(&map->immutable, &cur->tbm, sizeof(cur->tbm));
+	}
+
+	nan_flush_avail_entries(&sched_entries);
+}
+
+
+static void nan_peer_get_ndc_sched(struct nan_data *nan,
+				   struct nan_peer *peer,
+				   struct nan_peer_schedule *sched)
+{
+	if (!peer->ndl)
+		return;
+
+	nan_peer_set_sched(nan, peer, sched,
+			   peer->ndl->ndc_sched,
+			   peer->ndl->ndc_sched_len, true);
+}
+
+
+static void nan_peer_get_immut_sched(struct nan_data *nan,
+				     struct nan_peer *peer,
+				     struct nan_peer_schedule *sched)
+{
+	if (!peer->ndl)
+		return;
+
+	nan_peer_set_sched(nan, peer, sched,
+			   peer->ndl->immut_sched,
+			   peer->ndl->immut_sched_len, false);
+}
+
+
+
+/*
+ * nan_peer_get_schedule_info - Get peer's schedule information
+ * @nan: NAN module context from nan_init()
+ * @addr: NAN MAC address of the peer
+ * @sched: on return would hold the schedule information.
+ * Return 0 on success; -1 otherwise.
+ */
+int nan_peer_get_schedule_info(struct nan_data *nan, const u8 *addr,
+			       struct nan_peer_schedule *sched)
+{
+	struct nan_peer *peer;
+
+	if (!nan || !sched)
+		return -1;
+
+	os_memset(sched, 0, sizeof(*sched));
+
+	peer = nan_get_peer(nan, addr);
+	if (!peer)
+		return -1;
+
+	nan_peer_get_committed_avail(nan, peer, sched);
+	nan_peer_get_ndc_sched(nan, peer, sched);
+	nan_peer_get_immut_sched(nan, peer, sched);
+
+	return 0;
+}
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 4b8cc437c2..1f9e4edfb1 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -319,6 +319,42 @@ struct nan_channels {
 	struct nan_channel_info *chans;
 };
 
+#define NAN_MAX_MAPS 8
+#define NAN_MAX_CHAN_ENTRIES 16
+
+/**
+ * struct nan_peer_schedule - NAN peer schedule information
+ *
+ * @n_maps: Number of maps
+ * @maps: Array of maps
+ * @map_id: Map ID
+ * @n_chans: Number of channels in the map
+ * @chans: Array of channels in the map
+ * @committed: Committed schedule bitmap for the channel
+ * @rx_nss: Number of spatial streams supported by the peer for RX on this
+ *     channel
+ * @chan: Channel information
+ * @tbm: Time bitmap for the channel
+ * @ndc: NDC time bitmap for the map
+ * @immutable: Immutable time bitmap for the map
+ */
+struct nan_peer_schedule {
+	u8 n_maps;
+	struct nan_map {
+		u8 map_id;
+		u8 n_chans;
+		struct nan_map_chan{
+			bool committed;
+			u8 rx_nss;
+			struct nan_sched_chan chan;
+			struct nan_time_bitmap tbm;
+		} chans[NAN_MAX_CHAN_ENTRIES];
+
+		struct nan_time_bitmap ndc;
+		struct nan_time_bitmap immutable;
+	} maps[NAN_MAX_MAPS];
+};
+
 struct nan_config {
 	void *cb_ctx;
 	u8 nmi_addr[ETH_ALEN];
@@ -455,4 +491,8 @@ int nan_peer_get_tk(struct nan_data *nan, const u8 *addr,
 		    const u8 *peer_ndi, const u8 *local_ndi,
 		    u8 *tk, size_t *tk_len,
 		    enum nan_cipher_suite_id *csid);
+int nan_peer_get_schedule_info(struct nan_data *nan, const u8 *addr,
+			       struct nan_peer_schedule *sched);
+int nan_peer_dump_sched_to_buf(struct nan_peer_schedule *sched,
+			       char *buf, size_t buflen);
 #endif /* NAN_H */
diff --git a/src/nan/nan_util.c b/src/nan/nan_util.c
index 304b9cc66c..2b7afc1fb9 100644
--- a/src/nan/nan_util.c
+++ b/src/nan/nan_util.c
@@ -1446,3 +1446,112 @@ fail:
 	bitfield_free(res_bf);
 	return NULL;
 }
+
+
+/**
+ * nan_peer_dump_sched_to_buf - Dump peer schedule to a buffer
+ *
+ * @sched: Peer schedule
+ * @buf: Output buffer
+ * @buflen: The length of &buf in bytes
+ *
+ * Returns: The number of characters written to the buffer, or -1 on error,
+ * which indicates that the buffer was too small.
+ */
+int nan_peer_dump_sched_to_buf(struct nan_peer_schedule *sched,
+			       char *buf, size_t buflen)
+{
+	int i, j, ret;
+	char *pos = buf;
+	char *end = buf + buflen;
+
+	for (i = 0; i < sched->n_maps; i++) {
+		struct nan_map *map = &sched->maps[i];
+
+		ret = wpa_scnprintf(pos, end - pos,
+				    "MAP [%u]\n\tmap_id=%u\n\tn_chans=%u\n",
+				    i, map->map_id, map->n_chans);
+		if (os_snprintf_error(end - pos, ret))
+			goto err;
+		pos += ret;
+
+		for (j = 0; j < map->n_chans; j++) {
+			struct nan_map_chan *chan = &map->chans[j];
+
+			ret = wpa_scnprintf(pos, end - pos,
+					    "\tchannel[%u]: committed=%u rx_nss=%u freq=%u bw=%u cfreq1=%u cfreq2=%u\n",
+					    j, chan->committed, chan->rx_nss,
+					    chan->chan.freq, chan->chan.bandwidth,
+					    chan->chan.center_freq1,
+					    chan->chan.center_freq2);
+			if (os_snprintf_error(end - pos, ret))
+				goto err;
+			pos += ret;
+
+			ret = wpa_scnprintf(pos, end - pos,
+					    "\t\tbitmap: period=%u duration=%u offset=%u ",
+					    BIT(6 + chan->tbm.period),
+					    BIT(4 + chan->tbm.duration),
+					    16 * chan->tbm.offset);
+			if (os_snprintf_error(end - pos, ret))
+				goto err;
+			pos += ret;
+
+			ret = wpa_scnprintf(pos, end - pos, "bitmap=");
+			if (os_snprintf_error(end - pos, ret))
+				goto err;
+			pos += ret;
+
+			ret = wpa_snprintf_hex(pos, end - pos, chan->tbm.bitmap,
+					       chan->tbm.len);
+			if (os_snprintf_error(end - pos, ret))
+				goto err;
+			pos += ret;
+
+			ret = wpa_scnprintf(pos, end - pos, "\n");
+			if (os_snprintf_error(end - pos, ret))
+				goto err;
+			pos += ret;
+		}
+
+		ret = wpa_scnprintf(pos, end - pos,
+				    "\tndc: period=%u duration=%u offset=%u bitmap=",
+				    BIT(6 + map->ndc.period),
+				    BIT(4 + map->ndc.duration),
+				    16 * map->ndc.offset);
+		if (os_snprintf_error(end - pos, ret))
+			goto err;
+		pos += ret;
+
+		ret = wpa_snprintf_hex(pos, end - pos, map->ndc.bitmap,
+				       map->ndc.len);
+		if (os_snprintf_error(end - pos, ret))
+			goto err;
+		pos += ret;
+
+		ret = wpa_scnprintf(pos, end - pos,
+				    "\n\timmutable: period=%u duration=%u offset=%u bitmap=",
+				    BIT(6 + map->immutable.period),
+				    BIT(4 + map->immutable.duration),
+				    16 * map->immutable.offset);
+		if (os_snprintf_error(end - pos, ret))
+			goto err;
+		pos += ret;
+
+		ret = wpa_snprintf_hex(pos, end - pos, map->immutable.bitmap,
+				       map->immutable.len);
+		if (os_snprintf_error(end - pos, ret))
+			goto err;
+		pos += ret;
+
+		ret = wpa_scnprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			goto err;
+		pos += ret;
+	}
+
+	return pos - buf;
+err:
+	wpa_printf(MSG_DEBUG, "NAN: Buffer too small to dump peer schedule");
+	return -1;
+}
-- 
2.49.0




More information about the Hostap mailing list