【linux网络】imx6ul网卡驱动分析

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 = <&ethphy0>;
	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、收包流程分析

 

 

 

 

已标记关键词 清除标记
<p> <b><span style="background-color:#FFE500;">【超实用课程内容】</span></b> </p> <p> <br /> </p> <p> <br /> </p> <p> 本课程内容包含讲解<span>解读Nginx的基础知识,</span><span>解读Nginx的核心知识、带领学员进行</span>高并发环境下的Nginx性能优化实战,让学生能够快速将所学融合到企业应用中。 </p> <p> <br /> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><br /> </b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><span style="background-color:#FFE500;">【课程如何观看?】</span></b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> PC端:<a href="https://edu.csdn.net/course/detail/26277"><span id="__kindeditor_bookmark_start_21__"></span></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 移动端:CSDN 学院APP(注意不是CSDN APP哦) </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 本课程为录播课,课程永久有效观看时长,大家可以抓紧时间学习后一起讨论哦~ </p> <p style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <br /> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <strong><span style="background-color:#FFE500;">【学员专享增值服务】</span></strong> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b>源码开放</b> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化 </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 下载方式:电脑登录<a href="https://edu.csdn.net/course/detail/26277"></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a>,播放页面右侧点击课件进行资料打包下载 </p> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p>
相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页