RK3288 GMAC整理


一、源文件

源碼路徑:\drivers\net\ethernet\rockchip\gmac

源碼閱讀順序:

     

二、重要探針函數stmmac_dvr_probe

1. alloc_etherdev

申請網卡設備和私有數據。

struct net_device *ndev = NULL;

struct stmmac_priv *priv;

ndev = alloc_etherdev(sizeof(struct stmmac_priv));

priv = netdev_priv(ndev);

網卡設備和私有數據緊緊的挨在一起:網卡設備+私用數據結構,通過netdev_priv獲取私有數據。網卡設備是通用數據結構,私有數據則為各個MAC控制器的數據結構。

2. stmmac_hw_init

初始化MAC設備。檢測要添加的設備(GMAC/MAC10-100),檢查HW能力並設置驅動程序的特性。

3. netif_napi_add

初始化一個NAPI上下文,輪詢接收數據包接口。

netif_napi_add(ndev, &priv->napi, stmmac_poll, 64);

stmmac_poll是輪詢接口

該接口完成兩個事情:

1)當數據發送完成時產生中斷,進入該函數進行資源回收。

2)收到數據產生中斷,進入該函數進行數據接收和處理。

復制代碼
static int stmmac_poll(struct napi_struct *napi, int budget)
{
  ......
  stmmac_tx_clean(priv);  //傳輸完成后回收資源
  work_done = stmmac_rx(priv, budget); //數據包接收處理
  stmmac_enable_dma_irq(priv); //使能dma終端
  ......
}
復制代碼

stmmac_rx函數解析

作用:再次填充並使用skb預先分配的緩存,由NAPI輪詢方法調用,可以獲得環內所有幀。

函數:static int stmmac_rx(struct stmmac_priv *priv, int limit)

函數內定義:

1)priv->hw->desc->get_rx_owner(p)

判斷當前描述子的歸屬:描述子數據結構中OWN位,

  0: 當前描述子應該由CPU操作,

  1: 前描述子應該由GMAC操作。

對於接收,初始化dma描述子隊列時設置為1。

GMAC根據寄存器配置,獲取可用接收描述子,然后從RxFIFO中讀取從PHY接收的Ethernet報文,如果報文符合接收條件,將該報文寫入接收描述子指向的數據緩沖區,並回寫接收描述子。這個回寫就會將OWN位設置為0。

2)next_entry = (++priv->cur_rx) % rxsize;

獲取下一幀描述符,

p_next =priv->dma_rx + next_entry;

priv->cur_rx:已經傳遞給協議層的index。

3)status =(priv->hw->desc->rx_status(&priv->dev->stats, &priv->xstats,p));

獲取收到幀狀態,如果是丟棄幀,則什么都不處理;否則上傳到上層網絡。

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。

  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設備,並將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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM