Cs8900网卡驱动程序分析
首先介绍Cs8900一些寄存器的情况:
LINECTL(线控制寄存器)(0112H)用于决定网卡工作模式情况。
RXCTL(接收控制寄存器)(0104H)表示接收网络上的广播或目的接收地址同本地mac地址 相同的数据包。
RXCFG(接收配置寄存器)(0102H)当收到包后是否产生接受中断。
BUSCT(0116H)可控制芯片的I/O接口的一些操作,设置初始值为8017H,打开cs8900的中断总控位。
ISQ(中断状态寄存器)保存了到底发生那种中断,内部映射接受中断状态寄存器和发送中断状态寄存器的内容。
PORT0(0000H):发送和接收数据时,CPU通过PORT0传输数据。
TXCMD(0004H):发送控制寄存器,如果写入数据00C0H,那么网卡芯片在全部写入数据后开始发送数据。
TXLENG(0006H):发送数据长度寄存器,发送数据时,首先写入发送数据长度,然后将数据通过PORT0写入芯片。
系统工作之前,应首先对网卡芯片初始化,即写寄存器LINECTL,RXCTL,RCCFG,BUSCT。
发送数据时,按如下流程操作寄存器:将发送命令写入TXCMD,把数据长度写入TXLENG,最后将发送数据依次写入PORT0口。接受:当数据到达之后,网卡会产生一个中断,利用中断把数据通过PORT0读出数据。
下面分析程序
1:模块初始化函数
static int __init init_cs8900a_s3c2410(void)
{
struct net_local *lp ;
int ret = 0 ;
dev_cs89x0.irq = irq ;
dev_cs89x0.base_addr = io ;
dev_cs89x0.init = cs89x0_probe ; //struct net_device初始化
request_region(dev_cs89x0.base_addr, NETCARD_IO_EXITENT, “cs8900a”) ;
//申请使用I/O端口
if (register_netdev (&dev_cs89x0) != 0) //注册一个网卡
//当使用register注册设备时,内核会调用init(),即probe(), 并不知道网卡存在。
}
static int __init cs89x0_probel (struct net_device *dev, int ioaddr) {
//获取芯片类型
rev_type = readreg(dev, PRODUCT_ID_ADD);
lp->chip_type = rev_type&~REVOSON_BITS;
lp->chip_revision = ((rev_type & REVISION_BITS) >> 8) + ‘A’;
if(lp->chip_type != CS8900) { //设备匹配
printk(__FILE__ “:wrong device !\n”);
ret =- ENODEV;
Goto after_kmalloc;
}
dev->dev_addr[0] = 0x00 ;
dev->dev_addr[1] = 0x00 ;
dev->dev_addr[2] = 0xc0 ;
dev->dev_addr[3] = 0xff ;
dev->dev_addr[4] = 0xee ;
dev->dev_addr[5] = 0x08 ;
set_mac_address(dev, dev->dev_addr) ;
dev_irq = IRQ_LAN ;
printk(“ , IRQ %d”, dev_irq) ;
//struct net_device成员和方法设置
dev->open = net_open ;
dev->stop = net_device ;
dev->tx_timeout = net_timeout ;
dev->watchdog_timeo = 3 Hz ;
dev->hard_start_xmit = net_send_packet ; //设置数据发送函数
dev->get_stats = net_get_stats ;
dev->set_multicast_list = set_multicast_list ;
dev->set_mac_address = set_mac_address ;
ether_setup(dev) ; //以太网卡设置
}
2:数据发送:
static int net_send_paket (struct sk_buff *skb, struct net_device *dev) {
netif_stop_queue(dev) ; //发送数据时要停掉发送队列
writeword(dev, TX_CMD_PORT, lp->send_cmd) ;
wrtieword(dev, TX_LEN_PORT, skb->len) ;
if((reagreg(dev, PP_BusST) & READ_FOR_TX_NOW) == 0) {
spin_unlock_irq(&lp->lock) ;
DPRINTK(1, “cs89x0: Tx buffer not free !\n”) ;
return 1;
}
writeblock(dev, skb->data, skb->len) ; skb->data:skb有效数据地址
return 0;
}
inline void writeblock(struct net_device *dev, char *pData, int Length) {
int i;
for (i = 0 ; i < (Length/2); i++) {
writeword(dev, TX_FRAME_PORT, *(u16 *)pData );
//16位寄存器,每次发16位数据,Length以字节(8位)计数
pData += 2;
}
if (Length % 2) {
u16 OddWordValue = *pData;
writeword(dev, TX_FRAME_PORT, OddWordValue);
}
}
inline void writeword(struct net_device *dev, int portno, int value) {
//把数据写到PORT0寄存器
outw(value, dev->base_addr + portno);
}
3:中断
static void net_interrupt (int irq, void *dev_id, struct pt_regs *regs) {
while((status = readword(dev, ISQ_PORT))) { //由ISQ寄存器判断中断类型
switch (status & ISQ_EVENT_MASK) {
case ISQ_RECEIVER_EVENT : //接收中断
net_rx (dev) ;
break ;
case ISQ_TRANSMITTER_EVENT : //发送中断
lp->status.tx_packets++ ;
netif_wake_queue (dev) ;
break ;
}
}
}
4:接收
static void net_rx (struct net_device *dev) {
status = inw (ioaddr + RX_FRAME_PORT) ;
if ((status & RX_OK) == 0) { //RX_OK判断接收是否是正确接收
count_rx_errors(status, lp) ;
return ;
}
Length = inw(ioaddr + RX_FRAME_PORT) ;
//从寄存器中获取接收数据包的长度
skb = dev_alloc_skb (length + 2) ;//利用长度分配skb
if(NULL == skb) {
lp = stats.rx_dropped++ ;
return ;
}
skb_reserve (skb, 2) ;
//mac头是14字节,一开始保留两个字节,正是为了保证IP头的开始是4字节对其的
skb->len = length ;
skb->dev = dev ;
readblock(dev, skb->data, skb->len) ;
//从硬件将数据读到skb中。
skb->protocol=eth_type_trans(skb, dev) ;
netif_rx (skb) ;
}
inline void readblock(struct net_device *dev, char *pData, int Length) {
u16 InputWord;
int i;
for (i=0; i < (Length/2); i++) {
InputWord = readword(dev, RX_FRAME_PORT);
*(u8*)pData++ = (u8) InputWord & 0xFF;
*(u8*)pData++ = (u8) (InputWord >> 8) & 0xFF;
}
if (Length & 0x1) //判断奇数,读取最后一个字节
*pData = (u8) (readword(dev, RX_FRAME_PORT) & 0xff);
netif_rx(skb);//把收到的包交给设备无关层,按照协议类型交给相应的协议处理函数
}
发送由内核主动调用函数指针引起的,接收是当网卡受到完整数据包后产生中断。