文章目录
一,前言
前面分析了网络设备驱动的框架以及构建设备驱动的一般流程,现在通过对DM9000C网卡驱动程序的分析进一步加强对网络设备驱动的理解。
二,硬件电路
2.1 DM9000C
SD0~SD15:数据/地址传输。
CMD:该管脚决定是访问DM9000C的数据寄存器还是索引寄存器。将通过LDATA0 ~ LDATA15传输的地址写到DM9000C的索引寄存器中,还是从DM9000C的数据寄存器读取数据通过LDATA0 ~ LDATA15传输给s3c2440的内存控制器。
INT:中断
CS:片选
RST:复位
2.2 S3c2440
DM9000C的中断号为EINT7
2.3 S3c2440内存控制器
操作地址在0x2000 0000 ~ 0x2800 0000之前的地址时,片选nGCS4。
取基地址为0x2000 0000。操作DM9000C的索引寄存器时,需要LADDR2(CMD)为0,即操作DM9000C的索引寄存器就是对地址0x2000 0000访问。操作DM9000C的数据寄存器时,需要LADDR2(CMD)为1,即操作DM9000C的数据寄存器就是对地址(0x2000 0000 + 4)访问。
需要对内存控制器进行配置,片选nGCS4对应于BANK4。即需要对寄存器BWSCON(0x48000000)和BANKCON4(0x48000014)进行配置。
三,probe函数分析
static int __init dm9dev9000c_init(void)
{
volatile unsigned long *bwscon; // 0x48000000
volatile unsigned long *bankcon4; // 0x48000014
unsigned long val;
iobase = (int)ioremap(0x20000000, 1024);
irq = IRQ_EINT7;
/* 设置S3C2440的memory controller */
bwscon = ioremap(0x48000000, 4);
bankcon4 = ioremap(0x48000014, 4);
/* DW4[17:16]: 01-16bit
* WS4[18] : 0-WAIT disable
* ST4[19] : 0 = Not using UB/LB (The pins are dedicated nWBE[3:0])
*/
val = *bwscon;
val &= ~(0xf<<16);
val |= (1<<16);
*bwscon = val;
/*
* Tacs[14:13]: 发出片选信号之前,多长时间内要先发出地址信号
* DM9000C的片选信号和CMD信号可以同时发出,
* 所以它设为0
* Tcos[12:11]: 发出片选信号之后,多长时间才能发出读信号nOE
* DM9000C的T1>=0ns,
* 所以它设为0
* Tacc[10:8] : 读写信号的脉冲长度,
* DM9000C的T2>=10ns,
* 所以它设为1, 表示2个hclk周期,hclk=100MHz,就是20ns
* Tcoh[7:6] : 当读信号nOE变为高电平后,片选信号还要维持多长时间
* DM9000C进行写操作时, nWE变为高电平之后, 数据线上的数据还要维持最少3ns
* DM9000C进行读操作时, nOE变为高电平之后, 数据线上的数据在6ns之内会消失
* 我们取一个宽松值: 让片选信号在nOE放为高电平后,再维持10ns,
* 所以设为01
* Tcah[5:4] : 当片选信号变为高电平后, 地址信号还要维持多长时间
* DM9000C的片选信号和CMD信号可以同时出现,同时消失
* 所以设为0
* PMC[1:0] : 00-正常模式
*
*/
*bankcon4 = (1<<8)|(1<<6); /* 对于DM9000C可以设Tacc为1, 对于DM9000E,Tacc要设大一点,比如最大值7 */
//*bankcon4 = (7<<8)|(1<<6); /* MINI2440使用DM9000E,Tacc要设大一点 */
iounmap(bwscon);
iounmap(bankcon4);
switch(mode) {
case DM9KS_10MHD:
case DM9KS_100MHD:
case DM9KS_10MFD:
case DM9KS_100MFD:
media_mode = mode;
break;
default:
media_mode = DM9KS_AUTO;
}
dmfe_dev = dmfe_probe();
if(IS_ERR(dmfe_dev))
return PTR_ERR(dmfe_dev);
return 0;
}
struct net_device * __init dmfe_probe(void)
{
struct net_device *dev;
int err;
DMFE_DBUG(0, "dmfe_probe()",0);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
dev = init_etherdev(NULL, sizeof(struct board_info));
//ether_setup(dev);
#else
// 申请分配一个net_device结构体
dev= alloc_etherdev(sizeof(struct board_info)); // -> alloc_netdev(sizeof_priv, "eth%d", ether_setup);
#endif
if(!dev)
return ERR_PTR(-ENOMEM);
SET_MODULE_OWNER(dev);
// 设置net_device结构体
err = dmfe_probe1(dev);
if (err)
goto out;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
// 注册
err = register_netdev(dev);
if (err)
goto out1;
#endif
return dev;
out1:
release_region(dev->base_addr,2);
out:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
kfree(dev);
#else
free_netdev(dev);
#endif
return ERR_PTR(err);
}
3.1 读取DM9000C的设备ID
.......
outb(DM9KS_VID_L, iobase);
id_val = inb(iobase + 4);
outb(DM9KS_VID_H, iobase);
id_val |= inb(iobase + 4) << 8;
outb(DM9KS_PID_L, iobase);
id_val |= inb(iobase + 4) << 16;
outb(DM9KS_PID_H, iobase);
id_val |= inb(iobase + 4) << 24;
printk(KERN_ERR"line %d <DM9KS> I/O: %x, VID: %x \n", __LINE__, iobase, id_val);
if (id_val == DM9KS_ID || id_val == DM9010_ID)
{
.......
}
.......
3.2 设置net_device结构体
......
/* driver system function */
dev->base_addr = iobase; // 设置设备基地址
dev->irq = irq; // 中断号
dev->open = &dmfe_open; // 提供open接口
dev->hard_start_xmit = &dmfe_start_xmit; // 提供发包接口
dev->watchdog_timeo = 5*HZ; // 设置超时时间
dev->tx_timeout = dmfe_timeout; // 提供超时处理接口
dev->stop = &dmfe_stop; // 提供stop接口
dev->get_stats = &dmfe_get_stats; // 提供获取统计信息接口
dev->set_multicast_list = &dm9000_hash_table;
dev->do_ioctl = &dmfe_do_ioctl;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,28)
dev->ethtool_ops = &dmfe_ethtool_ops;
#endif
#ifdef CHECKSUM
//dev->features |= NETIF_F_IP_CSUM;
dev->features |= NETIF_F_IP_CSUM|NETIF_F_SG;
#endif
......
3.3 注册中断
......
request_irq(dev->irq,&dmfe_interrupt,0,dev->name,dev)
......
static irqreturn_t dmfe_interrupt(int irq, void *dev_id) /* for kernel 2.6.20*/
{
struct net_device *dev = dev_id;
board_info_t *db;
int int_status,i;
u8 reg_save;
DMFE_DBUG(0, "dmfe_interrupt()", 0);
/* A real interrupt coming */
db = (board_info_t *)dev->priv;
spin_lock(&db->lock);
/* Save previous register address */
reg_save = inb(db->io_addr);
/* Disable all interrupt */
iow(db, DM9KS_IMR, DM9KS_DISINTR);
// 获取网卡中断的类型
int_status = ior(db, DM9KS_ISR); /* Got ISR */
iow(db, DM9KS_ISR, int_status); /* Clear ISR status */
// 链接状态改变中断
if (int_status & DM9KS_LINK_INTR)
{
netif_stop_queue(dev);
for(i=0; i<500; i++) /*wait link OK, waiting time =0.5s */
{
phy_read(db,0x1);
if(phy_read(db,0x1) & 0x4) /*Link OK*/
{
/* wait for detected Speed */
for(i=0; i<200;i++)
udelay(1000);
/* set media speed */
if(phy_read(db,0)&0x2000) db->Speed =100;
else db->Speed =10;
break;
}
udelay(1000);
}
netif_wake_queue(dev);
//printk("[INTR]i=%d speed=%d\n",i, (int)(db->Speed));
}
// 接收数据中断
if (int_status & DM9KS_RX_INTR)
dmfe_packet_receive(dev); // -> netif_rx(skb); 将接收到的数据传送到上层网络层
// 检验中断
if (int_status & DM9KS_TX_INTR)
dmfe_tx_done(0);
if (db->cont_rx_pkt_cnt>=CONT_RX_PKT_CNT)
{
iow(db, DM9KS_IMR, 0xa2);
}
else
{
/* Re-enable interrupt mask */
iow(db, DM9KS_IMR, DM9KS_REGFF);
}
/* Restore previous register address */
outb(reg_save, db->io_addr);
spin_unlock(&db->lock);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
return IRQ_HANDLED;
#endif
}
四,总结
构建网络设备驱动程序的一般流程(设备驱动功能层)
- 调用alloc_netdev接口申请分配一个net_device结构体。
- 设置net_device结构体,至少提供一个发包函数。
- 根据硬件电路申请一个网卡相关中断,在中断中接收网卡接收到的其他设备发送过来的数据,通过netif_rx函数发送给上层处理。
- 调用register_netdev接口注册该驱动。