cs8900网卡驱动解析(四)

现在开始说一下网卡的收发函数。其实当时程序看到这里的时候我倒有一种灯火伊人的感觉,不过伊人就是伊人,还是有盲区。

首先说一下网卡接收函数。网卡的接收函数是在中断中被调用的。




  while ((status = readword(dev, ISQ_PORT))) {
      DPRINTK(4, "%s: event=%04x\n", dev->name, status);
      switch(status & ISQ_EVENT_MASK) {
      case ISQ_RECEIVER_EVENT:
	/* Got a packet(s). */
	//读取数据包,所以这个case的作用是用来接收的。
	//这个函数的主要作用是将sk_buffer传递给上层协议。
	net_rx(dev);

但是我手中的这套代码不知道为什么没有net_rx的实现,同时linux-2.3.2中也没有实现这一系列代码。不过这个代码的实现如下所示:




static void net_rx(struct net_device *dev) {

    status = inw(ioaddr + RX_FRAME_PORT);
	/*接收的状态和接收的长度*/
    if ((status & RX_OK) == 0) {
      count_rx_errors(status, lp);
      return;
    }

    length = inw(ioaddr + RX_FRAME_PORT);

    /* Malloc up new buffer. */
	/*分配一个skb的结构,+2的原因是:*/
    skb = dev_alloc_skb(length + 2);
    
	if (skb == NULL) {
      lp->stats.rx_dropped++;
      return;
    }
	/*移动指针,空出两个字节,原因是:IP对齐导致的。使用的4字节对齐的方式。*/
    skb_reserve(skb, 2);
    /*  mac头是14个字节,一开始保留两个字节,正是为了保证ip头的开始是四字节对齐的。*/
    skb->len = length;
    skb->dev = dev;
	/*往网卡里面读数据*/
    readblock(dev, skb->data, skb->len);


    skb->protocol=eth_type_trans(skb,dev);
	/*把skb包送给设备无关接口去处理*/
    netif_rx(skb);
}

对了,还要回头说一下这个sk_buf,一句话,只要你要接收和发送数据包,就离不开这个sk_buf,这货是一个大结构体。




struct sk_buff {
         /* These two members must be first. */
         struct sk_buff               *next;
         struct sk_buff               *prev;
 
         struct sock                    *sk;
         ktime_t                          tstamp;
         struct net_device           *dev;
 
         struct  dst_entry          *dst;
         struct sec_path              *sp;
 
         /*
          * This is the control buffer. It is free to use for every
          * layer. Please put your private variables there. If you
          * want to keep them across layers you have to do a skb_clone()
          * first. This is owned by whoever has the skb queued ATM.
          */
         char                     cb[48];
 
         unsigned int         len,
                                     data_len;
         __u16                  mac_len,
                                     hdr_len;
         union {
                   __wsum     csum;
                   struct {
                            __u16         csum_start;
                            __u16         csum_offset;
                   };
         };
         __u32                  priority;
         __u8                    local_df:1,
                                     cloned:1,
                                     ip_summed:2,
                                     nohdr:1,
                                     nfctinfo:3;
         __u8                    pkt_type:3,
                                     fclone:2,
                                     ipvs_property:1,
                                     peeked:1,
                                     nf_trace:1;
         __be16                protocol;
 
         void                     (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
         struct nf_conntrack       *nfct;
         struct sk_buff               *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
         struct nf_bridge_info    *nf_bridge;
#endif
 
         int                        iif;
#ifdef CONFIG_NETDEVICES_MULTIQUEUE
         __u16                           queue_mapping;
#endif
#ifdef CONFIG_NET_SCHED
         __u16                           tc_index;    /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
         __u16                           tc_verd;      /* traffic control verdict */
#endif
#endif
         /* 2 byte hole */
 
#ifdef CONFIG_NET_DMA
         dma_cookie_t               dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
         __u32                           secmark;
#endif
 
         __u32                           mark;
 
         sk_buff_data_t              transport_header;
         sk_buff_data_t              network_header;
         sk_buff_data_t              mac_header;
         /* These elements must be at the end, see alloc_skb() for details.  */
         sk_buff_data_t              tail;
         sk_buff_data_t              end;
         unsigned char               *head,
                                              *data;
         unsigned int                  truesize;
         atomic_t                        users;
};

也许你看到这个庞大的结构(实际上它并不是很庞大)有些晕!但是更令人晕的是内核把sk_buff组织成一个双向链表。而且这个链表的结构要比常见的双向链表的结构还要复杂。sk_buff中用两个指针(nextprev)表示前后一个节点。这个链表还有另一个需求:每个sk_buff结构都必须能够很快找到链表头节点。为了满足这个需求,在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点。在Linux内核世界中,图示永远比代码好理解,所以你要善于看图说话。

本来我很想多介绍一下skb,但是又怕那将令大家再一次陷入内核的迷雾,所以还是见好就收吧。你现在知道,网卡传输数据离不开skb,因此我们需要填充这个skb。当skb搞定后,只要下面一句就可以:

    netif_rx (skb);

它把skb扔到了上层。netif_rx函数原型在net/core/dev.c中,它有两个返回值:

NET_RX_SUCCESS  (no congestion)

NET_RX_DROP     (packet was dropped)

大神一句话总结:网卡的数据接收是通过netif_rx完成的,而传递给netif_rx的是一个包含数据的socket buffer。

 

说完了接收函数再再说发送函数。

发送函数的流程上图一张:


具体的发送函数实现代码如下所示:





//传递进来的参数有skb这个结构体的值,这个是必须的。
static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
    struct net_local *lp = (struct net_local *)dev->priv;
	//读取寄存器的值,貌似是中断寄存器。
	//PP_BusCTL这个寄存器的地址映射和定义都是在内核中完成的。
    writereg(dev, PP_BusCTL, 0x0);
    writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);

    DPRINTK(3, "%s: sent %d byte packet of type %x\n",
	    dev->name, skb->len,
		//
	    (skb->data[ETH_ALEN+ETH_ALEN] << 8) |
	    (skb->data[ETH_ALEN+ETH_ALEN+1]));

    /* keep the upload from being interrupted, since we
       ask the chip to start transmitting before the
       whole packet has been completely uploaded. */
	
	//给中断程序上锁,告诉这暂时是哥的天下了。
    spin_lock_irq(&lp->lock);
	//停止发送队列。
    netif_stop_queue(dev);

    /* initiate a transmit sequence */
	/*这两个函数的整体作用是启动发送队列。*/
    writeword(dev, TX_CMD_PORT, lp->send_cmd);
    writeword(dev, TX_LEN_PORT, skb->len);

    /* Test to see if the chip has allocated memory for the packet */
	//判断此时内存是否分配了可数据包的位置。
    if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
      /*
      /*
       * Gasp!  It hasn't.  But that shouldn't happen since
       * we're waiting for TxOk, so return 1 and requeue this packet.
       */
	  //对中断程序解锁,重头来过。
      spin_unlock_irq(&lp->lock);
      DPRINTK(1, "cs89x0: Tx buffer not free!\n");
      return 1;
    }
    /* Write the contents of the packet */
	//这个函数的实现在上面。
    writeblock(dev, skb->data, skb->len);
	//作者的意思是无论这个包到底行不行,要么被送到上层去了,要么消失了,反正都要重新开始了。
    spin_unlock_irq(&lp->lock);
    dev->trans_start = jiffies;
	//释放skb这个结构。
    dev_kfree_skb (skb);

    /*
     * We DO NOT call netif_wake_queue() here.
     * We also DO NOT call netif_start_queue().
     *
     * Either of these would cause another bottom half run through
     * net_send_packet() before this packet has fully gone out.  That causes
     * us to hit the "Gasp!" above and the send is rescheduled.  it runs like
     * a dog.  We just return and wait for the Tx completion interrupt handler
     * to restart the netdevice layer
     */

    return 0;
}

关于发送函数有很多要注意的,大家看程序吧。同时里面的netif_stop_queue(dev);这个函数的作用是当发送队列为满或者因为其他原因来不及送当前传下来的包,则调用此函数组织上层继续向网络设备驱动传递数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值