wifi: esp32: add config to enable automatic DHCPV4 negotiation

ESP32 wifi connection uses Zephyr's net stack. Once WiFi connects
in station mode, is requires the application to handle DHCPv4
negotiation. This PR adds support to automatic negotiation by
handling the dhcpv4 calls in driver layer.

Signed-off-by: Sylvio Alves <sylvio.alves@espressif.com>
diff --git a/drivers/wifi/esp32/Kconfig.esp32 b/drivers/wifi/esp32/Kconfig.esp32
index 4064678..5abca09 100644
--- a/drivers/wifi/esp32/Kconfig.esp32
+++ b/drivers/wifi/esp32/Kconfig.esp32
@@ -29,10 +29,12 @@
 	help
 	  WiFi password (WPA or WPA2) for the example to use.
 
-config ESP32_WIFI_STA_AUTO
-	bool "Automatically connect to configured WiFi SSID"
+config ESP32_WIFI_STA_AUTO_DHCPV4
+	bool "Automatically starts DHCP4 negotiation"
+	depends on NET_DHCPV4
+	depends on NET_IPV4
 	help
-	  WiFi driver will automatically connect to SSID.
+	  WiFi driver will automatically initiate DHCPV4 negotiation when connected.
 
 config ESP32_WIFI_STA_RECONNECT
 	bool "WiFi connection retry"
diff --git a/drivers/wifi/esp32/src/esp_wifi_drv.c b/drivers/wifi/esp32/src/esp_wifi_drv.c
index c702335..248527e 100644
--- a/drivers/wifi/esp32/src/esp_wifi_drv.c
+++ b/drivers/wifi/esp32/src/esp_wifi_drv.c
@@ -23,6 +23,8 @@
 #include "esp_system.h"
 #include "esp_wpa.h"
 
+#define DHCPV4_MASK (NET_EVENT_IPV4_DHCP_BOUND | NET_EVENT_IPV4_DHCP_STOP)
+
 /* use global iface pointer to support any ethernet driver */
 /* necessary for wifi callback functions */
 static struct net_if *esp32_wifi_iface;
@@ -39,7 +41,6 @@
 };
 
 struct esp32_wifi_runtime {
-	struct net_if *iface;
 	uint8_t mac_addr[6];
 	uint8_t frame_buf[NET_ETH_MAX_FRAME_SIZE];
 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
@@ -55,6 +56,22 @@
 K_THREAD_STACK_DEFINE(esp_wifi_event_stack, CONFIG_ESP32_WIFI_EVENT_TASK_STACK_SIZE);
 static struct k_thread esp_wifi_event_thread;
 
+static struct net_mgmt_event_callback esp32_dhcp_cb;
+
+static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
+			       struct net_if *iface)
+{
+	const struct wifi_status *status = (const struct wifi_status *)cb->info;
+
+	switch (mgmt_event) {
+	case NET_EVENT_IPV4_DHCP_BOUND:
+		wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, 0);
+		break;
+	default:
+		break;
+	}
+}
+
 /* internal wifi library callback function */
 esp_err_t esp_event_send_internal(esp_event_base_t event_base,
 				  int32_t event_id,
@@ -161,21 +178,53 @@
 			}
 
 			if (esp32_data.scan_cb) {
-				esp32_data.scan_cb(esp32_data.iface, 0, &res);
+				esp32_data.scan_cb(esp32_wifi_iface, 0, &res);
 
 				/* ensure notifications get delivered */
 				k_yield();
 			}
 		}
+	} else {
+		LOG_INF("Unable to retrieve AP records");
 	}
+
 	k_free(ap_list_buffer);
 
 out:
 	/* report end of scan event */
-	esp32_data.scan_cb(esp32_data.iface, 0, NULL);
+	esp32_data.scan_cb(esp32_wifi_iface, 0, NULL);
 	esp32_data.scan_cb = NULL;
 }
 
+static void esp_wifi_handle_connect_event(void)
+{
+	esp32_data.state = ESP32_STA_CONNECTED;
+	if (IS_ENABLED(CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4)) {
+		net_dhcpv4_start(esp32_wifi_iface);
+	} else {
+		wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, 0);
+	}
+}
+
+static void esp_wifi_handle_disconnect_event(void)
+{
+	if (esp32_data.state == ESP32_STA_CONNECTED) {
+		if (IS_ENABLED(CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4)) {
+			net_dhcpv4_stop(esp32_wifi_iface);
+		}
+		wifi_mgmt_raise_disconnect_result_event(esp32_wifi_iface, 0);
+	} else {
+		wifi_mgmt_raise_disconnect_result_event(esp32_wifi_iface, -1);
+	}
+
+	if (IS_ENABLED(CONFIG_ESP32_WIFI_STA_RECONNECT)) {
+		esp32_data.state = ESP32_STA_CONNECTING;
+		esp_wifi_connect();
+	} else {
+		esp32_data.state = ESP32_STA_STARTED;
+	}
+}
+
 static void esp_wifi_event_task(void)
 {
 	system_event_t evt;
@@ -194,16 +243,10 @@
 			net_if_down(esp32_wifi_iface);
 			break;
 		case ESP32_WIFI_EVENT_STA_CONNECTED:
-			esp32_data.state = ESP32_STA_CONNECTED;
-			wifi_mgmt_raise_connect_result_event(esp32_wifi_iface, 0);
+			esp_wifi_handle_connect_event();
 			break;
 		case ESP32_WIFI_EVENT_STA_DISCONNECTED:
-			if (esp32_data.state == ESP32_STA_CONNECTED) {
-				wifi_mgmt_raise_disconnect_result_event(esp32_wifi_iface, 0);
-			} else {
-				wifi_mgmt_raise_disconnect_result_event(esp32_wifi_iface, -1);
-			}
-			esp32_data.state = ESP32_STA_STARTED;
+			esp_wifi_handle_disconnect_event();
 			break;
 		case ESP32_WIFI_EVENT_SCAN_DONE:
 			scan_done_handler();
@@ -360,7 +403,7 @@
 {
 	struct esp32_wifi_data *data = dev->data;
 
-	esp_err_t err = esp_wifi_set_mode(WIFI_MODE_NULL);
+	esp_err_t ret = esp_wifi_set_mode(WIFI_MODE_NULL);
 
 	ret |= esp_wifi_start();
 	if (ret != ESP_OK) {
@@ -376,7 +419,6 @@
 	const struct device *dev = net_if_get_device(iface);
 	struct esp32_wifi_runtime *dev_data = dev->data;
 
-	dev_data->iface = iface;
 	esp32_wifi_iface = iface;
 	dev_data->state = ESP32_STA_STOPPED;
 
@@ -414,6 +456,11 @@
 
 	k_thread_name_set(tid, "esp_event");
 
+	if (IS_ENABLED(CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4)) {
+		net_mgmt_init_event_callback(&esp32_dhcp_cb, wifi_event_handler, DHCPV4_MASK);
+		net_mgmt_add_event_callback(&esp32_dhcp_cb);
+	}
+
 	wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
 	esp_err_t ret = esp_wifi_init(&config);