[PATCH 52/71] nl80211: Add support for sending NAN schedule commands

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


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

Add support for configuring and sending:

- NAN local schedule.
- NAN peer schedule.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/drivers/driver_nl80211.c | 365 +++++++++++++++++++++++++++++++++--
 1 file changed, 345 insertions(+), 20 deletions(-)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 58f6e9632f..6869ccd4da 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5408,6 +5408,36 @@ err:
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
+static int nl80211_bw_to_nl(int bw, enum nl80211_chan_width *cw,
+			    int center_freq2)
+{
+	switch (bw) {
+	case 20:
+		*cw = NL80211_CHAN_WIDTH_20;
+		break;
+	case 40:
+		*cw = NL80211_CHAN_WIDTH_40;
+		break;
+	case 80:
+		if (center_freq2)
+			*cw = NL80211_CHAN_WIDTH_80P80;
+		else
+			*cw = NL80211_CHAN_WIDTH_80;
+		break;
+	case 160:
+		*cw = NL80211_CHAN_WIDTH_160;
+		break;
+	case 320:
+		*cw = NL80211_CHAN_WIDTH_320;
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int nl80211_put_freq_params(struct nl_msg *msg,
 				   const struct hostapd_freq_params *freq,
 				   struct i802_bss *bss)
@@ -5434,28 +5464,16 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
 	if (freq->vht_enabled ||
 	    ((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) {
 		enum nl80211_chan_width cw;
+		int ret;
 
 		wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
-		switch (freq->bandwidth) {
-		case 20:
-			cw = NL80211_CHAN_WIDTH_20;
-			break;
-		case 40:
-			cw = NL80211_CHAN_WIDTH_40;
-			break;
-		case 80:
-			if (freq->center_freq2)
-				cw = NL80211_CHAN_WIDTH_80P80;
-			else
-				cw = NL80211_CHAN_WIDTH_80;
-			break;
-		case 160:
-			cw = NL80211_CHAN_WIDTH_160;
-			break;
-		case 320:
-			cw = NL80211_CHAN_WIDTH_320;
-			break;
-		default:
+		ret = nl80211_bw_to_nl(freq->bandwidth, &cw,
+				       freq->center_freq2);
+
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "  * Unsupported bandwidth %d MHz",
+				   freq->bandwidth);
 			return -EINVAL;
 		}
 
@@ -15514,6 +15532,311 @@ static void wpa_driver_nl80211_nan_stop(void *priv)
 	nl80211_nan_stop(bss);
 }
 
+
+static int
+wpa_driver_nl80211_nan_set_channel(struct wpa_driver_nl80211_data *drv,
+				     struct nan_schedule_channel *chan,
+				     struct nl_msg *msg)
+{
+	struct nlattr *chan_attr;
+	enum nl80211_chan_width cw;
+	int ret;
+
+	chan_attr = nla_nest_start(msg, NL80211_ATTR_NAN_CHANNEL);
+	if (!chan)
+		return -ENOBUFS;
+
+	ret = nl80211_bw_to_nl(chan->bandwidth, &cw, chan->center_freq2);
+	if (ret)
+		return -EINVAL;
+
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->freq) ||
+	    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chan->center_freq1) ||
+	    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chan->center_freq2) ||
+	    nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+	    nla_put(msg, NL80211_ATTR_NAN_CHANNEL_ENTRY,
+		    sizeof(chan->chan_entry), chan->chan_entry)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: NAN: Failed to put channel attributes");
+		return -EINVAL;
+	}
+
+	if (chan->rx_nss &&
+	    nla_put_u8(msg, NL80211_ATTR_NAN_RX_NSS, chan->rx_nss)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: NAN: Failed to put RX NSS attribute");
+		return -EINVAL;
+	}
+
+	nla_nest_end(msg, chan_attr);
+	return 0;
+}
+
+
+static int
+wpa_driver_nl80211_nan_set_slots(struct wpa_driver_nl80211_data *drv,
+				 struct wpabuf *chan_time_bitmap,
+				 u8 *slots, u8 chan_idx)
+{
+	const u8 *time_bitmap;
+	size_t time_bitmap_len, idx;
+
+	if (!chan_time_bitmap)
+		return 0;
+
+	time_bitmap = wpabuf_head(chan_time_bitmap);
+	time_bitmap_len = wpabuf_len(chan_time_bitmap);
+	if (!time_bitmap || !time_bitmap_len)
+		return 0;
+
+	/*
+	 * nl80211 always uses a period of 512 TUs with slot duration of
+	 * 16 TUs, so the map length must be equal to 4
+	 */
+	if (time_bitmap_len != 4) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: NAN: Invalid time bitmap len=%zu",
+			   time_bitmap_len);
+		return -EINVAL;
+	}
+
+	for (idx = 0; idx < time_bitmap_len * 8; idx++) {
+		size_t byte_idx = idx / 8;
+		size_t bit_idx = idx % 8;
+
+		if (time_bitmap[byte_idx] & BIT(bit_idx))
+			slots[idx] = chan_idx;
+	}
+
+	return 0;
+}
+
+
+static int
+wpa_driver_nl80211_nan_set_schedule(struct wpa_driver_nl80211_data *drv,
+				    struct nan_schedule_config *sched,
+				    struct nl_msg *msg)
+{
+	u8 slots[32];
+	size_t i;
+
+	os_memset(slots, NL80211_NAN_SCHED_NOT_AVAIL_SLOT, sizeof(slots));
+
+	for (i = 0; i < sched->num_channels; i++) {
+		int ret = wpa_driver_nl80211_nan_set_channel(drv,
+							     &sched->channels[i],
+							     msg);
+		if (ret)
+			return ret;
+
+		ret = wpa_driver_nl80211_nan_set_slots(drv,
+						       sched->channels[i].time_bitmap,
+						       slots, i);
+		if (ret)
+			return ret;
+	}
+
+	return nla_put(msg, NL80211_ATTR_NAN_TIME_SLOTS, sizeof(slots), slots);
+}
+
+
+static int
+wpa_driver_nl80211_nan_config_schedule(void *priv, u8 map_id,
+				       struct nan_schedule_config *sched)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started)
+		return -EOPNOTSUPP;
+
+	if (!sched)
+		return -EINVAL;
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NAN_SET_LOCAL_SCHED);
+	if (!msg) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to alloc NAN local schedule command");
+		return -ENOMEM;
+	}
+
+	ret = wpa_driver_nl80211_nan_set_schedule(drv, sched, msg);
+	if (ret)
+		goto fail;
+
+	if (sched->avail_attr &&
+	    nl80211_attr_supported(drv, NL80211_ATTR_NAN_AVAIL_BLOB) &&
+	    nla_put(msg, NL80211_ATTR_NAN_AVAIL_BLOB,
+		    wpabuf_len(sched->avail_attr),
+		    wpabuf_head(sched->avail_attr)))
+		goto fail;
+
+	ret = send_and_recv_cmd(bss->drv, msg);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to send NAN local schedule command: %d",
+			   ret);
+
+	return ret;
+
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int
+wpa_driver_nl80211_nan_config_peer_schedule(void *priv, const u8 *peer,
+					    u16 cdw, u8 sequence_id,
+					    u16 max_chan_switch_time,
+					    const struct wpabuf *ulw,
+					    struct nan_peer_schedule_config *sched)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nan_schedule_channel *channels[MAX_NUM_NAN_MAPS * MAX_NUM_NAN_SCHEDULE_CHANNELS];
+	u8 chan_remap[MAX_NUM_NAN_MAPS][MAX_NUM_NAN_SCHEDULE_CHANNELS];
+	size_t map_idx, chan_idx, n_channels = 0;
+	struct nl_msg *msg;
+	struct nlattr *maps;
+	int ret;
+
+	if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started)
+		return -EOPNOTSUPP;
+
+	if (!sched)
+		return -EINVAL;
+
+	os_memset(channels, 0, sizeof(channels));
+	os_memset(chan_remap, 0, sizeof(chan_remap));
+
+	/*
+	 * Kernel expects channels at the top level, and peer maps reference
+	 * them by index. Build a global channel list from all maps.
+	 */
+	for (map_idx = 0; map_idx < sched->n_maps; map_idx++) {
+		struct nan_schedule_map *map = &sched->maps[map_idx];
+
+		for (chan_idx = 0; chan_idx < map->sched.num_channels;
+		     chan_idx++) {
+			size_t i;
+
+			/* Check if we already have this channel */
+			for (i = 0; i < n_channels; i++) {
+				if (os_memcmp(&map->sched.channels[chan_idx].chan_entry,
+					      channels[i]->chan_entry,
+					      sizeof(channels[i]->chan_entry)) == 0)
+					break;
+			}
+
+			if (i < n_channels) {
+				chan_remap[map_idx][chan_idx] = i;
+				continue;
+			}
+
+			channels[n_channels] = &map->sched.channels[chan_idx];
+			chan_remap[map_idx][chan_idx] = n_channels++;
+		}
+	}
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NAN_SET_PEER_SCHED);
+	if (!msg) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to alloc NAN peer schedule command");
+		return -ENOMEM;
+	}
+
+	if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
+	    nla_put_u16(msg, NL80211_ATTR_NAN_COMMITTED_DW, cdw) ||
+	    nla_put_u16(msg, NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME,
+			max_chan_switch_time) ||
+	    (sched->n_maps &&
+	     nla_put_u8(msg, NL80211_ATTR_NAN_SEQ_ID, sequence_id))) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: NAN: Failed to put peer schedule attributes");
+		goto fail;
+	}
+
+	if (ulw && wpabuf_len(ulw)) {
+		if (nla_put(msg, NL80211_ATTR_NAN_ULW,
+			    wpabuf_len(ulw), wpabuf_head(ulw))) {
+			wpa_printf(MSG_ERROR,
+				   "nl80211: NAN: Failed to put ULW attribute");
+			goto fail;
+		}
+	}
+
+	/* Set the channels */
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: NAN: Setting %zu channels for peer schedule",
+		   n_channels);
+
+	for (chan_idx = 0; chan_idx < n_channels; chan_idx++) {
+		ret = wpa_driver_nl80211_nan_set_channel(drv,
+							 channels[chan_idx],
+							 msg);
+		if (ret)
+			goto fail;
+	}
+
+	/* Set the maps */
+	maps = nla_nest_start(msg, NL80211_ATTR_NAN_PEER_MAPS);
+	if (!maps)
+		goto fail;
+
+	for (map_idx = 0; map_idx < sched->n_maps; map_idx++) {
+		struct nlattr *map = nla_nest_start(msg, map_idx + 1);
+		u8 slots[32];
+
+		if (!map)
+			goto fail;
+
+		if (nla_put_u8(msg, NL80211_NAN_PEER_MAP_ATTR_MAP_ID,
+			       sched->maps[map_idx].map_id))
+			goto fail;
+
+		os_memset(slots, NL80211_NAN_SCHED_NOT_AVAIL_SLOT,
+			  sizeof(slots));
+
+		for (chan_idx = 0;
+		     chan_idx < sched->maps[map_idx].sched.num_channels;
+		     chan_idx++) {
+			struct nan_schedule_channel *chan =
+				&sched->maps[map_idx].sched.channels[chan_idx];
+
+			ret = wpa_driver_nl80211_nan_set_slots(drv,
+							       chan->time_bitmap,
+							       slots,
+							       chan_remap[map_idx][chan_idx]);
+			if (ret)
+				goto fail;
+		}
+
+		if (nla_put(msg, NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS,
+			    sizeof(slots), slots))
+			goto fail;
+
+		nla_nest_end(msg, map);
+	}
+
+	nla_nest_end(msg, maps);
+
+	ret = send_and_recv_cmd(bss->drv, msg);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to send NAN peer schedule command: %d",
+			   ret);
+
+	return ret;
+
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
 #endif /* CONFIG_NAN */
 
 
@@ -15692,5 +16015,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.nan_start = wpa_driver_nl80211_nan_start,
 	.nan_stop = wpa_driver_nl80211_nan_stop,
 	.nan_change_config = wpa_driver_nl80211_nan_change_config,
+	.nan_config_schedule = wpa_driver_nl80211_nan_config_schedule,
+	.nan_config_peer_schedule = wpa_driver_nl80211_nan_config_peer_schedule,
 #endif /* CONFIG_NAN */
 };
-- 
2.53.0




More information about the Hostap mailing list