LWIP的底层结构(物理层)

2009-5-14 LWIP 的底层结构(物理层)
       我们前面讲到说是 ip 层的发送和接收都是直接调用了底层,也就是设备驱动层的函数实现,在这里暂且称之为物理层吧。下面就接着 ip 层的讲,不过由于这里的设备驱动各平台的都不一样,为此,我们选择 ARM9_STR91X_IAR 这个 Demo 作为实例,该平台的网络设备驱动在 \library\source\91x_enet.c 文件中。而 ethernetif.c 文件就是我们需要的,它是连接设备驱动程序与 ip 层的桥梁。
 
       Ethernetif.c 文件中提供的函数主要有以下这么几个:
(1)    low_level_init
(2)    low_level_input
(3)    low_level_output
(4)    ethernetif_init
(5)    ethernetif_input
(6)    ethernetif_output
这里对外的接口只有 ethernetif_init 函数,它是 main 函数中通过
netif_add( &EMAC_if, &xIpAddr, &xNetMast, &xGateway, NULL, ethernetif_init, tcpip_input ); 来被调用的。我们可以清楚的看到, tcpip_input 的使用,它就是被用来当有数据接收的时候被调用的以使接收到的数据进入 tcpip 协议栈。
 
       netif_add 函数中,我们可以看到
netif->input = input;
if (init(netif) != ERR_OK)
{
    return NULL;
}
 
Ok ,从这里就进入到 ethernetif_init 函数了,在这个函数中,我们主要看以下几句:
netif->output = ethernetif_output ;
netif->linkoutput = low_level_output ;
low_level_init(netif);
etharp_init();
可以看到, netif->output netif->linkoutput 被赋值了,这个很重要的,等会再说。
 
好,再接着看 low_level_init 函数
s_pxNetIf = netif; // 对全局变量 s_pxNetIf 赋初值
ENET_InitClocksGPIO();
ENET_Init();
ENET_Start(); // 3 句是对网络设备的寄存等的配置
xTaskCreate( ethernetif_input, ( signed portCHAR * ) "ETH_INT", netifINTERFACE_TASK_STACK_SIZE, NULL, netifINTERFACE_TASK_PRIORITY, NULL );
ethernet_input 创建 task ,这个函数也很有意思,首先可以看到的是一个无限循环,在循环体中有以下调用:
p = low_level_input( s_pxNetIf );
s_pxNetIf->input(p, s_pxNetIf); //tcpip_input
虽然有了这两句,还不是很清楚,可以确定的是后一句是把接收到的数据送入 tcpip 协议栈处理,为此,我们想到上一句是从硬件读出数据。看下具体的 low_level_input 函数实现:
len = ENET_HandleRxPkt(s_rxBuff);
这个函数很好理解,主要的是上面的那一句。
/******************************************************************************
* Function Name  : ENET_HandleRxPkt
* Description    : receive a packet and copy it to memory pointed by ppkt.
* Input          : ppkt: pointer on application receive buffer.
* Output         : None
* Return         : ENET_NOK - If there is no packet
*                : ENET_OK  - If there is a packet
******************************************************************************/
u32 ENET_HandleRxPkt ( void *ppkt)
{
ENET_DMADSCRBase *pDescr;
u16 size;
static int iNextRx = 0;
 
       if( dmaRxDscrBase[ iNextRx ].dmaPackStatus &
DMA_DSCR_RX_STATUS_VALID_MSK )
       {
              return 0;
       }
 
       pDescr = &dmaRxDscrBase[ iNextRx ];
 
       /*Get the size of the packet*/
       size = ((pDescr->dmaPackStatus & 0x7ff) - 4);
 
       //MEMCOPY_L2S_BY4((u8*)ppkt, RxBuff, size); /*optimized memcopy function*/
       memcpy(ppkt, RxBuff[iNextRx], size);   //string.h library*/
 
       /* Give the buffer back to ENET */
       pDescr->dmaPackStatus = DMA_DSCR_RX_STATUS_VALID_MSK;
 
       iNextRx++;
 
       if( iNextRx >= ENET_NUM_RX_BUFFERS )
       {
              iNextRx = 0;
       }
 
       /* Return no error */
       return size;
}
这个函数也很好理解,是从 DMA 中直接拷贝数据到指定的 pBuf 。至此, input 过程完事了,从代码调用流程上看真是千回百转,一会 low_level ,一会 ethernetif 。不过,总体来说是系统通过一个 task ethernetif_input )轮询检查 DMA 控制器的状态以判断是否有数据接收到。
 
       下面再研究下 output 过程。 Output 过程是由应用程序以主动方式触发的,经过前面几篇的介绍,我们知道发送的数据后来被传递给了 ip_output_if 函数。我们就接着这个函数看,它直接调用了 netif->output 函数,刚才我们看到在 ethernetif_init 中有对这个变量【函数指针】赋值,它就是 ethernetif_output 。它倒是简单,直接 return etharp_output () ;而它有在最后调用了 etharp_send_ip ,在这个函数的最后调用了 return netif->linkoutput(netif, p) ; 好了终于找到根了这里的 linkoutput 函数也就是 low_level_output ,果然有如下调用:
memcpy(&TxBuff[l], (u8_t*)q->payload, q->len);
 
       还记得 ENET_Init 嘛,它的函数实现中有如下两句调用:
ENET_TxDscrInit();
ENET_RxDscrInit();
它们就是初始化 DMA 发送和接收的地址的,也就是上面所说的 TxBuff RxBuff
 
       OK ,大功告成,自上而下的 LWIP 大体流程都说到了。如有以后碰到什么需要注意的细节之类,再补上吧。

你可能感兴趣的:(职场,休闲)