數據接收
為了了解網卡數據接收的過程。有必要先討論DMA的具體過程。
DMA傳輸數據可以分為以下幾個步驟:
首先:CPU向DMA送命令,如DMA方式,主存地址,傳送的字數等,之後CPU執行原來的程序.
然後DMA 控制在 I/O 設備與主存間交換數據。接收數據完後, 向CPU發DMA請求,取得總線控制權,進行數據傳送,修改卡上主存地址,修改字數計數器內且檢查其值是否為零,不為零則繼續傳送,若已為零,則向 CPU發中斷請求.。
也就是說,網卡收到包時,將它放入當前skb->data中。再來一個包時。DMA會修改卡上主存地址,轉到skb->next,將數據放入其中。這也就是,一個skb->data存儲一個數據包的原因。
好了,現在就可以來看具體的代碼實現了。
當網絡數據到絡,網卡將其放到DMA內存,然後DMA向CPU報告中斷,CPU根據中斷向量,找到中斷處理例程,也就是我們前面注冊的e100_intr()進行處理。
static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *netdev = dev_id;
struct nic *nic = netdev_priv(netdev);
u8 stat_ack = readb(&nic->csr->scb.stat_ack);
DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack);
if(stat_ack == stat_ack_not_ours || /* Not our interrupt */
stat_ack == stat_ack_not_present) /* Hardware is ejected */
return IRQ_NONE;
/* Ack interrupt(s) */
//發送中斷ACK。Cpu向設備發送ACK。表示此中斷已經處理
writeb(stat_ack, &nic->csr->scb.stat_ack);
/* We hit Receive No Resource (RNR); restart RU after cleaning */
if(stat_ack & stat_ack_rnr)
nic->ru_running = 0;
//禁用中斷
e100_disable_irq(nic);
//CPU開始調度此設備。轉而會運行netdev->poll
netif_rx_schedule(netdev);
return IRQ_HANDLED;
}
netif_rx_schedule(netdev)後,cpu開始調度此設備,輪詢設備是否有數據要處理。轉後調用netdev->poll函數,即:e100_poll()
static int e100_poll(struct net_device *netdev, int *budget)
{
struct nic *nic = netdev_priv(netdev);
unsigned int work_to_do = min(netdev->quota, *budget);
unsigned int work_done = 0;
int tx_cleaned;
//開始對nic中,DMA數據的處理
e100_rx_clean(nic, &work_done, work_to_do);
tx_cleaned = e100_tx_clean(nic);
/* If no Rx and Tx cleanup work was done, exit polling mode. */
if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) {
netif_rx_complete(netdev);
e100_enable_irq(nic);
return 0;
}
*budget -= work_done;
netdev->quota -= work_done;
return 1;
}
跟蹤進e100_rx_clean():
static inline void e100_rx_clean(struct nic *nic, unsigned int *work_done,
unsigned int work_to_do)
{
struct rx *rx;
/* Indicate newly arrived packets */
//遍歷環形DMA中的數據,調用e100_rx_indicate()進行處理
for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {
if(e100_rx_indicate(nic, rx, work_done, work_to_do))
break; /* No more to clean */
}
/* Alloc new skbs to refill list */
for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) {
if(unlikely(e100_rx_alloc_skb(nic, rx)))
break; /* Better luck next time (see watchdog) */
}
e100_start_receiver(nic);
}
在這裡,它會遍歷環形DMA中的數據,即從nic->rx_to_clean開始的數據,直至數據全部處理完
進入處理函數:e100_rx_indicate()
static inline int e100_rx_indicate(struct nic *nic, struct rx *rx,
unsigned int *work_done, unsigned int work_to_do)
{
struct sk_buff *skb = rx->skb;
//從這裡取得rfd.其中包括了一些接收信息,但不是鏈路傳過來的有效數據
struct rfd *rfd = (struct rfd *)skb->data;
u16 rfd_status, actual_size;
if(unlikely(work_done && *work_done >= work_to_do))
return -EAGAIN;
//同步DMA緩存
pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr,
sizeof(struct rfd), PCI_DMA_FROMDEVICE);
//取得接收狀態
rfd_status = le16_to_cpu(rfd->status);
DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status);
/* If data isn't ready, nothing to indicate */
//沒有接收完全,返回