Chinaunix首页 | 论坛 | 博客
  • 博客访问: 573067
  • 博文数量: 117
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 359
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-13 21:58
个人简介

爱上香烟

文章分类

全部博文(117)

文章存档

2018年(3)

2017年(8)

2016年(65)

2015年(41)

我的朋友

分类: LINUX

2016-01-26 16:00:29

原文地址:e1000驱动分析 作者:tuibo

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

阅读(1505) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~