RK3288 GMAC finishing
1. Source files
Source path: \drivers\net\ethernet\rockchip\gmac
Source code reading order:
2. Important probe function stmmac_dvr_probe
1. alloc_etherdev
Apply for network card equipment and private data.
struct net_device *ndev = NULL;
struct stmmac_priv *priv;
ndev = alloc_etherdev(sizeof(struct stmmac_priv));
priv = netdev_priv (ndev);
The network card device and private data are closely next to each other: network card device + private data structure, private data is obtained through netdev_priv . The network card device is a general data structure, and the private data is the data structure of each MAC controller.
2. stmmac_hw_init
Initialize the MAC device. Detect the device to be added (GMAC/MAC10-100) , check the HW capability and set the characteristics of the driver.
3. netif_napi_add
Initializes a NAPI context and polls the interface for receiving packets.
netif_napi_add(ndev, &priv->napi, stmmac_poll, 64);
stmmac_poll is the polling interface
This interface accomplishes two things:
1 ) When the data transmission is completed, an interrupt is generated, and the function is entered for resource recovery.
2 ) Interrupt when data is received, enter this function to receive and process data.
static int stmmac_poll( struct napi_struct *napi, int budget) { ...... stmmac_tx_clean(priv); // Recycle resources after transmission work_done = stmmac_rx(priv, budget); // Data packet reception processing stmmac_enable_dma_irq(priv) ; // Enable dma terminal ... }
stmmac_rx function analysis
Function: Refill and use the skb pre-allocated buffer, called by the NAPI polling method, to obtain all frames in the ring.
函数:static int stmmac_rx(struct stmmac_priv *priv, int limit)
Definition inside the function:
1)priv->hw->desc->get_rx_owner(p)
Determine the attribution of the current descriptor: the OWN bit in the descriptor data structure,
0: The current descriptor should be operated by the CPU ,
1: The predescriptor should be manipulated by GMAC .
For receive, set to 1 when initializing the dma description subqueue .
GMAC obtains the available receive descriptor according to the register configuration, and then reads the Ethernet packet received from the PHY from the RxFIFO . If the packet meets the receiving conditions, the packet is written into the data buffer pointed to by the receive descriptor, and written back Receive descriptor. This writeback will set the OWN bit to 0 .
2)next_entry = (++priv->cur_rx) % rxsize;
Get the next frame descriptor,
p_next =priv->dma_rx + next_entry;
priv->cur_rx : The index that has been passed to the protocol layer .
3)status =(priv->hw->desc->rx_status(&priv->dev->stats, &priv->xstats,p));
Get the status of the received frame, if the frame is discarded, do nothing; otherwise, upload it to the upper network.
4)frame_len = priv->hw->desc->get_rx_frame_len(p, coe); 获取帧长度
5)skb = priv->rx_skbuff[entry];
priv->rx_skbuff[entry] = NULL;
注意:skb将有上层网络处理完后进行释放。
6)skb_put(skb, frame_len);
dma_unmap_single(priv->device,priv->rx_skbuff_dma[entry],priv->dma_buf_sz, DMA_FROM_DEVICE); 设置skb数据长度和解除流式DMA映射
7)获取skb的协议类型
skb->protocol = eth_type_trans(skb,priv->dev);
skb->dev = priv->dev;
8)napi_gro_receive(&priv->napi,skb);
将skb通过NAPI接口上传上层网络协议处理。
9)stmmac_rx_refill(priv); 重新填充接收队列
三、打开网卡设备接口stmmac_open
网卡刚起来时是关闭的,要用命令去打开,ifconfig eth0 up 时调用net_device_ops的.ndo_open,这里为stmmac_open。
- netdev_priv 获得网络设备私有数据
- stmmac_check_ether_addr 检测MAC地址是否有效,若无效,随机产生一个
- stmmac_mdio_register注册MII总线
mdiobus_register()
a.注册总线设备device_register(&bus->dev);
b.复位总线bus->reset(bus);
c.扫描总线上的PHY设备,最大支持32个
4. stmmac_init_phy初始化PHY设备,并将PHY和MAC绑定
5. request_irq 申请中断
ret = request_irq(dev->irq, stmmac_interrupt,
IRQF_SHARED, dev->name, dev);
注册中断请求线IRQ,中断处理函数stmmac_interrupt用于接收DMA数据,配合NAPI处理。
static irqreturn_t stmmac_interrupt(int irq, void *dev_id) { ...... /* To handle DMA interrupts */ stmmac_dma_interrupt(priv); } static void stmmac_dma_interrupt(struct stmmac_priv *priv) { ...... status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); if (likely((status & handle_rx)) || (status & handle_tx)) { if (likely(napi_schedule_prep(&priv->napi))) { stmmac_disable_dma_irq(priv); __napi_schedule(&priv->napi); //加入poll流程 } } ...... }
四、发送数据接口stmmac_xmit
该接口实现了Scatter/Gather I/O功能,通过skb_shinfo宏来判断数据包是由一个数据片段组成,还是由大量数据片段组成。
1、entry= priv->cur_tx % txsize;
desc= priv->dma_tx + entry; 获取可用发送描述子
first= desc; 保存第一个数据片段
2、priv->tx_skbuff[entry]= skb;
priv->tx_page[entry]= NULL; 将skb放到发送队列
3、unsignedint nopaged_len = skb_headlen(skb);
desc->des2=dma_map_single(priv->device,skb->data,nopaged_len, DMA_TO_DEVICE);
priv->hw->desc->prepare_tx_desc(desc,1, nopaged_len, csum_insertion);
发送单个或第一个数据包,当只有一个数据片段时,skb->data将发送所有数据;当有多个数据片段时,skb->data则指向第一个数据片段,数据长度skb->len –skb->data_len,其他数据存放在共享数据结构frags数组中。(skb->len:数据包中全部数据的长度,skb->data_len分隔存储数据片段长度)
4、
for (i = 0; i < nfrags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; int len = skb_frag_size(frag); entry = (++priv->cur_tx) % txsize; if (priv->extend_desc) desc = (struct dma_desc *)(priv->dma_etx + entry); else desc = priv->dma_tx + entry; TX_DBG("\t[entry %d] segment len: %d\n", entry, len); desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len, DMA_TO_DEVICE); priv->tx_skbuff_dma[entry] = desc->des2; priv->tx_skbuff[entry] = NULL; priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, priv->mode); wmb(); priv->hw->desc->set_tx_owner(desc); wmb(); }
发送剩余数据片段。
对于多个数据片段时,还要进行数据片段的发送,采用页处理。直接处理页结构,而不是内核虚拟地址。
5、priv->hw->desc->set_tx_owner(first);
priv->cur_tx ++;
将第一个数据片段描述子交给GMAC,记录当前发送index
6、priv->hw->dma->enable_dma_transmission(priv->dma_ioaddr,
priv->dma_channel);
写任何值唤醒处于挂起的RxDMA