RTThread 自動網路卡使用問題

浇筑菜鸟發表於2024-06-25

最近使用 STM32 測試了一下 lwip 和 esp8266 的網路連線問題,使用 RTThread 的自動網路卡時,發現不能很好的自動切換預設網路卡,不能滿足需求,所以自己簡單的改了一下。

一、準備材料

  • MCU:STM32F103ZT6
  • RTThread:5.0.2
  • 工具:RTThread studio
  • 網路晶片:DM9000(FSMC連線)
  • WIFI:ESP8266(uart連線)

二、測試現象

  • 如果預設網路卡斷開是,無其他網路卡聯網時,其他網路卡重新聯網,不會自動切換預設網路卡
  • 初始化時,如果預設網路卡為初始成功,即時存在能聯網的網路卡時,也不會切換預設網路卡

三、問題原因

遇到問題的時候,我們首先檢視一下官方的說明文件: https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev

找到預設網路卡的說明,如下圖所示:

從圖中不難看出,他只有網路卡從 up → down 的時候,才會去切換狀態,所以也能解釋上面我測試到的現象了

四、解決辦法

這裡有兩個辦法可以解決,方式一,直接就該原始碼;方式二,從新新增相應的處理程式,如下所示:

方式一

程式原始碼的檔案是 rt-thread/components/net/netdev/src/netdev.c,

  1. 先來看一下自動切換網路卡的實現方式, 程式如下

    /* Change to the first link_up network interface device automatically */
    static void netdev_auto_change_default(struct netdev *netdev)
    {
        struct netdev *new_netdev = RT_NULL;
    
        if (netdev->flags & NETDEV_FLAG_LINK_UP)
        {
            if (!(netdev_default->flags & NETDEV_FLAG_LINK_UP))
            {
                netdev_set_default(netdev);
            }
            return;
        }
        if (rt_memcmp(netdev, netdev_default, sizeof(struct netdev)) == 0)
        {
            new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP);
            if (new_netdev)
            {
                netdev_set_default(new_netdev);
            }
        }
    }
    

    程式中不難看出切換網路卡的思路,確實很厲害,程式碼也比較簡潔,所以切換部分的程式碼沒有問題

  2. 呼叫的自動切換部分的程式

    /**
     * This function will set network interface device status.
     * @NOTE it can only be called in the network interface device driver.
     *
     * @param netdev the network interface device to change
     * @param is_up the new status
     */
    void netdev_low_level_set_status(struct netdev *netdev, rt_bool_t is_up)
    {
        if (netdev && netdev_is_up(netdev) != is_up)
        {
            if (is_up)
            {
                netdev->flags |= NETDEV_FLAG_UP;
            }
            else
            {
                netdev->flags &= ~NETDEV_FLAG_UP;
    
    #ifdef NETDEV_USING_AUTO_DEFAULT
                /* change to the first link_up network interface device automatically */
                netdev_auto_change_default(netdev);
    #endif /* NETDEV_USING_AUTO_DEFAULT */
            }
    
            /* execute  network interface device status change callback function */
            if (netdev->status_callback)
            {
                netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_UP : NETDEV_CB_STATUS_DOWN);
            }
        }
    }
    
    
    /**
     * This function will set network interface device active link status.
     * @NOTE it can only be called in the network interface device driver.
     *
     * @param netdev the network interface device to change
     * @param is_up the new link status
     */
    void netdev_low_level_set_link_status(struct netdev *netdev, rt_bool_t is_up)
    {
        if (netdev && netdev_is_link_up(netdev) != is_up)
        {
            if (is_up)
            {
                netdev->flags |= NETDEV_FLAG_LINK_UP;
    
    #ifdef RT_USING_SAL
                /* set network interface device flags to internet up */
                if (netdev_is_up(netdev) && !ip_addr_isany(&(netdev->ip_addr)))
                {
                    sal_check_netdev_internet_up(netdev);
                }
    #endif /* RT_USING_SAL */
            }
            else
            {
                netdev->flags &= ~NETDEV_FLAG_LINK_UP;
    
                /* set network interface device flags to internet down */
                netdev->flags &= ~NETDEV_FLAG_INTERNET_UP;
    
    #ifdef NETDEV_USING_AUTO_DEFAULT
                /* change to the first link_up network interface device automatically */
                netdev_auto_change_default(netdev);
    #endif /* NETDEV_USING_AUTO_DEFAULT */
            }
    
            /* execute link status change callback function */
            if (netdev->status_callback)
            {
                netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_LINK_UP : NETDEV_CB_STATUS_LINK_DOWN);
            }
        }
    }
    

    從程式中不難看出,它是隻有當網路狀態為 down 的時候,才去呼叫切換網路狀態函式

  3. 解決辦法
    只需要將 netdev_low_level_set_link_status 函式中的呼叫位移,移除 down 的判斷條件即可解決這個問題。這個問題解決很簡單,但是為啥會是這樣的設計我還沒弄明白,也能是開發人員粗心了(按理說不應該),也有可能是另有深意,但是不滿足我得需求,所以還是需要更改一下。這裡我沒有改源程式,而是從新寫了一個函式去實現的,具體流程如下

方式二

透過提供的 API 重寫了一下預設網路卡切換的方法,程式如下

#include <rtthread.h>
/* 當需要網路卡操作是,需要包含這兩個標頭檔案 */
#include <arpa/inet.h>
#include <netdev.h>

#define DBG_TAG "app_net_monitor"
#include <rtdbg.h>

typedef struct net_device_info
{
    const char *name;    // 裝置名稱
    struct netdev *dev;  // 網路裝置指標
} net_device_info_t;

/* 初始網路卡裝置,第一個為預設網路卡 */
static net_device_info_t net_device[] =
{
        {"e0", RT_NULL},
        {"esp0", RT_NULL}
};

/* 預設網路卡裝置  */
struct netdev *network_default = RT_NULL;

rt_uint16_t net_dev_number = sizeof(net_device)/sizeof(net_device_info_t);


/**
 * @brief 自動更改為第一個link_up網路介面裝置
 *
 * @param netdev 網路裝置物件
 */
static void network_auto_change_default(struct netdev *netdev)
{
    struct netdev *new_netdev = RT_NULL;

    if (netdev->flags & NETDEV_FLAG_LINK_UP)
    {
        if (!(network_default->flags & NETDEV_FLAG_LINK_UP))
        {
            netdev_set_default(netdev);
            network_default = netdev;
        }
        return;
    }
    if (rt_memcmp(netdev, network_default, sizeof(struct netdev)) == 0)
    {
        new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP);
        if (new_netdev)
        {
            netdev_set_default(new_netdev);
            network_default = new_netdev;
        }
    }
}


/**
 * @brief 網路裝置狀態回撥函式
 *
 * @param netdev 網路裝置物件
 * @param type 網路狀態型別
 */
void network_status_callback(struct netdev *netdev, enum netdev_cb_type type)
{
    switch (type)
    {
    /* 網路斷開 */
    case NETDEV_CB_STATUS_LINK_DOWN:
        network_auto_change_default(netdev);
        break;

    /* 網路連線 */
    case NETDEV_CB_STATUS_INTERNET_UP:
        network_auto_change_default(netdev);
        break;

    default:
        break;
    }
}

/**
 * @brief 返回裝置聯網狀態
 *
 * @return RT_EOK 表示網路連線
 */
rt_bool_t network_is_up()
{
    return netdev_is_link_up(network_default);
}

/**
 * @brief 初始化自動網路卡設定
 *
 */
static int app_network_init(void)
{

    for (int i = 0; i < net_dev_number; i++)
    {
        /* 獲取網路卡裝置 */
        net_device[i].dev = netdev_get_by_name(net_device[i].name);
        if (net_device[i].dev == RT_NULL)
        {
            LOG_E("not find network interface device name(%s).\n", net_device[i].name);
            continue;
        }

        /* 設定網路裝置狀態變化的回撥函式 */
        netdev_set_status_callback(net_device[i].dev, network_status_callback);

        /* 設定一個預設網路卡  */
        if (network_default == RT_NULL)
        {
            network_default = net_device[i].dev;
            netdev_set_default(network_default);
        }

    }

    return RT_EOK;
}

INIT_APP_EXPORT(app_network_init);

相關文章