LINUX网卡驱动分析――Intel(R) PRO/100 Network Driver
最近学习LINUX驱动开发,看的是《LINUX DEVICE DRIVER》这本书,差不多能看懂,不过说实在的,都是些理论上的东西,没有什么实践,感觉提升比较慢,所以想拿LINUX自带的E100网卡驱动来分析和学习一下,看看人家大师们怎么写驱动的。然后如果有时间再写一个关于我的开发板的S3C2410上的网卡(CS8900A)驱动。
注:以下分析的是基于2.6.14上带的e100.c驱动源代码。
网卡是一个网络设备,同时也是一个PCI设备。E100网卡驱动就是按照PCI规范来编写的,同时又设及到驱动程序的内存映射和DMA操作,所以是比较综合的一个驱动程序。
一、模块的初始化。
module_init(e100_init_module);
// 2.6
内核模块初始化注册
module_exit(e100_cleanup_module);
//
模块清除注册
接着step into - àe100_init_module,
static int __init e100_init_module(void)
{
//
检查打印级别是否大于1
if(((1 << debug) - 1) & NETIF_MSG_DRV) {
printk(KERN_INFO PFX "%s, %s/n", DRV_DESCRIPTION, DRV_VERSION);
printk(KERN_INFO PFX "%s/n", DRV_COPYRIGHT);
}
//
调用
PCI
的模块注册函数,因为网卡是一个
PCI
设备
return pci_module_init(&e100_driver);
}
static void __exit e100_cleanup_module(void)
{
//
清除PCI注册信息
pci_unregister_driver(&e100_driver);
}
接下来看一下,pci_module_init(&e100_driver);
e100_driver是一个struct pci_driver类型。在代码中,做如下初始化:
static struct pci_driver e100_driver = {
.name = DRV_NAME,
// DRIVER
名称
.id_table = e100_id_table,
//e100
驱动支持的
PCI
设备列表
.probe = e100_probe,
//PCI
探测函数指针
.remove = __devexit_p(e100_remove),
//
移除函数
#ifdef CONFIG_PM
.suspend = e100_suspend,
//
挂起操作
.resume = e100_resume,
//
恢复
#endif
.shutdown = e100_shutdown,
//
关闭,注意:
LINUX DEVICE DRIVER
这本书中没有这一项。
};
pci_module_init其实是pci_register_driver的宏定义,实际执行pci模块注册过程。PCI注册过程除了初始化pci_driver 内部struct device_driver结构以外,还执行一些与linux设备模型相关的操作,可以参考drivers/pci.c中的初始化代码;下面我们还是将主要精力放在分析网卡驱动代码上。
接下来看一下探测函数:e100_probe;为了方便还是将代码贴一下:
static int __devinit e100_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *netdev;
//
声明网络设备指针
struct nic *nic;
//
网卡信息结构指针
int err;
//
一看就知道了,分配空间嘛,然后根据打印级别控制打印
if(!(netdev = alloc_etherdev(sizeof(struct nic)))) {
if(((1 << debug) - 1) & NETIF_MSG_PROBE)
printk(KERN_ERR PFX "Etherdev alloc failed, abort./n");
return -ENOMEM;
}
//
网络设备的初始化,相关的函数注册。
netdev->open = e100_open;
//
打开
netdev->stop = e100_close;
//
关闭
netdev->hard_start_xmit = e100_xmit_frame;
//
开始传输
netdev->get_stats = e100_get_stats;
//
获取状态
//
设置多播列表
netdev->set_multicast_list = e100_set_multicast_list;
//
设置物理MAC地址
netdev->set_mac_address = e100_set_mac_address;
netdev->change_mtu = e100_change_mtu;
netdev->do_ioctl = e100_do_ioctl;
SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops);
netdev->tx_timeout = e100_tx_timeout;
netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
netdev->poll = e100_poll;
netdev->weight = E100_NAPI_WEIGHT;
#ifdef CONFIG_NET_POLL_CONTROLLER
netdev->poll_controller = e100_netpoll;
#endif
strcpy(netdev->name, pci_name(pdev));
//
初始化完网络设备,然后与网卡信息进行绑定
//
netdev_priv
是取一个指针,
pointer to private data
nic = netdev_priv(netdev);
nic->netdev = netdev;
nic->pdev = pdev;
nic->msg_enable = (1 << debug) - 1;
pci_set_drvdata(pdev, netdev);
//
完成之后,激活设备
if((err = pci_enable_device(pdev))) {
DPRINTK(PROBE, ERR, "Cannot enable PCI device, aborting./n");
goto err_out_free_dev;
}
//
取得和资源相关的标志
if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
DPRINTK(PROBE, ERR, "Cannot find proper PCI device "
"base address, aborting./n");
err = -ENODEV;
goto err_out_disable_pdev;
}
// 获取相关PCI资源,应该是配置寄存器映射的内存区
if((err = pci_request_regions(pdev, DRV_NAME))) {
DPRINTK(PROBE, ERR, "Cannot obtain PCI resources, aborting./n");
goto err_out_disable_pdev;
}
// 设置32位DMA位掩码,一方面也为了测试配置
if((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) {
DPRINTK(PROBE, ERR, "No usable DMA configuration, aborting./n");
goto err_out_free_res;
}
// 空操作
SET_MODULE_OWNER(netdev);
SET_NETDEV_DEV(netdev, &pdev->dev);
// io映射成虚拟地址,供内核使用
nic->csr = ioremap(pci_resource_start(pdev, 0), sizeof(struct csr));
if(!nic->csr) {
DPRINTK(PROBE, ERR, "Cannot map device registers, aborting./n");
err = -ENOMEM;
goto err_out_free_res;
}
if(ent->driver_data)
nic->flags |= ich;
else
nic->flags &= ~ich;
e100_get_defaults(nic);
/* locks must be initialized before calling hw_reset */
spin_lock_init(&nic->cb_lock);
spin_lock_init(&nic->cmd_lock);
/* Reset the device before pci_set_master() in case device is in some
* funky state and has an interrupt pending - hint: we don't have the
* interrupt handler registered yet. */
e100_hw_reset(nic);
pci_set_master(pdev);
init_timer(&nic->watchdog);
nic->watchdog.function = e100_watchdog;
nic->watchdog.data = (unsigned long)nic;
init_timer(&nic->blink_timer);
nic->blink_timer.function = e100_blink_led;
nic->blink_timer.data = (unsigned long)nic;
INIT_WORK(&nic->tx_timeout_task,
(void (*)(void *))e100_tx_timeout_task, netdev);
if((err = e100_alloc(nic))) {
DPRINTK(PROBE, ERR, "Cannot alloc driver memory, aborting./n");
goto err_out_iounmap;
}
if((err = e100_eeprom_load(nic)))
goto err_out_free;
e100_phy_init(nic);
memcpy(netdev->dev_a