[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