Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2762470
  • 博文数量: 79
  • 博客积分: 30130
  • 博客等级: 大将
  • 技术积分: 2608
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-22 14:58
个人简介

博所搬至http://xiaogr.com

文章存档

2015年(2)

2009年(3)

2008年(56)

2007年(18)

分类: LINUX

2007-10-23 16:13:52

------------------------------------------

本文系本站原创,欢迎转载!

转载请注明出处:http://ericxiao.cublog.cn/

------------------------------------------

五:数据的发送

在进入到发送函数之前,我们先来看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 skb\n");

                   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网卡对发送数据的处理,只需将地址,命令写入相应的寄存器即可。详细资料可以查看intel 100M网卡的说明。

令人不解的是,在发送数据时,不要将发送长度写入相关寄存器吗?那他又是如何截取的呢?

阅读(3923) | 评论(3) | 转发(1) |
给主人留下些什么吧!~~

chinaunix网友2008-11-28 18:36:44

发送长度放在 cb->u.tcb.tbd.size 里,见 e100_xmit_prepare :-)

chinaunix网友2008-11-28 18:36:44

发送长度放在 cb->u.tcb.tbd.size 里,见 e100_xmit_prepare :-)

chinaunix网友2008-11-28 18:36:44

发送长度放在 cb->u.tcb.tbd.size 里,见 e100_xmit_prepare :-)