《LwIP协议栈源码详解——TCP/IP协议的实现》以太网数据接收

姓名:朱小鹏    学号:16010130023

转载:

http://blog.sina.com.cn/s/blog_62a85b950101am9n.html

【嵌牛导读】:low_level_init函数是与我们使用的与硬件密切相关初始化函数

【嵌牛鼻子】:以太网数据接收

【嵌牛提问】:LWIP是怎样来处理以太网数据接收?

【嵌牛正文】:

昨天说到low_level_init函数是与我们使用的与硬件密切相关初始化函数,看看:

static void low_level_init(struct netif *netif)

{

netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置变量enc28j60的hwaddr_len字段

netif->hwaddr[0] = 'F';//初始化变量enc28j60的MAC地址

netif->hwaddr[1] = 'O';//设什么地址用户自由发挥吧,但是不要与其他

netif->hwaddr[2] = 'R';//网络设备的MAC地址重复。

netif->hwaddr[3] = 'E';

netif->hwaddr[4] = 'S';

netif->hwaddr[5] = 'T';

netif->mtu = 1500;//最大允许传输单元

//允许该网卡广播和ARP功能,并且该网卡允许有硬件链路连接

netif->flags= NETIF_FLAG_BROADCAST |\

NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

enc28j60_init(netif->hwaddr);//与底层驱动硬件驱动程序密切相关的硬件初始化函数

}

至此,终于变量enc28j60被初始化好了,而且它描述的网卡芯片enc28j60也被初始化好了,而且变量enc28j60也被链入链表netif_list。

接着上上上上面的语句(8)调用netif_set_default函数初始化缺省网络接口。协议栈除了有个netif_list全局变量指向netif网络接口结构的链表,还有个全局变量netif_default全局变量指向缺省的网络接口结构。当IP层有数据发送时,它首先会以netif_list为索引选择满足某个条件的网络接口发送数据包,但是,当找不到这样的接口时,协议栈就会调用缺省的网络接口直接发送数据包,所以(8)中的意思是把变量enc28j60描述的网络接口设置为缺省的网络接口。

(9)调用函数netif_set_up使能网络接口,这通过一个简单语句来实现:

netif->flags |= NETIF_FLAG_UP;

至此,网卡初始化完成,能正常接收和发送数据包了。下面我们来讨论讨论关于网卡数据包的接收和发送。

LWIP中实现了接收一个数据包和发送一个数据包函数的框架,这两个函数分别是low_level_input和low_level_output,用户需要使用实际网卡驱动程序完成这两个函数。在第一篇中讲过,一个典型的LWIP应用系统包括这样的三个进程:首先是上层应用程序进程,然后是LWIP协议栈进程,最后是底层硬件数据包接收进程。这里我们就来讲讲第三个进程,看看数据包是怎样被接收并往上层传递的。但在这之前,有必要说说以太网网卡所收到的数据包的格式。如下图,

LWIP使用了一个eth_hdr的数据结构来描述以太网数据包包头的14个字节。如下,

PACK_STRUCT_BEGIN

struct eth_hdr {

PACK_STRUCT_FIELD(struct eth_addr dest);//目标MAC地址

PACK_STRUCT_FIELD(struct eth_addr src);//源MAC地址

PACK_STRUCT_FIELD(u16_t type);//类型

} PACK_STRUCT_STRUCT;

PACK_STRUCT_END

其中PACK_STRUCT_xxx都是与编译器字对齐相关的宏定义,这里不作详细介绍了。上面的dest、src和type三个字段分别和上图中的目的MAC地址、源MAC地址和类型域字段对应。

在上面讨论的基础上,我们来看看这个数据包接收进程,源代码如下:

voidethernetif_input(void *arg)//创建该进程时,要将某个网络接口结构的netif结构指

{//针作为参数传入

struct eth_hdr *ethhdr;

struct pbuf *p;

struct netif *netif = (struct netif *)arg;

while (1)

{

p = low_level_input (netif);//接收一个数据包

if (p == NULL)//如果数据包为空,

continue;//则循环结束,启动下次接收过程

ethhdr = p->payload;//取得数据包内数据

switch (htons(ethhdr->type))//判断数据包类型

{//只对IP数据包和ARP数据包进行处理

case ETHTYPE_IP://IP数据包

case ETHTYPE_ARP://ARP数据包

if (netif->input(p, netif)!=ERR_OK)//将数据包发送到上层应用函数

{

pbuf_free(p);

p = NULL;

}

break;

default:

pbuf_free(p);

p = NULL;

break;

}//switch

}//while

}//main函数

要创建上面的这个进程,需要把个网络接口结构的netif结构指针作为参数传入,在UC/OSII中要用到下面的语句实现,

OSTaskCreate(ethernetif_input,(void *)&enc28j60,

&T_ETHERNETIF_INPUT_STK[T_ETHERNETIF_INPUT_STKSIZE-1]

ETH_IF_TASK_PRIO);

在数据包接收进程中,有三个需要注意的地方。一是数据包接收的方法是查询方式,即处理器不断向网卡芯片中读取数据,如果读不到数据,则控制器会重新启动一个读取时序;如果能够成功读取到数据,则将数据通过网卡注册的input函数交往上层进行处理。使用查询方式实现的数据包接收进程其优先级必须低于系统中其他进程的优先级,否则它会阻塞比它优先级低的进程的运行。上面的程序有个可以改进的地方,即在读取到的数据包为空时,接收进程调用系统函数将自己延时一段时间再启动下一个读取过程,这样可以使其不能阻止优先级更低的进程的运行,缺点是数据包的接收得不到及时的响应。其实数据包的接收可以采用中断的方式来实现,这种方式是一种比较好的方式。一般的网卡芯片都有中断功能,即当网卡接收到一个数据包后,它可以产生中断信号告诉控制器自己接收到一个数据包。控制器此时启动一个读取数据包时序,就能有效的读取到非空数据包。所以可以这样来实现一个接收数据包进程:在无数据包收到时,数据包接收进程阻塞在一个信号量下,当有数据包到来时,网卡芯片产生一个中断信号,处理器进入中断处理,并释放一个信号量。中断退出后,数据包接收进程得到信号量,并从网卡芯片中读取数据包,并将数据包递交给上层进行处理。

第二个需要注意的地方是htons(ethhdr->type)函数的使用,htons函数的功能是将一个半字长的数据从网络字节顺序转换到我们的处理器支持的字节顺序。解释一下,在计算机体系结构和计算机通信领域中,对于半字、字等的存储机制有可能不同。目前通常采用的存储机制主要有两种:big-endian和little-endian,即大端和小端。对于大端模式,某个半字或字数据的高位字节被在内存的低地址端,低位字节排放在内存的高地址端。对于小端模式,则恰好相反。由于我们使用的ARM处理器使用的是小端模式,而接收到的网络字节数据用的是大端模式,所以这里调用函数htons实现大端与小端的转换,实际就是将两个字节交换顺序即可。这样调用htons(ethhdr->type)后,ethhdr->type的值就为0x0800或0x0806等。

最后需要注意的地方,netif->input在结构enc28j60初始化时已经被设置为指向tcpip_input函数,所以实际上上面是调用tcpip_input函数往上层递交数据包。tcpip_input属于IP层函数,从这里我们可以看出LWIP的一个很大的特点,即各层之间没有明显的界限划分。像前面所讲的那样,LWIP协议栈进程完成初始化相关工作后,会阻塞在一个邮箱上等待数据包的输入,这就对了,tcpip_input函数就是向这个邮箱发送一条消息,且该消息中包含了收到的数据包存储的地址。LWIP协议栈进程从邮箱中取到该地址后就可以对数据包进行处理了。

至此,数据包的接收可算大功告成,关于数据包的发送,这点很简单,因为它不必像数据包接收那样要使用一个专门的进程来实现,而是这样的:当上层有数据包要发送时,直接调用netif->linkoutput发送数据包就可以了。netif->linkoutput在结构enc28j60初始化时已经被设置为指向low_level_output函数,该函数和底层硬件驱动密切相关,用于实现发送一个数据包的功能。用户应该结合具体网卡驱动实现该函数。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容