本文共 2571 字,大约阅读时间需要 8 分钟。
上一篇已经实现了网卡DM9000的驱动,但我们没有验证驱动程序是否正确,这一篇通过ARP协议来验证驱动程序的正确。通过ARP协议,开发板给pc机发送一个以太网数据包,pc机接收到数据包并发送一个应答包,开发板接收应答包并提取数据,根据数据判断DM9000的驱动程序是否正确。
在计算机网络中,数据发送的过程,就是一个把数据按照各层协议层层封装的过程。在这个过程中,最终要使用的协议通常是以太网协议(数据链路层协议)。以太网协议包格式为:
目的MAC地址:接收者的地址;
源MAC地址:发送者的地址; 类型:标明高层的数据使用的协议类型; 数据:高层的数据; CRC:校验码。两台计算机要进行通信则必须知道对方的物理地址,若不知道对方的物理地址,可通过ARP协议(地址解析协议)获取物理地址。利用ARP协议,用户首先向局域网中的所有计算机发送一个ARP协议请求包,收到请求包且满足条件的计算机会回应一个包含自身物理地址的应答包给用户,用户即可知道对方的物理地址。ARP协议包格式如下:
ARP包是通过OP字段区分请求包和应答包的。
arp.c文件代码为:
#include "arp.h"#define HON(n) ((((u16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))/*1.发送arp请求包*/void arp_request(){ /*1.构成arp请求包*/ memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6); memcpy(arpbuf.ethhdr.s_mac,mac_addr,6); arpbuf.ethhdr.type = HON(0x0806); /*type:ARP*/ arpbuf.hwtype = HON(1); arpbuf.protocol = HON(0x0800); arpbuf.hwlen = 6; arpbuf.protolen = 4; arpbuf.opcode = HON(1); memcpy(arpbuf.smac,mac_addr,6); memcpy(arpbuf.sipaddr,ip_addr,4); memcpy(arpbuf.dipaddr,host_ip_addr,4); packet_len = 14+28; /*2.调用dm9000发送函数,发送应答包*/ dm9000_tx(buffer,packet_len);}/*2.解析arp应答包,提取mac*/u8 arp_process(){ u32 i; if (packet_len<28) return 0; memcpy(host_ip_addr,arpbuf.sipaddr,4); printf("host ip is : "); for(i=0;i<4;i++) printf("%03d ",host_ip_addr[i]); printf("\n\r"); memcpy(host_mac_addr,arpbuf.smac,6); printf("host mac is : "); for(i=0;i<6;i++) printf("%02x ",host_mac_addr[i]); printf("\n\r");}void dm9000_arp(){ while(1) arp_request(); }
arp.h文件代码为:
typedef unsigned int u32;typedef unsigned short u16;typedef unsigned char u8;typedef struct eth_hdr{ u8 d_mac[6]; u8 s_mac[6]; u16 type;}ETH_HDR;typedef struct arp_hdr{ ETH_HDR ethhdr; u16 hwtype; u16 protocol; u8 hwlen; u8 protolen; u16 opcode; u8 smac[6]; u8 sipaddr[4]; u8 dmac[6]; u8 dipaddr[4];}ARP_HDR;ARP_HDR arpbuf;extern u8 host_mac_addr[6];extern u8 mac_addr[6];extern u8 ip_addr[4];extern u8 host_ip_addr[4];extern u16 packet_len;extern u8 *buffer;
下载程序到开发板后,只要接好网线即可实现功能。
代码中使用了memcrp函数,给函数是c语言使用的内存拷贝函数,原型为:
void *memcpy(void *dest, const void *src, size_t n);
函数功能为从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
源文件中的#define HON(n) ((((u16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
表示大小端的转换,这里涉及到网络字节序的知识点,网络字节序即字节数在网络传输过程中的顺序。整数在内存中存放的顺序由cpu采用的字节序决定,常见的字节序有大端和小端。小端采用低位字节存储在低位地址,高位字节存储在高位地址;大端采用低位字节存储在高位地址,高位字节存储在低位地址。比如存储0x0506,小端方式存储,在低地址存放0x06,高地址存放0x05;大端方式存储则在低地址存放0x05,高地址存放0x06。网络字节序采用的是大端方式。因此我们进行网络传输时需要根据cpu的字节序决定需不需要修改传输的数据,若需要修改,则将地址信息外的数据都进行修改。