一、源文件
源碼路徑:\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。
- 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