1、概述
本文以NXP网卡驱动为例,使用imx6硬件,分析网卡驱动整个框架。主要分析了网卡驱动probe中完成的初始化操作,以及网卡发包与收包的流程。网卡驱动,尤其要理解描述符以及网卡操作中与寄存器的对应关系。主要包括:
- 网卡驱动probe分析
- 设备树解析
- 分析网卡驱动发送过程
- 分析网卡驱动接受过程
- napi简析
- 寄存器简析
2、imx6u硬件、内核代码、相关参考资料
使用fec1及其MDIO
PHY为LAN7820A
linux系统版本:4.1.15
NPX网卡驱动:
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fec.h
设备树文件:
arch/arm/boot/dts/myb-y6ull-14x14.dts
PHY MDIO驱动:
drivers/net/phy/mdio_bus.c
PHY LAN8720A驱动
drivers/net/phy/smsc.c
LAN8720 phy手册下载
https://download.csdn.net/download/fengyuwuzu0519/16016232
3、网卡驱动probe分析
platform驱动注册:
static struct platform_driver fec_driver = {
.driver = {
.name = DRIVER_NAME,
.pm = &fec_pm_ops,
.of_match_table = fec_dt_ids,
},
.id_table = fec_devtype,
.probe = fec_probe,
.remove = fec_drv_remove,
};
module_platform_driver(fec_driver);
static const struct of_device_id fec_dt_ids[] = {
{ .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
{ .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
{ .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
{ .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], },
{ .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], },
{ .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fec_dt_ids);
fep->quirks = pdev->id_entry->driver_data;
static struct platform_device_id fec_devtype[] = {
{
.name = "imx6ul-fec",
.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
FEC_QUIRK_HAS_VLAN,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, fec_devtype);
设备树配置:
fec1: ethernet@02188000 {
compatible = "fsl,imx6ul-fec", "fsl,imx6q-fec";
reg = <0x02188000 0x4000>;
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ENET>,
<&clks IMX6UL_CLK_ENET_AHB>,
<&clks IMX6UL_CLK_ENET_PTP>,
<&clks IMX6UL_CLK_ENET_REF>,
<&clks IMX6UL_CLK_ENET_REF>;
clock-names = "ipg", "ahb", "ptp",
"enet_clk_ref", "enet_out";
stop-mode = <&gpr 0x10 3>;
fsl,num-tx-queues=<1>;
fsl,num-rx-queues=<1>;
fsl,magic-packet;
fsl,wakeup_irq = <0>;
status = "disabled";
};
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
phy-reset-duration = <26>;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
smsc,disable-energy-detect;
reg = <0>;
};
};
};
设备树中的资源
- 时钟
- 收发队列数量
- 复位引脚
- 接口类型
- rmii引脚初始化
- mdio信息
- 寄存器基址 IORESOURCE_MEM资源
- of_device_id 中包含FEC配置宏
platform驱动probe:
static int
fec_probe(struct platform_device *pdev)
{
struct fec_enet_private *fep;
struct fec_platform_data *pdata;
struct net_device *ndev;
int i, irq, ret = 0;
struct resource *r;
const struct of_device_id *of_id;
static int dev_id;
struct device_node *np = pdev->dev.of_node, *phy_node;
int num_tx_qs;
int num_rx_qs;
//解析设备树,获取队列长度
fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
/* Init network device */
//分配net_device
ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private),
num_tx_qs, num_rx_qs);
if (!ndev)
return -ENOMEM;
/* 将网络设备的基类dev父设备指向了平台设备的设备基类dev */
//关联net_device与platform device
SET_NETDEV_DEV(ndev, &pdev->dev);
/* setup board info structure 私有数据段*/
//私有数据赋予struct fec_enet_private *fep;
fep = netdev_priv(ndev);
//匹配platform_driver fec_driver
of_id = of_match_device(fec_dt_ids, &pdev->dev);
if (of_id)
pdev->id_entry = of_id->data;
//获取fec_device数据中的网卡参数
fep->quirks = pdev->id_entry->driver_data;
//填充私有数据中关于网卡和队列数量
fep->netdev = ndev;
fep->num_rx_queues = num_rx_qs;
fep->num_tx_queues = num_tx_qs;
#if !defined(CONFIG_M5272)
/* default enable pause frame auto negotiation */
if (fep->quirks & FEC_QUIRK_HAS_GBIT)
fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
#endif
/* Select default pin state */
pinctrl_pm_select_default_state(&pdev->dev);
/* 获取platform device中的资源信息
DBUG at FUNC: fec_probe +3504 r->start=2188000 r->end=218bfff
r->name=/soc/aips-bus@02100000/ethernet@02188000
获取设备树中寄存器地址并映射到虚拟地址空间,为寄存器操作做准备
*/
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* 映射到虚拟地址 */
fep->hwp = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(fep->hwp)) {
ret = PTR_ERR(fep->hwp);
goto failed_ioremap;
}
//私有数据中填充平台设备信息
fep->pdev = pdev;
fep->dev_id = dev_id++;
//设置pdev的数据为网卡信息
platform_set_drvdata(pdev, ndev);
fec_enet_of_parse_stop_mode(pdev);
if (of_get_property(np, "fsl,magic-packet", NULL))
fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
phy_node = of_parse_phandle(np, "phy-handle", 0);
if (!phy_node && of_phy_is_fixed_link(np)) {
ret = of_phy_register_fixed_link(np);
if (ret < 0) {
dev_err(&pdev->dev,
"broken fixed-link specification\n");
goto failed_phy;
}
phy_node = of_node_get(np);
}
fep->phy_node = phy_node;
/* 从设备树获取接口类型: rmii */
ret = of_get_phy_mode(pdev->dev.of_node);
if (ret < 0) {
pdata = dev_get_platdata(&pdev->dev);
if (pdata)
fep->phy_interface = pdata->phy;
else
fep->phy_interface = PHY_INTERFACE_MODE_MII;
} else {
fep->phy_interface = ret;
}
//时钟相关
fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(fep->clk_ipg)) {
ret = PTR_ERR(fep->clk_ipg);
goto failed_clk;
}
fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(fep->clk_ahb)) {
ret = PTR_ERR(fep->clk_ahb);
goto failed_clk;
}
fep->itr_clk_rate = clk_get_rate(fep->clk_ahb);
/* enet_out is optional, depends on board */
fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out");
if (IS_ERR(fep->clk_enet_out))
fep->clk_enet_out = NULL;
fep->ptp_clk_on = false;
mutex_init(&fep->ptp_clk_mutex);
/* clk_ref is optional, depends on board */
fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref");
if (IS_ERR(fep->clk_ref))
fep->clk_ref = NULL;
fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX;
fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
if (IS_ERR(fep->clk_ptp)) {
fep->clk_ptp = NULL;
fep->bufdesc_ex = false;
}
pm_runtime_enable(&pdev->dev);
ret = fec_enet_clk_enable(ndev, true);
if (ret)
goto failed_clk;
/* 电源管理相关 */
fep->reg_phy = devm_regulator_get(&pdev->dev, "phy");
if (!IS_ERR(fep->reg_phy)) {
ret = regulator_enable(fep->reg_phy);
if (ret) {
dev_err(&pdev->dev,
"Failed to enable phy regulator: %d\n", ret);
goto failed_regulator;
}
} else {
fep->reg_phy = NULL;
}
/* 复位PHY */
fec_reset_phy(pdev);
reset_phy(fep);
if (fep->bufdesc_ex)
fec_ptp_init(pdev);
/* 网卡初始化:收发队列、操作函数等 */
ret = fec_enet_init(ndev);
if (ret)
goto failed_init;
//申请中断-发送完成中断,mii中断等处理函数
for (i = 0; i < FEC_IRQ_NUM; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0) {
if (i)
break;
ret = irq;
goto failed_irq;
}
ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt,
0, pdev->name, ndev);
if (ret)
goto failed_irq;
fep->irq[i] = irq;
}
ret = of_property_read_u32(np, "fsl,wakeup_irq", &irq);
if (!ret && irq < FEC_IRQ_NUM)
fep->wake_irq = fep->irq[irq];
else
fep->wake_irq = fep->irq[0];
init_completion(&fep->mdio_done);
/* mii mdio初始化 */
ret = fec_enet_mii_init(pdev);
if (ret)
goto failed_mii_init;
/* Carrier starts down, phylib will bring it up */
netif_carrier_off(ndev);
fec_enet_clk_enable(ndev, false);
pinctrl_pm_select_sleep_state(&pdev->dev);
/* 注册网卡设备 */
ret = register_netdev(ndev);
if (ret)
goto failed_register;
device_init_wakeup(&ndev->dev, fep->wol_flag &
FEC_WOL_HAS_MAGIC_PACKET);
if (fep->bufdesc_ex && fep->ptp_clock)
netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
fep->rx_copybreak = COPYBREAK_DEFAULT;
INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
return 0;
failed_register:
fec_enet_mii_remove(fep);
failed_mii_init:
failed_irq:
failed_init:
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
failed_regulator:
fec_enet_clk_enable(ndev, false);
failed_clk:
failed_phy:
of_node_put(phy_node);
failed_ioremap:
free_netdev(ndev);
return ret;
}
static int fec_enet_init(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct fec_enet_priv_tx_q *txq;
struct fec_enet_priv_rx_q *rxq;
struct bufdesc *cbd_base;
dma_addr_t bd_dma;
int bd_size;
unsigned int i;
//对齐长度
#if defined(CONFIG_ARM)
fep->rx_align = 0xf;
fep->tx_align = 0xf;
#else
fep->rx_align = 0x3;
fep->tx_align = 0x3;
#endif
//分配队列,初始化ring长度
fec_enet_alloc_queue(ndev);
//单个描述符的大小-寄存器决定
if (fep->bufdesc_ex)
fep->bufdesc_size = sizeof(struct bufdesc_ex);
else
fep->bufdesc_size = sizeof(struct bufdesc);
/* buffer descriptor 的大小是接收和发送缓冲总大小 */
//计算总描述符空间大小
bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) *
fep->bufdesc_size;
/* Allocate memory for buffer descriptors. */
//分配描述符空间 cbd_base为虚拟地址 bd_dma为物理地址空间
cbd_base = dma_alloc_coherent(NULL, bd_size, &bd_dma,
GFP_KERNEL);
if (!cbd_base) {
return -ENOMEM;
}
//初始化描述符空间
memset(cbd_base, 0, bd_size);
//获取mac地址-设置mac地址-写入mac寄存器
/* Get the Ethernet address */
fec_get_mac(ndev);
/* make sure MAC we just acquired is programmed into the hw */
fec_set_mac_address(ndev, NULL);
/* Set receive and transmit descriptor base. */
for (i = 0; i < fep->num_rx_queues; i++) {
rxq = fep->rx_queue[i];
rxq->index = i;
/* 虚拟地址 */
rxq->rx_bd_base = (struct bufdesc *)cbd_base;
/* 物理地址 */
rxq->bd_dma = bd_dma;
if (fep->bufdesc_ex) {
bd_dma += sizeof(struct bufdesc_ex) * rxq->rx_ring_size;
cbd_base = (struct bufdesc *)
(((struct bufdesc_ex *)cbd_base) + rxq->rx_ring_size);
} else {
bd_dma += sizeof(struct bufdesc) * rxq->rx_ring_size;
cbd_base += rxq->rx_ring_size;
}
}
for (i = 0; i < fep->num_tx_queues; i++) {
txq = fep->tx_queue[i];
txq->index = i;
txq->tx_bd_base = (struct bufdesc *)cbd_base;
txq->bd_dma = bd_dma;
if (fep->bufdesc_ex) {
bd_dma += sizeof(struct bufdesc_ex) * txq->tx_ring_size;
cbd_base = (struct bufdesc *)
(((struct bufdesc_ex *)cbd_base) + txq->tx_ring_size);
} else {
bd_dma += sizeof(struct bufdesc) * txq->tx_ring_size;
cbd_base += txq->tx_ring_size;
}
}
/* The FEC Ethernet specific entries in the device structure */
//初始化网卡操作函数
ndev->watchdog_timeo = TX_TIMEOUT;
ndev->netdev_ops = &fec_netdev_ops;
//初始化ethtool_ops
ndev->ethtool_ops = &fec_enet_ethtool_ops;
writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
if (fep->quirks & FEC_QUIRK_HAS_VLAN)
/* enable hw VLAN support */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
if (fep->quirks & FEC_QUIRK_HAS_CSUM) {
ndev->gso_max_segs = FEC_MAX_TSO_SEGS;
/* enable hw accelerator */
ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
| NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO);
fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
}
if (fep->quirks & FEC_QUIRK_HAS_AVB) {
fep->tx_align = 0;
fep->rx_align = 0x3f;
}
ndev->hw_features = ndev->features;
//重启mac
fec_restart(ndev);
return 0;
}
mdio
http://www.wowotech.net/linux_kenrel/469.html
static const struct net_device_ops fec_netdev_ops = {
.ndo_open = fec_enet_open,
.ndo_stop = fec_enet_close,
.ndo_start_xmit = fec_enet_start_xmit,
.ndo_select_queue = fec_enet_select_queue,
.ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = fec_timeout,
.ndo_set_mac_address = fec_set_mac_address,
.ndo_do_ioctl = fec_enet_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = fec_poll_controller,
#endif
.ndo_set_features = fec_set_features,
};
https://lyy-0217.blog.csdn.net/article/details/79018894
http://blog.chinaunix.net/uid-25002135-id-3229029.html
4、发包流程分析
- .ndo_start_xmit = fec_enet_start_xmit
- 调用fec_enet_txq_submit_skb进行发送
- 发送完成后触发fec_enet_interrupt中断
- 中断中判断发送完成调度__napi_schedule(&fep->napi);
- 调度napi轮循函数fec_enet_rx_napi被执行
- fec_enet_rx_napi中调用fec_enet_tx(ndev);
- fec_enet_tx(ndev);完成对发送后的处理
struct fec_enet_priv_tx_q {
int index;
unsigned char *tx_bounce[TX_RING_SIZE];
struct sk_buff *tx_skbuff[TX_RING_SIZE];
dma_addr_t bd_dma;
struct bufdesc *tx_bd_base;
uint tx_ring_size;
unsigned short tx_stop_threshold;
unsigned short tx_wake_threshold;
struct bufdesc *cur_tx;
struct bufdesc *dirty_tx;
char *tso_hdrs;
dma_addr_t tso_hdrs_dma;
};
fec_enet_enable_ring(struct net_device *ndev)
writel(rxq->bd_dma, fep->hwp + FEC_R_DES_START(i));
/*
* Define the buffer descriptor structure.
* 数据手册寄存器:
* [22.6.14.2 Enhanced transmit buffer descriptor] 定义如下结构体
*/
struct bufdesc {
unsigned short cbd_datlen; /* Data length:长度 */
unsigned short cbd_sc; /* Control and status info:控制和状态信息 */
unsigned long cbd_bufaddr; /* Buffer address:数据物理地址 */
};
struct bufdesc_ex {
struct bufdesc desc;
unsigned long cbd_esc;
unsigned long cbd_prot;
unsigned long cbd_bdu;
unsigned long ts;
unsigned short res0[4];
};
芯片手册:Table 22-37. Enhanced transmit buffer descriptor field definitions
BD:
https://blog.csdn.net/weixin_30768175/article/details/95272914
fec_enet_start_xmit:
static netdev_tx_t
fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
int entries_free;
unsigned short queue;
struct fec_enet_priv_tx_q *txq;
struct netdev_queue *nq;
int ret;
/* 从skb中获取发送queue索引 */
queue = skb_get_queue_mapping(skb);
/* 驱动发送队列 */
txq = fep->tx_queue[queue];
nq = netdev_get_tx_queue(ndev, queue);
if (skb_is_gso(skb))
ret = fec_enet_txq_submit_tso(txq, skb, ndev);
else
/* 调用SKB发送函数 */
ret = fec_enet_txq_submit_skb(txq, skb, ndev);
if (ret)
return ret;
entries_free = fec_enet_get_free_txdesc_num(fep, txq);
if (entries_free <= txq->tx_stop_threshold)
netif_tx_stop_queue(nq);
return NETDEV_TX_OK;
}
fec_enet_txq_submit_skb
static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
struct sk_buff *skb, struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
int nr_frags = skb_shinfo(skb)->nr_frags;
struct bufdesc *bdp, *last_bdp;
void *bufaddr;
dma_addr_t addr;
unsigned short status;
unsigned short buflen;
unsigned short queue;
unsigned int estatus = 0;
unsigned int index;
int entries_free;
//计算当前发送描述符数量
entries_free = fec_enet_get_free_txdesc_num(fep, txq);
//判断描述符过少的时候开始丢包
if (entries_free < MAX_SKB_FRAGS + 1) {
dev_kfree_skb_any(skb);
if (net_ratelimit())
netdev_err(ndev, "NOT enough BD for SG!\n");
return NETDEV_TX_OK;
}
/* Protocol checksum off-load for TCP and UDP. */
if (fec_enet_clear_csum(skb, ndev)) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
/* Fill in a Tx ring entry */
/* 从待发送队列获取待发送buffer对应的buffer描述符指针,并获取当前描述符的状态
* 第一次发送的时候这里获取的是第一个描述符bufdesc
*/
bdp = txq->cur_tx;
last_bdp = bdp;
status = bdp->cbd_sc;
/*
* 清bufdesc所有状态
* #define BD_ENET_TX_STATS ((ushort)0x0fff) All status bits
*/
status &= ~BD_ENET_TX_STATS;
/* Set buffer length and buffer pointer */
/* 提取待发送buf的虚拟地址和载荷长度 */
bufaddr = skb->data;
buflen = skb_headlen(skb);
queue = skb_get_queue_mapping(skb);
index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
/* 字节对齐处理 */
if (((unsigned long) bufaddr) & fep->tx_align ||
fep->quirks & FEC_QUIRK_SWAP_FRAME) {
memcpy(txq->tx_bounce[index], skb->data, buflen);
bufaddr = txq->tx_bounce[index];
/* 判断是否需要大小端的转换 */
if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
swap_buffer(bufaddr, buflen);
}
/* Push the data cache so the CPM does not get stale memory data. */
/* 对待发送的socket buffer进行DMA映射,映射的过程即刷新data cache
* 后面会将映射后的地址和长度写入描述符
*/
addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE);
if (dma_mapping_error(&fep->pdev->dev, addr)) {
dev_kfree_skb_any(skb);
if (net_ratelimit())
netdev_err(ndev, "Tx DMA memory map failed\n");
return NETDEV_TX_OK;
}
if (nr_frags) {
last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev);
if (IS_ERR(last_bdp))
return NETDEV_TX_OK;
} else {
/* 更新映射后的状态 */
status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
if (fep->bufdesc_ex) {
estatus = BD_ENET_TX_INT;
if (unlikely(skb_shinfo(skb)->tx_flags &
SKBTX_HW_TSTAMP && fep->hwts_tx_en))
estatus |= BD_ENET_TX_TS;
}
}
if (fep->bufdesc_ex) {
struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
fep->hwts_tx_en))
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
if (fep->quirks & FEC_QUIRK_HAS_AVB)
estatus |= FEC_TX_BD_FTYPE(queue);
if (skb->ip_summed == CHECKSUM_PARTIAL)
estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
ebdp->cbd_bdu = 0;
ebdp->cbd_esc = estatus;
}
index = fec_enet_get_bd_index(txq->tx_bd_base, last_bdp, fep);
/* Save skb pointer */
/* 把socket buffer指针存放在之前分配好的tx_skbuff[]指针数组 */
txq->tx_skbuff[index] = skb;
/* 将DMA映射号的buff信息写入描述符 */
bdp->cbd_datlen = buflen;
bdp->cbd_bufaddr = addr;
/* Send it on its way. Tell FEC it's ready, interrupt when done,
* it's the last BD of the frame, and to put the CRC on the end.
*/
/* 更新描述符状态为准备好 */
status |= (BD_ENET_TX_READY | BD_ENET_TX_TC);
bdp->cbd_sc = status;
/* If this was the last BD in the ring, start at the beginning again. */
/* 更新描述符 */
bdp = fec_enet_get_nextdesc(last_bdp, fep, queue);
skb_tx_timestamp(skb);
/* Make sure the update to bdp and tx_skbuff are performed before
* cur_tx.
*/
wmb();
/* 获取新的描述符给下一次使用的 */
txq->cur_tx = bdp;
/* Trigger transmission start */
/* 写寄存器触发硬件的发送操作 */
writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue));
return 0;
}
fec_enet_txq_submit_skb最后调用writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue));进行发送操作,待发送完成后会触发中断调用fec_enet_interrupt
static irqreturn_t
fec_enet_interrupt(int irq, void *dev_id)
{
struct net_device *ndev = dev_id;
struct fec_enet_private *fep = netdev_priv(ndev);
uint int_events;
irqreturn_t ret = IRQ_NONE;
int_events = readl(fep->hwp + FEC_IEVENT);
writel(int_events, fep->hwp + FEC_IEVENT);
fec_enet_collect_events(fep, int_events);
//发送完成/接收完成/link上 进行poll调度
if ((fep->work_tx || fep->work_rx) && fep->link) {
ret = IRQ_HANDLED;
if (napi_schedule_prep(&fep->napi)) {
/* Disable the NAPI interrupts */
writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
__napi_schedule(&fep->napi);
}
}
if (int_events & FEC_ENET_MII) {
ret = IRQ_HANDLED;
complete(&fep->mdio_done);
}
if (fep->ptp_clock)
fec_ptp_check_pps_event(fep);
return ret;
}
fec_enet_rx_napi中调用fec_enet_tx(ndev);进行发送后的处理操作
static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
{
struct net_device *ndev = napi->dev;
struct fec_enet_private *fep = netdev_priv(ndev);
int pkts;
pkts = fec_enet_rx(ndev, budget);
fec_enet_tx(ndev);
if (pkts < budget) {
napi_complete(napi);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
}
return pkts;
}
fec_enet_tx调用到fec_enet_tx_queue
static void
fec_enet_tx(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
u16 queue_id;
/* First process class A queue, then Class B and Best Effort queue */
for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) {
clear_bit(queue_id, &fep->work_tx);
fec_enet_tx_queue(ndev, queue_id);
}
return;
}
fec_enet_tx_queue中完成包计数更新和释放操作。
static void
fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)
{
struct fec_enet_private *fep;
struct bufdesc *bdp, *bdp_t;
unsigned short status;
struct sk_buff *skb;
struct fec_enet_priv_tx_q *txq;
struct netdev_queue *nq;
int index = 0;
int i, bdnum;
int entries_free;
printk("yangf DBUG at FUNC: %s +%d\n",__func__, __LINE__);
fep = netdev_priv(ndev);
queue_id = FEC_ENET_GET_QUQUE(queue_id);
txq = fep->tx_queue[queue_id];
/* get next bdp of dirty_tx */
nq = netdev_get_tx_queue(ndev, queue_id);
bdp = txq->dirty_tx;
index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
printk("yangf DBUG at FUNC: %s +%d bd index=%d\n",__func__, __LINE__, index);
/* get next bdp of dirty_tx */
bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
printk("yangf DBUG at FUNC: %s +%d bd index=%d\n",__func__, __LINE__, index);
index = fec_enet_get_bd_index(txq->tx_bd_base, txq->cur_tx, fep);
printk("yangf DBUG at FUNC: %s +%d cur index=%d\n",__func__, __LINE__, index);
while (bdp != READ_ONCE(txq->cur_tx)) {
printk("yangf DBUG at FUNC: %s +%d\n",__func__, __LINE__);
/* Order the load of cur_tx and cbd_sc */
rmb();
status = READ_ONCE(bdp->cbd_sc);
if (status & BD_ENET_TX_READY)
break;
bdp_t = bdp;
bdnum = 1;
index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep);
printk("yangf DBUG at FUNC: %s +%d bd index=%d\n",__func__, __LINE__, index);
skb = txq->tx_skbuff[index];
while (!skb) {
bdp_t = fec_enet_get_nextdesc(bdp_t, fep, queue_id);
index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep);
skb = txq->tx_skbuff[index];
bdnum++;
}
if ((status = bdp_t->cbd_sc) & BD_ENET_TX_READY)
break;
for (i = 0; i < bdnum; i++) {
if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr))
dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
bdp->cbd_datlen, DMA_TO_DEVICE);
bdp->cbd_bufaddr = 0;
if (i < bdnum - 1)
bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
}
txq->tx_skbuff[index] = NULL;
/* Check for errors. */
if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
BD_ENET_TX_RL | BD_ENET_TX_UN |
BD_ENET_TX_CSL)) {
ndev->stats.tx_errors++;
if (status & BD_ENET_TX_HB) /* No heartbeat */
ndev->stats.tx_heartbeat_errors++;
if (status & BD_ENET_TX_LC) /* Late collision */
ndev->stats.tx_window_errors++;
if (status & BD_ENET_TX_RL) /* Retrans limit */
ndev->stats.tx_aborted_errors++;
if (status & BD_ENET_TX_UN) /* Underrun */
ndev->stats.tx_fifo_errors++;
if (status & BD_ENET_TX_CSL) /* Carrier lost */
ndev->stats.tx_carrier_errors++;
} else {
printk("yangf DBUG at FUNC: %s +%d\n",__func__, __LINE__);
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
}
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
fep->bufdesc_ex) {
struct skb_shared_hwtstamps shhwtstamps;
struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
fec_enet_hwtstamp(fep, ebdp->ts, &shhwtstamps);
skb_tstamp_tx(skb, &shhwtstamps);
}
/* Deferred means some collisions occurred during transmit,
* but we eventually sent the packet OK.
*/
if (status & BD_ENET_TX_DEF)
ndev->stats.collisions++;
/* Free the sk buffer associated with this last transmit */
dev_kfree_skb_any(skb);
/* Make sure the update to bdp and tx_skbuff are performed
* before dirty_tx
*/
wmb();
txq->dirty_tx = bdp;
index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
printk("yangf DBUG at FUNC: %s +%d bd index=%d\n",__func__, __LINE__, index);
/* Update pointer to next buffer descriptor to be transmitted */
bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
/* Since we have freed up a buffer, the ring is no longer full
*/
if (netif_queue_stopped(ndev)) {
entries_free = fec_enet_get_free_txdesc_num(fep, txq);
if (entries_free >= txq->tx_wake_threshold)
netif_tx_wake_queue(nq);
}
}
/* ERR006538: Keep the transmitter going */
if (bdp != txq->cur_tx &&
readl(fep->hwp + FEC_X_DES_ACTIVE(queue_id)) == 0)
writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue_id));
}
5、收包流程分析