[PATCH 36/71] NAN: Configure peer NMI station and schedule

Andrei Otcheretianski andrei.otcheretianski at intel.com
Wed Apr 1 15:01:45 PDT 2026


Add a new callback for peer schedule configuration and call it during
NDL establishment (as peer schedule may change while NDL is being
negotiated).

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/nan/nan.c              | 84 ++++++++++++++++++++++++++++++++++++--
 src/nan/nan.h              | 20 +++++++++
 src/nan/nan_i.h            |  4 ++
 src/nan/nan_module_tests.c | 45 ++++++++++++++++++--
 src/nan/nan_ndl.c          | 81 ++++++++++++++++++++++++++++++++++++
 5 files changed, 227 insertions(+), 7 deletions(-)

diff --git a/src/nan/nan.c b/src/nan/nan.c
index 6b0bd2b6fd..487e5a8ba5 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -1177,7 +1177,8 @@ static bool nan_ndp_supported(struct nan_data *nan)
 	if (nan->cfg->ndp_action_notif && nan->cfg->ndp_connected &&
 	    nan->cfg->ndp_disconnected &&
 	    nan->cfg->send_naf && nan->cfg->get_chans &&
-	    nan->cfg->is_valid_publish_id)
+	    nan->cfg->is_valid_publish_id &&
+	    nan->cfg->set_peer_schedule)
 		return true;
 
 	wpa_printf(MSG_DEBUG, "NAN: NDP operations are not supported");
@@ -1185,6 +1186,62 @@ static bool nan_ndp_supported(struct nan_data *nan)
 }
 
 
+static void nan_peer_get_committed_avail(const struct nan_data *nan,
+					 const struct nan_peer *peer,
+					 struct nan_peer_schedule *sched);
+
+
+static int nan_configure_peer_schedule(struct nan_data *nan,
+				       struct nan_peer *peer)
+{
+	int ret;
+	struct nan_dev_capa_entry *cur;
+	struct nan_device_capabilities *capa = NULL;
+	struct nan_peer_schedule sched;
+
+	wpa_printf(MSG_DEBUG, "NAN: Configure peer schedule");
+
+	if (!nan_ndl_peer_schedule_intersects(nan, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Cannot configure peer NMI STA - no intersecting schedule");
+		return 0;
+	}
+
+	dl_list_for_each(cur, &peer->info.dev_capa,
+			 struct nan_dev_capa_entry, list) {
+		/*
+		 * Take the first one, as both CDW and channel switch time are
+		 * identical across all attributes
+		 */
+		capa = &cur->capa;
+		break;
+	}
+
+	if (!capa) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Cannot configure peer NMI STA - no device capabilities");
+		return -1;
+	}
+
+	os_memset(&sched, 0, sizeof(sched));
+	nan_peer_get_committed_avail(nan, peer, &sched);
+
+	ret = nan->cfg->set_peer_schedule(nan->cfg->cb_ctx, peer->nmi_addr,
+					  !peer->configured, capa->cdw_info,
+					  peer->info.seq_id,
+					  capa->channel_switch_time, &sched,
+					  NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to set peer schedule");
+		return ret;
+	}
+
+	peer->configured = true;
+	return 0;
+}
+
+
 static void nan_peer_state_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct nan_data *nan = eloop_ctx;
@@ -1416,8 +1473,8 @@ static int nan_action_rx_ndp(struct nan_data *nan, struct nan_peer *peer,
 	if (peer->ndp_setup.state == NAN_NDP_STATE_DONE &&
 	    peer->ndl->state == NAN_NDL_STATE_DONE) {
 		wpa_printf(MSG_DEBUG, "NAN: NAF: NDP setup done");
-
-		if (nan_ndp_connected(nan, peer))
+		if (nan_configure_peer_schedule(nan, peer) ||
+		    nan_ndp_connected(nan, peer))
 			nan_ndp_disconnected(nan, peer,
 					     NAN_REASON_UNSPECIFIED_REASON);
 		return 0;
@@ -1591,7 +1648,8 @@ int nan_tx_status(struct nan_data *nan, const u8 *dst, const u8 *data,
 	if (peer->ndp_setup.state == NAN_NDP_STATE_DONE &&
 	    peer->ndl->state == NAN_NDL_STATE_DONE) {
 		wpa_printf(MSG_DEBUG, "NAN: TX status: NDP setup done");
-		if (nan_ndp_connected(nan, peer))
+		if (nan_configure_peer_schedule(nan, peer) ||
+		    nan_ndp_connected(nan, peer))
 			nan_ndp_disconnected(nan, peer,
 					     NAN_REASON_UNSPECIFIED_REASON);
 	}
@@ -1631,6 +1689,12 @@ int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params)
 
 		naf_oui = NAN_SUBTYPE_DATA_PATH_REQUEST;
 		timeout = NAN_NDP_SETUP_TIMEOUT_LONG;
+		ret = nan_configure_peer_schedule(nan, peer);
+		if (ret) {
+			nan_ndp_setup_stop(nan, peer);
+			return ret;
+		}
+
 		break;
 	case NAN_NDP_ACTION_RESP:
 		/*
@@ -1646,6 +1710,12 @@ int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params)
 
 		if (peer->ndp_setup.status != NAN_NDP_STATUS_REJECTED) {
 			ret = nan_ndl_setup(nan, peer, params);
+			if (!ret) {
+				ret = nan_configure_peer_schedule(nan, peer);
+				if (ret)
+					peer->ndl->send_naf_on_error = 1;
+			}
+
 			if (ret) {
 				if (peer->ndl && peer->ndl->send_naf_on_error) {
 					nan_ndp_setup_failure(
@@ -1665,6 +1735,12 @@ int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params)
 		break;
 	case NAN_NDP_ACTION_CONF:
 		ret = nan_ndl_setup(nan, peer, params);
+		if (!ret) {
+			ret = nan_configure_peer_schedule(nan, peer);
+			if (ret)
+				peer->ndl->send_naf_on_error = 1;
+		}
+
 		if (ret) {
 			if (peer->ndl && peer->ndl->send_naf_on_error) {
 				nan_ndp_setup_failure(
diff --git a/src/nan/nan.h b/src/nan/nan.h
index c6f06382c3..1906d2eed2 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -494,6 +494,26 @@ struct nan_config {
 	 * instance ID; false otherse
 	 */
 	bool (*is_valid_publish_id)(void *ctx, u8 instance_id, u8 *service_id);
+
+	/**
+	 * set_peer_schedule - Configure peer schedule
+	 *
+	 * @ctx: Callback context from cb_ctx
+	 * @nmi_addr: NAN Management Interface address of the peer
+	 * @new_sta: Indicates whether this is a new STA (true) or an existing
+	 *     STA that is being re-configured (false).
+	 * @cdw: Committed DW information (from device capabilities)
+	 * @sequence_id: Schedule sequence ID
+	 * @max_channel_switch_time: Maximum channel switch time
+	 * @sched: Peer schedule information; can be NULL
+	 * @ulw_elems: ULW elements buffer; can be NULL
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_peer_schedule)(void *ctx, const u8 *nmi_addr, bool new_sta,
+				 u16 cdw, u8 sequence_id,
+				 u16 max_channel_switch_time,
+				 const struct nan_peer_schedule *sched,
+				 const struct wpabuf *ulw_elems);
 };
 
 struct nan_data * nan_init(const struct nan_config *cfg);
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index b7be2efa3c..701d98f773 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -406,6 +406,7 @@ struct nan_ndl {
  * struct nan_peer - Represents a known NAN peer
  * @list: List node for linking peers
  * @nmi_addr: NMI of the peer
+ * @configured: Indicates if the peer has been configured to the device
  * @last_seen: Timestamp of the last time this peer was seen
  * @info: Information about the peer
  * @ndps: List of NDPs associated with this peer
@@ -416,6 +417,7 @@ struct nan_ndl {
 struct nan_peer {
 	struct dl_list list;
 	u8 nmi_addr[ETH_ALEN];
+	bool configured;
 	struct os_reltime last_seen;
 	struct nan_peer_info info;
 
@@ -565,6 +567,8 @@ int nan_ndl_add_avail_attrs(struct nan_data *nan, const struct nan_peer *peer,
 void nan_ndl_add_elem_container_attr(const struct nan_data *nan,
 				     const struct nan_peer *peer,
 				     struct wpabuf *buf);
+bool nan_ndl_peer_schedule_intersects(struct nan_data *nan,
+				      struct nan_peer *peer);
 int nan_get_chan_bm(struct nan_data *nan, struct nan_sched_chan *chan,
 		    u8 *op_class, u16 *chan_bm, u16 *pri_chan_bm);
 int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id,
diff --git a/src/nan/nan_module_tests.c b/src/nan/nan_module_tests.c
index 9fc0064308..b3369ea19e 100644
--- a/src/nan/nan_module_tests.c
+++ b/src/nan/nan_module_tests.c
@@ -850,6 +850,28 @@ static bool nan_test_is_valid_publish_id_cb(void *ctx, u8 instance_id,
 	return true;
 }
 
+/*
+ * nan_test_set_peer_schedule_cb - Set peer schedule callback
+ *
+ * @ctx: Pointer to &struct nan_device
+ * @sched: Pointer to &struct nan_peer_schedule to be filled
+ */
+static int
+nan_test_set_peer_schedule_cb(void *ctx, const u8 *nmi_addr, bool new_sta,
+			      u16 cdw, u8 sequence_id,
+			      u16 max_channel_switch_time,
+			      const struct nan_peer_schedule *sched,
+			      const struct wpabuf *ulw_elems)
+{
+	struct nan_device *dev = (struct nan_device *)ctx;
+
+	DEV_NOT_INIT_ERR(dev);
+
+	wpa_printf(MSG_INFO, "%s: %s: Enter", dev->name, __func__);
+	wpa_printf(MSG_INFO, "%s: nmi_addr=" MACSTR " new_sta=%d cdw=%u sequence_id=%u max_channel_switch_time=%u",
+		   dev->name, MAC2STR(nmi_addr), new_sta, cdw, sequence_id, max_channel_switch_time);
+	return 0;
+}
 
 /**
  * nan_test_dev_init - Initialize a test device instance
@@ -871,6 +893,7 @@ static int nan_test_dev_init(struct nan_device *dev)
 	nan.send_naf = nan_test_send_naf_cb;
 	nan.get_chans = nan_test_get_chans_cb;
 	nan.is_valid_publish_id = nan_test_is_valid_publish_id_cb;
+	nan.set_peer_schedule = nan_test_set_peer_schedule_cb;
 
 	/* Awake on every DW on 2 GHz and 5 GHz */
 	nan.dev_capa.cdw_info = 0x9;
@@ -970,7 +993,23 @@ nan_test_setup_devices(struct nan_test_global *global,
 		.master_pref = 2,
 		.dual_band = 1,
 	};
-	const u8 pot_avail[] = {
+	/*
+	 * Device attributes containing:
+	 * 1. Device capability attribute (ID=0x0F, len=10):
+	 *    - map_id=0
+	 *    - cdw_info=0x0009 (awake on every DW on 2G and 5G)
+	 *    - supported_bands=0x07 (2G, 5G, 6G)
+	 *    - op_mode=0x01
+	 *    - n_antennas=0x22
+	 *    - channel_switch_time=0x000a (10)
+	 *    - capa=0x00
+	 * 2. Availability attribute (ID=0x12, len=12)
+	 */
+	const u8 attrs[] = {
+		/* Device capability attribute */
+		0x0f, 0x09, 0x00, 0x00, 0x09, 0x00, 0x07, 0x01,
+		0x22, 0x0a, 0x00, 0x00,
+		/* Availability attribute */
 		0x12, 0x0c, 0x00, 0x01, 0x20, 0x00, 0x07, 0x00,
 		0x1a, 0x00, 0x11, 0x51, 0xff, 0x07, 0x00,
 	};
@@ -989,8 +1028,8 @@ nan_test_setup_devices(struct nan_test_global *global,
 	if (!sub)
 		goto fail;
 
-	nan_add_peer(pub->nan, sub_nmi, pot_avail, sizeof(pot_avail));
-	nan_add_peer(sub->nan, pub_nmi, pot_avail, sizeof(pot_avail));
+	nan_add_peer(pub->nan, sub_nmi, attrs, sizeof(attrs));
+	nan_add_peer(sub->nan, pub_nmi, attrs, sizeof(attrs));
 
 	wpa_printf(MSG_INFO, "\n%s: Done\n", __func__);
 	return sub;
diff --git a/src/nan/nan_ndl.c b/src/nan/nan_ndl.c
index 64689bcbe7..701aed18e0 100644
--- a/src/nan/nan_ndl.c
+++ b/src/nan/nan_ndl.c
@@ -119,6 +119,13 @@ static void nan_ndl_clear(struct nan_data *nan, struct nan_peer *peer)
 		   MAC2STR(peer->nmi_addr),
 		   nan_ndl_state_str(peer->ndl->state), peer->ndl->state);
 
+	if (peer->configured) {
+		nan->cfg->set_peer_schedule(nan->cfg->cb_ctx, peer->nmi_addr,
+					    false, 0, 0, 0, NULL,
+					    NULL);
+		peer->configured = false;
+	}
+
 	os_free(ndl->ndc_sched);
 	ndl->ndc_sched = NULL;
 	ndl->ndc_sched_len = 0;
@@ -1751,3 +1758,77 @@ void nan_ndl_add_elem_container_attr(const struct nan_data *nan,
 	wpabuf_put_u8(buf, 0);
 	wpabuf_put_buf(buf, ndl->sched.elems);
 }
+
+
+/*
+ * nan_ndl_peer_schedule_intersects - Check if local and peer schedules
+ * intersect
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer with whom the NDL is being setup
+ * Returns: True if schedules intersect, false otherwise
+ *
+ * The function checks if the local device schedule intersects with the peer
+ * device schedule.
+ */
+bool nan_ndl_peer_schedule_intersects(struct nan_data *nan,
+				      struct nan_peer *peer)
+{
+	struct nan_schedule *sched;
+	size_t i;
+
+	if (!peer->ndl)
+		return false;
+
+	sched = &peer->ndl->sched;
+
+	/*
+	 * Iterate over all the channels included in the local schedule. For
+	 * each channel convert the committed and conditional slots to a
+	 * bitfield object and extract the operating class and channel bitmap.
+	 *
+	 * Using the operating class and channel bitmap find the peer
+	 * availability on that channel and check if it intersect with the
+	 * local one.
+	 */
+	wpa_printf(MSG_DEBUG, "NAN: n_chans=%u, ndc_map_id=%u",
+		   sched->n_chans, sched->ndc_map_id);
+
+	for (i = 0; i < sched->n_chans; i++) {
+		struct bitfield *own_chan_bf = NULL, *peer_chan_bf = NULL;
+		u16 cbm, pri_cbm;
+		u8 map_id, op_class;
+		int ret;
+
+		/* Convert the schedule for the current channel to bitfield */
+		ret = nan_ndl_convert_chan_sched_to_bf(nan, &sched->chans[i],
+						       &own_chan_bf, &map_id,
+						       &op_class, &cbm,
+						       &pri_cbm);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDL: Failed to convert chan sched to bitfield");
+			return false;
+		}
+
+		/* Get the peer availability for the current channel */
+		peer_chan_bf =
+			nan_avail_entries_to_bf(nan,
+						&peer->info.avail_entries,
+						op_class, cbm, pri_cbm);
+		if (!peer_chan_bf) {
+			bitfield_free(own_chan_bf);
+			continue;
+		}
+
+		ret = bitfield_intersects(own_chan_bf, peer_chan_bf);
+
+		bitfield_free(peer_chan_bf);
+		bitfield_free(own_chan_bf);
+
+		if (ret == 1)
+			return true;
+	}
+
+	return false;
+}
-- 
2.53.0




More information about the Hostap mailing list