以太网中断服务程序代码如下:
void lwIPEthernetIntHandler(void) { unsigned long ulStatus; // // Read and Clear the interrupt. // 读并清除中断标志,这个中断是由软件触发的,所以要检查一下中断状态看看是否真的有中断发生 ulStatus = EthernetIntStatus(ETH_BASE, false); EthernetIntClear(ETH_BASE, ulStatus); // // If a transmit/rx interrupt was active, run the low-level interrupt // handler. // 如果一个发送/接收中断被激活,运行底层中断处理程序. if(ulStatus) { //这个函数将从群星以太网FIFO中读取数据包放到pbuf队列.这个pbuf队列可以供更高级一层调用. //如果发送器空闲并且至少有一个包在发送队列,会将它发送到FIFO并开始发送. stellarisif_interrupt(&lwip_netif); //注1 } #if NO_SYS // // If no RTOS is enabled, then we do all lwIP procesing in the interrupt. // 如果不使用RTOS,那么所有的lwIP处理都在中断中实现 // // Service any packets on the receive queue. // 接收队列上的任何包处理 当已经准备好从接口中读取数据时,这个函数被调用.它使用 底层ethernet_input() // 函数处理来自网络接口的实际接收数据.它会根据已经接收的数据包类型来调用 // 适当的input函数. stellarisif_input(&lwip_netif); //注2 // // Service the lwIP timers. // lwIP定时器处理 lwIPServiceTimers(); #endif }
这个函数是以太网中断处理函数,处理lwIP TCP/IP协议栈的以太网中断。这个中断服务函数并非是接收到或发送出一个以太网包后由硬件触发,而是在SysTick定时器中断处理函数中软件触发的。这个函数处于TCP/IP协议的最底层,它将所有接收到的数据包放在可供高层处理的数据包队列中(pbuf链中),并检查发送数据队列是否为空,必要时通过以太网MAC地址发送数据。如果系统不使用RTOS,则lwIP的其他工作也在这个中断服务函数中执行,如处理接收的包或者处理lwIP定时器。
以太网中断服务函数(lwIPEthernetIntHandler(void))首先调用EthernetIntStatus (ETH_BASE, false)函数来检查以太网中断状态,因为这个以太网中断并非是硬件触发。可能并没有接收或发送以太网数据,但SysTick定时器中断服务函数仍尽忠职守的周期性触发这个以太网中断。之后,无论有无以太网中断,以太网中断服务函数都会调用EthernetIntClear(ETH_BASE, ulStatus)函数来清除以太网中断标志位。
如果检测到一个TX或RX中断,则首先调用stellarisif_interrupt(&lwip_netif)函数,这个函数会循环调用low_level_receive(netif)函数,从群星以太网FIFO中读取全部数据包放到pbuf中,并使用enqueue_packet(p, ðernetif->rxq)函数将每个pbuf压入pbuf队列中,这个pbuf队列可以供更高级一层调用.之后检查发送器,如果发送器空闲并且至少有一个包在发送队列,会将它发送到FIFO并开始发送。stellarisif_interrupt(&lwip_netif)函数代码如下所示:
/** * Process tx and rx packets at the low-level interrupt. * 处理TX和RX数据包,在低层次中断时 * Should be called from the Stellaris Ethernet Interrupt Handler. This * function will read packets from the Stellaris Ethernet fifo and place them * into a pbuf queue. If the transmitter is idle and there is at least one packet * on the transmit queue, it will place it in the transmit fifo and start the * transmitter. * 在群星以太网中断处理程序中被调用.这个函数将会从群星FIFO中读取数据包并且将它们放入 * pbuf队列.如果发送器空并且发送队列中至少有一个数据包时,该函数将把这个数据包放入发送 * FIFO并开始发送. */ void stellarisif_interrupt(struct netif *netif) { struct ethernetif *ethernetif; // struct pbuf *p; /* setup pointer to the if state data */ ethernetif = netif->state; /** * Process the transmit and receive queues as long as there is receive * data available *这个函数将会从群星以太网接口读取一个数据包,如果数据包有效则返回一个指向pbuf的指针. * 数据包的时间戳也将被放到pbuf结构体中(前提是LWIP_PTPD=1). */ p = low_level_receive(netif); //从RX FIFO中读取以太网数据并放入pbuf中 while(p != NULL) { /* Add the rx packet to the rx queue 原型: static int enqueue_packet(struct pbuf *p, struct pbufq *q) * Push a pbuf packet onto a pbuf packet queue * 将一个pbuf数据包压入一个pbuf数据包队列之上(将RX数据包放到RX队列) * @param p is the pbuf to push onto the packet queue. 要压入数据包队列的pbuf * @param q is the packet queue.数据包队列 * * @return 1 if successful, 0 if q is full. 返回1表示成功,0代表q已经满了 */ if(!enqueue_packet(p, ðernetif->rxq)) { /* Could not place the packet on the queue, bail out. */ pbuf_free(p); break; } /* Check if TX fifo is empty and packet available 检查TX FIFO是否为空并且数据包是否有效*/ if((HWREG(ETH_BASE + MAC_O_TR) & MAC_TR_NEWTX) == 0) { /** * Pop a pbuf packet from a pbuf packet queue * 从一个pbuf数据包队列中弹出一个pbuf数据包 * @param q is the packet queue from which to pop the pbuf. * q是一个数据队列中弹出的pbuf * @return pointer to pbuf packet if available, NULL otherwise. 原型:static struct pbuf * dequeue_packet(struct pbufq *q) */ p = dequeue_packet(ðernetif->txq); if(p != NULL) { low_level_transmit(netif, p); } } /* Read another packet from the RX fifo */ p = low_level_receive(netif); } /* One more check of the transmit queue/fifo */ if((HWREG(ETH_BASE + MAC_O_TR) & MAC_TR_NEWTX) == 0) { p = dequeue_packet(ðernetif->txq); if(p != NULL) { low_level_transmit(netif, p); } } }
如果应用程序不使用RTOS,则会在这个中断服务函数中处理一些其它的事情,主要是处理从以太网FIFO中读取的数据以及更新lwIP时钟。
stellarisif_input(&lwip_netif)函数处理来自以太网RX FIFO的数据,它会根据已经接收的数据包类型来调用适当的input函数。在stellarisif_input(&lwip_netif)函数中主要调用了ethernet_input(p, netif)函数,该函数根据以太网包的类型使用switch...case语句来调用不同的函数,比如htons(ethhdr->type)== ETHTYPE_IP,说明这是一个IP包,则调用ip_input(p, netif)函数。stellarisif_input(&lwip_netif);函数如下所示:
/** * This function should be called when a packet is ready to be read * from the interface. It uses the function low_level_input() that * should handle the actual reception of bytes from the network * interface. Then the type of the received packet is determined and * the appropriate input function is called. * 当一个数据包已经从接口处读取时,调用本函数.它使用 low_level_input()函 数处理从网络接口接收的实际字节.然后根据接收的数据包确定并调用适当的input函数. * @param netif the lwip network interface structure for this ethernetif */ int stellarisif_input(struct netif *netif) { struct ethernetif *ethernetif; struct pbuf *p; int count = 0; ethernetif = netif->state; /* move received packet into a new pbuf */ //从一个pbuf数据包队列中弹出一个pbuf数据包 while((p = dequeue_packet(ðernetif->rxq)) != NULL) { count++; /* process the packet.处理数据包 */ if (ethernet_input(p, netif)!=ERR_OK) { LWIP_DEBUGF(NETIF_DEBUG, ("stellarisif_input: input error/n")); pbuf_free(p); p = NULL; } } return(count); }