• RTThread 自动网卡使用问题


    最近使用 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
    /* 当需要网卡操作是,需要包含这两个头文件 */
    #include
    #include
    #define DBG_TAG "app_net_monitor"
    #include
    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);
  • 相关阅读:
    闭着眼睛安装Neoj4版本(5.12.0 Community windows)
    python线程和进程
    Python - 生成二维码、条形码
    controller-informer
    铁矿行业商业智能BI经营分析框架(三)财务能力分析
    算法+二分查找+冒泡排序+位运算【Interview】
    python 方法加强@ pytho中@ python@
    嵌入式系统双网卡同网段疑难问题处理笔记
    Latex 报错:The font cannot be found.
    JavaScript系列从入门到精通系列第十八篇:JavaScript中的函数作用域
  • 原文地址:https://www.cnblogs.com/jzcn/p/18266557