📄 etherdev.c
字号:
#include "etherdev.h"
void __Tmr_TickISR_Handler(void);
#define NET_RST 0x00000040
#define ETH_TX_PAGE_START 0x40 // 0x4000 Tx buffer is 6 * 256 = 1536 bytes
#define ETH_RX_PAGE_START 0x46 // 0x4600 Rx buffer is 26 * 256 = 6656 bytes
#define ETH_RX_PAGE_STOP 0x80 // 0x6000
#define ETH_MIN_PACKET_LEN 0x3C
static void etherdev_reg_write(uint8 reg, uint16 wr_data);
static uint16 etherdev_reg_read(uint8 reg);
static unsigned int etherdev_poll(void);
static uint8 tick_count = 0;
void __irq Tmr_TickISR_Handler(void){
tick_count++;
T0IR = 0x01; // 清除中断标志
VICVectAddr = 0x00; // 通知VIC中断处理结束
}
static uint16 etherdev_reg_read(uint8 reg){
return (uint16)(*((volatile uint16*)ETH_REG_BASE + reg));
}
static void etherdev_reg_write(uint8 reg, uint16 data){
*((volatile uint16*)ETH_REG_BASE + reg) = data;
}
// Manipulate PS1 & PS0 in CR to select RTL8019AS register page.
#define ETHERDEV_SELECT_REG_PAGE(page) \
do \
{ \
etherdev_reg_write(CR, etherdev_reg_read(CR) & ~(PS1 | PS0)); \
etherdev_reg_write(CR, etherdev_reg_read(CR) | (page << 6)); \
} while(0)
int etherdev_init(void){
int i, j;
IODIR = IODIR|NET_RST;
IOCLR = NET_RST;
IOSET = NET_RST;
for(i=0; i<200; i++){
for(j=0; j<200; j++);
}
IOCLR = NET_RST;
etherdev_reg_read(0x1f);
etherdev_reg_write(0x1f, 0x00);
for(i=0; i<200; i++){
for(j=0; j<200; j++);
}
etherdev_reg_write(CR, RD2 | STP);
etherdev_reg_write(DCR, FT1 | LS | BOS | WTS);
etherdev_reg_write(RBCR0, 0x00);//复位远程字节计数寄存器
etherdev_reg_write(RBCR1, 0x00);
etherdev_reg_write(RCR, MON); //设置接收配置寄存器为监听模式
etherdev_reg_write(TCR, LB0); //设置传输配置寄存器为内部回环模式
etherdev_reg_write(BNRY, ETH_RX_PAGE_START);
etherdev_reg_write(PSTART, ETH_RX_PAGE_START);
etherdev_reg_write(PSTOP, ETH_RX_PAGE_STOP);
etherdev_reg_write(TPSR, ETH_TX_PAGE_START);
etherdev_reg_write(ISR, 0xFF); //清除所有的中断标志位
etherdev_reg_write(IMR, 0x00); //屏蔽所有中断
etherdev_reg_write(CR, RD2 | PS0 | STP);//选择page1
etherdev_reg_write(CURR, ETH_RX_PAGE_START);
etherdev_reg_write(PAR0, UIP_ETHADDR0);
etherdev_reg_write(PAR1, UIP_ETHADDR1);
etherdev_reg_write(PAR2, UIP_ETHADDR2);
etherdev_reg_write(PAR3, UIP_ETHADDR3);
etherdev_reg_write(PAR4, UIP_ETHADDR4);
etherdev_reg_write(PAR5, UIP_ETHADDR5);
// Select RTL8019AS register page 0 and abort DMA operation.
etherdev_reg_write(CR, RD2 | STP);
// Restart RTL8019AS.
etherdev_reg_write(CR, RD2 | STA);
etherdev_reg_write(RCR, AB);
etherdev_reg_write(TCR, 0x00);
VICIntSelect &= ~(1 << VIC_TIMER0); /* 选择TIMER0为IRQ */
VICVectAddr2 = (uint32)Tmr_TickISR_Handler; /* 设置中断服务程序地址 */
VICVectCntl2 = 0x20 | VIC_TIMER0; /* 分配TIMER0为slot2 */
VICIntEnable = (1 << VIC_TIMER0); /* 使能中断 */
T0TCR = 0; /* 禁止 timer 0. */
T0PC = 0; /* 时钟不分频 */
T0MR0 = CPU_PERIPHERAL_CLK_FREQ / OS_TICKS_PER_SEC; /* 计数到这个值 */
T0MCR = 3; /*设置T0MR0匹配后复位T0TC,并产生中断标志 */
T0CCR = 0; /* 禁止捕获功能 */
T0EMR = 0; /* 无外部匹配输出 */
T0TCR = 1; /* 使能timer 0 */
return 1;
}
void etherdev_send(void){
int i;
uint16* ptr_16 = (uint16 *)uip_buf;
etherdev_reg_write(CR, RD2 | STA);
// Wait until pending transmit operation completes.
while(etherdev_reg_read(CR) & TXP) continue;
// Clear remote DMA complete interrupt status register bit.
etherdev_reg_write(ISR, RDC);
// Set remote DMA start address registers to indicate where to load packet.
etherdev_reg_write(RSAR0, 0x00);
etherdev_reg_write(RSAR1, ETH_TX_PAGE_START);
// Set remote DMA byte count registers to indicate length of packet load.
//设置远程DMA字节计数寄存器来表示packet的长度
etherdev_reg_write(RBCR0, (uint8)(uip_len & 0xFF));
etherdev_reg_write(RBCR1, (uint8)(uip_len >> 8));
// Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS.
//初始化DMA发送
etherdev_reg_write(CR, RD1 | STA);
// DMA transfer packet from uip_buf & uip_appdata to RTL8019AS local
// transmit buffer memory.发送到RTL8018AS的本地发送缓冲区
for(i = 0; i < (uip_len + 1)/2; i++){
/*首先发送在uip_buf中的link level headers和TCP/IP headers,
这两个头部的总大小是40 + UIP_LLH_LEN。*/
if(i == (40 + UIP_LLH_LEN)/2 ){
/*这时,发送应用程序的数据部分,因为第(40 + UIP_LLH_LEN)个字节应该是应用程序的数据*/
ptr_16 = (uint16 *)uip_appdata;
}
etherdev_reg_write(RDMA, *ptr_16++);
}
// Wait until remote DMA operation complete.
//等待,直到远程DMA操作完成。
while(!(etherdev_reg_read(ISR) & RDC)) continue;
// Abort/ complete DMA operation.
//中止/完成DMA操作
etherdev_reg_write(CR, RD2 | STA);
// Set transmit page start to indicate packet start.
//设置PSTART,它指向packet的开始
etherdev_reg_write(TPSR, ETH_TX_PAGE_START);
// Ethernet packets must be > 60 bytes, otherwise are rejected as runts.
//以太网packets必须大于60字节,否则认为不完整,而会被丢弃
if(uip_len < ETH_MIN_PACKET_LEN){
uip_len = ETH_MIN_PACKET_LEN;
}
// Set transmit byte count registers to indicate packet length.
//设置TBCR,指出packet的长度
etherdev_reg_write(TBCR0, (unsigned char)(uip_len & 0xFF));
etherdev_reg_write(TBCR1, (unsigned char)(uip_len >> 8));
// Issue command for RTL8019AS to transmit packet from it's local buffer.
//发出命令,让NIC把packet从它的本地缓冲区中发送到网络上。
etherdev_reg_write(CR, RD2 | TXP | STA);
return;
}
//这个函数从网卡中读出一个packet,并将其放入缓冲区uip_buf中。如果必须等待超过0.5秒
//返回0。相反当一个成功读取了一个full packet并放入uip_buf缓冲区时,则返回packet的长度。
unsigned int etherdev_read(void)
{
unsigned int bytes_read;
/* tick_count threshold should be 12 for 0.5 sec bail-out
One second (24) worked better for me, but socket recycling
is then slower. I set UIP_TIME_WAIT_TIMEOUT 60 in uipopt.h
to counter this. Retransmission timing etc. is affected also. */
/*一秒(24)可能更好,这时socket循环会更慢一点,但是为了安全起见,tick_count阀值应该是12。
与此对应我在uipopt.h中设置UIP_TIME_WAIT_TIMEOUT为60。重发等也受到影响*/
while ((!(bytes_read = etherdev_poll())) && (tick_count < 12)) continue;
tick_count = 0; //在此处将tick_count清零
return bytes_read; //返回读取的字节的个数
}
/*轮询网卡是否有到来的packets*/
static unsigned int etherdev_poll(void){
int len = 0;
// Check if there is a packet in the rx buffer.
//检查在rx缓冲区中是否有一个packet
if(etherdev_reg_read(ISR) & PRX){
// Check if the rx buffer has overflowed.
//检查rx缓冲区是否溢出
if(etherdev_reg_read(ISR) & OVW){
int i,j;
int resend = 0;
uint8 status = (uint8)etherdev_reg_read(CR) & TXP;
etherdev_reg_write(CR, RD2 | STP);
for(i=0; i<200; i++){
for(j=0; j<200; j++);
}
// Wait for controller to halt after any current tx completes.
while(!(etherdev_reg_read(ISR) & RST)) continue;
etherdev_reg_write(RBCR0, 0x00);
etherdev_reg_write(RBCR1, 0x00);
if(status){
if(!((etherdev_reg_read(ISR) & PTX) || (etherdev_reg_read(ISR) & TXE))){
resend = 1;
}
}
// Set transmit configuration register to loopback internally.
etherdev_reg_write(TCR, LB0);
// Restart the RTL8019AS.
etherdev_reg_write(CR, RD2 | STA);
// Re-initialise last receive buffer read pointer.
etherdev_reg_write(BNRY, ETH_RX_PAGE_START);
// Select RTL8019AS register page 1.
ETHERDEV_SELECT_REG_PAGE(1);
// Re-initialise current packet receive buffer page pointer.
etherdev_reg_write(CURR, ETH_RX_PAGE_START);
// Select RTL8019AS register page 0.
ETHERDEV_SELECT_REG_PAGE(0);
// Clear rx buffer overflow & packet received interrupt flags.
etherdev_reg_write(ISR, PRX | OVW);
etherdev_reg_write(TCR, 0x00);
if(resend){
// Retransmit packet in RTL8019AS local tx buffer.
etherdev_reg_write(CR, RD2 | TXP | STA);
}
}
// Rx buffer has not overflowed, so read a packet into uip_buf.
//没有溢出,因此读出一个packet放入uip_buf
else {
int i;
uint8 next_rx_packet; //指向下一个packet的页位置
uint8 current;
uint16* pbuf_16 = (uint16*)uip_buf;
union {
uint16 words;
uint8 bytes[2];
}status;
// Retrieve packet header. (status, next_ptr, length_l, length_h)
//获取packet头部。(状态,指向下一个packet的指针,packet长度的低位字节,packet长度的高位字节)
// Clear remote DMA complete interrupt status register bit.
//复位远程DMA ISR寄存器的RDC位(此位在远程DMA操作完成后置位)写"1"清除对应位的标志
//使此次操作从一个全新的状态开始
etherdev_reg_write(ISR, RDC);
// Set remote DMA start address registers to packet header.
//将远程DMA起始地址寄存器设置为packet头部
etherdev_reg_write(RSAR0, 0x00);
etherdev_reg_write(RSAR1, etherdev_reg_read(BNRY));//注意第一个packet就放在BNRY指向的页
// Set remote DMA byte count registers to packet header length.
//将远程DMA字节计数寄存器设置为packet头部的长度
//首先读取四个字节的数据,包含了(status, next_ptr, length_l, length_h)
etherdev_reg_write(RBCR0, 0x04);
etherdev_reg_write(RBCR1, 0x00);
// Initiate DMA transfer of packet header.
//初始化packet头部的DMA传输
etherdev_reg_write(CR, RD0 | STA);
// Drop packet status. We don't use it.
//丢弃packet状态,状态对我们没有多大用处,总是假设是好的
status.words = etherdev_reg_read(RDMA);
// Save next packet pointer.
//保存指向下一个packet的指针。
next_rx_packet = status.bytes[1];
// Retrieve packet data length and subtract CRC bytes.
//获取packet的数据长度并减去CRC(4个字节)的长度。
len = etherdev_reg_read(RDMA);
len -= 4;
// Wait until remote DMA operation completes.
//等待直到远程DMA操作完成,操作完成时,RDC置位
while(!(etherdev_reg_read(ISR) & RDC)) continue;
// Abort/ complete DMA operation.
//中止/完成DMA操作
etherdev_reg_write(CR, RD2 | STA);
// Retrieve packet data.
//前面只是读取了一些有用的信息,后面将获取packet的数据
// Check if incoming packet will fit into rx buffer.
//检查packet的长度,看看它是否大于UIP_BUFSIZE
if(len <= UIP_BUFSIZE)
{
// Clear remote DMA complete interrupt status register bit.
//依然是复位远程DMA ISR寄存器的RDC位(此位在远程DMA操作完成后置位)写"1"清除对应位的标志
//使此次操作从一个全新的状态开始
etherdev_reg_write(ISR, RDC);
// Set remote DMA start address registers to packet data.
//设置远程DMA RSAR,注意这次跳过了前四个字节
etherdev_reg_write(RSAR0, 0x04);
etherdev_reg_write(RSAR1, etherdev_reg_read(BNRY));
// Set remote DMA byte count registers to packet data length.
//设置远程DMA字节计数寄存器为packet的数据长度,已经减去了CRC的那四个字节
etherdev_reg_write(RBCR0, (uint8)(len & 0xFF));
etherdev_reg_write(RBCR1, (uint8)(len >> 8));
// Initiate DMA transfer of packet data.
etherdev_reg_write(CR, RD0 | STA);
// Read packet data directly into uip_buf.
for(i = 0; i < (len + 1)/2; i++){
*(pbuf_16 + i) = etherdev_reg_read(RDMA);
}
// Wait until remote DMA operation complete.
while(!(etherdev_reg_read(ISR) & RDC)) continue;
// Abort/ complete DMA operation.
etherdev_reg_write(CR, RD2 | STA);
}
else{
// Incoming packet too big, so dump it.
//到来的包太大,因此丢弃它
len = 0;
}
// Advance boundary pointer to next packet start.
//设置指针BNRY为下一个packet所在的页
etherdev_reg_write(BNRY, next_rx_packet);
// Select RTL8019AS register page 1.
//选择第一页寄存器
ETHERDEV_SELECT_REG_PAGE(1);
// Retrieve current receive buffer page
//获取指针CURR
current = etherdev_reg_read(CURR);
// Select RTL8019AS register page 0.
//选择第0页寄存器
ETHERDEV_SELECT_REG_PAGE(0);
// Check if last packet has been removed from rx buffer.
//检查是否最后一个packet已经被从接收缓冲区中取走
if(next_rx_packet == current){
// Clear packet received interrupt flag.
//如果是的话,清除接收中断标志。
etherdev_reg_write(ISR, PRX);
}
}
}
//返回接收的packet的长度
return len;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -