
/* eth2ap (Ethernet to Wi-Fi AP packet forwarding) Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_eth.h"

//#include "esp_netif_types.h"
//#include "esp_netif.h"

#include "nvs_flash.h"
#include "esp_private/wifi.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "ble_spp_server_demo.h"

#include "lwip/err.h"
#include "lwip/sys.h"

#define IP_3SEGMENT 7

#define LOCAL_IP_ADDR1 192
#define LOCAL_IP_ADDR2 168
#define LOCAL_IP_ADDR3 IP_3SEGMENT
#define LOCAL_IP_ADDR4 110

#define LOCAL_IP_GW1  192
#define LOCAL_IP_GW2  168
#define LOCAL_IP_GW3  IP_3SEGMENT
#define LOCAL_IP_GW4  120

static const char *TAG = "eth_example";

static esp_eth_handle_t s_eth_handle = NULL;
static xQueueHandle flow_control_queue = NULL;
static bool s_sta_is_connected = false;
static bool s_ethernet_is_connected = false;
static uint8_t s_eth_mac[6];

esp_netif_ip_info_t local_ip;
esp_netif_t *local_netif = NULL;
esp_err_t res_ap_get;
esp_err_t res_ap_set;

esp_err_t ret;

#define FLOW_CONTROL_QUEUE_TIMEOUT_MS (100)
#define FLOW_CONTROL_QUEUE_LENGTH (40)
#define FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS (100)

typedef struct {
    void *packet;
    uint16_t length;
} flow_control_msg_t;

// Forward packets from Wi-Fi to Ethernet
static esp_err_t pkt_wifi2eth(void *buffer, uint16_t len, void *eb)
{
    if (s_ethernet_is_connected) {
        if (esp_eth_transmit(s_eth_handle, buffer, len) != ESP_OK) {
            ESP_LOGE(TAG, "Ethernet send packet failed");
        }
    }
    esp_wifi_internal_free_rx_buffer(eb);
    return ESP_OK;
}

// Forward packets from Ethernet to Wi-Fi
// Note that, Ethernet works faster than Wi-Fi on ESP32,
// so we need to add an extra queue to balance their speed difference.
static esp_err_t pkt_eth2wifi(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t len, void* priv)
{
    esp_err_t ret = ESP_OK;
    flow_control_msg_t msg = {
        .packet = buffer,
        .length = len
    };
    if (xQueueSend(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) != pdTRUE) {
        ESP_LOGE(TAG, "send flow control message failed or timeout");
        free(buffer);
        ret = ESP_FAIL;
    }
    return ret;
}

// This task will fetch the packet from the queue, and then send out through Wi-Fi.
// Wi-Fi handles packets slower than Ethernet, we might add some delay between each transmitting.
static void eth2wifi_flow_control_task(void *args)
{
    flow_control_msg_t msg;
    int res = 0;
    uint32_t timeout = 0;
    while (1) {
        if (xQueueReceive(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) == pdTRUE) {
            timeout = 0;
            if (s_sta_is_connected && msg.length) {
                do {
                    vTaskDelay(pdMS_TO_TICKS(timeout));
                    timeout += 2;
                    res = esp_wifi_internal_tx(ESP_IF_WIFI_AP, msg.packet, msg.length);
                } while (res && timeout < FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS);
                if (res != ESP_OK) {
                    ESP_LOGE(TAG, "WiFi send packet failed: %d", res);
                }
            }
            free(msg.packet);
        }
    }
    vTaskDelete(NULL);
}

// Event handler for Ethernet
static void eth_event_handler(void *arg, esp_event_base_t event_base,
                              int32_t event_id, void *event_data)
{
    switch (event_id) {
    case ETHERNET_EVENT_CONNECTED:
        ESP_LOGI(TAG, "Ethernet Link Up");
        s_ethernet_is_connected = true;
        esp_eth_ioctl(s_eth_handle, ETH_CMD_G_MAC_ADDR, s_eth_mac);
		
		ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
            s_eth_mac[0], s_eth_mac[1], s_eth_mac[2], s_eth_mac[3], s_eth_mac[4], s_eth_mac[5]);
		
        esp_wifi_set_mac(WIFI_IF_AP, s_eth_mac); //读取eth的MAC, 然后设置wifi的MAC， 使两者的MAC保持一致
		
		/*printf self mac and IP*/
		ret = esp_netif_get_ip_info(local_netif, &local_ip);
	
		if(ret == ESP_OK)
		{
			ESP_LOGI(TAG,"ethernet up, get is success\n");
			ESP_LOGI(TAG, "now_self_ip:"IPSTR"\n",IP2STR(&local_ip.ip));
			ESP_LOGI(TAG, "now_self_netmask:"IPSTR"\n",IP2STR(&local_ip.netmask));
			ESP_LOGI(TAG, "now_self_gw:"IPSTR"\n",IP2STR(&local_ip.gw)); 
		}
		else
		{
			ESP_LOGE(TAG,"get local_ip failed\n");
		}
		
        ESP_ERROR_CHECK(esp_wifi_start());
		
        break;
    case ETHERNET_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "Ethernet Link Down");
        s_ethernet_is_connected = false;
        ESP_ERROR_CHECK(esp_wifi_stop());
        break;
    case ETHERNET_EVENT_START:
        ESP_LOGI(TAG, "Ethernet Started");
        break;
    case ETHERNET_EVENT_STOP:
        ESP_LOGI(TAG, "Ethernet Stopped");
        break;
    default:
        break;
    }
}

// /** Event handler for IP_EVENT_ETH_GOT_IP */
// static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
                                 // int32_t event_id, void *event_data)
// {
    // ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
    // const esp_netif_ip_info_t *ip_info = &event->ip_info;

    // ESP_LOGI(TAG, "Ethernet Got IP Address");
    // ESP_LOGI(TAG, "~~~~~~~~~~~");
    // ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
    // ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
    // ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
    // ESP_LOGI(TAG, "~~~~~~~~~~~");
// }

// Event handler for Wi-Fi
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    switch (event_id) {
    case WIFI_EVENT_AP_STACONNECTED:
        ESP_LOGI(TAG, "Wi-Fi AP got a station connected");
        s_sta_is_connected = true;
        esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, pkt_wifi2eth);
		
		/*printf self mac and IP*/
		res_ap_get = esp_netif_get_ip_info(local_netif, &local_ip);
	
		if(res_ap_get == ESP_OK)
		{
			ESP_LOGI(TAG,"get is success\n");
			ESP_LOGI(TAG, "now_self_ip:"IPSTR"\n",IP2STR(&local_ip.ip));
			ESP_LOGI(TAG, "now_self_netmask:"IPSTR"\n",IP2STR(&local_ip.netmask));
			ESP_LOGI(TAG, "now_self_gw:"IPSTR"\n",IP2STR(&local_ip.gw)); 
		}
		else
		{
			ESP_LOGE(TAG,"get local_ip failed\n");
		}
		
        break;
    case WIFI_EVENT_AP_STADISCONNECTED:
        ESP_LOGI(TAG, "Wi-Fi AP got a station disconnected");
        s_sta_is_connected = false;
        esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, NULL);
        break;
    default:
        break;
    }
}

static void initialize_ethernet(void)
{
    ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL));
	// ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
	
    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
    phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
    phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
    mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
    mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
    esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_LAN8720
    esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
    esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config);
#endif
#elif CONFIG_EXAMPLE_USE_DM9051
    gpio_install_isr_service(0);
    spi_device_handle_t spi_handle = NULL;
    spi_bus_config_t buscfg = {
        .miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
        .mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
        .sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
    };
    ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
    spi_device_interface_config_t devcfg = {
        .command_bits = 1,
        .address_bits = 7,
        .mode = 0,
        .clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
        .spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
        .queue_size = 20
    };
    ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
    /* dm9051 ethernet driver is based on spi driver */
    eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
    dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
    esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
    esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
#endif
    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
	
    config.stack_input = pkt_eth2wifi;
    ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle));
    esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, (void *)true);
    esp_eth_start(s_eth_handle);
}

static void initialize_wifi(void)
{
	ESP_ERROR_CHECK(esp_netif_init());
	//ESP_ERROR_CHECK(esp_event_loop_create_default());
    local_netif = esp_netif_create_default_wifi_ap();
	
	if(NULL == local_netif)
	{
		ESP_LOGE(TAG, "new local_netif failed");
	}
	else
	{
		ESP_LOGI(TAG, "new local_netif succeeded");
	}
	
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL));
    
#if 1	
	wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    //ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
#endif	
	
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = CONFIG_EXAMPLE_WIFI_SSID,
            .ssid_len = strlen(CONFIG_EXAMPLE_WIFI_SSID),
            .password = CONFIG_EXAMPLE_WIFI_PASSWORD,
            .max_connection = CONFIG_EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
	
    if (strlen(CONFIG_EXAMPLE_WIFI_PASSWORD) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
	ESP_ERROR_CHECK(esp_wifi_start());
		
#if 1 /* 为了读取并修改AP的IP添加 */
	/*you must call esp_wifi_start() before set your new IP,
	because the WiFi system's start always load the original IP information
	from the tcpip_adapter_init(),if you want change the original IP ,
	please change the IP in tcpip_adapter_init(),otherwise,please allow the 
	system load the original IP firstly when call esp_wifi_start(), 
	and then you can set your new IP*/

    ESP_LOGI(TAG, "wifi_init_softap finished\r\n SSID:%s \r\n password:%s",
             CONFIG_EXAMPLE_WIFI_SSID, CONFIG_EXAMPLE_WIFI_PASSWORD);
			 
	/*print old IP information*/
	res_ap_get = esp_netif_get_ip_info(local_netif, &local_ip);
	if(res_ap_get == ESP_OK)
	{
		ESP_LOGI(TAG,"get ip_info succeeded\n");
	}
	else
	{
		ESP_LOGE(TAG,"get IP failed, ret = %d\n", res_ap_get);
	}
	
	ESP_LOGI(TAG, "old_self_ip:"IPSTR"\n",IP2STR(&local_ip.ip));
	ESP_LOGI(TAG, "old_self_netmask:"IPSTR"\n",IP2STR(&local_ip.netmask));
	ESP_LOGI(TAG, "old_self_gw:"IPSTR"\n",IP2STR(&local_ip.gw));
	 
	/*stop dhcps*/
	esp_err_t ret;
	esp_netif_dhcp_status_t dhcps_status;
	ret = esp_netif_dhcps_get_status(local_netif, &dhcps_status);
	if(ret != ERR_OK)
	{
		ESP_LOGE(TAG,"get dhcps_status failed , ret = %d",ret);
	}
	else
	{
		
	}
	
	ESP_LOGI(TAG,"dhcps_status = %d",dhcps_status);
	
#if 0	
	ESP_NETIF_DHCP_INIT = 0,    /**< DHCP client/server is in initial state (not yet started) */
    ESP_NETIF_DHCP_STARTED,     /**< DHCP client/server has been started */
    ESP_NETIF_DHCP_STOPPED,     /**< DHCP client/server has been stopped */
    ESP_NETIF_DHCP_STATUS_MAX
#endif	
	
	esp_err_t ret_dhcp;
	
	ret_dhcp = esp_netif_dhcps_stop(local_netif); 
	ESP_LOGI(TAG,"ret_dhcps_stop = %d", ret_dhcp);
	 
	ret = esp_netif_dhcps_get_status(local_netif, &dhcps_status);
	if(ret != ERR_OK)
	{
		ESP_LOGE(TAG,"get dhcps_status failed , ret = %d",ret);
	}
	else
	{
		ESP_LOGI(TAG,"after stopped, dhcps_status = %d",dhcps_status);
	}
	 
	/*write */
	esp_netif_set_ip4_addr(&local_ip.ip, LOCAL_IP_ADDR1, LOCAL_IP_ADDR2 , LOCAL_IP_ADDR3, LOCAL_IP_ADDR4);
	esp_netif_set_ip4_addr(&local_ip.gw, LOCAL_IP_GW1, LOCAL_IP_GW2 , LOCAL_IP_GW3, LOCAL_IP_GW4);
	esp_netif_set_ip4_addr(&local_ip.netmask, 255, 255 , 255, 0);
	
	/*print set IP information*/
	ESP_LOGI(TAG,"set ip:"IPSTR"\n",IP2STR(&local_ip.ip));
	ESP_LOGI(TAG,"set:netmask"IPSTR"\n",IP2STR(&local_ip.netmask));
	ESP_LOGI(TAG,"set gw:"IPSTR"\n",IP2STR(&local_ip.gw));
	
	/*set new IP*/
	res_ap_set = esp_netif_set_ip_info(local_netif, &local_ip);
	if(res_ap_set == ESP_OK)
	{
		ESP_LOGI(TAG,"set ip_info succeeded\n");
	}
	else
	{
		ESP_LOGE(TAG,"set ip_onfi failed, ret = %d\n", res_ap_set);
	}
	 
	/*restart adapter dhcps*/	 
	ret_dhcp = esp_netif_dhcps_start(local_netif); 
	ESP_LOGI(TAG,"restart, ret_dhcps_start = %d",ret_dhcp);
	
	ret = esp_netif_dhcps_get_status(local_netif, &dhcps_status);
	if(ret != ERR_OK)
	{
		ESP_LOGE(TAG,"get dhcps_status failed , ret = %d",ret);
	}
	else
	{
		ESP_LOGI(TAG,"after restarted, dhcps_status = %d",dhcps_status);
	}
	
	/*print new IP information*/
	res_ap_get = esp_netif_get_ip_info(local_netif, &local_ip);
	if(res_ap_get == ESP_OK)
	{
		ESP_LOGI(TAG,"get ip_info succeeded");
		ESP_LOGI(TAG, "new_self_ip:"IPSTR"\n",IP2STR(&local_ip.ip));
		ESP_LOGI(TAG, "new_self_netmask:"IPSTR"\n",IP2STR(&local_ip.netmask));
		ESP_LOGI(TAG, "new_self_gw:"IPSTR"\n",IP2STR(&local_ip.gw));
	}
	else
	{
		ESP_LOGE(TAG,"get ip_info failed, ret = %d\n", res_ap_get);
	}
	
	ESP_ERROR_CHECK(esp_wifi_stop());
#endif
}

static esp_err_t initialize_flow_control(void)
{
    flow_control_queue = xQueueCreate(FLOW_CONTROL_QUEUE_LENGTH, sizeof(flow_control_msg_t));
    if (!flow_control_queue) {
        ESP_LOGE(TAG, "create flow control queue failed");
        return ESP_FAIL;
    }
    BaseType_t ret = xTaskCreate(eth2wifi_flow_control_task, "flow_ctl", 2048, NULL, (tskIDLE_PRIORITY + 2), NULL);
    if (ret != pdTRUE) {
        ESP_LOGE(TAG, "create flow control task failed");
        return ESP_FAIL;
    }
    return ESP_OK;
}

void app_main(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
	
    ESP_ERROR_CHECK(ret);
	
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(initialize_flow_control());
    
    initialize_ethernet();
    initialize_wifi();
	
	//bt_setup(); //bluetooth ble setup
}
