回答Keyword1983朋友关于RTEMS网络 rtems_xxxx_attach 参数问题

Keyword1983 朋友在8月2日,在RTEMS 4.9.5:QEMU MINI2440 BSP 中的网络驱动开发(上)回复:

“您好 我有問題想請問 您的int rtems_dm9000_attach 帶了兩個參數 一個是struct rtems_bsdnet_ifconfig *config, 另一個是void *chip 不過我去看元代碼中其它的network driver的attach函數,其第一個參數都一樣沒錯,但是第二個參數有的是自定的chip configuration結構 有的卻只是個int attach的值.

這會造成一個問題, AP要初始化網卡,都是借由設定ifconfig結構達成,其中ifconfig第二個參數就是設定為你要使用的網卡的attach function point, 當呼叫rtems_bsdnet_initial()來初始化網路,接著會執行rtems_bsdnet_attach(ipf), 她會call剛剛記錄下來的attach function point 並且直接給予第二個參數值為1(ifp->attach(ifp, 1)),這樣一來不就表示 我們網卡所撰寫的attach的第二個參數只能為int型態,而不能使用自定的chip configuration結構? 那rtems source中的那些網路driver該如何使用呢??”


说实话,起初并没有准确理解这个问题。后来,仔细想了一下,这个问题倒有些意思。虽然是RTEMS网络特有的问题,但我觉得作为软件架构和数据结构抽象分析,具有一定的代表性。


RTEMS 网络控制的驱动链接函数原型如下所示:

int rtems_xxxx_attach(struct rtems_bsdnet_ifconfig *config,  void *chip );


第一个参数 config 存放rtems网络协议栈对以太网控制器的配置。

第二个参数 chip 存放以太网控制器驱动初始化所需要的特殊数据。


我们来看看RTEMS怎么调用这个函数的。RTEMS的网络配置,通过用户代码初始化数据结构:


static struct rtems_bsdnet_ifconfig netdriver_config = {
        RTEMS_BSP_NETWORK_DRIVER_NAME,  /* name */
        RTEMS_BSP_NETWORK_DRIVER_ATTACH,/* attach function */

#ifdef RTEMS_USE_LOOPBACK
        &loopback_config,               /* link to next interface */
#else
        NULL,                           /* No more interfaces */
#endif

#if 0
        NULL,                           /* BOOTP supplies IP address */
        NULL,                           /* BOOTP supplies IP net mask */
#else
        "10.0.0.9",                     /* IP address */
        "255.255.255.0",                /* IP net mask */
#endif /* !RTEMS_USE_BOOTP */

        ethernet_address,               /* Ethernet hardware address */
        0,                              /* ignore broadcast */
        0,                              /* mtu */
        0,                              /* rbuf_count */
        0,                              /* xbuf_count */
        0x300,                          /* port */
        9,                              /* irq */
        0,                              /* bpar */
        NULL                            /* driver control pointer */
};


/*
 * Network configuration
 */
struct rtems_bsdnet_config rtems_bsdnet_config = {
        &netdriver_config,

#if (defined (RTEMS_USE_BOOTP))
        rtems_bsdnet_do_bootp,
#else
        NULL,
#endif

        8,                    /* Default network task priority */
        256 * 1024,           /* Default mbuf capacity */
        256 * 1024,           /* Default mbuf cluster capacity */
        "leo",                /* Host name */
        "leo",                /* Domain name */
        "10.0.0.4",           /* Gateway */
        "10.0.0.4",           /* Log host */
        {"10.0.0.4"  },       /* Name server(s) */
        {"10.0.0.4"  },       /* NTP server(s) */
        0,                    /* efficiency */
        0,                    /* udp TX buffer */
        0,                    /* udp RX buffer */
        0,                    /* tcp TX buffer */
        0,                    /* tcp RX buffer */
};

这个是在用户代码中配置RTEMS网络的初始化数据。

cpukit/libnetworking/rtems下的RTEMS_glue.c中的

int rtems_bsdnet_initialize_network(void);函数完成对网络的初始化。核心代码970行:

    /*
     * Attach interfaces
     */
    for (ifp = rtems_bsdnet_config.ifconfig ; ifp ; ifp = ifp->next) {
        rtems_bsdnet_attach (ifp);
    }

这里的rtems_bsdnet_config是用户定义的。ifconfig就是用户定义的netdriver_config。链接代码时,直接将ifp传递到函数rtems_bsdnet_attach中。

/*
 * Attach a network interface.
 */
void rtems_bsdnet_attach(struct rtems_bsdnet_ifconfig *ifp)
{
    if (ifp) {
        rtems_bsdnet_semaphore_obtain ();
        (ifp->attach)(ifp, 1);
        rtems_bsdnet_semaphore_release ();
    }
}


在rtems_bsdnet_attach中,很明显,调用以太网驱动具体链接函数rtems_xxxx_attach时,第二个参数始终为1。原本这个东西并不难理,也不该有问题。但现在问题来了,rtems 网络协议栈对以太网控制器的配置应该是比较好抽象的,但是chip这个参数就比较麻烦了,这个参数属于和硬件底层高度相关的。我们在 struct rtems_bsdnet_ifconfig里也不方便安插与硬件高度相关的特殊数据。如果安插了,其结果只能是,代码可移植性变差,也变得难以理解。


RTEMS的相关资料缺乏,我们从源代码中找线索:

我们看c/src/libchip/network/sonic.c中的函数:

int rtems_sonic_driver_attach (
  struct rtems_bsdnet_ifconfig *config,
  sonic_configuration_t *chip
);


可以看到 1593 行 C 代码:

  sc->sonic = (void *) chip->base_address;
  sc->vector = chip->vector;
  sc->dcr_value = chip->dcr_value;
  sc->dc2_value  = chip->dc2_value;
  sc->write_register = chip->write_register;
  sc->read_register  = chip->read_register;


看红字标出的几个成员变量,应用层的话,是非常难以理解的。只有对应具体的驱动,才能明白具体的意思。所以从我个人的角度说,chip 这个变量是非常不适合作为高层调用传递的参数。所以在rtems_glue.c中,传递时用1表示挂载驱动,用0表示卸载驱动。只能是把chip这个参数作为整数。才能解决高层的封装问题,那么sonic.c这样的驱动中的attach函数里的第二个chip不能用整数传递的。


c/src/libchip/network/文件下有许多网络驱动。关于第二个参数chip也是五花8门,有的是正儿八经的与硬件高度相关的数据结构。如greth.c, smc91111.c;有的就是整数,如cs8900.c。


又怎么解决呢?怎么把它们统一起来呢?


在这里我提供一个简单的思路,算是抛砖引玉吧:


1.不可能改rtems_glue.c中的代码,使得第二个参数传递与硬件相关配置数据;(与rtems发行版本相抵触)

2.不可能在用户应用程序中对底层硬件结构的数据进行配置;(违反可移植性,代码难以理解)

3.不可能改变rtems中已经写好的c/src/libchip/network/下的驱动。(与rtems发行版本相抵触)


基于这三个思路,自己撰写驱动时,可以:


这些硬件特殊的数据和具体的bsp相关,个人认为在rtems的架构下应属于bsp相关的数据。仍然属于网络驱动部分。参考 c/src/lib/libbsp/arm/csb337/network/network.c,应该为每一个以太网控制器建立一个数据结构。在csb337中是,staic struct at91rm9200_emac_softc_t softc;


如果有两个控制器,那么就定义两个 static struct at91rm9200_emac_softc_t softc[2]; softc是驱动内部的数据。高层无权访问,也无需访问。怎么判断是哪个网络控制器呢?其实也很简单,

    if ((unitnumber = rtems_bsdnet_parse_driver_name (config, &unitname)) < 0)
        return 0;
    
    /*
     * Is driver free?
     */
    if (unitnumber != 0) {
        printf ("Bad AT91RM9200 EMAC unit number.\n");
        return 0;
    }

其实unitnumber就是控制器的编号,自己在做BSP的时候,不妨把第一个网络控制器称为eth0,第二个网络控制器称为eth1,以此类推。RTEMS_BSP_NETWORK_DRIVER_NAME, 定义的时候,在bsp.h中直接就写:

#define RTEMS_BSP_NETWORK_DRIVER_NAME "eth0"

#define RTEMS_BSP_NETWORK_DRIVER_ATTACH rtems_xxxx_attach

如果有第二个驱动,可以写:

#define RTEMS_BSP_NETWORK_DRIVER_2_NAME "eth1"

第二个网卡用这个名字就可以了,连接函数还是一样的。这样,就可以通过这个参数判断是哪个网络控制器,进而对类似的内部数据结构,如softc进行初始化。这个过程是由驱动自身完成的。


对于sonic.c这种*chip有特殊数据结构的函数,比如说,我在mycpu的bsp里使用它,我依旧写一个network.c的驱动,但是使用sonic.c驱动。


int rtems_mycpu_attach(struct rtems_bsdnet_ifconfig *config,  void *chip)

{

    sonic_configuration_t sonic_chip;

    ...

if (chip == 1)//连接驱动

{

    ...//获得当前驱动的名称,得到是哪一个芯片驱动,存储在unitnumber中

    switch(unitnumber)

{

case 0:

    ...//第一个芯片,初始化sonic_chip数据结构

    return rtems_sonic_driver_chip(config, &sonic_chip);

    break;

...

}

}

...

}

这个就是写一个自己的attach函数,把这些特殊的数据封装起来,既符合rtems的封装思路,又最大的利用了既有的成果。


欢迎拍砖!





你可能感兴趣的:(数据结构,网络,struct,Semaphore,网络协议,NetWork)