《Linux设备驱动开发详解》——第16章 Linux网络设备驱动二

来源:互联网 发布:境外网络电视直播.apk 编辑:程序博客网 时间:2024/05/18 04:55

16.4 网络设备的打开与释放

      网络设备打开:

              使能设备使用的硬件资源,申请I/O区域,中断和DMA通道等。

              调用Linux内核提供的netif_start_queue()函数,激活设备发送队列。

       网络设备关闭:

              调用Linux内核提供的netif_stop_queue()函数,停止设备传输包。

              释放设备所使用的I/O区域,中断和DMA资源。

<pre name="code" class="cpp">
/* 网络设备打开和释放函数模板 */int xxx_open(struct net_device *dev){     /* 申请端口,IRQ等,类似于fops->open */      ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);     ...      netif_start_queue(dev);     ...}

<pre name="code" class="plain">
int xxx_release(struct net_device *dev){     /* 释放端口,IRQ等,类似于 fops->close */     free_irq(dev->irq, dev);     ...     netif_stop_queue(dev);    /* can't transmit any more */     ...}

16.5 数据发送流程

        Linux网络子系统在发送数据包时,会调用驱动程序提供的hard_start_transmit()函数,该函数用于启动数据包的发送。

        网络设备驱动完成数据包发送的流程如下:

      (1)网络设备驱动程序从上层协议传递过来的sk_buff参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区。  

      (2)对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则临时缓冲区的末尾填充0.

      (3)设置硬件的寄存器,驱动网络设备进行数据发送操作。

/* 网络设备驱动程序的数据包发送函数模板 */int xxx_tx(struct sk_buff *skb, struct net_device *dev){   int len;
   char *data , shortpkt[ETH_ZLEN]; 
   if( xxx_send_available(...) ){ /* 发送队列未满,可以发送 */
         /* 获得有效数据指针和长度 */    
         data = skb->data;
         len = skb->len;
         if( len < ETH_ZLEN ){
              /* 如果帧长小于以太网最小长度,补0 */
              memset(shortpkt, 0, ETH_ZLEN);
              len = ETH_ZLEN;
              data = shortpkt;
         } 
         dev->trans_start = jiffies;     /* 记录发送实践戳 */
         /* 设置硬件寄存器让硬件把数据包发送出去 */
         xxx_hw_tx(data, len, dev);
         ...
   }else{
        netif_stop_queue(dev); //当发送队列为满或因其他原因来不及发送当前上层传下来的包,则调用次函数阻止上层继续向网络设备驱动传输数据包
        ...                    //当忙于发送的数据包被发送完成后,TX结束的中断处理中,应该调用netif_wake_queue()唤醒被阻塞的上层,以启动它继续向网络设备驱动传输数据包
   }}
    当数据传输超时时,意味着当前的发送操作失败或硬件已陷入未知状态,此时,数据包发送超时处理函数xxx_tx_timeout()将被调用,这个函数也需要调用Linux内核提供的netif_wake_queue()函数重启设备发送队列。

       /* 网络设备驱动程序的数据包发送超时函数 */       void xxx_tx_timeout(struct net_device *dev)       {           ...           netif_wake_queue(dev); //重启设备发送队列       }
      从上文可知,netif_wake_queue() 和 netif_stop_queue() 是数据发送流程中要调用的两个非常重要的函数,分别用于唤醒和阻止上层向下传送数据包,它们的原型定义在 include/linux/netdevice.h,如下:

              static inline void netif_wake_queue(struct net_device *dev):

              static inline void netif_stop_queue(struct net_device *dev):

16.6 数据接收流程

        网络设备接收数据的主要方式是由中断引发设备的中断处理函数。中断处理函数判断中断类型,如果为接收中断,则读取接收到的数据,分配sk_buff数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()将sk_buff传递给上层协议。

/* 网络设备驱动的中断处理函数 */static void xxx_interrupt(int irq, void *dev_id){        ...        switch(status & ISQ_EVENT_MASK){
             case ISQ_RECEIVER_EVENT:
                  /* 获取数据包 */
                  xxx_rx(dev);
                  break;
                  /* 其他类型的中断 */       }}
<pre name="code" class="cpp">
static void xxx_rx(struct xxx_device *dev){        ...        length = get_rev_len(...);        /* 分配新的套接字缓冲区 */        skb = dev_alloc_skb(length + 2);        skb_reserve(skb, 2);  //对齐        skb->dev = dev;        /* 读取硬件上接收到的数据 */、        insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1);        if( length & 1 )            skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);        /* 获取上层协议类型 */        skb->protocol = eth_type_trans(skb, dev);       /* 把数据包递交给上层 */       netif_rx(skb);       /* 记录接收实践戳 */       dev->last_rx = jiffies;       ...}

     如果是NAPI兼容的设备驱动,则可以通过poll方式接收数据包,这种情况下,我们需要为设备驱动提供作为netif_napi_add()参数的xxx_poll()函数














0 0
原创粉丝点击