接下来就是对CS8900A网卡的实际操作了,首先介绍下cs8900A网卡芯片:
CS8900芯片是Cirrus Logic公司生产的一种局域网处理芯片,在嵌入式领域中使用非常常见。它的封装是100-pin TQFP,内部集成了在片RAM、10BASE-T收发滤波器,并且提供8位和16位两种接口,一般在单片机中,使用了CS8900的8位接口模式。可选择1:1.414 YL18-1064S或1:2.5 YL18-1080S 变压比例的变压器
CS8900网卡工作原理:
CS8900与ARM芯片按照16位方式连接,网卡芯片复位后默认工作方式为I/O连接,基址是300H,下面对它的几个主要工作寄存器进行介绍(寄存器后括号内的数字为寄存器地址相对基址300H的偏移量)。
·LINECTL(0112H)
LINECTL决定CS8900的基本配置和物理接口。在本系统中,设置初始值为00d3H,选择物理接口为10BASE-T,并使能设备的发送和接收控制位。
·RXCTL(0104H)
RXCTL控制CS8900接收特定数据报。设置RXTCL的初始值为0d05H,接收网络上的广播或者目标地址同本地物理地址相同的正确数据报。
·RXCFG(0102H)
RXCFG控制CS8900接收到特定数据报后会引发接收中断。RXCFG可设置为0103H,这样当收到一个正确的数据报后,CS8900会产生一个接收中断。
·BUSCT(0116H)
BUSCT可控制芯片的I/O接口的一些操作。设置初始值为8017H,打开CS8900的中断总控制位。
·ISQ(0120H)
ISQ是网卡芯片的中断状态寄存器,内部映射接收中断状态寄存器和发送中断状态寄存器的内容。
·PORT0(0000H)
发送和接收数据时,CPU通过PORT0传递数据。
·TXCMD(0004H)
发送控制寄存器,如果写入数据00C0H,那么网卡芯片在全部数据写入后开始发送数据。
·TXLENG(0006H)
发送数据长度寄存器,发送数据时,首先写入发送数据长度,然后将数据通过PORT0写入芯片。
以上为几个最主要的工作寄存器(为16位),CS8900支持8位模式,当读或写16位数据时,低位字节对应偶地址,高位字节对应奇地址。例如,向TXCMD中写入00C0H,则可将00h写入305H,将C0H写入304H。
在GEC2410开发板上,CS8900A连接BANK3,中断EINT9,基址0xA7000300
我们从初始化慢慢来看:
1.CS8900Initialize
实现在cs8900.c,被CS8900RegisterAdapter(CS8900RegisterAdapter被MiniportInitialize调用),也就是在Miniport驱动初始化中进行了网卡的初始化.
代码如下:
#pragma NDIS_PAGEABLE_FUNCTION(CS8900Initialize) BOOLEAN CS8900Initialize( IN PCS8900_ADAPTER Adapter ) /*++ Routine Description: Initializes the card into a running state. Arguments: Adapter - pointer to the adapter block. Return Value: TRUE, if all goes well, else FALSE. --*/ { DWORD rdata; //网卡虚拟IO地址,在CS8900RegisterAdapter中获得 EthCommand = (unsigned char *)ioPacketPage; //首先获取GPIO,INTR,MEMCTRL寄存器的IO虚拟地址 v_pIOPRegs = VirtualAlloc(0, sizeof(S3C2410X_IOPORT_REG), MEM_RESERVE, PAGE_NOACCESS); if (v_pIOPRegs == NULL) { DEBUGMSG (1,(TEXT("[CS8900] v_pIOPRegs is not allocated/r/n"))); return FALSE; } if (!VirtualCopy((PVOID)v_pIOPRegs, (PVOID)(S3C2410X_BASE_REG_PA_IOPORT >> 8), sizeof(S3C2410X_IOPORT_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE)) { DEBUGMSG (1,(TEXT("[CS8900] v_pIOPRegs is not mapped/r/n"))); goto cs8900_fail; } DEBUGMSG (1,(TEXT("[CS8900] v_pIOPRegs is mapped to %x/r/n"), v_pIOPRegs)); v_pINTRegs = VirtualAlloc(0, sizeof(S3C2410X_INTR_REG), MEM_RESERVE, PAGE_NOACCESS); if (v_pINTRegs== NULL) { DEBUGMSG (1,(TEXT("[CS8900] v_pINTRegs is not allocated/r/n"))); goto cs8900_fail; } if (!VirtualCopy((PVOID)v_pINTRegs, (PVOID)(S3C2410X_BASE_REG_PA_INTR >> 8), sizeof(S3C2410X_INTR_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE)) { DEBUGMSG (1,(TEXT("[CS8900] v_pINTRegs is not mapped/n/r"))); goto cs8900_fail; } DEBUGMSG (1,(TEXT("[CS8900] v_pINTRegs is mapped to %x/r/n"), v_pINTRegs)); v_pMEMRegs = VirtualAlloc(0,sizeof(S3C2410X_MEMCTRL_REG), MEM_RESERVE,PAGE_NOACCESS); if(v_pMEMRegs == NULL) { DEBUGMSG (1,(TEXT("[CS8900] v_pMEMRegs is not allocated/r/n"))); goto cs8900_fail; } if(!VirtualCopy((PVOID)v_pMEMRegs,(PVOID)(S3C2410X_BASE_REG_PA_MEMCTRL >> 8),sizeof(S3C2410X_MEMCTRL_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE)) { DEBUGMSG (1,(TEXT("[CS8900] v_pMEMRegs is not mapped/r/n"))); goto cs8900_fail; } DEBUGMSG (1,(TEXT("[CS8900] v_pMEMRegs is mapped to %x/r/n"), v_pMEMRegs)); //设置BANK3(CS8900连接在BANK3上) // nGCS3=nUB/nLB(nSBHE),nWAIT,16-bit v_pMEMRegs->BWSCON = (v_pMEMRegs->BWSCON&~(0xf<<12))|(0xd<<12); // BANK3 access timing v_pMEMRegs->BANKCON3=((CS8900_Tacs<<13)+(CS8900_Tcos<<11)+(CS8900_Tacc<<8)+(CS8900_Tcoh<<6)/ +(CS8900_Tah<<4)+(CS8900_Tacp<<2)+(CS8900_PMC)); //设置外部中断寄存器 rdata = v_pIOPRegs->GPGCON; rdata &= ~(3 << 2); rdata |= (2 << 2); v_pIOPRegs->GPGCON = rdata; /* External Interrupt #9 Enable */ rdata = v_pIOPRegs->EXTINT1; rdata &= ~(7 << 4); rdata |= (4 << 4); v_pIOPRegs->EXTINT1 = rdata; /* Rising Edge Detect Mode */ DEBUGMSG(1, (TEXT("[CS8900] rGPGCON = %x/r/n"), v_pIOPRegs->GPGCON)); DEBUGMSG(1, (TEXT("[CS8900] rEXTINT1 = %x/r/n"), v_pIOPRegs->EXTINT1)); if (CSInit() == FALSE) { DEBUGMSG(1, (TEXT("CS8900:CSInit failure!!/r/n"))); RETAILMSG(1, (TEXT("CS8900:CSInit failure!!/r/n"))); return FALSE; } DEBUGMSG(1, (TEXT("CS8900:CSInit successfully!!/r/n"))); return TRUE; cs8900_fail: if (v_pIOPRegs) { VirtualFree((PVOID)v_pIOPRegs, 0, MEM_RELEASE); } if (v_pINTRegs) { VirtualFree((PVOID)v_pINTRegs, 0, MEM_RELEASE); } if (v_pMEMRegs) { VirtualFree((PVOID)v_pMEMRegs, 0, MEM_RELEASE); } return FALSE; }
其中在最后调用了CSInit,对网卡寄存器进行了初始化.
代码如下:
int CSInit() { // Find CS8900 chip. if (findCS() == FALSE) return FALSE; DEBUGMSG(1, (TEXT("Find CS8900 OK/r/n"))); /* Reset CS8900 chip. */ if (resetCS() == FALSE) return FALSE; DEBUGMSG(1, (TEXT("reset CS8900 OK/r/n"))); /* Initialize CS8900 chip. */ if (initCS() == FALSE) return FALSE; DEBUGMSG(1, (TEXT("CS8900 init OK/r/n"))); return TRUE; }
其中调用了findCS,resetCS,initCS分别进行判断芯片是否存在,复位,初始化工作
使用到的读写网卡寄存器的函数如下:
要通过IO模式访问cs8900A内部寄存器,必须先设置PacketPage Pointer,将要读写的目标寄存器偏移地址写入PacketPage Pointer Port(I/O Base+000AH),然后该目标寄存器的内容就被映射到PacketPage Data Port(I/O Base+000CH),然后就可以进行相应的读写工作了.CS8900ReadRegister和CS8900WriteRegister就是实现这样的方式.
unsigned short GetD_SlowIO(unsigned long regaddrs) { return (*(volatile unsigned short *)regaddrs); } void SetD_SlowIO(unsigned long regaddrs, unsigned short regvalue) { *(volatile unsigned short *)regaddrs = regvalue; } #define readIoPort(offset) ((unsigned short)GetD_SlowIO((unsigned long)&EthCommand[(offset)])) #define writeIoPort(offset, data) / (SetD_SlowIO((unsigned long)&EthCommand[(offset)], (data))) //Read data from a PacketPage register. unsigned short CS8900ReadRegister(unsigned short offset) { writeIoPort(IO_PACKET_PAGE_POINTER, offset); return readIoPort(IO_PACKET_PAGE_DATA_0); } // Write data to a PacketPage register. void CS8900WriteRegister(unsigned short offset, unsigned short data) { writeIoPort(IO_PACKET_PAGE_POINTER, offset); writeIoPort(IO_PACKET_PAGE_DATA_0, data); }
(1)findCS:
int findCS(void) { unsigned short sg; /* Check the signature. */ if ((sg = readIoPort(IO_PACKET_PAGE_POINTER)) != CS8900_SIGNATURE) { DEBUGMSG(1, (TEXT("Signature Error %x/n"), sg)); // return FALSE; } /* Check the EISA registration number. */ if (CS8900ReadRegister(PKTPG_EISA_NUMBER) != CS8900_EISA_NUMBER) { DEBUGMSG(1, (TEXT("Eisa Number Error/n"))); return FALSE; } /* Check the Product ID. */ if ((CS8900ReadRegister(PKTPG_PRDCT_ID_CODE) & CS8900_PRDCT_ID_MASK) != CS8900_PRDCT_ID) { DEBUGMSG(1, (TEXT("Product ID Error/n"))); return FALSE; } DEBUGMSG(1, (TEXT("Product ID = %x/n"), CS8900ReadRegister(PKTPG_PRDCT_ID_CODE))); return TRUE; }
(2)resetCS:
//Reset CS8900 chip. #define MAX_COUNT 0x100000 int resetCS(void) { int i; unsigned short dummy; /* Issue a reset command to the chip */ CS8900WriteRegister(PKTPG_SELF_CTL, SELF_CTL_RESET); /* Delay for 125 micro-seconds */ Sleep(10); /* Wait until initialization is done */ Sleep(100); /* Wait until INITD bit of SelfST register is set. */ i = 0; while (((dummy = CS8900ReadRegister(PKTPG_SELF_ST)) & SELF_ST_INITD) == 0) { if (++i > MAX_COUNT) { DEBUGMSG(1, (TEXT("resetCs8900 failed 1/n"))); return FALSE; } } /* Wait until SIBUSY bit of SelfST register is cleared. */ i = 0; while (((dummy = CS8900ReadRegister(PKTPG_SELF_ST)) & SELF_ST_SIBUSY) != 0) { if (++i > MAX_COUNT) { DEBUGMSG(1, (TEXT("resetCs8900 failed 2/n"))); return FALSE; } } return TRUE; }
(3)initCS:
int initCS() { //10BASE-T CS8900WriteRegister(PKTPG_LINE_CTL, LINE_CTL_10_BASE_T); //RX OK中断使能 CS8900WriteRegister(PKTPG_RX_CFG, RX_CFG_RX_OK_I_E); //接收有效数据,地址匹配数据,广播数据 CS8900WriteRegister(PKTPG_RX_CTL, RX_CTL_RX_OK_A | RX_CTL_IND_ADDR_A | RX_CTL_BROADCAST_A); //发送数据相关中断不做设置 CS8900WriteRegister(PKTPG_TX_CFG, 0); //缓冲区相关中断不做设置 CS8900WriteRegister(PKTPG_BUF_CFG, 0); //设置MAC地址 CS8900WriteRegister(PKTPG_INDIVISUAL_ADDR, 0x3322); CS8900WriteRegister(PKTPG_INDIVISUAL_ADDR + 2, 0x5544); CS8900WriteRegister(PKTPG_INDIVISUAL_ADDR + 4, 0x0F66); //设置中断 initIrq(); return TRUE; }
(4)initIrq:
void initIrq(void) { unsigned short rdata; // interrupt enabling.... //使用IRQ0作为中断输出,连接CPU EINT9 CS8900WriteRegister(PKTPG_INTERRUPT_NUMBER, INTERRUPT_NUMBER); //Enable IRQ rdata = CS8900ReadRegister(PKTPG_BUS_CTL) | BUS_CTL_ENABLE_IRQ; CS8900WriteRegister(PKTPG_BUS_CTL, rdata); //使能接收发送器 rdata = CS8900ReadRegister(PKTPG_LINE_CTL) | LINE_CTL_RX_ON | LINE_CTL_TX_ON; CS8900WriteRegister(PKTPG_LINE_CTL, rdata); }
2.CS8900ReadEthernetAddress:
初始化好网卡芯片控制器后,CS8900RegisterAdapter调用CS8900ReadEthernetAddress来获取网卡的MAC地址,这里直接进行赋值.
#pragma NDIS_PAGEABLE_FUNCTION(CS8900ReadEthernetAddress) BOOLEAN CS8900ReadEthernetAddress( IN PCS8900_ADAPTER Adapter ) /*++ Routine Description: Reads in the Ethernet address from the CS8900 Chip... Arguments: Adapter - pointer to the adapter block. Return Value: The address is stored in Adapter->PermanentAddress, and StationAddress if it is currently zero. --*/ { Adapter->PermanentAddress[0] = 0x22; Adapter->PermanentAddress[1] = 0x33; Adapter->PermanentAddress[2] = 0x44; Adapter->PermanentAddress[3] = 0x55; Adapter->PermanentAddress[4] = 0x66; Adapter->PermanentAddress[5] = 0x0F; DEBUGMSG(1, (TEXT("CS8900: PermanentAddress [ %02x-%02x-%02x-%02x-%02x-%02x ]/r/n"), Adapter->PermanentAddress[0], Adapter->PermanentAddress[1], Adapter->PermanentAddress[2], Adapter->PermanentAddress[3], Adapter->PermanentAddress[4], Adapter->PermanentAddress[5])); // // Use the burned in address as the station address, unless the // registry specified an override value. // if ((Adapter->StationAddress[0] == 0x00) && (Adapter->StationAddress[1] == 0x00) && (Adapter->StationAddress[2] == 0x00) && (Adapter->StationAddress[3] == 0x00) && (Adapter->StationAddress[4] == 0x00) && (Adapter->StationAddress[5] == 0x00) ) { DEBUGMSG(1, (TEXT("CS8900: StationAddress Modified!.../r/n"))); Adapter->StationAddress[0] = Adapter->PermanentAddress[0]; Adapter->StationAddress[1] = Adapter->PermanentAddress[1]; Adapter->StationAddress[2] = Adapter->PermanentAddress[2]; Adapter->StationAddress[3] = Adapter->PermanentAddress[3]; Adapter->StationAddress[4] = Adapter->PermanentAddress[4]; Adapter->StationAddress[5] = Adapter->PermanentAddress[5]; } return(TRUE); }
3.CS8900ReceiveEvent
在中断发生时,CS8900HandleInterrupt会被调用,当判断是接收中断时,调用CS8900ReceiveEvent来接收数据.
void CS8900ReceiveEvent(PCS8900_ADAPTER Adapter, unsigned short RxEvent) { #if WINCEDEBUG unsigned short i; #endif unsigned short Length, Type; unsigned short *pBuffer; unsigned short *pBufferLimit; unsigned char *cptr; WORD PacketOper; DEBUGMSG(1, (TEXT("++CS8900ReceiveEvent/r/n"))); // Verify that it is an RxOK event if (!(RxEvent & RX_EVENT_RX_OK)) { DEBUGMSG(1, (TEXT("CS8900ReceiveEvent: Receive Currupted Packet!/r/n"))); return; } //frame data开始放的是RxStatus Register(PacketPage base+0400h)和RxLength Register(PacketPage base+0402h)的内容 //获取接收帧的长度 readIoPort(IO_RX_TX_DATA_0); // Discard RxStatus Length = readIoPort(IO_RX_TX_DATA_0); //读取数据 pBuffer = (unsigned short *)Adapter->Lookahead; pBufferLimit = (unsigned short *)Adapter->Lookahead + (Length + 1) / 2; while (pBuffer < pBufferLimit) { *pBuffer = readIoPort(IO_RX_TX_DATA_0); pBuffer++; } //判断接收帧类型,调式用 //这里的Lookahead buffer大小为一个以太网帧大小(1518 - CS8900_HEADER_SIZE)=1504 pBuffer = (unsigned short *)Adapter->Lookahead; Type = pBuffer[6]; PacketOper = pBuffer[10]; if (Type == 0x0608) { if (PacketOper == 0x0100) DEBUGMSG(1, (TEXT("[CS8900] Receive ARP Request Packet/r/n"))); else if (PacketOper == 0x0200) DEBUGMSG(1, (TEXT("[CS8900] Receive ARP Response Packet/r/n"))); else if (PacketOper == 0x0300) DEBUGMSG(1, (TEXT("[CS8900] Receive RARP Request Packet/r/n"))); else if (PacketOper == 0x0400) DEBUGMSG(1, (TEXT("[CS8900] Receive RARP Response Packet/r/n"))); else DEBUGMSG(1, (TEXT("[CS8900] Receive Unknown ARP Packet/r/n"))); } else if (Type == 0x0008) DEBUGMSG(1, (TEXT("[CS8900] Receive IP Packet/r/n"))); cptr = (unsigned char *)Adapter->Lookahead; #if WINCEDEBUG DEBUGMSG(1, (TEXT("type = %x, length = %x/r/n"), Type, Length)); for (i=0; i<Length; i++) DEBUGMSG(1, (TEXT("%x "), *cptr++)); DEBUGMSG(1, (TEXT("/r/n"))); #endif cptr = (unsigned char *)Adapter->Lookahead; //通知NDIS以太网帧接收,NDIS将转发给协议层处理 NdisMEthIndicateReceive( Adapter->MiniportAdapterHandle, (NDIS_HANDLE)Adapter, (PCHAR)(Adapter->Lookahead), CS8900_HEADER_SIZE, (PCHAR)(cptr)+CS8900_HEADER_SIZE, Length - CS8900_HEADER_SIZE, Length - CS8900_HEADER_SIZE); //通知NDIS接收完成,NdisMEthIndicateReceive完成后必须调用 NdisMEthIndicateReceiveComplete(Adapter->MiniportAdapterHandle); return; }
4.SendHandler:CS8900Send
相应的还有对应的发送函数SendHandler,即MiniportSend的入口函数.
MiniportSend用来发送已经封装好协议的数据帧.
This function transfers a protocol-supplied packet over the network.
NDIS_STATUS CS8900Send( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET Packet, IN UINT Flags ) /*++ Routine Description: The CS8900Send request instructs a driver to transmit a packet through the adapter onto the medium. Arguments: MiniportAdapterContext - Context registered with the wrapper, really a pointer to the adapter. Packet - A pointer to a descriptor for the packet that is to be transmitted. SendFlags - Optional send flags Notes: This miniport driver will always accept a send. This is because the CS8900 has limited send resources and the driver needs packets to copy to the adapter immediately after a transmit completes in order to keep the adapter as busy as possible. This is not required for other adapters, as they have enough resources to keep the transmitter busy until the wrapper submits the next packet. --*/ { ULONG Len; PUCHAR CurBufAddress; UCHAR TotalPacket[2048]; PNDIS_BUFFER CurBuffer; UINT i; UINT Count = 0; UINT CurBufLen; UINT PacketLength; USHORT BusStatus; PCS8900_ADAPTER Adapter = (PCS8900_ADAPTER)(MiniportAdapterContext); DEBUGMSG(1, (TEXT("CS8900Send/r/n"))); //获得发送包的buffer描述符及大小 NdisQueryPacket( Packet, NULL, NULL, NULL, &Len ); NdisQueryPacket(Packet, NULL, NULL, &CurBuffer, &PacketLength); //获得buffer描述符对应的buffer地址及buffer大小 NdisQueryBuffer(CurBuffer, (PVOID *)&CurBufAddress, &CurBufLen); //将该buffer数据复制到TotalPacket for (i = 0; i < CurBufLen; i++) TotalPacket[Count++] = CurBufAddress[i]; //获取所有biffer数据赋值到TotalPacket中,作为一个完整的包送到网卡发送 NdisGetNextBuffer(CurBuffer, &CurBuffer); while (CurBuffer && (CurBufLen != 0)) { NdisQueryBuffer(CurBuffer, (PVOID *)&CurBufAddress, &CurBufLen); for (i = 0; i < CurBufLen; i++) { TotalPacket[Count++] = CurBufAddress[i]; } NdisGetNextBuffer(CurBuffer, &CurBuffer); } // Request that a transmit be started BusStatus = CS8900RequestTransmit(PacketLength); // If there was an error with the transmit bid if (BusStatus & BUS_ST_TX_BID_ERR) { DEBUGMSG(1, (TEXT("##### BUS_ST_TX_BID_ERR #####/r/n"))); } else if (BusStatus & BUS_ST_RDY4TXNOW) { // The chip is ready for transmission now // Copy the message to the chip to start the transmit CS8900CopyTxFrame((PCHAR)TotalPacket, PacketLength); return(NDIS_STATUS_SUCCESS); } return(NDIS_STATUS_FAILURE); }
其中调用了CS8900RequestTransmit来发出发送要求
unsigned short CS8900RequestTransmit ( UINT PacketLength ) { // Request that the transmit be started after all data has been copied writeIoPort(IO_TX_CMD, TX_CMD_START_ALL); writeIoPort(IO_TX_LENGTH, (unsigned short)PacketLength); // Return the BusStatus register which indicates success of the request return CS8900ReadRegister(PKTPG_BUS_ST); }
当返回状态BUS_ST_RDY4TXNOW为1时,说明CS8900A准备好发送数据,这时调用CS8900CopyTxFrame来发送数据帧
void CS8900CopyTxFrame ( PCHAR pPacket, UINT PacketLength ) { UINT i; PWORD pMsg; WORD PacketType, PacketOper; pMsg = (PWORD)pPacket; PacketType = *(pMsg + 6); PacketOper = *(pMsg + 10); //获取发送数据帧类型,调式用 if (PacketType == 0x0608) { if (PacketOper == 0x0100) DEBUGMSG(1, (TEXT("[CS8900] Send ARP Request Packet/r/n"))); else if (PacketOper == 0x0200) DEBUGMSG(1, (TEXT("[CS8900] Send ARP Response Packet/r/n"))); else if (PacketOper == 0x0300) DEBUGMSG(1, (TEXT("[CS8900] Send RARP Request Packet/r/n"))); else if (PacketOper == 0x0400) DEBUGMSG(1, (TEXT("[CS8900] Send RARP Response Packet/r/n"))); else DEBUGMSG(1, (TEXT("[CS8900] Send Unknown ARP Packet/r/n"))); } else if (PacketType == 0x0008) DEBUGMSG(1, (TEXT("[CS8900] Send IP Packet/r/n"))); //将发送数据帧发送到CS8900A数据缓冲区,从而完成一帧数据的发送 for (i = 0 ; i < (PacketLength + 1) / 2; i++) { writeIoPort(IO_RX_TX_DATA_0, *pMsg); pMsg++; } }
基于NDIS架构CS8900A的驱动在CE下的实现就简单介绍到这里,主要涉及了NDIS网络驱动结构及CS8900A芯片处理.关于NDIS的知识还有很多,有待于进一步学习.