基于S3C2440的Linux-3.6.6移植——DM9000网卡驱动移植
Linux-3.6.6很好的支持了DM9000,因此对于S3C2440芯片来说无需进行任何修改,甚至连menuconfig都已经默认配置了网卡驱动。但我们还需要设置网卡的MAC和IP等信息。有许多方法可以实现网卡的设置,在这里我们选择一种比较简单的方法——修改根文件系统的启动脚本文件rcS。在根文件etc/init.d/rcS文件中添加下列语句:
/sbin/ifconfig lo 127.0.0.1
/sbin/ifconfig eth0 hw ether 5e:f7:90:82:66:28
/sbin/ifconfig eth0 192.168.1.234 up
route add default gw 192.168.1.1
这样就完成了网卡的移植。我们测试一下网卡。在启动开发板时,系统会打印出类似下列信息:
dm9000 dm9000: eth0: link down
dm9000 dm9000: eth0: link up, 100Mbps,full-duplex, lpa 0xCDE1
如果在提示符下输入ifconfig命令,则:
[root@zhaocj /]#ifconfig
eth0 Link encap:Ethernet HWaddr5E:F7:90:82:66:28
inet addr:192.168.1.234 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:51 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5324 (5.1 KiB) TXbytes:0 (0.0 B)
Interrupt:51 Base address:0x2300
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0(0.0 B)
另外主机和开发板都可以互相ping通对方。
下面我们就简单介绍一下DM9000网卡驱动。
在arch/arm/mach-s3c24xx/mach-zhaocj2440.c文件内定义了DM9000网卡平台设备:
static struct resource zhaocj2440_dm9k_resource[]= {
[0]= DEFINE_RES_MEM(MACH_ZHAOCJ2440_DM9K_BASE, 4),
[1]= DEFINE_RES_MEM(MACH_ZHAOCJ2440_DM9K_BASE + 4, 4),
[2]= DEFINE_RES_NAMED(IRQ_EINT7, 1, NULL, IORESOURCE_IRQ \
|IORESOURCE_IRQ_HIGHEDGE),
};
/*
*The DM9000 has no eeprom, and it's MAC address is set by
*the bootloader before starting the kernel.
*/
static struct dm9000_plat_data zhaocj2440_dm9k_pdata= {
.flags = (DM9000_PLATF_16BITONLY |DM9000_PLATF_NO_EEPROM),
};
static struct platform_device zhaocj2440_device_eth= {
.name = "dm9000",
.id = -1,
.num_resources = ARRAY_SIZE(zhaocj2440_dm9k_resource),
.resource =zhaocj2440_dm9k_resource,
.dev ={
.platform_data = &zhaocj2440_dm9k_pdata,
},
};
并把网卡平台设备添加进平台设备数组内:
static structplatform_device *zhaocj2440_devices[] __initdata = {
……
&zhaocj2440_device_eth,
……
};
在drivers/net/ethernet/davicom/dm9000.c文件内定义了DM9000网卡平台驱动:
static struct platform_driver dm9000_driver= {
.driver = {
.name = "dm9000",
.owner = THIS_MODULE,
.pm = &dm9000_drv_pm_ops,
},
.probe = dm9000_probe,
.remove = __devexit_p(dm9000_drv_remove),
};
下面分析一下探测函数dm9000_probe:
static int__devinit
dm9000_probe(structplatform_device *pdev)
{
struct dm9000_plat_data *pdata =pdev->dev.platform_data;
struct board_info*db; /* Point a board informationstructure */
structnet_device *ndev;
constunsigned char *mac_src;
intret = 0;
intiosize;
inti;
u32id_val;
/*Init network device */
//初始化网络设备结构体net_device
ndev= alloc_etherdev(sizeof(struct board_info));
if(!ndev)
return-ENOMEM;
SET_NETDEV_DEV(ndev,&pdev->dev);
dev_dbg(&pdev->dev,"dm9000_probe()\n");
/*setup board info structure */
//设置板块信息结构体——db
db= netdev_priv(ndev);
db->dev= &pdev->dev;
db->ndev= ndev;
//自旋锁
spin_lock_init(&db->lock);
mutex_init(&db->addr_lock);
//初始化工作队列
INIT_DELAYED_WORK(&db->phy_poll,dm9000_poll_work);
//提取出DM9000网卡资源,即zhaocj2440_dm9k_resource
db->addr_res= platform_get_resource(pdev, IORESOURCE_MEM, 0); //地址
db->data_res= platform_get_resource(pdev, IORESOURCE_MEM, 1); //数据
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ,0); //中断
if(db->addr_res == NULL || db->data_res == NULL ||
db->irq_res == NULL) {
dev_err(db->dev,"insufficient resources\n");
ret =-ENOENT;
gotoout;
}
//获取网卡中断资源
db->irq_wake= platform_get_irq(pdev, 1);
if(db->irq_wake >= 0) {
dev_dbg(db->dev,"wakeup irq %d\n", db->irq_wake);
//申请网卡中断,中断函数为dm9000_wol_interrupt
ret= request_irq(db->irq_wake, dm9000_wol_interrupt,
IRQF_SHARED, dev_name(db->dev), ndev);
if(ret) {
dev_err(db->dev,"cannot get wakeup irq (%d)\n", ret);
}else {
/*test to see if irq is really wakeup capable */
ret= irq_set_irq_wake(db->irq_wake, 1);
if(ret) {
dev_err(db->dev,"irq %d cannot set wakeup (%d)\n",
db->irq_wake, ret);
ret = 0;
} else {
irq_set_irq_wake(db->irq_wake,0);
db->wake_supported = 1;
}
}
}
//申请网卡地址所需的内存空间
iosize= resource_size(db->addr_res);
db->addr_req= request_mem_region(db->addr_res->start, iosize,
pdev->name);
if(db->addr_req == NULL) {
dev_err(db->dev,"cannot claim address reg area\n");
ret= -EIO;
gotoout;
}
//地址内存空间映射
db->io_addr= ioremap(db->addr_res->start, iosize);
if(db->io_addr == NULL) {
dev_err(db->dev,"failed to ioremap address reg\n");
ret= -EINVAL;
gotoout;
}
//申请网卡数据所需的内存空间
iosize= resource_size(db->data_res);
db->data_req= request_mem_region(db->data_res->start, iosize,
pdev->name);
if(db->data_req == NULL) {
dev_err(db->dev,"cannot claim data reg area\n");
ret= -EIO;
gotoout;
}
//数据内存空间映射
db->io_data= ioremap(db->data_res->start, iosize);
if (db->io_data == NULL) {
dev_err(db->dev,"failed to ioremap data reg\n");
ret= -EINVAL;
gotoout;
}
/*fill in parameters for net-dev structure */
//设置网络设备结构体——ndev
ndev->base_addr= (unsigned long)db->io_addr;
ndev->irq = db->irq_res->start;
/*ensure at least we have a default set of IO routines */
//确保至少有一种IO路由方式,即先设置一种缺省的IO路由
dm9000_set_io(db,iosize);
/*check to see if anything is being over-ridden */
/*检查是否可以替代刚才设置的IO路由,由于我们在zhaocj2440_dm9k_pdata结构体内的flags元素定义了DM9000_PLATF_16BITONLY,所以缺省的IO路由方式会被替代,即执行dm9000_set_io(db, 2);*/
if(pdata != NULL) {
/*check to see if the driver wants to over-ride the
* default IO width */
if(pdata->flags & DM9000_PLATF_8BITONLY)
dm9000_set_io(db,1);
if(pdata->flags & DM9000_PLATF_16BITONLY)
dm9000_set_io(db,2);
if(pdata->flags & DM9000_PLATF_32BITONLY)
dm9000_set_io(db,4);
/*check to see if there are any IO routine
* over-rides */
if(pdata->inblk != NULL)
db->inblk= pdata->inblk;
if(pdata->outblk != NULL)
db->outblk= pdata->outblk;
if(pdata->dumpblk != NULL)
db->dumpblk= pdata->dumpblk;
db->flags= pdata->flags;
}
#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
db->flags|= DM9000_PLATF_SIMPLE_PHY;
#endif
//网卡复位
dm9000_reset(db);
/*try multiple times, DM9000 sometimes gets the read wrong */
//多次读取DM9000的ID,以确保能够正确识别DM9000
for (i = 0; i < 8; i++) {
id_val = ior(db, DM9000_VIDL);
id_val |= (u32)ior(db,DM9000_VIDH) << 8;
id_val |= (u32)ior(db, DM9000_PIDL)<< 16;
id_val |=(u32)ior(db, DM9000_PIDH) << 24;
//判断读取的值是否为0x90000A46
if(id_val == DM9000_ID)
break;
dev_err(db->dev,"read wrong id 0x%08x\n", id_val);
}
if(id_val != DM9000_ID) {
dev_err(db->dev,"wrong id: 0x%08x\n", id_val);
ret= -ENODEV;
gotoout;
}
/*Identify what type of DM9000 we are working on */
//读取CHIPR寄存器,获得type信息
id_val= ior(db, DM9000_CHIPR);
dev_dbg(db->dev,"dm9000 revision 0x%02x\n", id_val);
switch(id_val) {
caseCHIPR_DM9000A:
db->type= TYPE_DM9000A;
break;
caseCHIPR_DM9000B:
db->type= TYPE_DM9000B;
break;
default:
dev_dbg(db->dev,"ID %02x => defaulting to DM9000E\n", id_val);
db->type= TYPE_DM9000E;
}
/*dm9000a/b arecapable of hardware checksum offload */
if(db->type == TYPE_DM9000A|| db->type == TYPE_DM9000B) {
ndev->hw_features= NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
ndev->features|= ndev->hw_features;
}
/*from this point we assume that we have found a DM9000 */
/*driver system function */
//设置以太网卡设备,即把有关以太网设备的数据赋值给ndev
ether_setup(ndev);
ndev->netdev_ops = &dm9000_netdev_ops; //网卡设备操作集
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
ndev->ethtool_ops = &dm9000_ethtool_ops; //以太网卡操作集
db->msg_enable = NETIF_MSG_LINK;
db->mii.phy_id_mask = 0x1f;
db->mii.reg_num_mask= 0x1f;
db->mii.force_media = 0;
db->mii.full_duplex = 0;
db->mii.dev =ndev;
db->mii.mdio_read = dm9000_phy_read;
db->mii.mdio_write = dm9000_phy_write;
//通过不同方式读取网卡的MAC
mac_src= "eeprom";
/*try reading the node address from the attached EEPROM */
//从eeprom中读取MAC
for(i = 0; i < 6; i += 2)
dm9000_read_eeprom(db,i / 2, ndev->dev_addr+i);
/*如果从eeprom中读取MAC失败,则从平台设备中读取MAC,即提取出zhaocj2440_dm9k_pdata结构中的dev_addr数组元素*/
if(!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
mac_src= "platform data";
memcpy(ndev->dev_addr,pdata->dev_addr, 6);
}
/*如果到目前为止,还没有读取到正确的MAC,则从DM9000芯片的PAR寄存器内提取出MAC*/
if(!is_valid_ether_addr(ndev->dev_addr)) {
/*try reading from mac */
mac_src= "chip";
for (i = 0; i < 6; i++)
ndev->dev_addr[i] =ior(db, i+DM9000_PAR);
}
/*如果上述方法都不行,则MAC值最终是要通过ifconfig来确定。但由于现在还没有执行ifconfig命令,因此系统在启动初始化的过程中,要随机分配MAC。*/
if(!is_valid_ether_addr(ndev->dev_addr)) {
dev_warn(db->dev,"%s: Invalid ethernet MAC address. Please "
"set using ifconfig\n",ndev->name);
eth_hw_addr_random(ndev);
mac_src= "random";
}
//把ndev保存为平台设备pdev的私有数据,以后可以用platform_get_drvdata获取该数据
platform_set_drvdata(pdev,ndev);
//注册网络设备
ret= register_netdev(ndev);
//在系统启动过程中,打印网卡相关数据
if(ret == 0)
printk(KERN_INFO"%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
ndev->name,dm9000_type_to_char(db->type),
db->io_addr, db->io_data, ndev->irq,
ndev->dev_addr,mac_src);
return0;
out:
dev_err(db->dev,"not found (%d).\n", ret);
dm9000_release_board(pdev,db);
free_netdev(ndev);
returnret;
}
在dm9000_probe函数内定义了dm9000_netdev_ops:
static const struct net_device_opsdm9000_netdev_ops = {
.ndo_open = dm9000_open, //打开设备
.ndo_stop = dm9000_stop, //关闭设备
.ndo_start_xmit = dm9000_start_xmit, //开始发送数据
.ndo_tx_timeout = dm9000_timeout, //发送超时
.ndo_set_rx_mode = dm9000_hash_table, //设定多播列表
.ndo_do_ioctl = dm9000_ioctl, //io操作函数
.ndo_change_mtu = eth_change_mtu, //改变mtu
.ndo_set_features = dm9000_set_features,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = dm9000_poll_controller,
#endif
};
我们分析几个重要的回调函数。
当执行ifconfig命令时,会调用dm9000_open函数,以打开网络接口
static int
dm9000_open(struct net_device *dev)
{
board_info_t*db = netdev_priv(dev);
unsignedlong irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
if(netif_msg_ifup(db))
dev_dbg(db->dev,"enabling %s\n", dev->name);
/*If there is no IRQ type specified, default to something that
* may work, and tell the user that this is aproblem */
if(irqflags == IRQF_TRIGGER_NONE)
dev_warn(db->dev,"WARNING: no IRQ resource flags set.\n");
irqflags|= IRQF_SHARED;
/*GPIO0 on pre-activate PHY, Reg 1Fis not set by reset */
//将GPR寄存器的第0位清零,激活内部PHY
iow(db,DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */
mdelay(1);/* delay needs by DM9000B */
/*Initialize DM9000 board */
dm9000_reset(db); //复位DM9000
//初始化DM9000,主要就是配置DM9000的内部寄存器
dm9000_init_dm9000(dev);
//注册中断,中断函数为dm9000_interrupt,该中断负责收发数据
if(request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
return-EAGAIN;
/*Init driver variable */
db->dbug_cnt= 0;
//检测mii接口的状态
mii_check_media(&db->mii,netif_msg_link(db), 1);
//允许上层协议使用该设备,开启发送队列
netif_start_queue(dev);
/*在dm9000_probe函数中,使用INIT_DELAYED_WORK初始化了一个工作队列,现在就调用它,即执行dm9000_poll_work函数,用以检测并显示网络信息*/
dm9000_schedule_poll(db);
return 0;
}
关闭网卡设备函数dm9000_stop:
static int
dm9000_stop(struct net_device *ndev)
{
board_info_t*db = netdev_priv(ndev);
if(netif_msg_ifdown(db))
dev_dbg(db->dev,"shutting down %s\n", ndev->name);
//终止phy_poll队列中被延迟的任务
cancel_delayed_work_sync(&db->phy_poll);
//关闭发送队列
netif_stop_queue(ndev);
//载波丢失
netif_carrier_off(ndev);
/*free interrupt */
free_irq(ndev->irq,ndev); //释放中断
dm9000_shutdown(ndev); //关闭网卡
return0;
}
发送数据函数dm9000_start_xmit:
static int
dm9000_start_xmit(struct sk_buff *skb,struct net_device *dev)
{
unsignedlong flags;
board_info_t*db = netdev_priv(dev);
dm9000_dbg(db,3, "%s:\n", __func__);
if(db->tx_pkt_cnt > 1)
returnNETDEV_TX_BUSY;
//获得自旋锁
spin_lock_irqsave(&db->lock,flags);
/*Move data to DM9000 TX RAM */
//根据io操作数据宽度,来设置MWCMD,即填充数据完毕后指针加1还是加2
writeb(DM9000_MWCMD,db->io_addr);
//将数据从sk_buff中复制到DM9000内部发送RAM(TX_Buffer)中
(db->outblk)(db->io_data,skb->data, skb->len);
dev->stats.tx_bytes +=skb->len; //统计发送的字节数
db->tx_pkt_cnt++; //发送计数
/*TX control: First packet immediately send, second packet queue */
if(db->tx_pkt_cnt == 1) {
//计数值为1,立即发送
dm9000_send_packet(dev,skb->ip_summed, skb->len);
} else {
/* Secondpacket */
//如果是第2个数据包,加入队列以后,停止发送
db->queue_pkt_len= skb->len;
db->queue_ip_summed= skb->ip_summed;
netif_stop_queue(dev);
}
//解自旋锁
spin_unlock_irqrestore(&db->lock,flags);
/*free this SKB */
dev_kfree_skb(skb);
returnNETDEV_TX_OK;
}
dm9000_start_xmit函数只负责发送一个数据包,那么如何发送第二个数据包呢?网卡又如何接收数据呢?还记得在dm9000_open函数内,申请了一个中断,中断函数为dm9000_interrupt,该中断就是负责接收数据和发送多个数据包的任务:
static irqreturn_t dm9000_interrupt(intirq, void *dev_id)
{
struct net_device *dev = dev_id;
board_info_t *db = netdev_priv(dev);
int int_status;
unsigned long flags;
u8 reg_save;
dm9000_dbg(db, 3, "entering%s\n", __func__);
/* A realinterrupt coming */
/*holders of db->lock must always block IRQs */
//获取自旋锁
spin_lock_irqsave(&db->lock,flags);
/*Save previous register address */
//保存以前的寄存器地址
reg_save= readb(db->io_addr);
/*Disable all interrupts */
//禁止所有中断
iow(db,DM9000_IMR, IMR_PAR);
/*Got DM9000 interrupt status */
//得到中断状态,即是何种原因引起的中断
int_status= ior(db, DM9000_ISR); /* Got ISR */
iow(db,DM9000_ISR, int_status); /* ClearISR status */
if(netif_msg_intr(db))
dev_dbg(db->dev,"interrupt status %02x\n", int_status);
/*Received the coming packet */
//该中断为接收数据中断
if(int_status & ISR_PRS)
dm9000_rx(dev);
/*Trnasmit Interrupt check */
//该中断为发送数据结束中断
if(int_status & ISR_PTS)
dm9000_tx_done(dev,db);
if(db->type != TYPE_DM9000E) {
if(int_status & ISR_LNKCHNG) {
/*fire a link-change request */
schedule_delayed_work(&db->phy_poll,1);
}
}
/*Re-enable interrupt mask */
//开启中断
iow(db,DM9000_IMR, db->imr_all);
/*Restore previous register address */
writeb(reg_save,db->io_addr);
spin_unlock_irqrestore(&db->lock,flags);
returnIRQ_HANDLED;
}
从上面函数的分析可以看出,当是接收数据触发的中断时,会调用dm9000_rx函数来接收数据,然后调用netif_rx函数把数据传输到上层网络协议中;而当一个数据包发送完毕后也会触发该中断,并调用dm9000_tx_done函数。在该函数内,如果判断还有待发送的数据则调用执行dm9000_send_packet函数再次发送数据。例如我们要发送两个数据包,则首先调用dm9000_start_xmit函数发送第一个数据包,当发送完毕后触发中断dm9000_interrupt,并调用dm9000_tx_done函数发送第二个数据包,至此两个数据包都发送了出去。
更多推荐
所有评论(0)