⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 netdevice.c

📁 net device driver driver developer can enjoy it
💻 C
📖 第 1 页 / 共 3 页
字号:

 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_addr, nic->eeprom, ETH_ALEN);
    if(!is_valid_ether_addr(netdev->dev_addr)) {
        DPRINTK(PROBE, ERR, "Invalid MAC address from "
           "EEPROM, aborting.\n");
       err = -EAGAIN;
       goto err_out_free;
    }
 
    /* Wol magic packet can be enabled from eeprom */
    if((nic->mac >= mac_82558_D101_A4) &&
       (nic->eeprom[eeprom_id] & eeprom_id_wol))
        nic->flags |= wol_magic;
 
    /* ack any pending wake events, disable PME */
    pci_enable_wake(pdev, 0, 0);
 
    strcpy(netdev->name, "eth%d");
    if((err = register_netdev(netdev))) {
        DPRINTK(PROBE, ERR, "Cannot register net device, aborting.\n");
       goto err_out_free;
    }
 
    DPRINTK(PROBE, INFO, "addr 0x%lx, irq %d, "
       "MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n",
        pci_resource_start(pdev, 0), pdev->irq,
        netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
        netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
 
    return 0;
 
err_out_free:
    e100_free(nic);
err_out_iounmap:
    iounmap(nic->csr);
err_out_free_res:
    pci_release_regions(pdev);
err_out_disable_pdev:
    pci_disable_device(pdev);
err_out_free_dev:
    pci_set_drvdata(pdev, NULL);
    free_netdev(netdev);
    return err;
以上就是网卡探测的过程。
//把PCI总线读一下,强迫写完成。
static inline void e100_write_flush(struct nic *nic)
{
    /* Flush previous PCI writes through intermediate bridges
     * by doing a benign read */
    (void)readb(&nic->csr->scb.status);
}
 
static inline void e100_enable_irq(struct nic *nic)
{
    unsigned long flags;
  // 自旋锁之前,禁止中断
    spin_lock_irqsave(&nic->cmd_lock, flags);
    writeb(irq_mask_none, &nic->csr->scb.cmd_hi);
    spin_unlock_irqrestore(&nic->cmd_lock, flags);
  // 刷新
    e100_write_flush(nic);
}
 
static inline void e100_disable_irq(struct nic *nic)
{
    unsigned long flags;
 
    spin_lock_irqsave(&nic->cmd_lock, flags);
    writeb(irq_mask_all, &nic->csr->scb.cmd_hi);
    spin_unlock_irqrestore(&nic->cmd_lock, flags);
    e100_write_flush(nic);
}
 
static void e100_hw_reset(struct nic *nic)
{
    /* Put CU and RU into idle with a selective reset to get
     * device off of PCI bus */
    writel(selective_reset, &nic->csr->port);
    e100_write_flush(nic); udelay(20);
 
    /* Now fully reset device */
    writel(software_reset, &nic->csr->port);
    e100_write_flush(nic); udelay(20);
 
    /* Mask off our interrupt line - it's unmasked after reset */
 //关中断
    e100_disable_irq(nic);
}
 
//硬件初始化
static int e100_hw_init(struct nic *nic)
{
    int err;
 
    e100_hw_reset(nic);
 
    DPRINTK(HW, ERR, "e100_hw_init\n");
    if(!in_interrupt() && (err = e100_self_test(nic)))
        return err;
 
    if((err = e100_phy_init(nic)))
        return err;
    if((err = e100_exec_cmd(nic, cuc_load_base, 0)))
        return err;
    if((err = e100_exec_cmd(nic, ruc_load_base, 0)))
        return err;
    if((err = e100_exec_cb(nic, NULL, e100_load_ucode)))
        return err;
    if((err = e100_exec_cb(nic, NULL, e100_configure)))
        return err;
    if((err = e100_exec_cb(nic, NULL, e100_setup_iaaddr)))
        return err;
    if((err = e100_exec_cmd(nic, cuc_dump_addr,
        nic->dma_addr + offsetof(struct mem, stats))))
        return err;
    if((err = e100_exec_cmd(nic, cuc_dump_reset, 0)))
        return err;
 
    e100_disable_irq(nic);
 
    return 0;
}
//多播
static void e100_multi(struct nic *nic, struct cb *cb, struct sk_buff *skb)
{
    struct net_device *netdev = nic->netdev;
    struct dev_mc_list *list = netdev->mc_list;
    u16 i, count = min(netdev->mc_count, E100_MAX_MULTICAST_ADDRS);
 
    cb->command = cpu_to_le16(cb_multi);
    cb->u.multi.count = cpu_to_le16(count * ETH_ALEN);
   for(i = 0; list && i < count; i++, list = list->next)
        memcpy(&cb->u.multi.addr[i*ETH_ALEN], &list->dmi_addr,
           ETH_ALEN);
}
 
static void e100_set_multicast_list(struct net_device *netdev)
{
    struct nic *nic = netdev_priv(netdev);
 
    DPRINTK(HW, DEBUG, "mc_count=%d, flags=0x%04X\n",
        netdev->mc_count, netdev->flags);
 
    if(netdev->flags & IFF_PROMISC)
        nic->flags |= promiscuous;
    else
        nic->flags &= ~promiscuous;
 
    if(netdev->flags & IFF_ALLMULTI ||
        netdev->mc_count > E100_MAX_MULTICAST_ADDRS)
        nic->flags |= multicast_all;
    else
        nic->flags &= ~multicast_all;
 
    e100_exec_cb(nic, NULL, e100_configure);
    e100_exec_cb(nic, NULL, e100_multi);
}
 
static void e100_update_stats(struct nic *nic)
{
    struct net_device_stats *ns = &nic->net_stats;
    struct stats *s = &nic->mem->stats;
    u32 *complete = (nic->mac < mac_82558_D101_A4) ? &s->fc_xmt_pause :
        (nic->mac < mac_82559_D101M) ? (u32 *)&s->xmt_tco_frames :
        &s->complete;
 
    /* Device's stats reporting may take several microseconds to
     * complete, so where always waiting for results of the
     * previous command. */
 
    if(*complete == le32_to_cpu(cuc_dump_reset_complete)) {
        *complete = 0;
        nic->tx_frames = le32_to_cpu(s->tx_good_frames);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -