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:

1priv->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.

4frame_len = priv->hw->desc->get_rx_frame_len(p, coe); 获取帧长度

5skb = priv->rx_skbuff[entry];

priv->rx_skbuff[entry] = NULL;

注意:skb将有上层网络处理完后进行释放。

6skb_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

  1. netdev_priv 获得网络设备私有数据
  2. stmmac_check_ether_addr 检测MAC地址是否有效,若无效,随机产生一个
  3. stmmac_mdio_register注册MII总线

mdiobus_register()

a.注册总线设备device_register(&bus->dev);

b.复位总线bus->reset(bus);

c.扫描总线上的PHY设备,最大支持32

4. stmmac_init_phy初始化PHY设备,并将PHYMAC绑定

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宏来判断数据包是由一个数据片段组成,还是由大量数据片段组成。

1entry= priv->cur_tx % txsize;

desc= priv->dma_tx + entry; 获取可用发送描述子

first= desc; 保存第一个数据片段

2priv->tx_skbuff[entry]= skb;

priv->tx_page[entry]= NULL; skb放到发送队列

3unsignedint 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();
}

发送剩余数据片段。

对于多个数据片段时,还要进行数据片段的发送,采用页处理。直接处理页结构,而不是内核虚拟地址。

5priv->hw->desc->set_tx_owner(first);

priv->cur_tx ++;

将第一个数据片段描述子交给GMAC,记录当前发送index

6priv->hw->dma->enable_dma_transmission(priv->dma_ioaddr,

                          priv->dma_channel);

写任何值唤醒处于挂起的RxDMA