关于DM9000A驱动的问题和解决
一,问题描述
在nios中要驱动DM9000,网上能找到许多。大部分都是下图类似的一些文件,其中inc文件夹下面就是nios中要用到的驱动程序了!本文的重点就是这个驱动程序的问题
以下是DM9000A.c中的接收网络数据包的部分
/* Receive One Packet I/O routine */
unsigned int ReceivePacket (unsigned char *data_ptr,unsigned int *rx_len)
{
unsigned char rx_READY,GoodPacket;
unsigned int Tmp, RxStatus, i;
RxStatus = rx_len[0] = 0;
GoodPacket=FALSE;
/* mask NIC interrupts IMR: PAR only */
iow(IMR, PAR_set);
/* dummy read a byte from MRCMDX REG. F0H */
rx_READY = ior(MRCMDX);
/* got most updated byte: rx_READY */
rx_READY = IORD(DM9000A_BASE,IO_data)&0x03;
usleep(STD_DELAY);
/* check if (rx_READY == 0x01): Received Packet READY? */
if (rx_READY == DM9000_PKT_READY)
{
/* got RX_Status & RX_Length from RX SRAM */
IOWR(DM9000A_BASE, IO_addr, MRCMD); /* set MRCMD REG. F2H RX I/O port ready */
usleep(STD_DELAY);
RxStatus = IORD(DM9000A_BASE,IO_data);
usleep(STD_DELAY);
rx_len[0] = IORD(DM9000A_BASE,IO_data);
/* Check this packet_status GOOD or BAD? */
if ( !(RxStatus & 0xBF00) && (rx_len[0] < MAX_PACKET_SIZE) )
{
/* read 1 received packet from RX SRAM into RX buffer */
for (i = 0; i < rx_len[0]; i += 2)
{
usleep(STD_DELAY);
Tmp = IORD(DM9000A_BASE, IO_data);
data_ptr[i] = Tmp&0xFF;
data_ptr[i+1] = (Tmp>>8)&0xFF;
}
GoodPacket=TRUE;
} /* end if (GoodPacket) */
else
{
/* this packet is bad, dump it from RX SRAM */
for (i = 0; i < rx_len[0]; i += 2)
{
usleep(STD_DELAY);
Tmp = IORD(DM9000A_BASE, IO_data);
}
printf("
Error
");
rx_len[0] = 0;
} /* end if (!GoodPacket) */
} /* end if (rx_READY == DM9000_PKT_READY) ok */
else if(rx_READY) /* status check first byte: rx_READY Bit[1:0] must be "00"b or "01"b */
{
/* software-RESET NIC */
iow(NCR, 0x03); /* NCR REG. 00 RST Bit [0] = 1 reset on, and LBK Bit [2:1] = 01b MAC loopback on */
usleep(20); /* wait > 10us for a software-RESET ok */
iow(NCR, 0x00); /* normalize */
iow(NCR, 0x03);
usleep(20);
iow(NCR, 0x00);
/* program operating registers~ */
iow(NCR, NCR_set); /* NCR REG. 00 enable the chip functions (and disable this MAC loopback mode back to normal) */
iow(0x08, BPTR_set); /* BPTR REG.08 (if necessary) RX Back Pressure Threshold in Half duplex moe only: High Water 3KB, 600 us */
iow(0x09, FCTR_set); /* FCTR REG.09 (if necessary) Flow Control Threshold setting High/ Low Water Overflow 5KB/ 10KB */
iow(0x0A, RTFCR_set); /* RTFCR REG.0AH (if necessary) RX/TX Flow Control Register enable TXPEN, BKPM (TX_Half), FLCE (RX) */
iow(0x0F, 0x00); /* Clear the all Event */
iow(0x2D, 0x80); /* Switch LED to mode 1 */
/* set other registers depending on applications */
iow(ETXCSR, ETXCSR_set); /* Early Transmit 75% */
/* enable interrupts to activate DM9000 ~on */
iow(IMR, INTR_set); /* IMR REG. FFH PAR=1 only, or + PTM=1& PRM=1 enable RxTx interrupts */
/* enable RX (Broadcast/ ALL_MULTICAST) ~go */
iow(RCR , RCR_set | RX_ENABLE | PASS_MULTICAST); /* RCR REG. 05 RXEN Bit [0] = 1 to enable the RX machine/ filter */
} /* end NIC H/W system Data-Bus error */
return GoodPacket ? DMFE_SUCCESS : DMFE_FAIL;
}
仔细阅读便不难发现其中的问题,
读者要注意红 {MOD}注释部分。在函数的开始禁用了接收中断,然后是个判断(判断是否是满足接收数据的条件),如果判断为真就接收数据,否则软件reset。进行reset没什么好说的。为真的话接收数据完之后
并没有重新使能中断。那么下次怎么还会触发接收中断啊?后来发现TransmitPacket中开始禁用了中断,结束是使能了中断,这样的话,如果每次接收完一包数据后紧接着发一包或者多包数据的话貌似就没有问题了!如果不这么做的话,就只能触发接收一次,下次就不行了。真的开始怀疑网上大片的这个dm9000驱动,到底出自哪里?发现这个问题就只能改了!
二,解决方法
1,将DM9000A.c文件中的ReceivePacket函数中的iow(IMR, PAR_set);注释掉。
2,在DM9000A.c文件中添加如下函数,并在DM9000A.h中声明。
void disableInterrupt()
{
iow(IMR, PAR_set);
}
void enableInterrupt()
{
iow(IMR, INTR_set);
}
void clearISR()
{
iow(ISR, 0x3F);
}
unsigned short getISR()
{
return ior(ISR);
}
3,修改你的中断ISR。
void DM9000_Interrupt(void *context) {
unsigned short isr=0;
int ret=0;
int rx_len=0,writeIndex=0;
isr = getISR();//获取中断状态寄存器
clearISR();//清除中断状态寄存器
disableInterrupt();//禁用中断
if(isr&0x01)//如果是接收中断
{
ret = ReceivePacket(context+writeIndex, &rx_len);//接收数据包
writeIndex += rx_len;
analysisPacket(comm);
writeIndex = 0;
while (!ret) {
ret = ReceivePacket(comm->buf + comm->writeIndex, &rx_len);
writeIndex += rx_len;
analysisPacket(comm);
writeIndex = 0;
}
}