1. 开发环境

  • 操作系统:SylixOS

  • 编程环境:RealEvo-IDE3.1

  • 硬件平台:IMX6Q实验箱

2. 技术实现

SylixOS网卡驱动框架篇》里提过,网卡驱动的收发功能是通过netdev结构里的transmitreceive两个成员实现的。本篇文章将介绍SylixOS里的LWIP协议栈是如何调用底层网卡驱动里的这两个成员函数的。

2.1      网卡发送函数的调用

整个网卡驱动发送函数的调用关系如 21所示。

网卡驱动在向SylixOS注册网卡驱动时,需要调用netdev_add函数,这个函数接收一个netdev结构的参数。因为在网卡驱动初始化过程中,已经在底层驱动里实现了netdev下的transmitreceive两个成员。所以netdev_add函数可以通过netdev这个参数找到驱动里的收发功能函数。

netdev_add函数里会调用netifapi_netif_add 来添加API函数,netifapi_netif_add接收的一个函数参数是netdev_netif_initnetifapi_netif_add函数最终会调用netdev_netif_init函数。

netdev_netif_init函数会把netdev_netif_linkoutput赋值给netif结构体下的linkoutputnetdev_netif_linkoutput的具体实现如程序清单 21 所示。

LWIP协议栈里最终调用到底层驱动时,就是通过netiflinkoutput实现的,如程序清单 21所示,netdev_netif_linkoutput会调用宏NETDEV_TRANSMIT,这个宏的实现如程序清单 22 所示NETDEV_TRANSMIT会调用netdev里的transmit成员函数来进行数据包的发送。

程序清单 21 netdev_netif_linkoutput函数

/* lwip netif linkoutput hook function */static err_t  netdev_netif_linkoutput(struct netif *netif, struct pbuf *p){  netdev_t *netdev = (netdev_t*)(netif->state);  int ret; #if ETH_PAD_SIZE  pbuf_header(p, -ETH_PAD_SIZE);#endif   ret = NETDEV_TRANSMIT(netdev, p);  #if ETH_PAD_SIZE  pbuf_header(p, ETH_PAD_SIZE);#endif   if (ret < 0) {    return (ERR_IF);  }  return (ERR_OK);}

程序清单 22NETDEV_TRANSMIT

 #define  NETDEV_TRANSMIT(netdev, a)    (netdev)->drv->transmit((netdev), a)

2.2    网卡接收函数的调用

SylixOS网卡驱动实现篇》里提到过,网卡驱动里的接收中断使用netdev_notify函数来通知协议栈已经收到数据,并让协议栈调用netdevreceive成员函数来进行接收报文的处理。

接收处理函数enetCoreRecv里接收两个参数,一个是netdev结构体,另一个是input类型的函数。

netdev_notify的具体实现如程序清单 23 所示,里面会通过q_en这个参数来决定是否使用队列的方式来接收,如果不使用,则直接调用宏NETDEV_RECEIVE,否则通过netJobAdd把接收处理函数添加到队列中。不管哪一种方式,最后都会调用netdev下的receive成员函数,而receive的第二个input类型的参数就是程序清单 23 中的netdev_netif_linkinput

因此,底层驱动的接收处理函数会通过netdev_netif_linkinput把网卡收到的数据一层层的传到协议栈中去进行对应的处理。

程序清单 23 netdev_notify函数

/*if netdev detected a packet in netdev buffer, driver can callthis function to receive this packet.   notify:0 can transmit 1: can receive    qen:0 do not use netjob queue1:use netjob queue */int  netdev_notify (struct netdev *netdev,netdev_inout inout, int q_en){  if (!netdev || (netdev->magic_no !=NETDEV_MAGIC)) {    return (-1);  }    if (inout != LINK_INPUT) {    return (0);  }    if (q_en) {    if (netJobAdd(netdev->drv->receive,netdev,                   (void*)netdev_netif_linkinput, 0, 0, 0, 0) == 0) {      return (0);        } else {      return (-1);    }  }    NETDEV_RECEIVE(netdev,netdev_netif_linkinput);    return (0);}

3. 参考资料