From: Andre Eikmeyer <dev@deq.rocks>
To: arend.vanspriel@broadcom.com
Cc: linux-wireless@vger.kernel.org, brcm80211@lists.linux.dev,
brcm80211-dev-list.pdl@broadcom.com,
linux-kernel@vger.kernel.org, Andre Eikmeyer <dev@deq.rocks>,
Yossef Osman <Yossef.Osman@proton.me>,
Aditya Garg <aditya.garg@linux.dev>
Subject: [PATCH 4/7] wifi: brcmfmac: create and remove the AWDL interface
Date: Wed, 4 Nov 2026 16:15:44 +0100 [thread overview]
Message-ID: <20261104151547.70333-5-dev@deq.rocks> (raw)
In-Reply-To: <20261104151547.70333-1-dev@deq.rocks>
Firmware with AWDL support takes an interface of type AWDL from
interface_create. The host gives the MAC address, the firmware
interface index 2 and the AWDL BSSID 00:25:00:ff:94:73. The firmware
answers with an interface event like for any other interface.
The AWDL interface lives outside cfg80211. Add brcmf_awdl_add() and
brcmf_awdl_del(), called with RTNL and the wiphy mutex held, which
create and remove it together with a network device "awdl%d":
- The interface events of the AWDL interface complete a wait in
brcmf_awdl_add() and brcmf_awdl_del() instead of going to the
cfg80211 handler. A vif_event armed for a P2P or AP interface at the
same time stays with that interface. When the firmware removes the
AWDL interface on its own, the event worker removes it as before.
- The network device gets its own net_device_ops with open, stop and
start_xmit before it is registered. The firmware filters AWDL traffic
itself.
- brcmf_net_attach() and brcmf_net_detach() also register and
unregister a network device outside cfg80211 with RTNL held.
- brcmf_awdl_add() needs the AWDL feature and a free firmware
interface index 2.
- The AWDL interface uses direct addressing like an AP, every peer
gets its own flowrings. brcmf_awdl_del() deletes them before it
stops AWDL and removes the interface.
- The AWDL interface is removed in the cfg80211 suspend callback while
the firmware still takes commands.
Tested-by: Yossef Osman <Yossef.Osman@proton.me>
Tested-by: Aditya Garg <aditya.garg@linux.dev>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
.../broadcom/brcm80211/brcmfmac/Makefile | 3 +-
.../broadcom/brcm80211/brcmfmac/awdl.c | 268 ++++++++++++++++++
.../broadcom/brcm80211/brcmfmac/awdl.h | 47 +++
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 6 +
.../broadcom/brcm80211/brcmfmac/core.c | 59 +++-
.../broadcom/brcm80211/brcmfmac/core.h | 7 +
.../broadcom/brcm80211/brcmfmac/fweh.c | 17 ++
7 files changed, 398 insertions(+), 9 deletions(-)
create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.c
create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.h
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
index e5ca0f51182..36c634199a2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
@@ -25,7 +25,8 @@ brcmfmac-objs += \
btcoex.o \
vendor.o \
pno.o \
- xtlv.o
+ xtlv.o \
+ awdl.o
brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \
bcdc.o \
fwsignal.o
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.c
new file mode 100644
index 00000000000..485ed74f3d6
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 André Eikmeyer <dev@deq.rocks>
+ *
+ * AWDL interface of the Apple firmware of the BCM4364 and BCM4377.
+ *
+ * The firmware runs Apple Wireless Direct Link itself once the host creates
+ * an interface of type AWDL with interface_create and configures it through
+ * the awdl_* iovars. The AWDL interface has a network device but no cfg80211
+ * vif. Its interface events are handled here and not by cfg80211.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+
+#include "core.h"
+#include "debug.h"
+#include "feature.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "proto.h"
+#include "awdl.h"
+
+/* The firmware interface index of the AWDL interface */
+#define BRCMF_AWDL_IFIDX 2
+#define BRCMF_AWDL_IF_TIMEOUT msecs_to_jiffies(2000)
+
+/* The BSSID of all AWDL frames */
+static const u8 brcmf_awdl_bssid[ETH_ALEN] = {
+ 0x00, 0x25, 0x00, 0xff, 0x94, 0x73,
+};
+
+void brcmf_awdl_attach(struct brcmf_pub *drvr)
+{
+ spin_lock_init(&drvr->awdl.lock);
+ init_completion(&drvr->awdl.if_event);
+}
+
+/*
+ * Called from the firmware event worker after an interface was added. Takes
+ * the interface if it is the AWDL interface being created.
+ */
+bool brcmf_awdl_if_added(struct brcmf_pub *drvr, struct brcmf_if *ifp)
+{
+ struct brcmf_awdl *awdl = &drvr->awdl;
+ bool own;
+
+ spin_lock(&awdl->lock);
+ own = awdl->adding && ifp->ndev &&
+ ether_addr_equal(ifp->mac_addr, awdl->mac);
+ if (own) {
+ ifp->awdl = true;
+ strscpy(ifp->ndev->name, "awdl%d", IFNAMSIZ);
+ awdl->ifp = ifp;
+ awdl->adding = false;
+ complete(&awdl->if_event);
+ }
+ spin_unlock(&awdl->lock);
+ return own;
+}
+
+/*
+ * Called from the firmware event worker before an interface is removed.
+ * Returns true if the removal of the AWDL interface was requested by
+ * brcmf_awdl_del(), which then removes it. When the firmware removes the
+ * AWDL interface on its own, it is forgotten here and removed by the caller.
+ */
+bool brcmf_awdl_if_removed(struct brcmf_pub *drvr, struct brcmf_if *ifp)
+{
+ struct brcmf_awdl *awdl = &drvr->awdl;
+ bool requested = false;
+
+ if (!ifp->awdl)
+ return false;
+
+ spin_lock(&awdl->lock);
+ if (awdl->ifp == ifp) {
+ requested = awdl->removing;
+ if (requested)
+ complete(&awdl->if_event);
+ else
+ awdl->ifp = NULL;
+ }
+ spin_unlock(&awdl->lock);
+ if (!requested)
+ brcmf_proto_configure_addr_mode(drvr, ifp->ifidx,
+ ADDR_INDIRECT);
+ return requested;
+}
+
+/* The firmware interface index of the AWDL interface must be free */
+static bool brcmf_awdl_ifidx_used(struct brcmf_pub *drvr)
+{
+ int i;
+
+ for (i = 1; i < BRCMF_MAX_IFS; i++)
+ if (drvr->iflist[i] && drvr->iflist[i]->ifidx == BRCMF_AWDL_IFIDX)
+ return true;
+ return false;
+}
+
+/* Undoes interface_create for an interface that was not attached */
+static void brcmf_awdl_drop_if(struct brcmf_pub *drvr, struct brcmf_if *ifp)
+{
+ struct net_device *ndev = ifp->ndev;
+
+ brcmf_fil_iovar_data_set(ifp, "interface_remove", NULL, 0);
+ brcmf_proto_configure_addr_mode(drvr, ifp->ifidx, ADDR_INDIRECT);
+ brcmf_proto_del_if(drvr, ifp);
+ if (drvr->iflist[ifp->bsscfgidx] == ifp)
+ drvr->iflist[ifp->bsscfgidx] = NULL;
+ if (drvr->if2bss[ifp->ifidx] == ifp->bsscfgidx)
+ drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID;
+ free_netdev(ndev);
+}
+
+/**
+ * brcmf_awdl_add() - create the AWDL interface.
+ *
+ * @drvr: device.
+ * @mac: MAC address of the AWDL interface.
+ * @ndev: set to the network device of the AWDL interface.
+ *
+ * Called with RTNL and the wiphy mutex held. The interface uses the firmware interface index 2
+ * and the AWDL BSSID 00:25:00:ff:94:73, which the host passes to the
+ * firmware. Return: 0 or a negative error code.
+ */
+int brcmf_awdl_add(struct brcmf_pub *drvr, const u8 *mac,
+ struct net_device **ndev)
+{
+ struct brcmf_awdl *awdl = &drvr->awdl;
+ struct brcmf_if *pri = brcmf_get_ifp(drvr, 0);
+ struct wl_interface_create_v3 req = {};
+ struct brcmf_if *ifp;
+ long left = 0;
+ int err;
+
+ ASSERT_RTNL();
+ lockdep_assert_wiphy(drvr->wiphy);
+
+ if (!pri || !brcmf_feat_is_enabled(pri, BRCMF_FEAT_AWDL))
+ return -EOPNOTSUPP;
+ if (!is_valid_ether_addr(mac))
+ return -EINVAL;
+ if (awdl->detached)
+ return -ENODEV;
+ if (awdl->ifp)
+ return -EBUSY;
+ if (brcmf_awdl_ifidx_used(drvr)) {
+ bphy_err(drvr, "interface index %d is in use, no AWDL\n",
+ BRCMF_AWDL_IFIDX);
+ return -EBUSY;
+ }
+
+ spin_lock(&awdl->lock);
+ ether_addr_copy(awdl->mac, mac);
+ awdl->adding = true;
+ reinit_completion(&awdl->if_event);
+ spin_unlock(&awdl->lock);
+
+ req.ver = WL_INTERFACE_CREATE_VER_3;
+ req.len = sizeof(req);
+ req.fixed_len = sizeof(req);
+ req.iftype = WL_INTERFACE_CREATE_AWDL;
+ req.flags = WL_INTERFACE_MAC_USE | WL_INTERFACE_IF_INDEX_USE |
+ WL_INTERFACE_BSSID_USE;
+ ether_addr_copy(req.mac_addr, mac);
+ ether_addr_copy(req.bssid, brcmf_awdl_bssid);
+ req.if_index = BRCMF_AWDL_IFIDX;
+ err = brcmf_fil_iovar_data_get(pri, "interface_create", &req,
+ sizeof(req));
+ if (!err)
+ left = wait_for_completion_timeout(&awdl->if_event,
+ BRCMF_AWDL_IF_TIMEOUT);
+
+ spin_lock(&awdl->lock);
+ awdl->adding = false;
+ ifp = awdl->ifp;
+ spin_unlock(&awdl->lock);
+
+ if (err) {
+ bphy_err(drvr, "AWDL interface_create failed: %d\n", err);
+ return err;
+ }
+ if (!left || !ifp) {
+ bphy_err(drvr, "no interface event for the AWDL interface\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Like an AP, AWDL sends to each peer through its own flowrings */
+ brcmf_proto_configure_addr_mode(drvr, ifp->ifidx, ADDR_DIRECT);
+ err = brcmf_net_attach(ifp, true);
+ if (err) {
+ spin_lock(&awdl->lock);
+ awdl->ifp = NULL;
+ spin_unlock(&awdl->lock);
+ brcmf_awdl_drop_if(drvr, ifp);
+ return err;
+ }
+ brcmf_dbg(INFO, "%s: AWDL interface, bsscfgidx %d ifidx %d\n",
+ ifp->ndev->name, ifp->bsscfgidx, ifp->ifidx);
+ *ndev = ifp->ndev;
+ return 0;
+}
+
+/**
+ * brcmf_awdl_del() - remove the AWDL interface.
+ *
+ * @drvr: device.
+ *
+ * Called with RTNL and the wiphy mutex held. Deletes the flowrings of the AWDL interface, stops
+ * AWDL and removes the interface in the firmware and on the host.
+ * Return: 0 or a negative error code.
+ */
+int brcmf_awdl_del(struct brcmf_pub *drvr)
+{
+ struct brcmf_awdl *awdl = &drvr->awdl;
+ struct brcmf_if *ifp;
+ int err;
+
+ ASSERT_RTNL();
+ lockdep_assert_wiphy(drvr->wiphy);
+
+ ifp = awdl->ifp;
+ if (!ifp)
+ return -ENOENT;
+
+ netif_tx_disable(ifp->ndev);
+ /* Deletes the flowrings of all peers */
+ brcmf_proto_configure_addr_mode(drvr, ifp->ifidx, ADDR_INDIRECT);
+ brcmf_fil_iovar_int_set(ifp, "awdl", 0);
+
+ spin_lock(&awdl->lock);
+ awdl->removing = true;
+ reinit_completion(&awdl->if_event);
+ spin_unlock(&awdl->lock);
+
+ err = brcmf_fil_iovar_data_set(ifp, "interface_remove", NULL, 0);
+ if (!err && !wait_for_completion_timeout(&awdl->if_event,
+ BRCMF_AWDL_IF_TIMEOUT)) {
+ bphy_err(drvr, "no interface event for the AWDL removal\n");
+ err = -ETIMEDOUT;
+ }
+
+ spin_lock(&awdl->lock);
+ awdl->removing = false;
+ awdl->ifp = NULL;
+ spin_unlock(&awdl->lock);
+
+ brcmf_remove_interface(ifp, true);
+ return err;
+}
+
+/* Called before the interfaces of the device are removed */
+void brcmf_awdl_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_awdl *awdl = &drvr->awdl;
+
+ rtnl_lock();
+ wiphy_lock(drvr->wiphy);
+ spin_lock(&awdl->lock);
+ awdl->detached = true;
+ awdl->ifp = NULL;
+ spin_unlock(&awdl->lock);
+ wiphy_unlock(drvr->wiphy);
+ rtnl_unlock();
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.h
new file mode 100644
index 00000000000..b12cbd32187
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/awdl.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026 André Eikmeyer <dev@deq.rocks>
+ *
+ * AWDL interface of the Apple firmware, see awdl.c
+ */
+#ifndef BRCMFMAC_AWDL_H
+#define BRCMFMAC_AWDL_H
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+
+struct brcmf_pub;
+struct brcmf_if;
+
+/**
+ * struct brcmf_awdl - AWDL interface of a device.
+ *
+ * @lock: protects @mac, @adding, @removing and @ifp against the firmware
+ * event worker. Changes of @ifp also hold RTNL and the wiphy mutex.
+ * @if_event: completed by the interface event of the AWDL interface.
+ * @mac: MAC address requested for the AWDL interface.
+ * @adding: interface_create was sent, its interface event is awaited.
+ * @removing: interface_remove was sent, its interface event is awaited.
+ * @detached: the device goes away, no new AWDL interface is created.
+ * @ifp: the AWDL interface, NULL when there is none.
+ */
+struct brcmf_awdl {
+ spinlock_t lock; /* AWDL state against the event worker */
+ struct completion if_event;
+ u8 mac[ETH_ALEN];
+ bool adding;
+ bool removing;
+ bool detached;
+ struct brcmf_if *ifp;
+};
+
+void brcmf_awdl_attach(struct brcmf_pub *drvr);
+void brcmf_awdl_detach(struct brcmf_pub *drvr);
+int brcmf_awdl_add(struct brcmf_pub *drvr, const u8 *mac,
+ struct net_device **ndev);
+int brcmf_awdl_del(struct brcmf_pub *drvr);
+bool brcmf_awdl_if_added(struct brcmf_pub *drvr, struct brcmf_if *ifp);
+bool brcmf_awdl_if_removed(struct brcmf_pub *drvr, struct brcmf_if *ifp);
+
+#endif /* BRCMFMAC_AWDL_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 1037abe6d58..4f35dbef082 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -4279,6 +4279,12 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
brcmf_dbg(TRACE, "Enter\n");
+ /* The AWDL interface does not survive sleep, the firmware still
+ * takes commands here. User space creates it again after resume.
+ */
+ if (cfg->pub->awdl.ifp)
+ brcmf_awdl_del(cfg->pub);
+
/* if the primary net_device is not READY there is nothing
* we can do but pray resume goes smoothly.
*/
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 92ec269f43b..37c558b6f0d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -32,8 +32,6 @@
#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950)
-#define BRCMF_BSSIDX_INVALID -1
-
#define RXS_PBPRES BIT(2)
#define D11_PHY_HDR_LEN 6
@@ -642,6 +640,42 @@ static const struct net_device_ops brcmf_netdev_ops_pri = {
.ndo_set_rx_mode = brcmf_netdev_set_multicast_list
};
+static int brcmf_awdl_netdev_open(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ if (ifp->drvr->bus_if->state != BRCMF_BUS_UP)
+ return -EAGAIN;
+
+ /* The firmware runs AWDL, the link is there while it exists */
+ netif_carrier_on(ndev);
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int brcmf_awdl_netdev_stop(struct net_device *ndev)
+{
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+ return 0;
+}
+
+/*
+ * The AWDL interface gets no multicast list and no MAC address change, the
+ * firmware filters AWDL traffic itself.
+ */
+static const struct net_device_ops brcmf_netdev_ops_awdl = {
+ .ndo_open = brcmf_awdl_netdev_open,
+ .ndo_stop = brcmf_awdl_netdev_stop,
+ .ndo_start_xmit = brcmf_netdev_start_xmit,
+};
+
+static bool brcmf_netdev_is_brcmf(struct net_device *ndev)
+{
+ return ndev->netdev_ops == &brcmf_netdev_ops_pri ||
+ ndev->netdev_ops == &brcmf_netdev_ops_awdl;
+}
+
int brcmf_net_attach(struct brcmf_if *ifp, bool locked)
{
struct brcmf_pub *drvr = ifp->drvr;
@@ -653,7 +687,10 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool locked)
ndev = ifp->ndev;
/* set appropriate operations */
- ndev->netdev_ops = &brcmf_netdev_ops_pri;
+ if (ifp->awdl)
+ ndev->netdev_ops = &brcmf_netdev_ops_awdl;
+ else
+ ndev->netdev_ops = &brcmf_netdev_ops_pri;
ndev->needed_headroom += drvr->hdrlen;
ndev->ethtool_ops = &brcmf_ethtool_ops;
@@ -665,10 +702,12 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool locked)
INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
- if (locked)
+ if (!locked)
+ err = register_netdev(ndev);
+ else if (ndev->ieee80211_ptr)
err = cfg80211_register_netdevice(ndev);
else
- err = register_netdev(ndev);
+ err = register_netdevice(ndev);
if (err != 0) {
bphy_err(drvr, "couldn't register the net device\n");
goto fail;
@@ -689,10 +728,12 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool locked)
void brcmf_net_detach(struct net_device *ndev, bool locked)
{
if (ndev->reg_state == NETREG_REGISTERED) {
- if (locked)
+ if (!locked)
+ unregister_netdev(ndev);
+ else if (ndev->ieee80211_ptr)
cfg80211_unregister_netdevice(ndev);
else
- unregister_netdev(ndev);
+ unregister_netdevice(ndev);
} else {
brcmf_cfg80211_free_vif(ndev);
free_netdev(ndev);
@@ -943,7 +984,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
netif_stop_queue(ifp->ndev);
}
- if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ if (brcmf_netdev_is_brcmf(ifp->ndev)) {
cancel_work_sync(&ifp->multicast_work);
cancel_work_sync(&ifp->ndoffload_work);
}
@@ -1371,6 +1412,7 @@ int brcmf_attach(struct device *dev)
drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
mutex_init(&drvr->proto_block);
+ brcmf_awdl_attach(drvr);
/* Link to bus module */
drvr->hdrlen = 0;
@@ -1486,6 +1528,7 @@ void brcmf_detach(struct device *dev)
unregister_inet6addr_notifier(&drvr->inet6addr_notifier);
#endif
+ brcmf_awdl_detach(drvr);
brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
/* make sure primary interface removed last */
for (i = BRCMF_MAX_IFS - 1; i > -1; i--) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index 399b6810e39..fc8a713e80c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -12,6 +12,7 @@
#include <net/cfg80211.h>
#include "fweh.h"
+#include "awdl.h"
#if IS_MODULE(CONFIG_BRCMFMAC)
#define BRCMF_EXPORT_SYMBOL_GPL(__sym) EXPORT_SYMBOL_NS_GPL(__sym, "BRCMFMAC")
@@ -25,6 +26,8 @@
/* For supporting multiple interfaces */
#define BRCMF_MAX_IFS 16
+#define BRCMF_BSSIDX_INVALID -1
+
/* Small, medium and maximum buffer size for dcmd
*/
#define BRCMF_DCMD_SMLEN 256
@@ -145,6 +148,8 @@ struct brcmf_pub {
u8 sta_mac_idx;
const struct brcmf_fwvid_ops *vops;
void *vdata;
+
+ struct brcmf_awdl awdl;
};
/* forward declarations */
@@ -184,6 +189,7 @@ enum brcmf_netif_stop_reason {
* @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
* @pend_8021x_wait: used for signalling change in count.
* @fwil_fwerr: flag indicating fwil layer should return firmware error codes.
+ * @awdl: this is the AWDL interface, it has no cfg80211 vif.
*/
struct brcmf_if {
struct brcmf_pub *drvr;
@@ -202,6 +208,7 @@ struct brcmf_if {
struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES];
u8 ipv6addr_idx;
bool fwil_fwerr;
+ bool awdl;
};
int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
index 1cff4ba7694..8c098029104 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -170,6 +170,9 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
return;
if (!is_p2pdev)
brcmf_proto_add_if(drvr, ifp);
+ /* The AWDL interface is attached by brcmf_awdl_add() */
+ if (brcmf_awdl_if_added(drvr, ifp))
+ return;
if (!drvr->fweh->evt_handler[BRCMF_E_IF])
if (brcmf_net_attach(ifp, false) < 0)
return;
@@ -178,6 +181,14 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
brcmf_proto_reset_if(drvr, ifp);
+ /* The AWDL interface has no vif, cfg80211 does not know it */
+ if (ifp && ifp->awdl) {
+ if (ifevent->action == BRCMF_E_IF_DEL &&
+ !brcmf_awdl_if_removed(drvr, ifp))
+ brcmf_remove_interface(ifp, false);
+ return;
+ }
+
brcmf_fweh_call_event_handler(drvr, ifp, emsg->event_code, emsg,
data);
@@ -313,6 +324,12 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
ifp = drvr->iflist[0];
else
ifp = drvr->iflist[emsg.bsscfgidx];
+ /* The AWDL interface has no vif, its events are not for
+ * the cfg80211 handlers
+ */
+ if (ifp && ifp->awdl)
+ goto event_free;
+
err = brcmf_fweh_call_event_handler(drvr, ifp, event->code,
&emsg, event->data);
if (err) {
next prev parent reply other threads:[~2026-11-04 15:15 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 15:15 [PATCH 0/7] wifi: brcmfmac: AWDL for AirDrop on Apple T2 Macs Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 1/7] wifi: brcmfmac: match firmware capabilities as whole words Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 2/7] wifi: brcmfmac: move the interface_create definitions to fwil_types.h Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 3/7] wifi: brcmfmac: detect AWDL support of the firmware Andre Eikmeyer
2026-11-04 15:15 ` Andre Eikmeyer [this message]
2026-11-04 15:15 ` [PATCH 5/7] wifi: brcmfmac: add the AWDL data path Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 6/7] wifi: brcmfmac: add vendor commands for AWDL Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 7/7] wifi: brcmfmac: pass AWDL frames and role changes to user space Andre Eikmeyer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261104151547.70333-5-dev@deq.rocks \
--to=dev@deq.rocks \
--cc=Yossef.Osman@proton.me \
--cc=aditya.garg@linux.dev \
--cc=arend.vanspriel@broadcom.com \
--cc=brcm80211-dev-list.pdl@broadcom.com \
--cc=brcm80211@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox