数据接收
解网卡数据接收过程。有必先讨论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%02Xn", 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%04Xn", rfd_status); /* If data isn't ready, nothing to indicate */ //没有接收完全,返回 if(unlikely(!(rfd_status & cb_complete))) return -EAGAIN; //得接收数据长度 actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF; if(unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd))) actual_size = RFD_BUF_LEN - sizeof(struct rfd); //消DMA缓存映射 pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_FROMDEVICE); //由于RFD不链路传入数据,清除 skb_reserve(skb, sizeof(struct rfd)); //调整skbtail指针,len更新 skb_put(skb, actual_size); //得链路层协议 skb->protocol = eth_type_trans(skb, nic->netdev); //接收失败 if(unlikely(!(rfd_status & cb_ok))) { /* Don't indicate if hardware indicates errors */ nic->net_stats.rx_dropped++; dev_kfree_skb_any(skb); } //数据超长。Drop it else if(actual_size > nic->netdev->mtu + VLAN_ETH_HLEN) { /* Don't indicate oversized frames */ nic->rx_over_length_errors++; nic->net_stats.rx_dropped++; dev_kfree_skb_any(skb); } else { //成功接收,更新统计计数 nic->net_stats.rx_packets++; nic->net_stats.rx_bytes += actual_size; nic->netdev->last_rx = jiffies; //送至次协议处理 netif_receive_skb(skb); if(work_done) (*work_done)++; } rx->skb = NULL; return 0; } 面代码去判断接收否完全,什么去判断呢?根据DMA机制,网卡数据放入DMA。DMA再向CPU发断嘛?呵呵。这里进行接收完全判断因:1:由其原因造成断2:处理断时候。数据又达。网卡依然放至个skb。而代码处理遍历处理,就说处理个skb时候,能网卡传数据。
好 ,运行 netif_receive_skb() ,数据包被送 层。
二数据 发送 进入 发送函数 , 们先 看e100_up()->e100_alloc_cbs函数: static int e100_alloc_cbs(struct nic *nic) { struct cb *cb; unsigned int i, count = nic->params.cbs.count; nic->cuc_cmd = cuc_start; nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = NULL; nic->cbs_avail = 0; //线性DMA映射,这里返回虚拟地址,供CPU使 nic->cbs = pci_alloc_consistent(nic->pdev, sizeof(struct cb) * count, &nic->cbs_dma_addr); if(!nic->cbs) return -ENOMEM; //建立环形发送缓冲区 for(cb = nic->cbs, i = 0; i < count; cb++, i++) { cb->next = (i + 1 < count) ? cb + 1 : nic->cbs; cb->prev = (i == 0) ? nic->cbs + count - 1 : cb - 1; cb->dma_addr = nic->cbs_dma_addr + i * sizeof(struct cb); cb->link = cpu_to_le32(nic->cbs_dma_addr + ((i+1) % count) * sizeof(struct cb)); cb->skb = NULL; } //始化各指针,使其指向缓冲始位置 nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = nic->cbs; nic->cbs_avail = count; return 0; } 这 段代码里,完成 发送 准备工作,建立 发送环形缓存。 发送数剧时,只 数据送入缓存即 数据最终 调 dev-> hard_start_xmit函数。 e100代码里, 就 e100_xmit_frame(). 进入里面看 : static int e100_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct nic *nic = netdev_priv(netdev); int err; if(nic->flags & ich_10h_workaround) { e100_exec_cmd(nic, cuc_nop, 0); udelay(1); } err = e100_exec_cb(nic, skb, e100_xmit_prepare); switch(err) { case -ENOSPC: /* We queued the skb, but now we're out of space. */ netif_stop_queue(netdev); break; case -ENOMEM: /* This is a hard error - log it. */ DPRINTK(TX_ERR, DEBUG, "Out of Tx resources, returning skbn"); netif_stop_queue(netdev); return 1; } netdev->trans_start = jiffies; return 0; } 继续跟踪进 e100_exec_cb(nic, skb, e100_xmit_prepare); static inline int e100_exec_cb(struct nic *nic, struct sk_buff *skb, void (*cb_prepare)(struct nic *, struct cb *, struct sk_buff *)) { struct cb *cb; unsigned long flags; int err = 0; spin_lock_irqsave(&nic->cb_lock, flags); if(unlikely(!nic->cbs_avail)) { err = -ENOMEM; goto err_unlock; } //skb 推入环形发送缓冲 //cb_to_use:发送缓冲当使位置 cb = nic->cb_to_use; nic->cb_to_use = cb->next; nic->cbs_avail--; cb->skb = skb; if(unlikely(!nic->cbs_avail)) err = -ENOSPC; cb_prepare(nic, cb, skb); /* Order is important otherwise we'll be in a race with h/w: * set S-bit in current first, then clear S-bit in previous. */ cb->command |= cpu_to_le16(cb_s); wmb(); cb->prev->command &= cpu_to_le16(~cb_s); //当发送数据不空。余数剧全部发送 while(nic->cb_to_send != nic->cb_to_use) { if(unlikely(e100_exec_cmd(nic, nic->cuc_cmd, nic->cb_to_send->dma_addr))) { /* Ok, here's where things get sticky. It's * possible that we can't schedule the command * because the controller is too busy, so * let's just queue the command and try again * when another command is scheduled. */ break; } else { nic->cuc_cmd = cuc_resume; nic->cb_to_send = nic->cb_to_send->next; } } err_unlock: spin_unlock_irqrestore(&nic->cb_lock, flags); return err; } 这里 们看 ,发送数据过程主 由e100_exec_cmd完成。跟踪进去 static inline int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr) { unsigned long flags; unsigned int i; int err = 0; spin_lock_irqsave(&nic->cmd_lock, flags); /* Previous command is accepted when SCB clears */ for(i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) { if(likely(!readb(&nic->csr->scb.cmd_lo))) break; cpu_relax(); if(unlikely(i > (E100_WAIT_SCB_TIMEOUT >> 1))) udelay(5); } if(unlikely(i == E100_WAIT_SCB_TIMEOUT)) { err = -EAGAIN; goto err_unlock; } if(unlikely(cmd != cuc_resume)) //数据存放地址放入应寄存器 writel(dma_addr, &nic->csr->scb.gen_ptr); //发送操作入控制寄存器 writeb(cmd, &nic->csr->scb.cmd_lo); err_unlock: spin_unlock_irqrestore(&nic->cmd_lock, flags); return err; }
从此 以看 。Intel 100M网卡 发送数据 处理,只需 地址,命令 入相应 寄存器即 。
|