/ Net ETH.C
//
// This module is the Ethernet layer
//----------------------------------------------------------------------------
#include
#include
#include "C8051f.h"
#include "net.h"
#include "serial.h"
#include "arp.h"
#include "ip.h"
#include "eth.h"
/----------------------------------------------------------------------------
#define reg00 XBYTE[0x8000] //reg00- 10为isa网卡接口的寄存器地址300-310;
#define reg01 XBYTE[0x8001]
#define reg02 XBYTE[0x8002]
#define reg03 XBYTE[0x8003]
#define reg04 XBYTE[0x8004]
#define reg05 XBYTE[0x8005]
#define reg06 XBYTE[0x8006]
#define reg07 XBYTE[0x8007]
#define reg08 XBYTE[0x8008]
#define reg09 XBYTE[0x8009]
#define reg0a XBYTE[0x800a]
#define reg0b XBYTE[0x800b]
#define reg0c XBYTE[0x800c]
#define reg0d XBYTE[0x800d]
#define reg0e XBYTE[0x800e]
#define reg0f XBYTE[0x800f]
#define reg10 XBYTE[0x8010]
bit txd_buffer_select=0; //选择网卡的发送缓冲区
extern UCHAR idata debug;
extern UCHAR xdata arpbuf[]; //arp缓存
extern UCHAR code my_hwaddr[]; //我的硬件地址缓存
void Delay1ms(unsigned char T);
extern UCHAR idata rcve_buf_allocated; //空间申请标志位
extern UINT volatile event_word; //当前行为字段
#define Rtl8019ResetLow P5 &= ~(0x4); // P52
#define Rtl8019ResetHigh P5 |= 0x4; // P52
//------------------------------------------------------------------------
// Initialize the Cirrus Logic 8019 chip
//------------------------------------------------------------------------
void page(unsigned char pagenumber) //翻页操作
{
unsigned char data temp;
temp=reg00;
temp=temp&0x3B ;
pagenumber=pagenumber <<6;
temp=temp | pagenumber;
reg00=temp;
}
void Rtl8019AS_Reset() //复位网卡
{
Rtl8019ResetHigh;;
Delay1ms(200);
Rtl8019ResetLow;
Delay1ms(200);
}
void ReadRtl8019NodeID(void) //读出网卡的物理地址存到my_ethernet_address.bytes[6]里
{
unsigned char data i;
page(0);
reg09=0; //读取网卡的ram的地址为0x0000(高八位)
reg08=0; //低八位
reg0b=0; //(字节计数器高八位)
reg0a=12; //读取12个字节(低八位)
reg00=0x0a; //启动读ram
for (i=0;i<6;i++) //保存物理地址
{
// my_hwaddr[i]=reg10;
// my_hwaddr[i]=reg10;
}
}
void WriteRtl8019NodeID() //向8019中写入地址
{
page(1);
reg01=my_hwaddr[0];
reg02=my_hwaddr[1];
reg03=my_hwaddr[2];
reg04=my_hwaddr[3];
reg05=my_hwaddr[4];
reg06=my_hwaddr[5];
page(0);
}
void init_8019(void)
{
Delay1ms(10);
Rtl8019AS_Reset(); //复位8019
R8019_CHIP_SELECT; //????
reg00=0x21; //使芯片处于停止模式,这时进行寄存器设置 停止模式下,将不会发送和接收数据包
Delay1ms(10); //延时10毫秒,确保芯片进入停止模式
page(0);
reg0a=0x00; reg0b=0x00;
reg0c= 0xe0; //monitor mode (no packet receive)
reg0d= 0xe2; //loop back mode 使芯片处于mon和loopback模式,跟外部网络断开
reg01=0x4c; //接收缓冲区起始地址
reg02=0x80; //接收缓冲区停止页面地址
reg03=0x4c; //接收缓冲区最后页面地址(接收时自动递增)
reg04=0x40; //发送缓冲区开始页面地址
reg07=0xff; //清除所有中断标志位(写1清除中断位)
reg0f=0x00; //disable all interrupt(屏蔽所有中断)
reg0e=0xc8; //byte dma 8位dma方式 11001000(最后一位选择了8位dma传输)
page(1); //翻页至第一页
reg07=0x4d; //当前页面寄存器地址 CURR
reg08=0x00; //
reg09=0x00;
reg0a=0x00;
reg0b=0x00;
reg0c=0x00;
reg0d=0x00;
reg0e=0x00;
reg0f=0x00; //07-0f为多播地址,无用
reg00=0x22; //这时让芯片开始工作 0010 0010,处于完成dma读写状态
ReadRtl8019NodeID(); //读出网卡的物理地址48位
WriteRtl8019NodeID(); //将网卡地址写入到mar寄存器
page(0);
reg0c=0xcc; //关闭monitor,将网卡设置成正常的模式,跟外部网络连接 1100 1100
reg0d=0xe0; //传输配置寄存器,使正常传输
reg00=0x22; //这时让芯片开始工作
reg07=0xff; //清除所有中断标志位
}
//------------------------------------------------------------------------
// This functions checks 8019 status then sends an ethernet
// frame to it by calling an assembler function.
//------------------------------------------------------------------------
/********发送一个数据包********/
void send_frame(UCHAR xdata * outbuf, UINT len)/*发送一个数据包的命令,长度最小为60字节,最大1514字节*/
{
UCHAR i;
UINT ii;
page(0);
if(len<60)len=60; //最小长度为60字节
txd_buffer_select=!txd_buffer_select; //已选择传送缓冲区(因为可以传送两个最大报文,所以需要决定写入的是哪一个报文缓冲区)
if (txd_buffer_select)
reg09=0x40 ; //txdwrite highaddress(第一个发送缓冲区地址)
else
reg09=0x46 ; //txdwrite highaddress(第二个发送缓冲区地址)
reg08=0x00; //read page address low(低八位地址)
reg0b=len>>8; //需要读取字节数的高八位地址
reg0a=len&0xff; //需要读取字节数的低八位地址
reg00=0x12; //write dma, page0 0001 0010 启动dma的写操作
for (ii=0;ii<len;ii++) //for (ii=4;ii<len+4;ii++) //是否加4有待验证
{
reg10=*(outbuf+ii);
}
//将数据写入发送dma区间,发送数据
/* 以下3句为中止dma的操作,可以不要 */
reg0b=0x00; //read count high 中止DMA操作
reg0a=0x00; //read count low;
reg00=0x22; //complete dma page 0(abort dma)
{
for(ii=0;ii<1000;ii++) //检查txp为是否为低,为低表示发送完毕
{
if ((reg00&0x04)==0) break; //发送完毕,跳出循环
}
if ((reg04&0x01)!=0) break; //表示无差错得发送成功
reg00=0x3e; //0011 1110 数据包重发
}
reg07=0xff; //屏蔽所有中断标志位
if(txd_buffer_select) //准备发送第二个缓冲区的数据包
reg04=0x40; //txd packet start;
else
reg04=0x46; //txd packet start;
reg06=len>>8; //high byte counter
reg05=len&0xff; //low byte counter
reg07=0xff;
reg00=0x3e; //to sendpacket;
free(outbuf); //释放发送缓冲区
}
//------------------------------------------------------------------------
// This functions checks the 8019 receive event status
// word to see if an ethernet frame has arrived. If so,
// set EVENT_ETH_ARRIVED bit in global event_word
//------------------------------------------------------------------------
/******查看是否有新的数据包到达******/
void query_8019(void)
{
char bnry,curr;
page(0); //翻到第0页
bnry=reg03; //bnry page have read 读BNRY指针值
page(1); //翻到第一页
curr=reg07; //curr writepoint 读8019写页CURR指针值
page(0);
if ((curr==0)) return;
bnry=bnry++;
if (bnry>0x7f) bnry=0x4c;
if (bnry!=curr) //此时表示有新的数据包在缓冲区里
{
EA = 0; //停mcu中断,防止发生冲突
event_word |= EVENT_ETH_ARRIVED;
EA = 1; //重新开中断
}
/******关闭dma操作******/
reg0b=0x00; reg0a=0x00; reg00=0x22;//complete dma page 0
}
//------------------------------------------------------------------------
// This function gets an incoming Ethernet frame from the 8019.
// There may be more than 1 waiting but just allocate memory for
// one and read one in. Use the 8019 to queue incoming packets.
//------------------------------------------------------------------------
UCHAR xdata * rcve_frame(void)//如果收到一个有效的数据包,返回收到的数据,否则返回NULL
{
UCHAR bnry,curr,next_page;
UINT len, ii;
UCHAR temp;
UCHAR xdata * buf;
page(0);
bnry=reg03; //bnry page have read 读页指针
page(1);
curr=reg07; //curr writepoint 8019写页指针
page(0);
if ((curr==0)) return NULL; //读的过程出错
next_page=bnry;
bnry=bnry++;
if (bnry>0x7f) bnry=0x4c;
if (bnry!=curr) //此时表示有新的数据包在缓冲区里
{
//读取一包的前4个字节:4字节的8019头部
page(0);
reg09=bnry; //read page address high
reg08=0x00; //read page address low
reg0b=0x00; //read count high
reg0a=4; //read count low;
reg00=0x0a; //read dma
temp = reg10; temp = reg10;
//读取dma区段的数据,将数据保存到temp中,
//因为第一个2字节的数据没什么用可以直接被第二个2字节的数据覆盖
//而第二个2字节的数据是数据长度的高八位和低八位
next_page = temp-1; //next page start-1(下一个数据页存储地址减1)
len = reg10; //读取数据长度的高八位长度
temp = reg10; //保存数据长度的低八位地址
len += temp<<8; //得到数据的长度值
reg0b=0x00;
reg0a=0x00;
reg00=0x22; //complete dma page 0(完成dma操作)
// Allocate enough memory to hold the incoming frame
buf = (UCHAR xdata *)malloc(len);
if (buf == NULL) //如果申请不到这么大的空间
{
// out of RAM
// Tell 8019 to skip the frame,直接跳过这个数据包
page(1);
curr=reg07; //page1,当前接收缓冲器的地址
page(0); //切换回page0
bnry = curr -1; //此时无数据到达
if (bnry < 0x4c) bnry =0x7f;
reg03=bnry; //write to bnry
reg07=0xff; //清除中断状态可以不用
return NULL; //返回
}
// This flag keeps track of allocated rcve memory
rcve_buf_allocated = TRUE;
// Call the assembler function to get the incoming frame
reg09=bnry; //read page address high
reg08=4; //read page address low,注意前4个字节为8019的串
reg0b=len>>8; //read count high
reg0a=len&0xff; //read count low;
reg00=0x0a; //read dma
for(ii=0;ii<len;ii++)
{
buf[ii]=reg10;
}
reg0b=0x00; reg0a=0x00; reg00=0x22; //dma complete page0
// Return pointer to start of buffer
bnry=next_page;
if (bnry<0x4c) bnry=0x7f;
reg03=bnry; //write to bnry
reg07=0xff; //屏蔽中断
return (buf); //返回缓冲
}
return NULL;
}
/**********以太网数据发送函数*********/
void eth_send(UCHAR xdata * outbuf, UCHAR * hwaddr, UINT ptype, UINT len) //加上以太网帧的首部
{
ETH_HEADER xdata * eth;
eth = (ETH_HEADER xdata *)outbuf;
// Add 14 byte Ethernet header
memcpy(eth->dest_hwaddr, hwaddr, 6);
memcpy(eth->source_hwaddr, my_hwaddr, 6);
eth->frame_type = ptype;
// We just added 14 bytes to length
send_frame(outbuf, len + 14);
}
//------------------------------------------------------------------------
// This is the handler for incoming Ethernet frames
// This is designed to handle standard Ethernet (RFC 893) frames
// See "TCP/IP Illustrated, Volume 1" Sect 2.2
//------------------------------------------------------------------------
void eth_rcve(UCHAR xdata * inbuf) //接受以太网数据包,按照不同的上层协议分别处理
{
ETH_HEADER xdata * eth;
eth = (ETH_HEADER xdata *)inbuf;
// Reject frames in IEEE 802 format where Eth type field
// is used for length. Todo: Make it handle this format
if (eth->frame_type < 1520)
{
if (debug) serial_send("ETH: IEEE 802 pkt rejected\r");
return;
}
// Figure out what type of frame it is from Eth header
// Call appropriate handler and supply address of buffer
switch (eth->frame_type)
{
case ARP_PACKET:
arp_rcve(inbuf);
break;
case IP_PACKET:
ip_rcve(inbuf);
break;
default:
if (debug) serial_send("Error: Unknown pkt rcvd\r");
break;
}
}