赞
踩
主要分析一下驱动的主要框架,必要地方细致分析下
文件位置:
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函数的一些主要调用
主要执行了以下操作
接下来就进入到stmmac_dvr_probe函数,在此函数中主要完成net_device结构体的分配,和填充,填充中最重要的是设置了net_device结构体中的netdev_ops,最后使用register_netdev(ndev)注册到内核
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; }
以下是对上述代码用到的数据结构的一个整理,以便于把握整体脉络
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。