2. CS8900A以太网驱动程序
硬件电路决定了CS8900的物理地址是在BANK3的区间内,CS8900是16位的寄存器,故我们设置BANK3的BUS WIDTH也为16位。设置BANK3: 总线宽度16,使能nWait,使能UB/LB
BANKCON3:0x1F7C
网卡CS8900的访问基址为0x19000000,之所以再偏移0x300是由它的特性决定的
#define CS8900_BASE 0x19000300
CS8900 读写寄存器的方式有些特别。要读一个寄存器,先向CS8900_PPTR中写入该寄存器地址,再从CS8900_PDATA中读出该寄存器值;要写一个寄存器,先向CS8900PPTR中写入该寄存器地址,再向CS8900_PDATA中写入要写入的值。不管是寄存器地址还是要读写的数值,都是16位的,也就是说都是unsigned short类型的。因此,读写寄存器的函数如下:
{
CS8900_PPTR = regno;
return CS8900_PDATA;
}
static void put_reg (int regno, unsigned short val)
{
CS8900_PPTR = regno;
CS8900_PDATA = val;
}
读芯片ID: CS8900的芯片ID存放在PP_ChipID寄存器中,读该寄存器得到的正确值应该是0x630E,这可以初步判断一些地址/引脚的设置是否正确,如果读出的不是0x630E,那么CS8900肯定不能正常工作。
设置MAC地址:
MAC地址并不是固定的,可以由我们随意设置。从寄存器PP_IA开始的6个字节存放MAC地址。比如下面的代码把MAC地址设为 00 09 58 D8 11 22:
put_reg (PP_IA + 0, 0x00 | 0x09 << 8);
put_reg (PP_IA + 2, 0x58 | 0xD8 << 8);
put_reg (PP_IA + 4, 0x11 | 0x22 << 8);
因为是Little Endian, 所以0x09<<8, 但是在寄存器内存中还是 0x00放在前面。
寄存器初始化: 设置CS8900的工作模式
/* 只接收目标地址为本网卡的无错误数据包 */
put_reg (PP_RxCTL, PP_RxCTL_IA | PP_RxCTL_Broadcast | PP_RxCTL_RxOK);
/* 当进行接收操作时,不要产生任何中断 */
put_reg (PP_RxCFG, 0);
/* 当进行发送操作时,不要产生任何中断 */
put_reg (PP_TxCFG, 0);
/* 当进行缓存操作时,不要产生任何中断 */
put_reg (PP_BufCFG, 0);
/* 使能发送和接收模式 */
put_reg (PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx);
发送数据包:
int eth_send (volatile void *packet, int length)
两个参数:要发送的数据包首地址、长度
TxCMD 和TxLen寄存器用来初始化数据包的发送,其具体含义见CS8900数据手册第70页。这里PP_TxCmd_TxStart_Full被定义为 0x00C0,表示直到整个数据侦都加载到CS8900内部缓存之后才开始发送,数据侦的长度为CS8900_TxLEN.
CS8900_TxCMD = PP_TxCmd_TxStart_Full;
CS8900_TxLEN = length;
使用TxCMD下达发送数据的命令后,再读取 PP_BusSTAT 总线状态寄存器判断是否做好发送数据的准备。当get_reg (PP_BusSTAT) & PP_BusSTAT_TxRDY 不等于零时表示可以发送了。 使用一个循环进行实际的发送操作:
{
CS8900_RTDATA = *addr++;
}
这里 addr 也是unsigned short类型的指针, 每次向CS8900_RTDATA写入两个字节数据。这里假设要发送的数据包长度为偶数。
最后,通过读取PP_TER寄存器可以知道是否发送完毕,是否发送成功。
接收数据包:
首先,通过读取PP_RER寄存器判断是否接收到数据。如果接收到数据,则连续两次读取 CS8900_RTDATA 的值,
status = CS8900_RTDATA; /* stat */
rxlen = CS8900_RTDATA; /* len */
rxlen 为接收到的数据长度。
然后用一个循环连续读取 rxlen 长度的数据:
i--)
*addr++ = CS8900_RTDATA;
if (rxlen & 1)
*addr++ = CS8900_RTDATA;
其中 RxBuf 为预先在内存中开辟的一块接收缓冲区。 每次循环读取两个字节,还需要处理长度为奇数的情况。
最后,把RxBuf交给上层的协议处理:net_receive( &RxBuf[0], rxlen );