DM9000裸机驱动及ARP实现
2015-06-04 12:47
891 查看
一、DM9000物理结构
1、原理图
工作实质就是MAC通过MII控制PHY的过程。
2、网卡和网络模型的映射关系
MAC对应的是数据链路层,PHY对应的是物理层
3、MAC的工作原理
当网络协议栈的IP包送到网卡的时候,先要到达MAC,MAC就根据数据链路层的协议对接收到的数据进行封装,将IP包封装成以太网包,完成数据帧的构建。当然它还具备数据纠错以及传送控制等功能。
4、关于PHY
PHY是物理接口收发器。主要和实际的传输硬件打交道。他接收到来自MAC的以太网包,先加上校检码。然后按照物理层的规则进行数据编码,然后传输到物理介质,接受过程则与之相反。
5、MII即媒体独立接口。表明在MAC一定情况下,更换PHY是不会影响系统的工作的。因为他们最后都要遵循MII接口。故MII起到了MAC和PHY之间通信的桥梁作用。
二、DM9000的编程接口
1、DM9000的接口不是绝对开放的,不能像访问nand控制器那样直接按照地址去访问相关寄存器。
2、但是他提供了两个可以供CPU访问的接口,一个是index另一个是数据端口。
3、index的地址在mini2440上是0x20000300.原因是
(1)mini2440的原理图中
dm9000的片选信号AEN就是接到nLAN_CS片选。再看CPU原理图
我们可以看到nLAN_CS实质接在nGCS4上。再看datasheet
可以看到nGCS4对应的片选信号是0x20000000开头的,在0x20000000-0x28000000之间。所以index的地址开头是0x2********。即片选地址
(2)再看DM9000的datasheet
再看DM9000的TXD【2:0】的引脚接线情况
发现都是0,所以I/O base的地址是300H。即相对片选地址的位置。(相对地址,片选地址相当于基地址)。
(3)不管是index端口还是数据端口都是用SD0-SD15来传递数据。所以要区分具体某一时刻,这些数据到底是给那一个接口用。于是利用CMD引脚来区别。当CMD引脚是高电平的时候,SD上的数据是给数据接口用的,当CMD是低电平的时候,是给index接口用的。index接口是用来传递偏移量的。可以看到CMD接到CPU的ADDR2,当他为1的时候,就是为数据接口传送数据,即地址是0x20000304.当ADDR2为0的时候,SD是为index接口传送偏移量,即ADDR2为0,即地址是0x20000300.故我们找到了,片选地址,I/O base地址,以及数据接口地址。
三、DM9000初始化
1、片选信息设置
(1)数据宽度(BWSCON)
(2)时序信号填写(BANKCON4)
2、中断初始化
(1)从原理图找到DM9000使用的中断源
(2)配置相应的中断引脚
(3)设置中断触发方式(高电平,EXTINT0)
(4)使能中断,设置中断屏蔽寄存器(INTMSK,EINTMSK)
(5)清除中断标志(SRCPND,INTPND,EINTPND)
3、复位设备
(1)实现往DM9000读写数据的函数
(2)设置I/O为输出模式
(3)通过对GPIO0写0为内部的PHY提供电源
(4)软件复位(自动清0),MAC内部回环模式
(5)对(4)中的寄存器全部写入0
(6)重复(4)(5)
4、捕获网卡
(1)读取厂家ID
(2)读取product的ID
(3)将两个ID组合与之前预定义的网卡ID进行对比
5、MAC初始化
参照u-boot设置MAC
6、填充MAC地址
利用循环分别填写六个寄存器,对应MAC地址的6个段
7、激活DM9000
参照u-boot设置相关寄存器即可。
8、初始化代码
四、发送函数(有两个参数,待发送数据和发送长度)
1、禁止中断,避免干扰
(1)DM9000的IMR寄存器。
2、写入发送数据的长度
将长度分两次写入寄存器
3、写入待发送的数据
(1)将MWCMD赋值给地址端口,做好准备,MWCMD会自动将数据送到TX SRAM中。
(2)利用循环,将数据写入数据端口
4、启动发送
往TCR寄存器中写入命令,请求开始发送
5、等待发送结束
当发送结束的时候,TCR的0位会自动清0,所以去等待他变0即可。
6、判断发送结果是否正确,清除发送状态
往NSR寄存器中写入0x2C即可
7、打开中断,等待接收数据
将IMR的中断打开即可。(设置最后一位为1即可)
五、接收函数
接收是中断处理的,接收到一个包就会产生中断。在中断处理的时候调用接收函数。
1、判断是否产生中断,是就继续,否则退出接收函数
读取ISR寄存器第0位即可。
2、清除中断
ISR寄存器第0位写1即可。
3、空读
读取MRCMDX寄存器
4、读取包的状态和长度
读取MRCMD寄存器得到状态,此时地址端口的数据就是对应MRCMD的偏移量,所以可以直接读取此时数据寄存器的值,不用再重新指定偏移量,就可以得到长度。在长度后面会自动送入有效的数据,所以后面可以页直接读数据寄存器得到有效数据。
5、读取包的数据
在读取数据之前应该对读到的长度进行检查,看是否小于以太网包的最大长度。然后利用for循环读取数据,注意数据的组合方式。
六、中断处理函数
1、调用接收函数存放接收到的数据
2、清除中断标志(SRCPND,INTPND,EINTPND)。
七、ARP协议实现
1、以太网通讯格式
在计算机网络中,根据不同层的协议对要传输的数据进行封装,最重要实现的协议通常是以太网协议,即数据链路层协议。以太网包格式。
2、ARP功能简介
在计算机网络中,MAC地址是物理层的地址,是计算机唯一的身份标识。两台计算机要实现通信,必须知道彼此的MAC地址,但是一般情况用户只知道目标机的IP地址,这个时候,就可以用ARP(地址解析协议)来向局域网中的所有计算机发送ARP请求包,当然这个请求包有目标机的IP地址,符合条件的目标机接收到请求包以后,会作出应答,不符合条件的忽略该请求包。在应答包中目标机会将自己的MAC地址反馈给用户。所以ARP是一种通过IP地址获取MAC地址的协议。
3、ARP包格式
4、代码实现
(1)发送ARP请求包
–》构造请求包
– –》ARP帧类型是0806
–》当未知MAC地址的时候,设置为全F
–》硬件类型是指硬件地址对应的类型,即以太网类型,编号为1
–》协议类型是指协议地址对应的类型,在这里协议地址是IP地址,IP类型的编号是0800
–》硬件地址长度即以太网地址长度,6个字节
–》协议地址长度即Ip地址长度4个字节
–》op为1表示请求,为0表示应答
–》发送端以太网地址就是以太网源地址,事先制定的,6个字节
–》发送端Ip地址是事先制定的,4个字节,注意:要和PC在同一网段
–》目的以太网地址,暂时不填,要获取的就是该数据
–》目的Ip地址就是PC的Ip实际地址。
–》调用DM9000发送函数发送包
—-》可以讲上述构建的ARp请求包的地址赋值给u8 的指针,让该数组指向该请求包
—-》然后发送长度为:(以太网)14 + (ARP真正请求部分)28.
–》头文件对数据结构进行创建
那些extern变量在dm9000.c里定义,根据自己PC的IP来决定host_ip_addr
(2)在网络中默认主机是大端格式的,涉及到网络字节序。所以在发送端存储数据的时候要按照大端格式存储。所以整个包都要征对象的修改,地址可以不用改,但是数据要改。只针对由两个字节构成的数据,一个字节长度的不用改。涉及到地址都会用到memcpy函数。
(3)解析ARP应答包,提取目标机MAC地址。
–》返回类型为u8
–》打印信息时候注意格式控制
附:dm9000.c相关寄存器地址
1、原理图
工作实质就是MAC通过MII控制PHY的过程。
2、网卡和网络模型的映射关系
MAC对应的是数据链路层,PHY对应的是物理层
3、MAC的工作原理
当网络协议栈的IP包送到网卡的时候,先要到达MAC,MAC就根据数据链路层的协议对接收到的数据进行封装,将IP包封装成以太网包,完成数据帧的构建。当然它还具备数据纠错以及传送控制等功能。
4、关于PHY
PHY是物理接口收发器。主要和实际的传输硬件打交道。他接收到来自MAC的以太网包,先加上校检码。然后按照物理层的规则进行数据编码,然后传输到物理介质,接受过程则与之相反。
5、MII即媒体独立接口。表明在MAC一定情况下,更换PHY是不会影响系统的工作的。因为他们最后都要遵循MII接口。故MII起到了MAC和PHY之间通信的桥梁作用。
二、DM9000的编程接口
1、DM9000的接口不是绝对开放的,不能像访问nand控制器那样直接按照地址去访问相关寄存器。
2、但是他提供了两个可以供CPU访问的接口,一个是index另一个是数据端口。
3、index的地址在mini2440上是0x20000300.原因是
(1)mini2440的原理图中
dm9000的片选信号AEN就是接到nLAN_CS片选。再看CPU原理图
我们可以看到nLAN_CS实质接在nGCS4上。再看datasheet
可以看到nGCS4对应的片选信号是0x20000000开头的,在0x20000000-0x28000000之间。所以index的地址开头是0x2********。即片选地址
(2)再看DM9000的datasheet
再看DM9000的TXD【2:0】的引脚接线情况
发现都是0,所以I/O base的地址是300H。即相对片选地址的位置。(相对地址,片选地址相当于基地址)。
(3)不管是index端口还是数据端口都是用SD0-SD15来传递数据。所以要区分具体某一时刻,这些数据到底是给那一个接口用。于是利用CMD引脚来区别。当CMD引脚是高电平的时候,SD上的数据是给数据接口用的,当CMD是低电平的时候,是给index接口用的。index接口是用来传递偏移量的。可以看到CMD接到CPU的ADDR2,当他为1的时候,就是为数据接口传送数据,即地址是0x20000304.当ADDR2为0的时候,SD是为index接口传送偏移量,即ADDR2为0,即地址是0x20000300.故我们找到了,片选地址,I/O base地址,以及数据接口地址。
三、DM9000初始化
1、片选信息设置
(1)数据宽度(BWSCON)
(2)时序信号填写(BANKCON4)
void cs_init() { /*1.数据宽度设置*/ BWSCON &= ~(3 << 16); BWSCON |= (1 <<16); /*2.时序信号设置*/ BANKCON4 = (0 << 13) | (0 << 11) | (7 << 8) | (1 << 6) | (0 << 4) | (0 << 2) | (0 << 0); }
2、中断初始化
(1)从原理图找到DM9000使用的中断源
(2)配置相应的中断引脚
(3)设置中断触发方式(高电平,EXTINT0)
(4)使能中断,设置中断屏蔽寄存器(INTMSK,EINTMSK)
(5)清除中断标志(SRCPND,INTPND,EINTPND)
void int_init() { /*设置中断引脚工作模式*/ GPFCON = GPFCON &(~(0x3<<14)); GPFCON = GPFCON |(0x2<<14); /*设置中断触发方式*/ EXTINT0 = EXTINT0 & (~(0x7<<28)); EXTINT0 = EXTINT0 | (0x1<<28); /*使能中断*/ INTMSK = INTMSK &(~(1<<4)); EINTMASK = EINTMASK & (~(0x1<<7)); /*清楚之前所有的中断标志*/ EINTPEND |= (1<<7); SRCPND = (1<<4); INTPND = (1<<4); }
3、复位设备
(1)实现往DM9000读写数据的函数
(2)设置I/O为输出模式
(3)通过对GPIO0写0为内部的PHY提供电源
(4)软件复位(自动清0),MAC内部回环模式
(5)对(4)中的寄存器全部写入0
(6)重复(4)(5)
void dm9000_write(u16 reg,u16 data) { DM_ADD = reg; DM_DAT = data; } u8 dm9000_read(u16 reg) { DM_ADD = reg; return DM_DAT; } void dm9000_reset() { /*1.设置I/O为输出模式*/ dm9000_write(DM9000_GPCR, GPCR_GPIO0_OUT); /*2.通过对GPIO0写入0为内部的PHY提供电源*/ dm9000_write(DM9000_GPR, 0); /*3.软件复位(自动清0),MAC内部回环模式*/ dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); /*4.对上一步的寄存器写入全0*/ dm9000_write(DM9000_NCR, 0); /*5.重复(3)(4),用两次实现真正复位*/ dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); dm9000_write(DM9000_NCR, 0); }
4、捕获网卡
(1)读取厂家ID
(2)读取product的ID
(3)将两个ID组合与之前预定义的网卡ID进行对比
u8 dm9000_probe() { u32 id_val; /*1.读取厂家ID*/ id_val = dm9000_read(DM9000_VIDL); id_val |= dm9000_read(DM9000_VIDH) << 8; /*2.读取产品ID并将其和厂家ID组合*/ id_val |= dm9000_read(DM9000_PIDL) << 16; id_val |= dm9000_read(DM9000_PIDH) << 24; if (id_val == DM9000_ID) { printf("dm9000 is found !\n\r"); return 0; } else { printf("dm9000 is not found !\n\r"); return -1; } }
5、MAC初始化
参照u-boot设置MAC
void dm9000_mac_init() { /* Program operating register, only internal phy supported */ dm9000_write(DM9000_NCR, 0x0); /* TX Polling clear */ dm9000_write(DM9000_TCR, 0); /* Less 3Kb, 200us */ dm9000_write(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US); /* Flow Control : High/Low Water */ dm9000_write(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8)); /* SH FIXME: This looks strange! Flow Control */ dm9000_write(DM9000_FCR, 0x0); /* Special Mode */ dm9000_write(DM9000_SMCR, 0); /* clear TX status */ dm9000_write(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END); /* Clear interrupt status */ dm9000_write(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS); }
6、填充MAC地址
利用循环分别填写六个寄存器,对应MAC地址的6个段
void dm9000_fill_macadd() { u16 oft = 0,i = 0; /* fill device MAC address registers */ for (i = 0; i < 6; i++) dm9000_write(DM9000_PAR + i, mac_addr[i]); /*maybe this is some problem*/ for (i = 0, oft = 0x16; i < 8; i++, oft++) dm9000_write(oft, 0xff); /* read back mac, just to be sure */ for (i = 0, oft = 0x10; i < 6; i++, oft++) printf("%02x:", dm9000_read(oft)); printf("\n\r"); }
7、激活DM9000
参照u-boot设置相关寄存器即可。
void dm9000_active() { /* RX enable */ dm9000_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);//要加上RCR_ALL,否则他会自动丢弃来自外部的广播包 /* Enable TX/RX interrupt mask */ dm9000_write(DM9000_IMR, IMR_PAR); }
8、初始化代码
void dm9000_init() { /*1.片选信息设置*/ dm9000_cs(); /*2.中断初始化*/ dm9000_int_init(); /*3.复位设备*/ dm9000_reset(); /*4.捕获网卡*/ if (dm9000_probe() < 0) return ; /*5.MAC初始化*/ dm9000_mac_init(); /*6.填充MAC地址*/ dm9000_fill_macadd(); /*7.激活DM9000*/ dm9000_active(); }
四、发送函数(有两个参数,待发送数据和发送长度)
1、禁止中断,避免干扰
(1)DM9000的IMR寄存器。
2、写入发送数据的长度
将长度分两次写入寄存器
3、写入待发送的数据
(1)将MWCMD赋值给地址端口,做好准备,MWCMD会自动将数据送到TX SRAM中。
(2)利用循环,将数据写入数据端口
4、启动发送
往TCR寄存器中写入命令,请求开始发送
5、等待发送结束
当发送结束的时候,TCR的0位会自动清0,所以去等待他变0即可。
6、判断发送结果是否正确,清除发送状态
往NSR寄存器中写入0x2C即可
7、打开中断,等待接收数据
将IMR的中断打开即可。(设置最后一位为1即可)
void dm9000_tx(u8* data, u32 length) { u32 i = 0; /*1.禁止中断。避免干扰*/ dm9000_write(DM9000_IMR,0x80); /*2.清除发送标志位*/ dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ /*3.写入发送数据长度*/ dm9000_write(DM9000_TXPLL, length & 0xff); dm9000_write(DM9000_TXPLH, (length >> 8) & 0xff); /*4.写入待发送数据*/ DM_ADD = DM9000_MWCMD; for(i=0;i<length;i+=2) { DM_DAT = data[i] | (data[i+1]<<8); } /*5.启动发送*/ dm9000_write(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */ /*6.等待发送完成*/ while(dm9000_read(DM9000_TCR) & 0x01); /*7.清除发送状态*/ dm9000_write(DM9000_NSR,0x2c); /*8.清除发送标志位*/ dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ /*9.使能接收中断*/ dm9000_write(DM9000_IMR,0x81); }
五、接收函数
接收是中断处理的,接收到一个包就会产生中断。在中断处理的时候调用接收函数。
1、判断是否产生中断,是就继续,否则退出接收函数
读取ISR寄存器第0位即可。
2、清除中断
ISR寄存器第0位写1即可。
3、空读
读取MRCMDX寄存器
4、读取包的状态和长度
读取MRCMD寄存器得到状态,此时地址端口的数据就是对应MRCMD的偏移量,所以可以直接读取此时数据寄存器的值,不用再重新指定偏移量,就可以得到长度。在长度后面会自动送入有效的数据,所以后面可以页直接读数据寄存器得到有效数据。
5、读取包的数据
在读取数据之前应该对读到的长度进行检查,看是否小于以太网包的最大长度。然后利用for循环读取数据,注意数据的组合方式。
u16 dm9000_rx(u8* data) { u32 i = 0; u16 status; u16 tmp,len; u8 ready; /*1.判断是否产生中断*/ if (!(dm9000_read(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */ return 0; /*2.清除中断*/ dm9000_write(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */ /*3.空读*/ ready = dm9000_read(DM9000_MRCMDX); /*if((ready & 0x01) != 0x01){ ready = dm9000_read(DM9000_MRCMDX); if ((ready & 0x01) != 0x01) return 0; }*/ /*4.读取包的状态和长度*/ status = dm9000_read(DM9000_MRCMD); len = DM_DAT; /*5.读取包的数据*/ if(len<DM9000_PKT_MAX) { for(i=0;i<len;i+=2) { tmp = DM_DAT; data[i] = tmp & 0x0ff; data[i+1] = (tmp>>8)&0x0ff; } } return len; }
六、中断处理函数
1、调用接收函数存放接收到的数据
2、清除中断标志(SRCPND,INTPND,EINTPND)。
void int_issue() { packet_len = dm9000_rx(&buffer[0]); net_handle(&buffer[0],packet_len); EINTPEND |= (1 << 7); SRCPND |= (1 << 4); INTPND |= (1 << 4); }
七、ARP协议实现
1、以太网通讯格式
在计算机网络中,根据不同层的协议对要传输的数据进行封装,最重要实现的协议通常是以太网协议,即数据链路层协议。以太网包格式。
2、ARP功能简介
在计算机网络中,MAC地址是物理层的地址,是计算机唯一的身份标识。两台计算机要实现通信,必须知道彼此的MAC地址,但是一般情况用户只知道目标机的IP地址,这个时候,就可以用ARP(地址解析协议)来向局域网中的所有计算机发送ARP请求包,当然这个请求包有目标机的IP地址,符合条件的目标机接收到请求包以后,会作出应答,不符合条件的忽略该请求包。在应答包中目标机会将自己的MAC地址反馈给用户。所以ARP是一种通过IP地址获取MAC地址的协议。
3、ARP包格式
4、代码实现
(1)发送ARP请求包
–》构造请求包
– –》ARP帧类型是0806
–》当未知MAC地址的时候,设置为全F
–》硬件类型是指硬件地址对应的类型,即以太网类型,编号为1
–》协议类型是指协议地址对应的类型,在这里协议地址是IP地址,IP类型的编号是0800
–》硬件地址长度即以太网地址长度,6个字节
–》协议地址长度即Ip地址长度4个字节
–》op为1表示请求,为0表示应答
–》发送端以太网地址就是以太网源地址,事先制定的,6个字节
–》发送端Ip地址是事先制定的,4个字节,注意:要和PC在同一网段
–》目的以太网地址,暂时不填,要获取的就是该数据
–》目的Ip地址就是PC的Ip实际地址。
–》调用DM9000发送函数发送包
—-》可以讲上述构建的ARp请求包的地址赋值给u8 的指针,让该数组指向该请求包
—-》然后发送长度为:(以太网)14 + (ARP真正请求部分)28.
–》头文件对数据结构进行创建
#ifndef __ARP_H__ #define __ARP_H__ typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; #define SWAP(n) ((((u16)n & 0xff) << 8) | ((((u16)n >> 8) & 0xff))) /*以太网头部结构体*/ typedef struct eth_header{ u8 d_mac[6]; u8 s_mac[6]; u16 frame_type; }ETH_HDR; /*ARP头部结构体*/ typedef struct arp_header{ ETH_HDR ethhdr; u16 hw_type; u16 protocol; u8 hwadd_len; u8 protoc_len; u16 opcode; u8 smac[6]; u8 sipaddr[4]; u8 dmac[6]; u8 dipaddr[4]; }ARP_HDR; /*IP头部结构体*/ typedef struct ip_hdr { ETH_HDR ethhdr; u8 vhl; u8 tos; u16 len; u16 ipid; u16 ipoffset; u8 ttl; u8 proto; u16 ipchksum; u8 srcipaddr[4]; u8 destipaddr[4]; }IP_HDR; /*UDP头部结构体*/ typedef struct udp_hdr { IP_HDR iphdr; u16 sport; u16 dport; u16 len; u16 udpchksum; }UDP_HDR; /*TFTP数据包结构体*/ typedef struct tftp_package { u16 opcode; u16 blocknum; u8 data[0]; }TFTP_PAK; ARP_HDR arpbuf; /*网络协议类型*/ #define PROTO_ARP 0x0806 #define PROTO_IP 0x0800 #define PROTO_UDP 0x11 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; #endif
那些extern变量在dm9000.c里定义,根据自己PC的IP来决定host_ip_addr
u8 host_mac_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; u8 mac_addr[6] = {9,8,7,6,5,4}; u8 ip_addr[4] = {192,168,1,30};//根据自己的电脑设置,要和电脑IP在同一网段 u8 host_ip_addr[4] = {192,168,1,100};//根据自己的电脑的IP设置 u16 packet_len;
(2)在网络中默认主机是大端格式的,涉及到网络字节序。所以在发送端存储数据的时候要按照大端格式存储。所以整个包都要征对象的修改,地址可以不用改,但是数据要改。只针对由两个字节构成的数据,一个字节长度的不用改。涉及到地址都会用到memcpy函数。
void arp_request() { /*1.构成ARP请求包*/ memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6); //以太网目的地址 memcpy(arpbuf.ethhdr.s_mac,mac_addr,6); //以太网源地址 arpbuf.ethhdr.frame_type = SWAP(0x0806); //帧类型 arpbuf.hw_type = SWAP(1); //硬件类型 arpbuf.protocol = SWAP(0x0800); //协议类型 arpbuf.hwadd_len = 6; //硬件地址长度 arpbuf.protoc_len = 4; //协议地址长度 arpbuf.opcode = SWAP(1); //操作码 memcpy(arpbuf.smac,mac_addr,6); //发送端以太网地址,即以太网原地址 memcpy(arpbuf.sipaddr,ip_addr,4); //发送端IP地址 memcpy(arpbuf.dipaddr,host_ip_addr,4); //目的IP地址 packet_len = 14+28; //包的长度 /*2.调用dm9000发送函数,发送请求包*/ dm9000_tx((u8*)&arpbuf,packet_len); }
(3)解析ARP应答包,提取目标机MAC地址。
–》返回类型为u8
–》打印信息时候注意格式控制
u8 arp_respond(u8* buf,u32 len) { ARP_HDR* p = (ARP_HDR*)buf; u32 i = 0; if(packet_len < 28) return 0; switch(SWAP(p->opcode)){ case 2://对PC发到开发板的应答包解析 memcpy(host_ip_addr,p->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,p->smac,6); printf("host mac is : "); for(i=0;i<6;i++) printf("%02X ",host_mac_addr[i]); printf("\n\r"); break; case 1://响应PC发到开发板的请求包 memcpy(arpbuf.ethhdr.d_mac,p->ethhdr.s_mac,6); //以太网目的地址 memcpy(arpbuf.ethhdr.s_mac,mac_addr,6); //以太网源地址 arpbuf.ethhdr.frame_type = SWAP(0x0806); //帧类型 arpbuf.hw_type = SWAP(1); //硬件类型 arpbuf.protocol = SWAP(0x0800); //协议类型 arpbuf.hwadd_len = 6; //硬件地址长度 arpbuf.protoc_len = 4; //协议地址长度 arpbuf.opcode = SWAP(2); //操作码 memcpy(arpbuf.smac,mac_addr,6); //发送端以太网地址,即以太网原地址 memcpy(arpbuf.sipaddr,ip_addr,4); //发送端IP地址 memcpy(arpbuf.dmac,p->smac,6); memcpy(arpbuf.dipaddr,p->sipaddr,4); //目的IP地址 packet_len = 14+28; //包的长度 /*2.调用dm9000发送函数,发送请求包*/ dm9000_tx((u8*)&arpbuf,packet_len); break; default: break; } return 1; }
附:dm9000.c相关寄存器地址
/* * dm9000 Ethernet */ #ifndef __DM9000_H__ #define __DM9000_H__ #define DM9000_ID 0x90000A46 #define DM9000_PKT_MAX 1536 /* Received packet max size */ #define DM9000_PKT_RDY 0x01 /* Packet ready to receive */ /* although the registers are 16 bit, they are 32-bit aligned. */ #define DM9000_NCR 0x00 #define DM9000_NSR 0x01 #define DM9000_TCR 0x02 #define DM9000_TSR1 0x03 #define DM9000_TSR2 0x04 #define DM9000_RCR 0x05 #define DM9000_RSR 0x06 #define DM9000_ROCR 0x07 #define DM9000_BPTR 0x08 #define DM9000_FCTR 0x09 #define DM9000_FCR 0x0A #define DM9000_EPCR 0x0B #define DM9000_EPAR 0x0C #define DM9000_EPDRL 0x0D #define DM9000_EPDRH 0x0E #define DM9000_WCR 0x0F #define DM9000_PAR 0x10 #define DM9000_MAR 0x16 #define DM9000_GPCR 0x1e #define DM9000_GPR 0x1f #define DM9000_TRPAL 0x22 #define DM9000_TRPAH 0x23 #define DM9000_RWPAL 0x24 #define DM9000_RWPAH 0x25 #define DM9000_VIDL 0x28 #define DM9000_VIDH 0x29 #define DM9000_PIDL 0x2A #define DM9000_PIDH 0x2B #define DM9000_CHIPR 0x2C #define DM9000_SMCR 0x2F #define DM9000_PHY 0x40 /* PHY address 0x01 */ #define DM9000_MRCMDX 0xF0 #define DM9000_MRCMD 0xF2 #define DM9000_MRRL 0xF4 #define DM9000_MRRH 0xF5 #define DM9000_MWCMDX 0xF6 #define DM9000_MWCMD 0xF8 #define DM9000_MWRL 0xFA #define DM9000_MWRH 0xFB #define DM9000_TXPLL 0xFC #define DM9000_TXPLH 0xFD #define DM9000_ISR 0xFE #define DM9000_IMR 0xFF #define NCR_EXT_PHY (1<<7) #define NCR_WAKEEN (1<<6) #define NCR_FCOL (1<<4) #define NCR_FDX (1<<3) #define NCR_LBK (3<<1) #define NCR_LBK_INT_MAC (1<<1) #define NCR_LBK_INT_PHY (2<<1) #define NCR_RST (1<<0) #define NSR_SPEED (1<<7) #define NSR_LINKST (1<<6) #define NSR_WAKEST (1<<5) #define NSR_TX2END (1<<3) #define NSR_TX1END (1<<2) #define NSR_RXOV (1<<1) #define TCR_TJDIS (1<<6) #define TCR_EXCECM (1<<5) #define TCR_PAD_DIS2 (1<<4) #define TCR_CRC_DIS2 (1<<3) #define TCR_PAD_DIS1 (1<<2) #define TCR_CRC_DIS1 (1<<1) #define TCR_TXREQ (1<<0) #define TSR_TJTO (1<<7) #define TSR_LC (1<<6) #define TSR_NC (1<<5) #define TSR_LCOL (1<<4) #define TSR_COL (1<<3) #define TSR_EC (1<<2) #define RCR_WTDIS (1<<6) #define RCR_DIS_LONG (1<<5) #define RCR_DIS_CRC (1<<4) #define RCR_ALL (1<<3) #define RCR_RUNT (1<<2) #define RCR_PRMSC (1<<1) #define RCR_RXEN (1<<0) #define RSR_RF (1<<7) #define RSR_MF (1<<6) #define RSR_LCS (1<<5) #define RSR_RWTO (1<<4) #define RSR_PLE (1<<3) #define RSR_AE (1<<2) #define RSR_CE (1<<1) #define RSR_FOE (1<<0) #define EPCR_EPOS_PHY (1<<3) #define EPCR_EPOS_EE (0<<3) #define EPCR_ERPRR (1<<2) #define EPCR_ERPRW (1<<1) #define EPCR_ERRE (1<<0) #define FCTR_HWOT(ot) (( ot & 0xf ) << 4 ) #define FCTR_LWOT(ot) ( ot & 0xf ) #define BPTR_BPHW(x) ((x) << 4) #define BPTR_JPT_200US (0x07) #define BPTR_JPT_600US (0x0f) #define IMR_PAR (1<<7) #define IMR_ROOM (1<<3) #define IMR_ROM (1<<2) #define IMR_PTM (1<<1) #define IMR_PRM (1<<0) #define ISR_ROOS (1<<3) #define ISR_ROS (1<<2) #define ISR_PTS (1<<1) #define ISR_PRS (1<<0) #define GPCR_GPIO0_OUT (1<<0) #define GPR_PHY_PWROFF (1<<0) #endif