linux网卡mdix,e1000驱动分析

写这篇应该达到:理解网卡工作原理,(思考一个问题: 高性能提高方法:小包和大包情况分开来论),其他不需要太多了解细节问题

linux 模块分析入口:module init 函数

【2.6.31】

1 设备发现过程

static int __init e1000_init_module(void)

{

int ret;

printk(KERN_INFO "%s: Intel(R) PRO/1000 Network Driver - %s\n",

e1000e_driver_name, e1000e_driver_version);

printk(KERN_INFO "%s: Copyright (c) 1999-2008 Intel Corporation.\n",

e1000e_driver_name);

// 由此可见,82574的侦测发现,是pci框架下发现并使用的。

ret = pci_register_driver(&e1000_driver);

pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, e1000e_driver_name,

PM_QOS_DEFAULT_VALUE);

return ret;

}

/* PCI Device API Driver */

static struct pci_driver e1000_driver = {

.name     = e1000e_driver_name,

/*猜测一下下面这个table, pci 通用驱动启动的时候会扫描pci设备,如果这个id表中的id对应,然后去调用probe函数,struct

pci_dev *pdev 参数应该是动态申请的内存,并通过读取configure space 获得一些通用的信息

*/

.id_table = e1000_pci_tbl,

// 一切始于此,e1000_probe

.probe    = e1000_probe,

.remove   = __devexit_p(e1000_remove),

#ifdef CONFIG_PM

/* Power Management Hooks */

.suspend  = e1000_suspend,

.resume   = e1000_resume,

#endif

.shutdown = e1000_shutdown,

.err_handler = &e1000_err_handler

};

2 分析下probe 函数

static int __devinit e1000_probe(struct pci_dev *pdev,

const struct pci_device_id *ent)

{

struct net_device *netdev;

struct e1000_adapter *adapter;

struct e1000_hw *hw;

const struct e1000_info *ei = e1000_info_tbl[ent->driver_data];

resource_size_t mmio_start, mmio_len;

resource_size_t flash_start, flash_len;

static int cards_found;

int i, err, pci_using_dac;

u16 eeprom_data = 0;

u16 eeprom_apme_mask = E1000_EEPROM_APME;

e1000e_disable_l1aspm(pdev);

err = pci_enable_device_mem(pdev);

if (err)

return err;

pci_using_dac = 0;

err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));

if (!err) {

err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));

if (!err)

pci_using_dac = 1;

} else {

err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));

if (err) {

err = pci_set_consistent_dma_mask(pdev,

DMA_BIT_MASK(32));

if (err) {

dev_err(&pdev->dev, "No usable DMA "

"configuration, aborting\n");

goto err_dma;

}

}

}

err = pci_request_selected_regions_exclusive(pdev,

pci_select_bars(pdev, IORESOURCE_MEM),

e1000e_driver_name);

if (err)

goto err_pci_reg;

/* AER (Advanced Error Reporting) hooks */

err = pci_enable_pcie_error_reporting(pdev);

if (err) {

dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed "

"0x%x\n", err);

/* non-fatal, continue */

}

pci_set_master(pdev);

/* PCI config space info */

err = pci_save_state(pdev);

if (err)

goto err_alloc_etherdev;

err = -ENOMEM;

//e1000_addapter在net_device结构体的后面,一块申请下来了,采用了内存对齐。

netdev = alloc_etherdev(sizeof(struct e1000_adapter));

if (!netdev)

goto err_alloc_etherdev;

//由此可以串起来从pci_dev -->net_device-->e1000_adapter

SET_NETDEV_DEV(netdev, &pdev->dev);

pci_set_drvdata(pdev, netdev);

adapter = netdev_priv(netdev);

hw = &adapter->hw;

//e1000_adapter也可以往回找到net_device和pci_dev

adapter->netdev = netdev;

adapter->pdev = pdev;

adapter->ei = ei;

adapter->pba = ei->pba;

adapter->flags = ei->flags;

adapter->flags2 = ei->flags2;

adapter->hw.adapter = adapter;

adapter->hw.mac.type = ei->mac;

adapter->max_hw_frame_size = ei->max_hw_frame_size;

adapter->msg_enable = (1 << NETIF_MSG_DRV | NETIF_MSG_PROBE) -

1;

// 0表示设备映射的内存的的bar

mmio_start = pci_resource_start(pdev, 0);

mmio_len = pci_resource_len(pdev, 0);

err = -EIO;

//ioremap是内核提供的用来映射外设寄存器到主存

的函数

adapter->hw.hw_addr = ioremap(mmio_start, mmio_len);

if (!adapter->hw.hw_addr)

goto err_ioremap;

//1表示设备映射的flash的地址

if ((adapter->flags & FLAG_HAS_FLASH) &&

(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {

flash_start = pci_resource_start(pdev, 1);

flash_len = pci_resource_len(pdev, 1);

adapter->hw.flash_address = ioremap(flash_start, flash_len);

if (!adapter->hw.flash_address)

goto err_flashmap;

}

/* construct the net_device struct */

/*现在这个函数是个核心操作函数*/

netdev->netdev_ops        = &e1000e_netdev_ops;

/*ethtool 提供的功能*/

e1000e_set_ethtool_ops(netdev);

netdev->watchdog_timeo        = 5 * HZ;

/*napi 功能开启*/

netif_napi_add(netdev, &adapter->napi, e1000_clean, 64);

strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);

netdev->mem_start = mmio_start;

netdev->mem_end = mmio_start + mmio_len;

adapter->bd_number = cards_found++;

e1000e_check_options(adapter);

/* setup adapter struct */

/*

接受对列的大小,帧大小,申请队列内存。 关闭中断

*/

err = e1000_sw_init(adapter);

if (err)

goto err_sw_init;

err = -EIO;

memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops));

memcpy(&hw->nvm.ops, ei->nvm_ops, sizeof(hw->nvm.ops));

memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops));

err = ei->get_variants(adapter);

if (err)

goto err_hw_init;

if ((adapter->flags & FLAG_IS_ICH) &&

(adapter->flags & FLAG_READ_ONLY_NVM))

e1000e_write_protect_nvm_ich8lan(&adapter->hw);

hw->mac.ops.get_bus_info(&adapter->hw);

adapter->hw.phy.autoneg_wait_to_complete = 0;

/* Copper options */

if (adapter->hw.phy.media_type == e1000_media_type_copper) {

adapter->hw.phy.mdix = AUTO_ALL_MODES;

adapter->hw.phy.disable_polarity_correction = 0;

adapter->hw.phy.ms_type = e1000_ms_hw_default;

}

if (e1000_check_reset_block(&adapter->hw))

e_info("PHY reset is blocked due to SOL/IDER session.\n");

netdev->features = NETIF_F_SG |

NETIF_F_HW_CSUM |

NETIF_F_HW_VLAN_TX |

NETIF_F_HW_VLAN_RX;

if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER)

netdev->features |= NETIF_F_HW_VLAN_FILTER;

netdev->features |= NETIF_F_TSO;

netdev->features |= NETIF_F_TSO6;

netdev->vlan_features |= NETIF_F_TSO;

netdev->vlan_features |= NETIF_F_TSO6;

netdev->vlan_features |= NETIF_F_HW_CSUM;

netdev->vlan_features |= NETIF_F_SG;

if (pci_using_dac)

netdev->features |= NETIF_F_HIGHDMA;

if (e1000e_enable_mng_pass_thru(&adapter->hw))

adapter->flags |= FLAG_MNG_PT_ENABLED;

/*

* before reading the NVM, reset the controller to

* put the device in a known good starting state

*/

adapter->hw.mac.ops.reset_hw(&adapter->hw);

/*

* systems with ASPM and others may see the checksum fail on the first

* attempt. Let's give it a few tries

*/

for (i = 0;; i++) {

if (e1000_validate_nvm_checksum(&adapter->hw) >= 0)

break;

if (i == 2) {

e_err("The NVM Checksum Is Not Valid\n");

err = -EIO;

goto err_eeprom;

}

}

e1000_eeprom_checks(adapter);

/* copy the MAC address out of the NVM */

if (e1000e_read_mac_addr(&adapter->hw))

e_err("NVM Read Error while reading MAC address\n");

memcpy(netdev->dev_addr, adapter->hw.mac.addr,

netdev->addr_len);

memcpy(netdev->perm_addr, adapter->hw.mac.addr,

netdev->addr_len);

if (!is_valid_ether_addr(netdev->perm_addr)) {

e_err("Invalid MAC Address: %pM\n", netdev->perm_addr);

err = -EIO;

goto err_eeprom;

}

/*watchdog定时器初始化*/

init_timer(&adapter->watchdog_timer);

adapter->watchdog_timer.function = &e1000_watchdog;

adapter->watchdog_timer.data = (unsigned long) adapter;

init_timer(&adapter->phy_info_timer);

adapter->phy_info_timer.function = &e1000_update_phy_info;

adapter->phy_info_timer.data = (unsigned long) adapter;

/*几个work_struct*/

INIT_WORK(&adapter->reset_task, e1000_reset_task);

INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);

INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround);

INIT_WORK(&adapter->update_phy_task, e1000e_update_phy_task);

/* Initialize link parameters. User can change them with ethtool */

adapter->hw.mac.autoneg = 1;

adapter->fc_autoneg = 1;

adapter->hw.fc.requested_mode = e1000_fc_default;

adapter->hw.fc.current_mode = e1000_fc_default;

adapter->hw.phy.autoneg_advertised = 0x2f;

/* ring size defaults */

adapter->rx_ring->count = 256;

adapter->tx_ring->count = 256;

/*

* Initial Wake on LAN setting - If APM wake is enabled in

* the EEPROM, enable the ACPI Magic Packet filter

*/

if (adapter->flags & FLAG_APME_IN_WUC) {

/* APME bit in EEPROM is mapped to WUC.APME */

eeprom_data = er32(WUC);

eeprom_apme_mask = E1000_WUC_APME;

if (eeprom_data & E1000_WUC_PHY_WAKE)

adapter->flags2 |= FLAG2_HAS_PHY_WAKEUP;

} else if (adapter->flags & FLAG_APME_IN_CTRL3) {

if (adapter->flags & FLAG_APME_CHECK_PORT_B &&

(adapter->hw.bus.func == 1))

e1000_read_nvm(&adapter->hw,

NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);

else

e1000_read_nvm(&adapter->hw,

NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);

}

/* fetch WoL from EEPROM */

if (eeprom_data & eeprom_apme_mask)

adapter->eeprom_wol |= E1000_WUFC_MAG;

/*

* now that we have the eeprom settings, apply the special cases

* where the eeprom may be wrong or the board simply won't support

* wake on lan on a particular port

*/

if (!(adapter->flags & FLAG_HAS_WOL))

adapter->eeprom_wol = 0;

/* initialize the wol settings based on the eeprom settings */

adapter->wol = adapter->eeprom_wol;

device_set_wakeup_enable(&adapter->pdev->dev,

adapter->wol);

/* save off EEPROM version number */

e1000_read_nvm(&adapter->hw, 5, 1, &adapter->eeprom_vers);

/* reset the hardware with the new settings */

e1000e_reset(adapter);

/*

* If the controller has AMT, do not set DRV_LOAD until the interface

* is up.  For all other cases, let the f/w know that the h/w is now

* under the control of the driver.

*/

if (!(adapter->flags & FLAG_HAS_AMT))

e1000_get_hw_control(adapter);

strcpy(netdev->name, "eth%d");

/* 初始化工作都做完了,可以注册net_device啦 */

err = register_netdev(netdev);

if (err)

goto err_register;

/* carrier off reporting is important to ethtool even BEFORE open */

netif_carrier_off(netdev);

/*打印一些信息出来*/

e1000_print_device_info(adapter);

return 0;

err_register:

if (!(adapter->flags & FLAG_HAS_AMT))

e1000_release_hw_control(adapter);

err_eeprom:

if (!e1000_check_reset_block(&adapter->hw))

e1000_phy_hw_reset(&adapter->hw);

err_hw_init:

kfree(adapter->tx_ring);

kfree(adapter->rx_ring);

err_sw_init:

if (adapter->hw.flash_address)

iounmap(adapter->hw.flash_address);

e1000e_reset_interrupt_capability(adapter);

err_flashmap:

iounmap(adapter->hw.hw_addr);

err_ioremap:

free_netdev(netdev);

err_alloc_etherdev:

pci_release_selected_regions(pdev,

pci_select_bars(pdev, IORESOURCE_MEM));

err_pci_reg:

err_dma:

pci_disable_device(pdev);

return err;

}

下面是e1000e正式工作的代码

/**

* e1000_open - Called when a network interface is made active

* @netdev: network interface device structure

*

* Returns 0 on success, negative value on failure

*

* The open entry point is called when a network interface is made

* active by the system (IFF_UP).  At this point all resources needed

* for transmit and receive operations are allocated, the interrupt

* handler is registered with the OS, the watchdog timer is started,

* and the stack is notified that the interface is ready.

**/

static int e1000_open(struct net_device *netdev)

{

struct e1000_adapter *adapter = netdev_priv(netdev);

struct e1000_hw *hw = &adapter->hw;

int err;

/* disallow open during test */

if (test_bit(__E1000_TESTING, &adapter->state))

return -EBUSY;

netif_carrier_off(netdev);

/* allocate transmit descriptors */

err = e1000e_setup_tx_resources(adapter);

if (err)

goto err_setup_tx;

/* allocate receive descriptors */

err = e1000e_setup_rx_resources(adapter);

if (err)

goto err_setup_rx;

e1000e_power_up_phy(adapter);

adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;

if ((adapter->hw.mng_cookie.status &

E1000_MNG_DHCP_COOKIE_STATUS_VLAN))

e1000_update_mng_vlan(adapter);

/*

* If AMT is enabled, let the firmware know that the network

* interface is now open

*/

if (adapter->flags & FLAG_HAS_AMT)

e1000_get_hw_control(adapter);

/*

* before we allocate an interrupt, we must be ready to handle it.

* Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt

* as soon as we call pci_request_irq, so we have to setup our

* clean_rx handler before we do so.

*/

e1000_configure(adapter);

err = e1000_request_irq(adapter);

if (err)

goto err_req_irq;

/*

* Work around PCIe errata with MSI interrupts causing some chipsets to

* ignore e1000e MSI messages, which means we need to test our MSI

* interrupt now

*/

if (adapter->int_mode != E1000E_INT_MODE_LEGACY) {

err = e1000_test_msi(adapter);

if (err) {

e_err("Interrupt allocation failed\n");

goto err_req_irq;

}

}

/* From here on the code is the same as e1000e_up() */

clear_bit(__E1000_DOWN, &adapter->state);

napi_enable(&adapter->napi);

e1000_irq_enable(adapter);

netif_start_queue(netdev);

/* fire a link status change interrupt to start the watchdog */

ew32(ICS, E1000_ICS_LSC);

return 0;

err_req_irq:

e1000_release_hw_control(adapter);

e1000_power_down_phy(adapter);

e1000e_free_rx_resources(adapter);

err_setup_rx:

e1000e_free_tx_resources(adapter);

err_setup_tx:

e1000e_reset(adapter);

return err;

}

阅读(626) | 评论(0) | 转发(0) |

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值