LINUX网卡驱动分析――Intel(R) PRO/100 Network Driver

 
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
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值