注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

TCP/IP驱动五数据发送函数  

2013-05-29 21:56:47|  分类: TCPIP驱动 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
TCP/IP驱动五数据发送函数
数据发送和接收是网卡的最主要和核心的功能,那么就不得不提到dm9000_start_xmit()这个函数。这是一个非常重要的发送数据包函数。从上层发送sk_buff包。DM9000发送数据包的的图示如下:

     TCP/IP驱动五数据发送函数 - 还东国 - 还东国的博客
 

如上图所示,在DM9000内部SRAM中,地址0x0000~0x0BFF(3K)是TX Buffer, 地址0x0C00~0x3FFF(12K)是RX Buffer
。在发送一个包之前,包中的有效数据必须先被存储到TX Buffer中并且使用输出端口命令来选择MWCMD寄存器。包的长度定义在TXPLL和TXPLH中。最后设置TXCR寄存器的bit[0] TXREQ来自动发送包。如果设置了IMR寄存器的PTM位,则DM9000会产生一个中断触发在ISR寄存器的bit[1]=PTS=1, 同时设置一个完成标志在NSR寄存器的bit[2]=TX1END或者 bit[3]=TX2END,表示包已经发送完了。发送一个包的具体步骤如下:

Step 1: 检查存储数据宽度。通过读取中断状态寄存器(ISR)的bit[7:6]来确定是8bit,16bit还是32bit。

Step 2: 写数据到TX SRAM中。

Step 3: 写传输长度到TXPLL和TXPLH寄存器中。

Step 4: 设置TXCR寄存器的bit[0]TXREQ来开始发送一个包。

代码清单如下,让我们看看在获得自旋锁这段期间都干了些什么:
 
/*   
 *  Hardware start transmission.   
 *  Send a packet to media from the upper layer.   
 */   
static int   
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)    
{    
    unsigned long flags;    
    board_info_t *db = netdev_priv(dev);    
   
    dm9000_dbg(db, 3, "%s:\n", __func__);    
   
    if (db->tx_pkt_cnt > 1)    
        return NETDEV_TX_BUSY;    
   
        /*获得自旋锁*/   
    spin_lock_irqsave(&db->lock, flags);     
   
    /* Move data to DM9000 TX RAM */   
        /*下面四行代码将skb中的data部分写入DM9000的TX RAM
,并更新已发送字节数和发送计数*/   
    writeb(DM9000_MWCMD, db->io_addr);    
   
    (db->outblk)(db->io_data, skb->data, skb->len);    
    dev->stats.tx_bytes += skb->len;    
   
    db->tx_pkt_cnt++;    
/* TX control: First packet immediately send, second packet queue */   
/*dm9000可以发送两个数据包,当发送一个数据包产生中断后,要确认一下队列中有没有第2个包需要发送。 */
        /*如果发送的是第一个包,则设置一下包的长度后直接发送*/   
        /*如果发的不是第一个包,*/   
    if (db->tx_pkt_cnt == 1) {    
        /* Set TX length to DM9000 */   
        iow(db, DM9000_TXPLL, skb->len);    
        iow(db, DM9000_TXPLH, skb->len >> 8);    
   
        /* Issue TX polling command */   
        iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */   
   
        dev->trans_start = jiffies;  /* save the time stamp */   
    } else {    
        /* Second packet */   
                
/*如果发送的是第二个数据包(表明队列中此时有包发送),则将其加入队列中:将skb->len和skb->ip_summed(控制校验操作)赋值给board_info_t中有关队列的相关成员。调用函数netif_stop_queue(dev),通知内核现在queue已满,不能再将发送数据传到队列中,注:第二个包的发送将在tx_done中实现。*/   
        db->queue_pkt_len = skb->len;    
        netif_stop_queue(dev);    
    }    
        /*释放自旋锁*/   
    spin_unlock_irqrestore(&db->lock, flags);    
   
    /* free this SKB */   
    dev_kfree_skb(skb);    
   
    return 0;    
}  


要想使网卡连续不断的发送数据,DM9000采用了中断处理机制,触发中断的时机发生在:
1)DM9000接收到一个包以后。2)DM9000发送完了一个包以后。看一下具体的中断处理函数:
中断处理函数在open函数( if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev)))的时候被注册进内核: 

static irqreturn_t dm9000_interrupt(int irq, void *dev_id)    
{    
    struct net_device *dev = dev_id;    
    board_info_t *db = netdev_priv(dev);    
    int int_status;    
    unsigned long flags;    
    u8 reg_save;    
   
    dm9000_dbg(db, 3, "entering %s\n", __func__);    
   
    /* A real interrupt coming */   
   
    /* holders of db->lock must always block IRQs */   
    spin_lock_irqsave(&db->lock, flags);    
   
    /* Save previous register address */   
    reg_save = readb(db->io_addr);    
   
    /* Disable all interrupts */   
    iow(db, DM9000_IMR, IMR_PAR);    
   
    /* Got DM9000 interrupt status */   
    int_status = ior(db, DM9000_ISR);   /* Got ISR */   
    iow(db, DM9000_ISR, int_status);    /* Clear ISR status */   
   
    if (netif_msg_intr(db))    
        dev_dbg(db->dev, "interrupt status %02x\n", int_status);    
   
    /* Received the coming packet */   
        /*如果是由于收到数据而触发的中断,显然调用dm9000_rx()把数据取走,传递给上层*/   
    if (int_status & ISR_PRS)    
        dm9000_rx(dev);    
   
    /* Trnasmit Interrupt check */   
        /*如果是由于发送完了数据而触发的中断,则调用dm9000_tx_done()函数,下面具体分析这个函数*/   
    if (int_status & ISR_PTS)    
        dm9000_tx_done(dev, db);    
   
    if (db->type != TYPE_DM9000E) {    
        if (int_status & ISR_LNKCHNG) {    
            /* fire a link-change request */   
            schedule_delayed_work(&db->phy_poll, 1);    
        }    
    }    
   
    /* Re-enable interrupt mask */   
    iow(db, DM9000_IMR, db->imr_all);    
   
    /* Restore previous register address */   
    writeb(reg_save, db->io_addr);    
   
    spin_unlock_irqrestore(&db->lock, flags);    
   
    return IRQ_HANDLED;    
}  


要想发送第二个包,就得看dm9000_tx_done()这个函数了

    (1)读取dm9000寄存器NSR(Network Status Register)获取发送的状态,存在变量tx_status中;

    (2)如果发送状态为NSR_TX2END(第2个包发送完毕)或者NSR_TX1END(第1个包发送完毕),则将待发送的数据包数量(db-> tx_pkt_cnt )减1,已发送的数据包数量(dev->stats.tx_packets)加1;

    (3)检查变量db-> tx_pkt_cnt(待发送的数据包)是否大于0(表明还有数据包要发送),则调用函数dm9000_send_packet发送队列中的数据包;

    (4)调用函数netif_wake_queue (dev)通知内核可以将待发送的数据包进入发送队列。

/*   
 * DM9000 interrupt handler   
 * receive the packet to upper layer, free the transmitted packet   
 */   
   
static void dm9000_tx_done(struct net_device *dev, board_info_t *db)    
{    
    int tx_status = ior(db, DM9000_NSR);    /* Got TX status */   
   
    if (tx_status & (NSR_TX2END | NSR_TX1END)) {    
        /* One packet sent complete */   
        db->tx_pkt_cnt--;    
        dev->stats.tx_packets++;    
   
        if (netif_msg_tx_done(db))    
            dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);    
   
        /* Queue packet check & send */   
        if (db->tx_pkt_cnt > 0) {    
            iow(db, DM9000_TXPLL, db->queue_pkt_len);    
            iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8);    
            iow(db, DM9000_TCR, TCR_TXREQ);    
            dev->trans_start = jiffies;    
        }    
        netif_wake_queue(dev);    //跟前面的xmit函数中相反,允许队列唤醒开始传输数据
    }    
}  
下面这个函数,在TCP/IP一中就介绍过,其注册到数据结构体dm9000_netdev_ops当中,如果不清楚可以回头翻看一下。当内核配置Netconsole时该函数dm9000_poll_controller生效:

#ifdef CONFIG_NET_POLL_CONTROLLER    
/*   
 *Used by netconsole   
 */   
static void dm9000_poll_controller(struct net_device *dev)    
{    
    disable_irq(dev->irq);    
    dm9000_interrupt(dev->irq, dev);    
    enable_irq(dev->irq);    
}   
#endif  

这篇的主要内容是参考了网上的资料,非常感谢。
站在别人的成功之处做为起点,会走得更快更好。

  评论这张
 
阅读(558)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017