[PATCH v3 18/46] nl80211: Add Proximity Detection (PD) wdev support
Kavita Kavita
kavita.kavita at oss.qualcomm.com
Wed May 13 02:59:42 PDT 2026
From: Veerendranath Jakkam <vjakkam at qti.qualcomm.com>
Introduce driver and nl80211 support for a Proximity Detection (PD)
virtual interface, enabled under CONFIG_PR.
Two new driver ops are added:
- pd_start(): create a non-netdev PD wdev (NL80211_IFTYPE_PD), activate
it via NL80211_CMD_START_PD, and return the effective PD MAC address
- pd_stop(): deactivate the PD wdev via NL80211_CMD_STOP_PD, then
destroy and delete it
On the nl80211 side, nl80211_pd_start() allocates a dedicated i802_bss
(drv->pd_bss) outside the regular BSS list, creates the PD wdev via
nl80211_create_iface(), and activates it. nl80211_pd_stop() tears it
down in the correct order: deactivate first, then destroy BSS resources,
then delete the wdev. The driver deinit path calls nl80211_pd_stop() if
a PD wdev is still active.
The wdev_info struct and its netlink callback are generalized and
renamed to nl80211_wdev_handler_info to capture both wdev_id and MAC
address, replacing the previous nl80211_wdev_handler. This handler is
shared between PD wdev creation and the existing non-netdev interface
path in wpa_driver_nl80211_if_add().
wpa_supplicant is updated with wpa_drv_pd_start() and wpa_drv_pd_stop()
wrappers that call into the driver if CONFIG_PR is enabled.
Signed-off-by: Veerendranath Jakkam <vjakkam at qti.qualcomm.com>
---
src/drivers/driver.h | 28 ++++++
src/drivers/driver_nl80211.c | 178 +++++++++++++++++++++++++++++------
src/drivers/driver_nl80211.h | 3 +
wpa_supplicant/driver_i.h | 20 ++++
4 files changed, 202 insertions(+), 27 deletions(-)
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index ba53dc60d..ff8d2fd27 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -5897,6 +5897,34 @@ struct wpa_driver_ops {
const struct wpabuf *ulw,
struct nan_peer_schedule_config *sched);
#endif /* CONFIG_NAN */
+
+#ifdef CONFIG_PR
+ /**
+ * pd_start - Create and activate a PD (Proximity Detection) wdev
+ * @priv: Private driver interface data
+ * @addr: Requested MAC address for the PD wdev
+ * @pd_addr: Buffer (ETH_ALEN) to receive the actual assigned MAC
+ * Returns: 0 on success, -1 on failure
+ *
+ * Creates a PD virtual interface in the driver and activates it via
+ * NL80211_CMD_START_PD. The PD wdev is associated with the parent
+ * BSS; all PD operations (key install, TX, peer measurement) are
+ * routed through it by matching the PD MAC address.
+ */
+ int (*pd_start)(void *priv, const u8 *addr, u8 *pd_addr);
+
+ /**
+ * pd_stop - Stop and destroy the PD wdev
+ * @priv: Private driver interface data
+ *
+ * Sends NL80211_CMD_STOP_PD, deletes the PD wdev via
+ * NL80211_CMD_DEL_INTERFACE, and frees all associated driver
+ * resources. Safe to call when no PD wdev is active (no-op).
+ */
+ void (*pd_stop)(void *priv);
+
+#endif /* CONFIG_PR */
+
};
/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index ea214b74b..c392a4b64 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -199,6 +199,9 @@ static int nl80211_put_mesh_config(struct nl_msg *msg,
#endif /* CONFIG_MESH */
static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason, int link_id);
+#ifdef CONFIG_PR
+static void nl80211_pd_stop(void *priv);
+#endif /* CONFIG_PR */
/* Converts nl80211_chan_width to a common format */
@@ -3240,6 +3243,31 @@ static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
return ret;
}
+struct wdev_info {
+ u64 wdev_id;
+ int wdev_id_set;
+ u8 macaddr[ETH_ALEN];
+};
+
+static int nl80211_wdev_handler_info(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct wdev_info *wi = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (tb[NL80211_ATTR_WDEV]) {
+ wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+ wi->wdev_id_set = 1;
+ }
+ if (tb[NL80211_ATTR_MAC])
+ os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+ return NL_SKIP;
+}
+
+
static int nl80211_set_pr_dev(struct i802_bss *bss, int start)
{
struct nl_msg *msg;
@@ -3257,6 +3285,120 @@ static int nl80211_set_pr_dev(struct i802_bss *bss, int start)
}
+#ifdef CONFIG_PR
+/**
+ * nl80211_pd_start - Create and activate a PD wdev
+ *
+ * Creates a PD virtual interface (NL80211_IFTYPE_PD) in the kernel and
+ * activates it via NL80211_CMD_START_PD. The resulting i802_bss is stored
+ * in drv->pd_bss with ctx pointing to the parent wpa_s so that events
+ * arriving on the PD wdev_id are routed back to the parent.
+ */
+static int nl80211_pd_start(void *priv, const u8 *addr, u8 *pd_addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct i802_bss *pd_bss;
+ struct wdev_info info;
+ int ret;
+
+ if (drv->pd_bss) {
+ wpa_printf(MSG_DEBUG, "nl80211: PD wdev already active");
+ if (pd_addr)
+ os_memcpy(pd_addr, drv->pd_bss->addr, ETH_ALEN);
+ return 0;
+ }
+
+ pd_bss = os_zalloc(sizeof(*pd_bss));
+ if (!pd_bss)
+ return -1;
+
+ pd_bss->drv = drv;
+ pd_bss->ctx = bss->ctx; /* parent wpa_s */
+ pd_bss->flink = &pd_bss->links[0];
+ os_strlcpy(pd_bss->ifname, "pd-wdev", sizeof(pd_bss->ifname));
+
+ os_memset(&info, 0, sizeof(info));
+ ret = nl80211_create_iface(drv, "pd-wdev", NL80211_IFTYPE_PD, addr,
+ 0, nl80211_wdev_handler_info, &info, 0);
+ if (ret || !info.wdev_id_set) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to create PD wdev (ret=%d)", ret);
+ os_free(pd_bss);
+ return -1;
+ }
+
+ pd_bss->wdev_id = info.wdev_id;
+ pd_bss->wdev_id_set = 1;
+ if (!is_zero_ether_addr(info.macaddr))
+ os_memcpy(pd_bss->addr, info.macaddr, ETH_ALEN);
+ else if (addr)
+ os_memcpy(pd_bss->addr, addr, ETH_ALEN);
+ os_memcpy(pd_bss->flink->addr, pd_bss->addr, ETH_ALEN);
+
+ /* Activate the PD device */
+ ret = nl80211_set_pr_dev(pd_bss, 1);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to start PD device (ret=%d)", ret);
+ goto failed;
+ }
+
+ if (nl80211_init_bss(pd_bss)) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to init PD BSS");
+ goto failed_stop;
+ }
+
+ if (nl80211_mgmt_subscribe_non_ap(pd_bss))
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to register frame processing for PD interface - ignore for now");
+
+ drv->pd_bss = pd_bss;
+
+ if (pd_addr)
+ os_memcpy(pd_addr, pd_bss->addr, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: PD wdev created wdev_id=0x%llx addr=" MACSTR,
+ (unsigned long long) pd_bss->wdev_id,
+ MAC2STR(pd_bss->addr));
+ return 0;
+
+failed_stop:
+ nl80211_set_pr_dev(pd_bss, 0);
+failed:
+ nl80211_del_non_netdev(pd_bss);
+ os_free(pd_bss);
+ return -1;
+}
+
+
+/**
+ * nl80211_pd_stop - Stop and destroy the PD wdev
+ */
+static void nl80211_pd_stop(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!drv->pd_bss)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Stopping PD wdev wdev_id=0x%llx addr=" MACSTR,
+ (unsigned long long) drv->pd_bss->wdev_id,
+ MAC2STR(drv->pd_bss->addr));
+
+ nl80211_set_pr_dev(drv->pd_bss, 0);
+ nl80211_destroy_bss(drv->pd_bss);
+ nl80211_del_non_netdev(drv->pd_bss);
+ os_free(drv->pd_bss);
+ drv->pd_bss = NULL;
+}
+#endif /* CONFIG_PR */
+
+
#ifdef CONFIG_NAN
static void nl80211_nan_stop(struct i802_bss *bss)
{
@@ -3621,6 +3763,10 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
eloop_unregister_read_sock(drv->eapol_tx_sock);
if (drv->eapol_tx_sock >= 0)
close(drv->eapol_tx_sock);
+#ifdef CONFIG_PR
+ if (drv->pd_bss)
+ nl80211_pd_stop(bss);
+#endif /* CONFIG_PR */
if (bss->nl_preq)
wpa_driver_nl80211_probe_req_report(bss, 0);
@@ -9529,32 +9675,6 @@ static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
}
-struct wdev_info {
- u64 wdev_id;
- int wdev_id_set;
- u8 macaddr[ETH_ALEN];
-};
-
-static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
-{
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct wdev_info *wi = arg;
-
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
- if (tb[NL80211_ATTR_WDEV]) {
- wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
- wi->wdev_id_set = 1;
- }
-
- if (tb[NL80211_ATTR_MAC])
- os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
- ETH_ALEN);
-
- return NL_SKIP;
-}
-
static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr,
@@ -9577,7 +9697,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
os_memset(&nonnetdev_info, 0, sizeof(nonnetdev_info));
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
- 0, nl80211_wdev_handler,
+ 0, nl80211_wdev_handler_info,
&nonnetdev_info, use_existing);
if (!nonnetdev_info.wdev_id_set || ifidx != 0) {
wpa_printf(MSG_ERROR,
@@ -16230,4 +16350,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.nan_config_schedule = wpa_driver_nl80211_nan_config_schedule,
.nan_config_peer_schedule = wpa_driver_nl80211_nan_config_peer_schedule,
#endif /* CONFIG_NAN */
+#ifdef CONFIG_PR
+ .pd_start = nl80211_pd_start,
+ .pd_stop = nl80211_pd_stop,
+#endif /* CONFIG_PR */
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 0f03da642..d1133a1fe 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -287,6 +287,9 @@ struct wpa_driver_nl80211_data {
#ifdef CONFIG_NAN
unsigned int nan_started:1;
#endif /* CONFIG_NAN */
+#ifdef CONFIG_PR
+ struct i802_bss *pd_bss; /* PD wdev; not in the BSS list */
+#endif /* CONFIG_PR */
};
struct nl80211_err_info {
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 034827488..c19c834b5 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -738,6 +738,26 @@ static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s,
return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers);
}
+#ifdef CONFIG_PR
+
+static inline int
+wpa_drv_pd_start(struct wpa_supplicant *wpa_s, const u8 *addr, u8 *pd_addr)
+{
+ if (!wpa_s->driver->pd_start)
+ return -1;
+ return wpa_s->driver->pd_start(wpa_s->drv_priv, addr, pd_addr);
+}
+
+static inline void
+wpa_drv_pd_stop(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->pd_stop)
+ return;
+ wpa_s->driver->pd_stop(wpa_s->drv_priv);
+}
+
+#endif /* CONFIG_PR */
+
static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
int vendor_id, int subcmd, const u8 *data,
size_t data_len,
--
2.34.1
More information about the Hostap
mailing list