S5PV210开发系列九_网卡驱动实现

来源:互联网 发布:win10网络唤醒 killer 编辑:程序博客网 时间:2024/06/03 03:57

S5PV210开发系列九

网卡驱动实现

象棋小子    1048272975

Internet作为全球最大的互联网络,几乎总汇了全球的信息资源。作为我们生活以及发展的基础设施,越来越多的设备需要连接Internet,共享信息资源。由于Internet的开放性,任何厂家基于TCP/IP协议、Internet技术生产的网络适配器均能接入Internet,与全球计算机互连通信。笔者此处就DM9000A网卡驱动实现作一个简单的介绍。

1. DM9000A概述

DM9000A为台湾DAVICOM公司推出的高速以太网接口芯片,该芯片完全集成的和符合成本效益单芯片快速以太网MAC控制器与一般处理接口,支持8位、16位、32位IO总线接口访问,完全符合IEEE 802.3u规范。

2. MAC帧

TCP/IP协议为Internet互联协议,采用4层层级结构:应用层、运输层、网络层、链路层。其中网卡工作在数据链路层,因此只能够发送或接收数据链路层的帧数据。大多数的TCP/IP均采用Ethernet V2帧格式,数据链路层的上一层的帧包,如网络层的数据包ARP包、RARP包、IP数据包(TCP包/UDP包)等,通过在包头加入6 字节MAC目的地址、6字节MAC源地址、2字节高层包类型,在包尾加入4字节帧检验序列即构成MAC帧。

MAC帧要求数据长度在64 byte ~ 1518 byte之间,这是由协议的防碰撞机制、信道利用率等决定的。因此链路层的上一层包帧长度要求在46 byte ~ 1500 byte,如果数据包长度小于46 byte,如ARP包、RARP包,则必须填充到46 byte,如果一次数据包大于1500byte,也必须进行分包传输。不然会被接收端网卡认为是错误的包,直接丢弃。

接收端网卡接收到CRC正确的MAC帧之后,根据MAC帧的MAC目的地址,比较自身MAC地址,如果一致,或者是组播地址、广播地址,则认为是传给自身的包,接收下来,并传给上一层进行处理,否则直接丢弃不属于自身的包。数据链路层并没有重传或者确定功能,对于传输过程出错的包,仅仅是丢弃,数据包的可靠传输、出错处理等需上面的层级处理。

2-1. MAC帧格式

3. DM9000A驱动

网卡驱动一般应实现最基本的三个接口,网卡初始化、网卡发送包、网卡接收包。

3.1. 网卡初始化

网卡初始化函数主要对DM9000A进行总线引脚的配置以及总线访问时序的设置,配置相应的中断处理,并初始化相应的DM9000A寄存器,最后使能相应的发送/接收回路。芯片总线访问时序应根据芯片手册设置最佳的参数,不然CPU直接访问总线将极大地降低CPU性能。中断处理为CPU提供了一种异步工作的方式,只有相应的事件到来,CPU才需处理相应的事件,能够加快系统响应以及提高系统效率,此处根据中断信号确定包发送完事件,包接收事件,网络连接/断开事件。最后,根据相应的手册或规范完成DM9000A寄存器的配置并使能收发回路。

int DM9000_Init(void)

{

uint32_t Id;

int32_t i;

int32_t oft;

   

// Memory Bank1, 16 bit

SROM_BW_REG &= ~(0xf << 4);

SROM_BW_REG |= (0<<7) | (0<<6) |(1<<5) | (1<<4);

// DM9000A最大的连续读/写时间为4clk(20ns/clk),一次读/写周期设置在100ns, 13 clk@133M

// Tacs 0, Tcos 1, Tacc 4, Tacp 6, Tcoh 1, Tcah 1

SROM_BC1_REG = (4<<4) | (1<<8) |(1<<12) | (6<<16) | (1<<24) | (0<<28);

MP01CON_REG = (MP01CON_REG & (~(0xf<<4))) |(2<<4); // SROM_CSn[1]

   

// set pin interrupt

GPH1CON_REG = GPH1CON_REG | (0xf<<4); // EINT_9

//GPH1PUD_REG = (GPH1PUD_REG & (~(0x3<<2)))| (0x1<<2); // pull down

EXT_INT_1_CON_REG = (EXT_INT_1_CON_REG &(~(0x7<<4))) | (3<<4); // rising edge

   

IRQ_RegisterInt(INT_EINT9, EINT9_Handler);

IRQ_EnableInt(INT_EINT9);

EXT_INT_1_PEND_REG |= (1<<1); // clear pending

EXT_INT_1_MASK_REG &= ~(1<<1); // enableEINT9

   

// RESET device

DM9000_WriteReg(DM9000_NCR, NCR_RST);

Delay_us(100);

DM9000_WriteReg(DM9000_NCR, NCR_RST);

Delay_us(100);

   

// Read id

Id = DM9000_ReadReg(DM9000_VIDL);

Id |= DM9000_ReadReg(DM9000_VIDH) << 8;

Id |= DM9000_ReadReg(DM9000_PIDL) << 16;

Id |= DM9000_ReadReg(DM9000_PIDH) << 24;

   

if (Id == DM9000_ID) {

    Debug("DM9000found at 0x%08x, id: 0x%08x\n", DM9000_IO, Id);      

} else {

    Debug("DM9000not found at 0x%08x, id: 0x%08x\n", DM9000_IO, Id);

    return -1;

}

   

DM9000_WriteReg(DM9000_GPR, 0x00);  // Enable PHY  

Delay_us(1000);

   

// Set PHY

DM9000_WritePhy(0x04, 0x101); // Set PHY media mode

DM9000_WritePhy(0x00, 0x3100);

 

// Program operating register

DM9000_WriteReg(DM9000_NCR, 0x0);   // only intern phy supported by now

DM9000_WriteReg(DM9000_TCR, 0); // TX Polling clear

DM9000_WriteReg(DM9000_BPTR, 0x3f); // Less 3Kb, 600us

DM9000_WriteReg(DM9000_FCTR,FCTR_HWOT(3)|FCTR_LWOT(8));// Flow Control : High/Low Water

DM9000_WriteReg(DM9000_FCR, 0x08);

DM9000_WriteReg(DM9000_SMCR, 0);    // Special Mode

DM9000_WriteReg(DM9000_NSR,NSR_WAKEST|NSR_TX2END|NSR_TX1END); // clear TX status

DM9000_WriteReg(DM9000_ISR, 0x0F);  // Clear interrupt status

   

// Set Node address

oft = 0x10;

for (i=0; i<6; i++) {

    DM9000_WriteReg(oft,default_enetaddr[i]);

    oft++;

}

oft = 0x16;

for (i=0; i<6; i++) {

    DM9000_WriteReg(oft,0);

    oft++;     

}

oft = 0x10;

Debug("MAC: ");

for (i=0; i<5; i++) {

    Debug("%02x:",DM9000_ReadReg(oft));

    oft++;

}

Debug("%02x\n", DM9000_ReadReg(oft));

 

// Activate DM9000

DM9000_WriteReg(DM9000_RCR, RCR_DIS_LONG|RCR_DIS_CRC|RCR_RXEN);//RX enable

DM9000_WriteReg(DM9000_IMR, 0xa3);  // Enable TX/RX/Link interrupt

return 0;

}

3.2. 网卡发送包

写入网卡发送缓存的数据包应该是MAC帧,一次写入不能大于MAC帧最大长度,由于初始化时使能了帧填充以及帧检测序列填充功能,在启动发送时,发送缓存数据如果小于64 byte,会自动进行填充到最小MAC帧长度包,并自动加入CRC发送出去。DM9000A写发送缓存需要相应的寄存器访问序列,应保证是原子操作,即写缓存过程中不能产生其它的DM9000A事件中断(如接收中断),不然在中断中访问了其它的DM9000A寄存器后再返回继续写缓存将不会成功。因此在写缓存前先禁止DM9000A中断,写完后再开启中断。

int DM9000_SendPacket(void *pBuffer, int32_t Len)

{

uint16_t *pTemp;

int32_t TempLen;

int32_t i;

int8_t TempStatus;

if ((pBuffer == NULL) || (Len==0)) {

    return -1;

}

   

// 禁止所有中断

DM9000_WriteReg(DM9000_IMR, 0x80);

// Move data to DM9000 TX RAM

__REGb(DM9000_IO) = DM9000_MWCMD;

pTemp = (uint16_t *)pBuffer;

TempLen = (Len+1) / 2;

for (i=0; i<TempLen; i++) {

    __REGw(DM9000_DATA)= pTemp[i];

}

// Set TX length to DM9000

DM9000_WriteReg(DM9000_TXPLL, Len&0xff);

DM9000_WriteReg(DM9000_TXPLH,(Len>>8)&0xff);

// Issue TX request command

DM9000_WriteReg(DM9000_TCR, TCR_TXREQ);

// 重新使能中断

DM9000_WriteReg(DM9000_IMR, 0xa3);

   

DM9000_TxStatus = 0;

while ((TempStatus = DM9000_TxStatus) != NET_TX_OK) {

    if(TempStatus != 0) {

        returnTempStatus;

    }

}

   

return 0;

}

3.3. 网卡接收包

在相应的中断处理中判断出相应的接收包事件后,即可启动读DM9000A接收缓存序列,将数据包从网卡读入到内存。同样,读数据包缓存也应保证原子操作。

int DM90000_ReceivePacket(void *pBuffer)

{

int Status;

int i;

int RxLen;

uint8_t RxReady;

 

if (!DM9000_RxStatus) {

    return 0; // 没有包接收事件

}

   

// 禁止所有中断

DM9000_WriteReg(DM9000_IMR, 0x80);

DM9000_RxStatus = 0;

 

// 接收到包

RxReady = DM9000_ReadReg(DM9000_MRCMDX); // Dummy read

RxReady = DM9000_ReadReg(DM9000_MRCMDX); // Got mostupdated data

       

// 0:not packet, 1:packet received, other:error

if (RxReady != 0x01) {

    if (RxReady){

        DM9000_WriteReg(DM9000_RCR,0x00); // Stop Device

        DM9000_WriteReg(DM9000_IMR,0x80); // mask int

        Debug("DM9000error: status check fail: 0x%x\n", RxReady);

        return-1;

    }

    DM9000_WriteReg(DM9000_IMR,0xa3);

    return 0;

}

 

// A packet ready now & Get status/length

__REGb(DM9000_IO) = DM9000_MRCMD;

__REGw(DM9000_DATA); // status

RxLen = __REGw(DM9000_DATA);

   

if ((RxLen>DM9000_PKT_MAX) || (RxLen<0x40)) {

    Debug("DM9000error packet: RX Len 0x%x\n", RxLen);

    Status = -1;

} else {

    Status =RxLen;

}

// Read received packet from RX SRAM

RxLen = (RxLen+1) / 2;

for (i=0; i<RxLen; i++) {

    ((uint16_t*)pBuffer)[i] = __REGw(DM9000_DATA);

}

 

DM9000_WriteReg(DM9000_IMR, 0xa3);

return Status;

}

3.4. 中断处理

DM9000A通过外部中断告知CPU相应的到来事件,应保证系统的实时,一般在中断中不应进行直接的事件处理,而是设置相应的事件标志,进而唤醒相应的事件进程进行处理。

static void EINT9_Handler(void)

{  

uint8_t IntStatus;

// 禁止所有中断

DM9000_WriteReg(DM9000_IMR, 0x80); 

IntStatus = DM9000_ReadReg(DM9000_ISR);

DM9000_WriteReg(DM9000_ISR, IntStatus);

 

if (IntStatus & (1<<0)) {

    DM9000_RxStatus= NET_RX_OK;

}

if (IntStatus & (1<<1)) {      

    // 发送完成

    DM9000_TxStatus= NET_TX_OK;

}

if (IntStatus & (1<<5)) {

    // 连接改变

    if(DM9000_ReadReg(DM9000_NSR) & (1<<6)) {

        Debug("Link OK\n");        

        DM9000_LinkStatus = 1;

    } else {

        Debug("Link failed\n");

        DM9000_LinkStatus = 0;

    }

}

   

EXT_INT_1_PEND_REG |= (1<<1); // clear pending

// 重新使能中断

DM9000_WriteReg(DM9000_IMR, 0xa3);

}

4. 驱动测试

此处应用以ARP包为例,ARP(地址解析协议)是根据IP地址来获取MAC地址的一种协议,上层应用是使用IP地址,但实现链路是使用MAC地址通信的。发送主机将包含目标IP地址的ARP请求广播到网络上的所有主机,目标IP地址的主机接收到该请求后,将返回ARP应答包给发送主机,应答包中即包含目标IP的MAC地址。

应用测试通过构造ARP包并发送出去,解析接收到的ARP应答包,并显示相应的目标MAC地址。

目标板可以通过路由器直接连接到局域网,也可以直连PC端网卡进行测试。连接PC端网卡时,应静态设置PC端IP地址以及目标板IP地址,应在同一局域网内。连接PC端网卡时并不用区分用交叉网线还是用直连网线,目前大部分的PC网卡都是自适应的,都是可以通信的。目标板连接路由器,应设置路由器端口所在的局域网IP。

void main(void)

{  

uint16_t PacketBuffer[512];

ARP_PKT Arp_Packet;

ARP_PKT *pArp;

uint32_t IP_Addr;

int Status;

int Len;   

uint8_t Command;

Uart_Init();

DM9000_Init();

memcpy(Arp_Packet.et_hdr.et_dst, NetBcastAddr, 6);

memcpy(Arp_Packet.et_hdr.et_src, default_enetaddr, 6);

Arp_Packet.et_hdr.et_type = htons(PROT_ARP);

// Ethernet hardware address

Arp_Packet.arp_hdr.ar_hrd = htons(0x0001);

Arp_Packet.arp_hdr.ar_pro = htons(PROT_IP);

Arp_Packet.arp_hdr.ar_hln = 6;

Arp_Packet.arp_hdr.ar_pln = 4;

Arp_Packet.arp_hdr.ar_op = htons (ARPOP_REQUEST);

// source ET addr

memcpy(Arp_Packet.arp_hdr.ar_sha, default_enetaddr,6);

// source ip addr

IP4_ADDR(IP_Addr, IP_ADDR[0], IP_ADDR[1], IP_ADDR[2],IP_ADDR[3]);

memcpy(Arp_Packet.arp_hdr.ar_spa, &IP_Addr, 4);

// dest ET addr = 0

memset(Arp_Packet.arp_hdr.ar_tha, 0, 6);

IP4_ADDR(IP_Addr, DST_ADDR[0], DST_ADDR[1],DST_ADDR[2], DST_ADDR[3]);

memcpy(Arp_Packet.arp_hdr.ar_tpa, &IP_Addr, 4);

   

printf("1. ARP Request test\n");

while (1) {

Len = DM90000_ReceivePacket(PacketBuffer);

if (Len > 0) {

    printf("Rxlen %d\n", Len);

    pArp =(ARP_PKT *)PacketBuffer;

    if(ntohs(pArp->et_hdr.et_type) == PROT_ARP) {

    if(ntohs(pArp->arp_hdr.ar_op) == ARPOP_REPLY) {

        // ARP 应答包

    printf("DstIP: %d.%d.%d.%d, Dst MAC: %2X:%2X:%2X:%2X:%2X:%2X\n",

    DST_ADDR[0],DST_ADDR[1], DST_ADDR[2], DST_ADDR[3],

    pArp->arp_hdr.ar_sha[0],pArp->arp_hdr.ar_sha[1], pArp->arp_hdr.ar_sha[2],

    pArp->arp_hdr.ar_sha[3],pArp->arp_hdr.ar_sha[4], pArp->arp_hdr.ar_sha[5] );

    printf("1. ARP Requesttest\n");

    }

    }

}

 

Command = Uart_TestChar();

if (!Command) {

    Command =Uart_WaitChar();

    Status =DM9000_GetLinkStatus();

    if (Status) {

    if (Command== '1') {

    printf("SourceIP: %d.%d.%d.%d, Source MAC: %2X:%2X:%2X:%2X:%2X:%2X\n",

    IP_ADDR[0],IP_ADDR[1], IP_ADDR[2], IP_ADDR[3],

    default_enetaddr[0],default_enetaddr[1], default_enetaddr[2],

    default_enetaddr[3],default_enetaddr[4], default_enetaddr[5] );             

     DM9000_SendPacket(&Arp_Packet,sizeof(ARP_PKT));

    }

    } else {

        printf("Waitingfor net connection\n");

    }

    }

}

}

 

5. 附录

OK210_IAR_DM9000.rar,包含DM9000A驱动模块实现及ARP测试应用工程。

http://pan.baidu.com/s/1ntpH3zN


 

0 0
原创粉丝点击