两个重要的结构体简单介绍

*sk_buff

如果把网络传输看成是运送货物的话,那么sk_buff就是这个“货物”了,所有经手这个货物的人都要干点什么事儿,要么加个包装,要么印个戳儿等等。收货的时候就要拆掉这些包装,得到我们需要的货物(payload data)。没有货物你还运输什么呢?由此可见sk_buff的重要性了。

*net_device

又是一个庞大的结构体。它在内核中就是指代了一个网络设备。驱动程序需要在探测的时候分配并初始化这个结构体,然后使用register_netdev来注册它,这样就可以把操作硬件的函数与内核挂接在一起。

两个重要结构体的说明

========================================================================================
1.net_device
========================================================================================

)全局信息
   )硬件信息 
   )接口信息
   )设备操作函数
  )辅助成员
     ================
 
     /* 此成员记录了最后的数据包开始发送时的时间戳 */
     unsigned long  trans_start; /* Time (in jiffies) of last Tx */
   
     /* 此成员记录了最后一次接收到数据包时的时间戳,注意:这两个时间戳记录的都是jiffies */
  unsigned long  last_rx; /* Time of last Rx */
 
}

========================================================================================
2.网络设备的初始
========================================================================================
(1)进行硬件上的准备工作,检查网络设备是否存在,如果存在,则检测设备所使用的硬件资源
(2)进行软件接口上的准备工作,分配net_device结构体并对其数据和函数指针成员赋值。
(3)获得设备的私有信息指针并初始化其各成员的值,如果私有信息中包括自选锁或信号量等并
     发或同步机制,则需对其进行初始化。
    
一个网络设备驱动初始化函数的模版如下所示:

void xxx_init(struct net_device *dev)
{
  /* 设备的私有信息结构体 */
  struct xxx_priv *priv
  
  /* 检测设备是否存在和设备所使用的硬件资源 */
  xxx_hw_init();
  
  /* 初始化以太网的共用成员 */
  ether_setup (dev);
  
  /* 设置设备的成员函数指针 */
  dev->open               = cs8900_start;
  dev->stop               = cs8900_stop;
  dev->hard_start_xmit    = cs8900_send_start;
  dev->get_stats          = cs8900_get_stats;
  dev->set_multicast_list = cs8900_set_receive_mode;
  dev->tx_timeout         = cs8900_transmit_timeout;
  dev->watchdog_timeo     = HZ;  
  
  /* 取得私有信息,并初始化它 */
  priv =netdev_priv(dev);
  
  ...  /* 初始化设备私有数据区 */
  
}

xxx_hw_init()函数完成的基本操作如下所示:
(1)探测xxx网络设备是否存在。探测的方法类似与数学上“反证法”,即先假设存在设备xxx,
     访问设备,如果设备的表现与预期的一致,就确定设备存在;否则,假设错误,设备xxx
     不存在
(2)探测设备的具体硬件配置。一些设备驱动编写得非常通用,对于同类设备使用统一的驱动,
     我们需要在初始化时探测设别的具体型号。另外,即便是同一设备,在硬件上的配置也可
     能不一样,我们也可以探测设备所使用的硬件资源
(3)申请设备所需要的硬件资源,如用request_region()函数进行I/O端口的申请等,但是这个
     过程可以放在设备的打开函数xxx_open()中完成。

========================================================================================
3. 数据发送流程
========================================================================================
(1)网络设备驱动程序从上层协议传递过来的sk_buff参数获得数据包的有效数据和长度,将有效数据
     放入一个临时缓冲区
(2)对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则给
    临时缓冲区的末尾填充0
(3)设备硬件的寄存器,驱动网络设备进行数据发送操作

, ETH_ZLEN);
     memcpy(shortpkt, skb->data, skb->len);
     len = ETH_ZLEN;
     data = shortpkt;
   } 
    dev->trans_start = jiffies; /* 记录发送时间戳 */
   /* 设置硬件寄存器让硬件把数据包发送出去 */
   xxx_hw_tx(data, len, dev);
  }   ...
  else
  {
    netif_stop_queue(dev);
    ... 
  }
}

注意:1)当发送队列满或因其他原因来不及发送当前上层传下来的包,则调用此函数阻止上层继续向网络设备
         驱动传递数据包,当忙于发送的数据包发送完成后,TX结束的中断处理中,应该调用netif_wake_queue
         来唤醒被阻塞的上层以启动它继续向网络设备驱动传递数据包。
      2)当数据传输超时时,意味着当前的发送操作失败或硬件已陷入未知状态,此时,数据包发送超时
         处理函数xxx_tx_timeout()将被调用,在此函数中也应该调用netif_wake_queue()函数重新启动
         设备发送队列。

========================================================================================
4. 数据接收流程
========================================================================================
    网络设备接收数据的主要方法是由中断引发设备的中断处理函数,中断处理函数判断中断类型,如果为接收中断,则读取接收到的数据,分配sk_buffer数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()函数将sk_buffer传递给上层协议 
    接收数据流程的典型模版如下所示:

); 
   
   ); );
   )
      kb] = inw(ioaddr + RX_FRAME_PORT); 
    
    /* 获取上层协议类型 */
   skb->protocol = eth_type_trans(skb, dev); 
   /* 把数据包交给上层 */
   netif_rx(skb); 
  
   /* 记录接收时间戳 */
   dev->last_rx = jiffies;
   ...
}

========================================================================================
5. 网络设备的连接状态分析
========================================================================================
    网络适配器硬件电路可以检测出链路上是否有载波,载波反映了网络的连接是否正常。
    网络设备驱动可以通过netif_carrier_on()和netif_carrier_off()函数改变设备的连接状态,
    如果驱动检测到连接状态发生变化,也应该以netif_carrier_on()和netif_carrier_off()函数显式地通知内核。

void netif_carrier_on(struct net_device *dev);
void netif_carrier_off(struct net_device *dev);
int netif_carrier_ok(struct net_device *dev);

========================================================================================
6. 参数设置和统计数据
   在网络设备的驱动程序中还提供一些方法供系统对设备的参数进行设置或读取设备相关的信息。
========================================================================================
    当用户调用ioctl()函数,并制定SIOCSIFHWADDR命令时,意味着要设备这个设备的MAC地址。设置网络设备的
    MAC地址可用如下代码清单模版:

;
}

注意:调用xxx_set_mac函数在网络适配器硬件内写入新的MAC地址。这要求设备在硬件上支持MAC地址的修改,而实际上,
      许多设备并不提供修改MAC地址的接口。

如果用户调用ioctl()时,命令类型在SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之间,系统会调用驱动程序的do_ioctl()函数
进行设备专用数据的设备,这个设置大多数情况下也并不需要。

驱动程序还应提供get_stats()函数用以向用户反馈设备状态和统计信息,该函数返回的是一个net_device_stats结构体,
    如下代码清单模版所示:

static struct net_device_stats *xxx_get_stats(struct net_device *dev)
{
  board_info_t *db = (board_info_t *) dev->priv;
  return &db->stats;
}
net_device_stats结构体定义在内核的include/linux/netdevice.h文件中,它包含了比较完整的统计信息,如代码清单所示:
struct net_device_stats
{
  unsigned long rx_packets;   /* 收到的数据包数 */
  unsigned long tx_packets;   /* 发送的数据包数 */
  unsigned long rx_bytes;   /* 收到的字节数 */
  unsigned long tx_bytes;   /* 发送的字节数 */
  unsigned long rx_errors;   /* 收到的错误数据包数 */
  unsigned long tx_errors;   /* 发送的错误数据包数 */
  ...
  ...
}

@成鹏致远

(blogs:http://lcw.cnblogs.com)

(emailwwwlllll@126.com)

【Linux高级驱动】网卡驱动分析的更多相关文章

  1. Linux下查看网卡驱动和版本信息

    Linux下查看网卡驱动和版本信息 查看网卡生产厂商和信号 查看基本信息:lspci 查看详细信息:lspci -vvv # 3个小写的v 查看网卡信息:lspci | grep Ethernet 查 ...

  2. 新装Linux系统没有网卡驱动的解决办法和步骤

    Linux下查看网卡驱动和版本信息 - CSDN博客 https://blog.csdn.net/guyan1101/article/details/72770424/ 检查网卡是否加载 - Linu ...

  3. 怎样在linux下安装网卡驱动

    由于我电脑的各种奇葩问题的存在,导致我装上Ubuntu13.10之后网卡居然无法使用,坚持了挺久使用无线网,终于坚持不住了,百度了各种解决方式,终于成功解决.这里也记录一下我的解决过程,供大家参考.大 ...

  4. linux回环网卡驱动设计

    回环网卡驱动 1.回环网卡和普通网卡的区别是他是虚拟的不是实际的物理网卡,它相当于把普通网卡的发送端和接收端短接在一起. 2.在内核源代码里的回环网卡程序(drivers/net/loopback.c ...

  5. linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】

    转自:http://my.oschina.net/u/274829/blog/285014 1,ioctl介绍 ioctl控制设备读写数据以及关闭等. 用户空间函数原型:int ioctl(int f ...

  6. Linux高级字符设备驱动

    转载:http://www.linuxidc.com/Linux/2012-05/60469p4.htm 1.什么是Poll方法,功能是什么? 2.Select系统调用(功能)      Select ...

  7. Linux系统安装-MacBook网卡驱动问题解决

    先附上MacBook的linux安装教程 需要注意的是第7步中可能无法识别出OS X的系统,也没关系,只要格式化磁盘的时候注意选择对应磁盘即可,格式化成EXT4分区. 安装好后发现无法连接无线网络,应 ...

  8. Linux高级字符设备驱动 poll方法(select多路监控原理与实现)

    1.什么是Poll方法,功能是什么? 2.Select系统调用(功能)      Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程.      int selec ...

  9. Linux网卡驱动

    <网络知识> a:网络模型               OSI模型               TCP模型 虽然OSI模型看着挺完美的,但是过于复杂,这样就会导致不实用,在Linux系统中 ...

  10. 【Linux高级驱动】如何分析并移植网卡驱动

    dm9000的驱动分析 m9000_init platform_driver_register(); db); db); );  ; id_val ; id_val ; /* 获取芯片型号 */ id ...

随机推荐

  1. 《Gradle权威指南》--Groovy基础

    No1: Groovy中分号不是必须的 No2: Groovy中,单引号和双引号都可以定义一个字符串常量,不同的是单引号标记的是纯粹的字符串常量,而不是对字符串里的表达式做运算,但是双引号可以. ta ...

  2. Visual Studio Code-GO tasks 设置 (实现在vsc下直接编译输出的功能)

    Visual Studio Code -GO 使用过程中发现,如果要编译输出某个文件需要去cmd窗口才行,感觉特别麻烦网上一直没找到解决办法,这几天查看Visual Studio Code文档发现它提 ...

  3. hdu1573 X问题【中国剩余定理】

    <题目链接> X问题 Problem Description 求在小于等于N的正整数中有多少个X满足:X mod a[0] = b[0], X mod a[1] = b[1], X mod ...

  4. 康托展开&&康托逆展开

    康托展开 简介:对于给定的一个排列,求它是第几个,比如54321是n=5时的第120个.(对于不是1~n的排列可以离散化理解) 做法: ans=a[n]*(n-1)!+a[n-1]*(n-2)!+~~ ...

  5. 004.Kickstart部署之FTP架构

    一 准备 1.1 完整架构:Kickstart+DHCP+VSFTP+TFTP+PXE 1.2 组件应用 Kickstart服务端IP:172.24.8.12 DHCP:提供客户端IP,网关,镜像路径 ...

  6. PHP7和PHP5在安全上的区别[更新]

    0X01 前言 本篇文章大多为转载,但是修正了一些不正确的说法,对某些功能点的变更指出具体是哪个版本变更,加入了一些小更新. (原文地址:https://www.freebuf.com/article ...

  7. [BJOI2014]大融合

    Description 给你一个n个点的森林,要求支持m个操作: 1.连接两个点 x,y 2.询问若断掉 x,y这条边,两点所在联通块乘积的大小 Hint: \(n,m<=10^5\) Solu ...

  8. C#高级编程9 第18章 部署

    C#高级编程9 第18章 部署 使用 XCopy 进行部署 本主题演示如何通过将应用程序文件从一台计算机复制到另一台计算机来部署应用程序. 1.将项目中生成的程序集复制到目标计算机,生成的程序集位于项 ...

  9. Zookeeper学习笔记——1 单机版本环境搭建

    下载 首先去官网下载: http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.11/ 然后执行tar -zxvf解压 启动 进入conf目录, ...

  10. Android开发中遇到的问题(三)——eclipse创建android项目无法正常预览布局文件

    一.问题描述 今天使用SDK Manager将Android SDK的版本更新到了Android 5.1的版本,eclipse创建android项目时,预览activity_main.xml文件时提示 ...