[RFC PATCH v2 12/23] nl80211: Add Proximity Detection (PD) wdev support
Peddolla Harshavardhan Reddy
peddolla.reddy at oss.qualcomm.com
Thu Apr 2 05:24:17 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.
A new set of driver ops is added:
- pd_start(): create and activate a PD wdev (NL80211_IFTYPE_PD), returning
the effective PD MAC address
- pd_stop(): stop and destroy the PD wdev
- get_pd_addr(): query the MAC address of the active PD wdev
On the nl80211 side:
- Create a PD-only i802_bss (drv->pd_bss) that is not part of the regular
BSS list but is associated with the parent wpa_s (ctx).
- nl80211_pd_start() creates a non-netdev PD interface via
nl80211_create_iface(), obtains its wdev_id and MAC address, and
activates it via nl80211_set_pr_dev(..., 1) which sends
NL80211_CMD_START_PD.
- nl80211_pd_stop() deactivates and removes the PD wdev via
nl80211_set_pr_dev(..., 0) and nl80211_del_non_netdev().
- nl80211_get_pd_addr() exposes the current PD MAC address to upper layers.
- Add helper lookups nl80211_get_pd_bss_by_addr() and
nl80211_get_pd_bss_by_wdev() to route events arriving on the PD wdev
back to the parent context.
The wdev_info helper and its netlink callback are generalized and renamed
to nl80211_wdev_handler_info so they can be reused both for PD creation
and for other non-netdev interfaces (replacing the previous
nl80211_wdev_handler).
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. All PD-related
operations are safe no-ops when no PD wdev is active.
Signed-off-by: Veerendranath Jakkam <vjakkam at qti.qualcomm.com>
---
src/drivers/driver.h | 35 +++++++
src/drivers/driver_nl80211.c | 193 ++++++++++++++++++++++++++++++-----
src/drivers/driver_nl80211.h | 29 ++++++
wpa_supplicant/driver_i.h | 20 ++++
4 files changed, 250 insertions(+), 27 deletions(-)
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 79e3230da..c7c4e7571 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -5677,6 +5677,41 @@ struct wpa_driver_ops {
*/
void (*nan_stop)(void *priv);
#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);
+
+ /**
+ * get_pd_addr - Return the MAC address of the active PD wdev
+ * @priv: Private driver interface data
+ * @addr: Buffer (ETH_ALEN) to receive the PD wdev MAC address
+ * Returns: 0 on success, -1 if no PD wdev is active
+ */
+ int (*get_pd_addr)(void *priv, u8 *addr);
+#endif /* CONFIG_PR */
+
};
/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index aa1ea3c61..8bf2ffebc 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 */
@@ -3186,6 +3189,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;
@@ -3203,6 +3231,134 @@ 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 (ret=%d)", ret);
+ goto failed;
+ }
+
+ 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:
+ 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_destroy_bss(drv->pd_bss);
+ nl80211_set_pr_dev(drv->pd_bss, 0);
+ nl80211_del_non_netdev(drv->pd_bss);
+ os_free(drv->pd_bss);
+ drv->pd_bss = NULL;
+}
+
+
+/**
+ * nl80211_get_pd_addr - Return the MAC address of the active PD wdev
+ */
+static int nl80211_get_pd_addr(void *priv, u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!drv->pd_bss || !addr)
+ return -1;
+
+ os_memcpy(addr, drv->pd_bss->addr, ETH_ALEN);
+ return 0;
+}
+#endif /* CONFIG_PR */
+
+
#ifdef CONFIG_NAN
static void nl80211_nan_stop(struct i802_bss *bss)
{
@@ -3567,6 +3723,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);
@@ -9386,32 +9546,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,
@@ -9434,7 +9568,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,
@@ -15717,4 +15851,9 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.nan_stop = wpa_driver_nl80211_nan_stop,
.nan_change_config = wpa_driver_nl80211_nan_change_config,
#endif /* CONFIG_NAN */
+#ifdef CONFIG_PR
+ .pd_start = nl80211_pd_start,
+ .pd_stop = nl80211_pd_stop,
+ .get_pd_addr = nl80211_get_pd_addr,
+#endif /* CONFIG_PR */
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index c0544aeb0..1d1187f62 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -286,12 +286,41 @@ 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 {
int link_id;
};
+static inline struct i802_bss *
+nl80211_get_pd_bss_by_addr(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr)
+{
+#ifdef CONFIG_PR
+ if (addr && drv->pd_bss && ether_addr_equal(addr, drv->pd_bss->addr))
+ return drv->pd_bss;
+#endif /* CONFIG_PR */
+
+ return NULL;
+}
+
+
+static inline struct i802_bss *
+nl80211_get_pd_bss_by_wdev(struct wpa_driver_nl80211_data *drv,
+ u64 wdev_id, int wdev_id_set)
+{
+#ifdef CONFIG_PR
+ if (wdev_id_set && drv->pd_bss && drv->pd_bss->wdev_id_set &&
+ wdev_id == drv->pd_bss->wdev_id)
+ return drv->pd_bss;
+#endif /* CONFIG_PR */
+
+ return NULL;
+}
+
void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, int flags, uint8_t cmd);
struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 5a8298288..f2eba74dc 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