Linux stmac网卡代码分析----probe
probe主要分析一下驱动的主要框架,必要地方细致分析下drivers/net/ethernet/stmicro/stmmac/stmmac_pci.cmodule_pci_driver(stmmac_pci_driver);stmmac_pci_driver结构体如下,里面包含了id_table、probe、remove。id_table里包含了支持设备的vender id、device id,
·
probe
主要分析一下驱动的主要框架,必要地方细致分析下
文件位置:
- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
从文件的最后看起:
module_pci_driver(stmmac_pci_driver);
stmmac_pci_driver结构体如下,里面包含了id_table、probe、remove。id_table里包含了支持设备的vender id、device id,当扫描到支持的pcie设备时(把venderid 和device id 和table里的id对比)就调用probe函数
static struct pci_driver stmmac_pci_driver = {
.name = STMMAC_RESOURCE_NAME,
.id_table = stmmac_id_table,
.probe = stmmac_pci_probe,
.remove = stmmac_pci_remove,
.driver = {
.pm = &stmmac_pm_ops,
},
};
以下是stmmac_pci_probe函数的一些主要调用
主要执行了以下操作
- 为plat_stmmacenet_data分配了空间
- 填充plat_stmmacenet_data
- 使能pci
- 填充plat_stmmacenet_data中一些网卡参数信息
- 获取pci的base address
- 调用stmmac_dvr_probe
接下来就进入到stmmac_dvr_probe函数,在此函数中主要完成net_device结构体的分配,和填充,填充中最重要的是设置了net_device结构体中的netdev_ops,最后使用register_netdev(ndev)注册到内核
stmmac_dvr_probe
int stmmac_dvr_probe(struct device *device,
struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *res)
{
struct net_device *ndev = NULL;
struct stmmac_priv *priv;
u32 queue, maxq;
int ret = 0;
ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv),
MTL_MAX_TX_QUEUES,
MTL_MAX_RX_QUEUES);
SET_NETDEV_DEV(ndev, device);
/*对priv的填充*/
priv = netdev_priv(ndev);
priv->device = device;
priv->dev = ndev;
//对ethtool的支持
stmmac_set_ethtool_ops(ndev);
priv->pause = pause;
priv->plat = plat_dat;
priv->ioaddr = res->addr;
priv->dev->base_addr = (unsigned long)res->addr;
priv->dev->irq = res->irq;
priv->wol_irq = res->wol_irq;
priv->lpi_irq = res->lpi_irq;
/***************/
if (res->mac)
memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN);
dev_set_drvdata(device, priv->dev);
/* Verify driver arguments */
stmmac_verify_args();
/* Allocate workqueue */
priv->wq = create_singlethread_workqueue("stmmac_wq");
if (!priv->wq) {
dev_err(priv->device, "failed to create workqueue\n");
ret = -ENOMEM;
goto error_wq;
}
INIT_WORK(&priv->service_task, stmmac_service_task);
/* Override with kernel parameters if supplied XXX CRS XXX
* this needs to have multiple instances
*/
if ((phyaddr >= 0) && (phyaddr <= 31))
priv->plat->phy_addr = phyaddr;
/* Init MAC and get the capabilities */
ret = stmmac_hw_init(priv);
if (ret)
goto error_hw_init;
/* Configure real RX and TX queues */
netif_set_real_num_rx_queues(ndev, priv->plat->rx_queues_to_use);
netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use);
//设置网卡的ops
ndev->netdev_ops = &stmmac_netdev_ops;
ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_RXCSUM;
ret = stmmac_tc_init(priv, priv);
if (!ret) {
ndev->hw_features |= NETIF_F_HW_TC;
}
if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) {
ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
priv->tso = true;
dev_info(priv->device, "TSO feature enabled\n");
}
ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
#ifdef STMMAC_VLAN_TAG_USED
/* Both mac100 and gmac support receive VLAN tag detection */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX;
#endif
priv->msg_enable = netif_msg_init(debug, default_msg_level);
/* MTU range: 46 - hw-specific max */
ndev->min_mtu = ETH_ZLEN - ETH_HLEN;
if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
ndev->max_mtu = JUMBO_LEN;
else if (priv->plat->has_xgmac)
ndev->max_mtu = XGMAC_JUMBO_LEN;
else
ndev->max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
/* Will not overwrite ndev->max_mtu if plat->maxmtu > ndev->max_mtu
* as well as plat->maxmtu < ndev->min_mtu which is a invalid range.
*/
if ((priv->plat->maxmtu < ndev->max_mtu) &&
(priv->plat->maxmtu >= ndev->min_mtu))
ndev->max_mtu = priv->plat->maxmtu;
else if (priv->plat->maxmtu < ndev->min_mtu)
dev_warn(priv->device,
"%s: warning: maxmtu having invalid value (%d)\n",
__func__, priv->plat->maxmtu);
if (flow_ctrl)
priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */
/* Setup channels NAPI */
maxq = max(priv->plat->rx_queues_to_use, priv->plat->tx_queues_to_use);
for (queue = 0; queue < maxq; queue++) {
struct stmmac_channel *ch = &priv->channel[queue];
ch->priv_data = priv;
ch->index = queue;
if (queue < priv->plat->rx_queues_to_use)
ch->has_rx = true;
if (queue < priv->plat->tx_queues_to_use)
ch->has_tx = true;
netif_napi_add(ndev, &ch->napi, stmmac_napi_poll,
NAPI_POLL_WEIGHT);
}
mutex_init(&priv->lock);
/*如果设置了plat->clk_csr那么CSR clock在运行中将会是固定不变的,
如果没有设置,那么click会根据csr实际的时钟输入动态的设置*/
if (!priv->plat->clk_csr)
stmmac_clk_csr_set(priv);
else
priv->clk_csr = priv->plat->clk_csr;
//检查是否支持Physical Coding Sublayer
stmmac_check_pcs_mode(priv);
if (priv->hw->pcs != STMMAC_PCS_RGMII &&
priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI) {
/* MDIO bus Registration */
ret = stmmac_mdio_register(ndev);
if (ret < 0) {
dev_err(priv->device,
"%s: MDIO bus (id: %d) registration failed",
__func__, priv->plat->bus_id);
goto error_mdio_register;
}
}
ret = register_netdev(ndev);
return ret;
error_netdev_register:
if (priv->hw->pcs != STMMAC_PCS_RGMII &&
priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev);
error_mdio_register:
for (queue = 0; queue < maxq; queue++) {
struct stmmac_channel *ch = &priv->channel[queue];
netif_napi_del(&ch->napi);
}
error_hw_init:
destroy_workqueue(priv->wq);
error_wq:
free_netdev(ndev);
return ret;
}
EXPORT_SYMBOL_GPL(stmmac_dvr_probe);
下面看看stmmac_hw_init(priv),这里有硬件相关的设置,特别是设置了操作硬件的ops,函数中主要调用了stmmac_hwif_init函数。
int stmmac_hwif_init(struct stmmac_priv *priv)
{
bool needs_xgmac = priv->plat->has_xgmac;
bool needs_gmac4 = priv->plat->has_gmac4;
bool needs_gmac = priv->plat->has_gmac;
const struct stmmac_hwif_entry *entry;
struct mac_device_info *mac;
bool needs_setup = true;
int i, ret;
u32 id;
if (needs_gmac) {
id = stmmac_get_id(priv, GMAC_VERSION);
} else if (needs_gmac4 || needs_xgmac) {
id = stmmac_get_id(priv, GMAC4_VERSION);
} else {
id = 0;
}
/* Save ID for later use */
priv->synopsys_id = id;
/* Lets assume some safe values first */
priv->ptpaddr = priv->ioaddr +
(needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET);
priv->mmcaddr = priv->ioaddr +
(needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET);
/* Check for HW specific setup first */
if (priv->plat->setup) {
mac = priv->plat->setup(priv);
needs_setup = false;
} else {
mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
}
if (!mac)
return -ENOMEM;
/* 为网卡设置硬件操作相关的回调函数*/
for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) {
/*在stmmac_hw数组中选取一个entry*/
entry = &stmmac_hw[i];
/*进行筛选*/
if (needs_gmac ^ entry->gmac)
continue;
if (needs_gmac4 ^ entry->gmac4)
continue;
if (needs_xgmac ^ entry->xgmac)
continue;
/* Use synopsys_id var because some setups can override this */
if (priv->synopsys_id < entry->min_id)
continue;
/* 到这里表示已经选到合适的entry,将它保存到mac结构体 */
mac->desc = mac->desc ? : entry->desc;
mac->dma = mac->dma ? : entry->dma;
mac->mac = mac->mac ? : entry->mac;
mac->ptp = mac->ptp ? : entry->hwtimestamp;
mac->mode = mac->mode ? : entry->mode;
mac->tc = mac->tc ? : entry->tc;
/*把mac挂接到priv->hw*/
priv->hw = mac;
priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
/* Save quirks, if needed for posterior use */
priv->hwif_quirks = entry->quirks;
return 0;
}
dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n",
id, needs_gmac, needs_gmac4);
return -EINVAL;
}
以下是对上述代码用到的数据结构的一个整理,以便于把握整体脉络
更多推荐
已为社区贡献1条内容
所有评论(0)