LWIP的底层结构(物理层)

转自:http://bluefish.blog.51cto.com/214870/158418

我们前面讲到说是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。


LWIP的底层结构(物理层)的更多相关文章

  1. V7000存储数据恢复_底层结构原理拆解及Mdisk磁盘掉线数据恢复方法

    Storwize V7000(也就是我们常说的V7000)是新推出的一款中端存储系统,这款系统的定位虽然在中端,但是Storwize V7000提供有存储管理功能,这一功能以前只有高端存储才拥有(例如 ...

  2. [转]谈NAND Flash的底层结构和解析

    这里我想以一个纯玩家的角度来谈谈关于NAND Flash的底层结构和解析,可能会有错误的地方,如果有这方面专家强烈欢迎指正. NAND Flash作为一种比较实用的固态硬盘存储介质,有自己的一些物理特 ...

  3. HP-lefthand底层结构具体解释及存储灾难数据恢复

    HP-lefthand底层结构具体解释及存储灾难数据恢复 一.HP-lefthand的特点 HP-lefhand是一款很不错的SAN存储,使用iscsi协议为client分配空间. 它支持RAID5. ...

  4. Redis底层结构全了解

    第一篇文章,思来想去,写一写Redis吧,最近在深入研究它. 一丶Redis底层结构 1. redis 存储结构 redis的存储结构从外层往内层依次是redisDb.dict.dictht.dict ...

  5. String,StringBuffer,StringBuillder的底层结构

    一:StringBuffer的底层 (1)线程安全的字符串操作类 (2)通过synchronized关键字声明同步方法,保证多线程环境下数据安全 public synchronized StringB ...

  6. java集合专题 (ArrayList、HashSet等集合底层结构及扩容机制、HashMap源码)

    一.数组与集合比较 数组: 1)长度开始时必须指定,而且一旦指定,不能更改 2)保存的必须为同一类型的元素 3)使用数组进行增加/删除元素-比较麻烦 集合: 1)可以动态保存任意多个对象,使用比较方便 ...

  7. Lwip与底层的接口

    Lwip有三套api,分别是: raw api:使用方法为使用回调函数,即先注册一个函数,当接受到数据之后调用这个函数.缺点是对于数据连续处理不好. Lwip api:把接收与处理放在一个线程里面.因 ...

  8. Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在曾经的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本的语法 ...

  9. HashMap底层结构、原理、扩容机制

    https://www.jianshu.com/p/c1b616ff1130 http://youzhixueyuan.com/the-underlying-structure-and-princip ...

随机推荐

  1. git系列1

    git clone支持多种协议,除了HTTP(s)以外,还支持SSH.Git.本地文件协议等,下面是一些例子. $ git clone http[s]://example.com/path/to/re ...

  2. Docker入门系列7 动态映射端口port mapping

    为何想要动态映射端口呢? 因为刚开始run启动容器时,并不知道里面需要映射哪些端口,等容器已创建了,想映射端口. 当然可以通过先commit成镜像,然后再次run时指定端口,但会生成中间的镜像,对于有 ...

  3. Android中RelativeLayout各个属性及其含义

    android:layout_above="@id/xxx"  --将控件置于给定ID控件之上android:layout_below="@id/xxx"  - ...

  4. Consumer Group Example

    面向kafka编程 Consumer Group Example https://cwiki.apache.org/confluence/display/KAFKA/Consumer+Group+Ex ...

  5. ABAP 程序运行时间记录表

    自建表记录程序运行时间,测试程序效率,可作为系统优化工具.

  6. vi中如何替换某字符成“回车”?

    vi中如何替换某字符成“回车”? 在 vi 中::s/,/^M/g (you need to type CTRL-V <CR> to get a ^M here)VIM - Vi IMpr ...

  7. 负载均衡,会话保持,session同步(转)

    一,什么负载均衡一个新网站是不要做负载均衡的,因为访问量不大,流量也不大,所以没有必要搞这些东西.但是随着网站访问量和流量的快速增长,单台服务器受自身硬件条件的限制,很难承受这么大的访问量.在这种情况 ...

  8. 最全面的HashMap和HashTable的区别

    找工作期间不少企业都会问到有关HashMap和HashTable两者直接的区别,很多博客里虽然有提及但总是没有那么全面,只是一些常用的不同,现在就我自己所总结的比较全面的不同,归纳以下: HashMa ...

  9. Hive- Hive安装

    Hive安装 1.1下载Hive安装包 官网:http://hive.apache.org/downloads.html 个人建议到这里下载:http://apache.forsale.plus/ 1 ...

  10. Android窗口系统第三篇---WindowManagerService中窗口的组织方式

    Android窗口系统第一篇—Window的类型与Z-Order确定 Android窗口系统第二篇—Window的添加过程 上面文章梳理了一个窗口的添加过程,系统中有很多应用,每个应用有多个Activ ...