net: dummy L2 for offloaded ifaces

Adds dummy link layer for offloaded ifaces, allowing
ifaces to directly receive l2_enable calls

Signed-off-by: Georges Oates_Larsen <georges.larsen@nordicsemi.no>
diff --git a/drivers/modem/hl7800.c b/drivers/modem/hl7800.c
index 792f0c8..c80787e 100644
--- a/drivers/modem/hl7800.c
+++ b/drivers/modem/hl7800.c
@@ -29,6 +29,7 @@
 #include <zephyr/net/net_offload.h>
 #include <zephyr/net/net_pkt.h>
 #include <zephyr/net/dns_resolve.h>
+#include <zephyr/net/offloaded_netdev.h>
 #if defined(CONFIG_NET_IPV6)
 #include "ipv6.h"
 #endif
@@ -6424,8 +6425,8 @@
 	}
 }
 
-static struct net_if_api api_funcs = {
-	.init = offload_iface_init,
+static struct offloaded_if_api api_funcs = {
+	.iface_api.init = offload_iface_init,
 };
 
 NET_DEVICE_DT_INST_OFFLOAD_DEFINE(0, hl7800_init, NULL, &ictx,
diff --git a/drivers/modem/quectel-bg9x.c b/drivers/modem/quectel-bg9x.c
index 75d49c4..40baa2c 100644
--- a/drivers/modem/quectel-bg9x.c
+++ b/drivers/modem/quectel-bg9x.c
@@ -1096,8 +1096,8 @@
 	net_if_socket_offload_set(iface, offload_socket);
 }
 
-static struct net_if_api api_funcs = {
-	.init = modem_net_iface_init,
+static struct offloaded_if_api api_funcs = {
+	.iface_api.init = modem_net_iface_init,
 };
 
 static bool offload_is_supported(int family, int type, int proto)
diff --git a/drivers/modem/quectel-bg9x.h b/drivers/modem/quectel-bg9x.h
index a4541b0..899f1ec 100644
--- a/drivers/modem/quectel-bg9x.h
+++ b/drivers/modem/quectel-bg9x.h
@@ -15,6 +15,7 @@
 #include <zephyr/init.h>
 
 #include <zephyr/net/net_if.h>
+#include <zephyr/net/offloaded_netdev.h>
 #include <zephyr/net/net_offload.h>
 #include <zephyr/net/socket_offload.h>
 
diff --git a/drivers/modem/simcom-sim7080.c b/drivers/modem/simcom-sim7080.c
index f92a885..a9256d9 100644
--- a/drivers/modem/simcom-sim7080.c
+++ b/drivers/modem/simcom-sim7080.c
@@ -7,6 +7,7 @@
 #define DT_DRV_COMPAT simcom_sim7080
 
 #include <zephyr/logging/log.h>
+#include <zephyr/net/offloaded_netdev.h>
 LOG_MODULE_REGISTER(modem_simcom_sim7080, CONFIG_MODEM_LOG_LEVEL);
 
 #include <zephyr/drivers/modem/simcom-sim7080.h>
@@ -760,8 +761,8 @@
 	.freeaddrinfo = offload_freeaddrinfo,
 };
 
-static struct net_if_api api_funcs = {
-	.init = modem_net_iface_init,
+static struct offloaded_if_api api_funcs = {
+	.iface_api.init = modem_net_iface_init,
 };
 
 static bool offload_is_supported(int family, int type, int proto)
diff --git a/drivers/modem/ublox-sara-r4.c b/drivers/modem/ublox-sara-r4.c
index d1f3216..2958783 100644
--- a/drivers/modem/ublox-sara-r4.c
+++ b/drivers/modem/ublox-sara-r4.c
@@ -19,6 +19,7 @@
 
 #include <zephyr/net/net_if.h>
 #include <zephyr/net/net_offload.h>
+#include <zephyr/net/offloaded_netdev.h>
 #include <zephyr/net/socket_offload.h>
 
 #if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
@@ -2103,8 +2104,8 @@
 	net_if_socket_offload_set(iface, offload_socket);
 }
 
-static struct net_if_api api_funcs = {
-	.init = modem_net_iface_init,
+static struct offloaded_if_api api_funcs = {
+	.iface_api.init = modem_net_iface_init,
 };
 
 static const struct modem_cmd response_cmds[] = {
diff --git a/drivers/modem/wncm14a2a.c b/drivers/modem/wncm14a2a.c
index ca805a1..1dc0a73 100644
--- a/drivers/modem/wncm14a2a.c
+++ b/drivers/modem/wncm14a2a.c
@@ -25,6 +25,7 @@
 #include <zephyr/net/net_context.h>
 #include <zephyr/net/net_if.h>
 #include <zephyr/net/net_offload.h>
+#include <zephyr/net/offloaded_netdev.h>
 #include <zephyr/net/net_pkt.h>
 #if defined(CONFIG_NET_IPV6)
 #include "ipv6.h"
@@ -1773,8 +1774,8 @@
 	ctx->iface = iface;
 }
 
-static struct net_if_api api_funcs = {
-	.init	= offload_iface_init,
+static struct offloaded_if_api api_funcs = {
+	.iface_api.init = offload_iface_init,
 };
 
 NET_DEVICE_DT_INST_OFFLOAD_DEFINE(0, wncm14a2a_init, NULL,
diff --git a/drivers/wifi/esp_at/esp.c b/drivers/wifi/esp_at/esp.c
index 4236c91..b1dcd9d 100644
--- a/drivers/wifi/esp_at/esp.c
+++ b/drivers/wifi/esp_at/esp.c
@@ -1217,13 +1217,13 @@
 }
 
 static const struct net_wifi_mgmt_offload esp_api = {
-	.wifi_iface.init = esp_iface_init,
-	.scan		= esp_mgmt_scan,
-	.connect	= esp_mgmt_connect,
-	.disconnect	= esp_mgmt_disconnect,
-	.ap_enable	= esp_mgmt_ap_enable,
-	.ap_disable	= esp_mgmt_ap_disable,
-	.iface_status	= esp_mgmt_iface_status,
+	.wifi_iface.iface_api.init = esp_iface_init,
+	.scan			   = esp_mgmt_scan,
+	.connect		   = esp_mgmt_connect,
+	.disconnect		   = esp_mgmt_disconnect,
+	.ap_enable		   = esp_mgmt_ap_enable,
+	.ap_disable		   = esp_mgmt_ap_disable,
+	.iface_status		   = esp_mgmt_iface_status,
 };
 
 static int esp_init(const struct device *dev);
diff --git a/drivers/wifi/eswifi/eswifi_core.c b/drivers/wifi/eswifi/eswifi_core.c
index b6960ff..79e403c 100644
--- a/drivers/wifi/eswifi/eswifi_core.c
+++ b/drivers/wifi/eswifi/eswifi_core.c
@@ -679,12 +679,12 @@
 }
 
 static const struct net_wifi_mgmt_offload eswifi_offload_api = {
-	.wifi_iface.init = eswifi_iface_init,
-	.scan		= eswifi_mgmt_scan,
-	.connect	= eswifi_mgmt_connect,
-	.disconnect	= eswifi_mgmt_disconnect,
-	.ap_enable	= eswifi_mgmt_ap_enable,
-	.ap_disable	= eswifi_mgmt_ap_disable,
+	.wifi_iface.iface_api.init = eswifi_iface_init,
+	.scan			   = eswifi_mgmt_scan,
+	.connect		   = eswifi_mgmt_connect,
+	.disconnect		   = eswifi_mgmt_disconnect,
+	.ap_enable		   = eswifi_mgmt_ap_enable,
+	.ap_disable		   = eswifi_mgmt_ap_disable,
 };
 
 NET_DEVICE_DT_INST_OFFLOAD_DEFINE(0, eswifi_init, NULL,
diff --git a/drivers/wifi/simplelink/simplelink.c b/drivers/wifi/simplelink/simplelink.c
index a60ac99..b75329e 100644
--- a/drivers/wifi/simplelink/simplelink.c
+++ b/drivers/wifi/simplelink/simplelink.c
@@ -264,10 +264,10 @@
 }
 
 static const struct net_wifi_mgmt_offload simplelink_api = {
-	.wifi_iface.init = simplelink_iface_init,
-	.scan		= simplelink_mgmt_scan,
-	.connect	= simplelink_mgmt_connect,
-	.disconnect	= simplelink_mgmt_disconnect,
+	.wifi_iface.iface_api.init = simplelink_iface_init,
+	.scan			   = simplelink_mgmt_scan,
+	.connect		   = simplelink_mgmt_connect,
+	.disconnect		   = simplelink_mgmt_disconnect,
 };
 
 static int simplelink_init(const struct device *dev)
diff --git a/drivers/wifi/winc1500/wifi_winc1500.c b/drivers/wifi/winc1500/wifi_winc1500.c
index 94049a9..329e18a 100644
--- a/drivers/wifi/winc1500/wifi_winc1500.c
+++ b/drivers/wifi/winc1500/wifi_winc1500.c
@@ -1100,12 +1100,12 @@
 }
 
 static const struct net_wifi_mgmt_offload winc1500_api = {
-	.wifi_iface.init = winc1500_iface_init,
-	.scan		= winc1500_mgmt_scan,
-	.connect	= winc1500_mgmt_connect,
-	.disconnect	= winc1500_mgmt_disconnect,
-	.ap_enable	= winc1500_mgmt_ap_enable,
-	.ap_disable	= winc1500_mgmt_ap_disable,
+	.wifi_iface.iface_api.init = winc1500_iface_init,
+	.scan			   = winc1500_mgmt_scan,
+	.connect		   = winc1500_mgmt_connect,
+	.disconnect		   = winc1500_mgmt_disconnect,
+	.ap_enable		   = winc1500_mgmt_ap_enable,
+	.ap_disable		   = winc1500_mgmt_ap_disable,
 };
 
 static int winc1500_init(const struct device *dev)
diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h
index 31928dd..58bf925 100644
--- a/include/zephyr/net/net_if.h
+++ b/include/zephyr/net/net_if.h
@@ -2468,6 +2468,7 @@
 				NET_IF_DEV_GET_NAME(dev_id, sfx)) = {	\
 		.dev = &(DEVICE_NAME_GET(dev_id)),			\
 		.mtu = _mtu,						\
+		.l2 = &(NET_L2_GET_NAME(OFFLOADED_NETDEV)),		\
 	};								\
 	static Z_DECL_ALIGN(struct net_if)				\
 		NET_IF_GET_NAME(dev_id, sfx)[NET_IF_MAX_CONFIGS]	\
diff --git a/include/zephyr/net/net_l2.h b/include/zephyr/net/net_l2.h
index 1c792cf..98ce34d 100644
--- a/include/zephyr/net/net_l2.h
+++ b/include/zephyr/net/net_l2.h
@@ -95,6 +95,11 @@
 NET_L2_DECLARE_PUBLIC(DUMMY_L2);
 #endif /* CONFIG_NET_L2_DUMMY */
 
+#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_SOCKETS_OFFLOAD)
+#define OFFLOADED_NETDEV_L2 OFFLOADED_NETDEV
+NET_L2_DECLARE_PUBLIC(OFFLOADED_NETDEV_L2);
+#endif /* CONFIG_NET_L2_ETHERNET */
+
 #ifdef CONFIG_NET_L2_ETHERNET
 #define ETHERNET_L2		ETHERNET
 NET_L2_DECLARE_PUBLIC(ETHERNET_L2);
diff --git a/include/zephyr/net/offloaded_netdev.h b/include/zephyr/net/offloaded_netdev.h
new file mode 100644
index 0000000..5f77868
--- /dev/null
+++ b/include/zephyr/net/offloaded_netdev.h
@@ -0,0 +1,58 @@
+/** @file
+ * @brief Offloaded network device iface API
+ *
+ * This is not to be included by the application.
+ */
+
+/*
+ * Copyright (c) 2022 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_INCLUDE_OFFLOADED_NETDEV_H_
+#define ZEPHYR_INCLUDE_OFFLOADED_NETDEV_H_
+
+#include <zephyr/kernel.h>
+#include <zephyr/types.h>
+#include <stdbool.h>
+#include <zephyr/net/net_if.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Offloaded Net Devices
+ * @defgroup offloaded_netdev Offloaded Net Devices
+ * @ingroup networking
+ * @{
+ */
+
+/**
+ * @brief Extended net_if_api for offloaded ifaces/network devices, allowing handling of
+ *	  admin up/down state changes
+ */
+struct offloaded_if_api {
+	/**
+	 * The net_if_api must be placed in first position in this
+	 * struct so that we are compatible with network interface API.
+	 */
+	struct net_if_api iface_api;
+
+	/** Enable or disable the device (in response to admin state change) */
+	int (*enable)(const struct net_if *iface, bool state);
+};
+
+/* Ensure offloaded_if_api is compatible with net_if_api */
+BUILD_ASSERT(offsetof(struct offloaded_if_api, iface_api) == 0);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZEPHYR_INCLUDE_OFFLOADED_NETDEV_H_ */
diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h
index 21282f0..b4538ec 100644
--- a/include/zephyr/net/wifi_mgmt.h
+++ b/include/zephyr/net/wifi_mgmt.h
@@ -15,6 +15,7 @@
 #include <zephyr/net/net_mgmt.h>
 #include <zephyr/net/wifi.h>
 #include <zephyr/net/ethernet.h>
+#include <zephyr/net/offloaded_netdev.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -288,7 +289,7 @@
 #ifdef CONFIG_WIFI_USE_NATIVE_NETWORKING
 	struct ethernet_api wifi_iface;
 #else
-	struct net_if_api wifi_iface;
+	struct offloaded_if_api wifi_iface;
 #endif
 
 	/* cb parameter is the cb that should be called for each
diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c
index 1b6f5a6..aa94c5b 100644
--- a/subsys/net/ip/net_if.c
+++ b/subsys/net/ip/net_if.c
@@ -4202,10 +4202,6 @@
 		goto out;
 	}
 
-	if (is_iface_offloaded(iface)) {
-		goto done;
-	}
-
 	/* If the L2 does not support enable just set the flag */
 	if (!net_if_l2(iface) || !net_if_l2(iface)->enable) {
 		goto done;
@@ -4246,10 +4242,6 @@
 	leave_mcast_all(iface);
 	leave_ipv4_mcast_all(iface);
 
-	if (is_iface_offloaded(iface)) {
-		goto done;
-	}
-
 	/* If the L2 does not support enable just clear the flag */
 	if (!net_if_l2(iface) || !net_if_l2(iface)->enable) {
 		goto done;
diff --git a/subsys/net/l2/CMakeLists.txt b/subsys/net/l2/CMakeLists.txt
index 1878286..0d68a4e 100644
--- a/subsys/net/l2/CMakeLists.txt
+++ b/subsys/net/l2/CMakeLists.txt
@@ -12,6 +12,10 @@
   add_subdirectory(dummy)
 endif()
 
+if (CONFIG_NET_OFFLOAD OR CONFIG_NET_SOCKETS_OFFLOAD)
+  add_subdirectory(offloaded_netdev)
+endif()
+
 if(CONFIG_NET_L2_ETHERNET)
   add_subdirectory(ethernet)
 endif()
diff --git a/subsys/net/l2/offloaded_netdev/CMakeLists.txt b/subsys/net/l2/offloaded_netdev/CMakeLists.txt
new file mode 100644
index 0000000..9cc64a7
--- /dev/null
+++ b/subsys/net/l2/offloaded_netdev/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright (c) 2022 Nordic Semiconductor ASA
+# SPDX-License-Identifier: Apache-2.0
+
+zephyr_library()
+zephyr_library_sources(offloaded_netdev.c)
diff --git a/subsys/net/l2/offloaded_netdev/offloaded_netdev.c b/subsys/net/l2/offloaded_netdev/offloaded_netdev.c
new file mode 100644
index 0000000..12fd3d0
--- /dev/null
+++ b/subsys/net/l2/offloaded_netdev/offloaded_netdev.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2022 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/net/net_l2.h>
+#include <zephyr/net/net_if.h>
+#include <zephyr/net/offloaded_netdev.h>
+
+static inline int offloaded_netdev_if_enable(struct net_if *iface, bool state)
+{
+	const struct offloaded_if_api *off_if = net_if_get_device(iface)->api;
+
+	if (!off_if || !(off_if->enable)) {
+		return 0;
+	}
+
+	return off_if->enable(iface, state);
+}
+
+NET_L2_INIT(OFFLOADED_NETDEV, NULL, NULL, offloaded_netdev_if_enable, NULL);
diff --git a/tests/net/all/src/main.c b/tests/net/all/src/main.c
index 83d5bfb..7e63508 100644
--- a/tests/net/all/src/main.c
+++ b/tests/net/all/src/main.c
@@ -18,23 +18,31 @@
 #include <zephyr/net/net_if.h>
 #include <zephyr/net/net_pkt.h>
 #include <zephyr/net/dummy.h>
+#include <zephyr/net/offloaded_netdev.h>
 
+
+/* Create blank dummy and offloaded APIs */
+static struct offloaded_if_api offload_dev_api;
+static const struct dummy_api dummy_dev_api;
 static struct offload_context {
 	void *none;
 } offload_context_data = {
 	.none = NULL
 };
 
-static struct dummy_api offload_if_api = {
-	.iface_api.init = NULL,
-	.send = NULL,
-};
+/* Create blank dummy and offloaded net devices */
+NET_DEVICE_INIT(dummy_dev, "dummy_dev",
+		NULL, NULL,
+		NULL, NULL,
+		CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
+		&dummy_dev_api,
+		DUMMY_L2, NET_L2_GET_CTX_TYPE(DUMMY_L2), 0);
 
 NET_DEVICE_OFFLOAD_INIT(net_offload, "net_offload",
 			NULL, NULL,
 			&offload_context_data, NULL,
 			CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
-			&offload_if_api, 0);
+			&offload_dev_api, 0);
 
 ZTEST(net_compile_all_test, test_ok)
 {
diff --git a/tests/net/socket/offload_dispatcher/src/main.c b/tests/net/socket/offload_dispatcher/src/main.c
index cf0d9f2..d6469c1 100644
--- a/tests/net/socket/offload_dispatcher/src/main.c
+++ b/tests/net/socket/offload_dispatcher/src/main.c
@@ -8,6 +8,7 @@
 #include <zephyr/logging/log.h>
 #include <zephyr/net/dummy.h>
 #include <zephyr/net/net_if.h>
+#include <zephyr/net/offloaded_netdev.h>
 #include <zephyr/net/socket.h>
 #include <sockets_internal.h>
 #include <zephyr/sys/fdtable.h>
@@ -306,8 +307,8 @@
 	net_if_socket_offload_set(iface, offload_1_socket);
 }
 
-static struct net_if_api offloaded_1_if_api = {
-	.init = offloaded_1_iface_init,
+static struct offloaded_if_api offloaded_1_if_api = {
+	.iface_api.init = offloaded_1_iface_init,
 };
 
 NET_DEVICE_OFFLOAD_INIT(offloaded_1, "offloaded_1", offloaded_1_init, NULL,
@@ -376,8 +377,8 @@
 	net_if_socket_offload_set(iface, offload_2_socket);
 }
 
-static struct net_if_api offloaded_2_if_api = {
-	.init = offloaded_2_iface_init,
+static struct offloaded_if_api offloaded_2_if_api = {
+	.iface_api.init = offloaded_2_iface_init,
 };
 
 NET_DEVICE_OFFLOAD_INIT(offloaded_2, "offloaded_2", offloaded_2_init, NULL,