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

*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. Sensor传感器(摇一摇)

    <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content ...

  2. ADI SHARC 学习之PLL

    PLL Block Diagram             Power Management Registers (PMCTL, PMCTL1)The following sections descr ...

  3. Python爬虫之PyQuery使用(六)

    Python爬虫之PyQuery使用 PyQuery简介 pyquery能够通过选择器精确定位 DOM 树中的目标并进行操作.pyquery相当于jQuery的python实现,可以用于解析HTML网 ...

  4. npm link中文文档

    Symlink(符号链接)一个package文件夹. 概括 npm link (in package dir) npm link [<@scope>/]<pkg>[@<v ...

  5. 三篇文章带你极速入门php(三)之php原生实现登陆注册

    看下成果 ps:纯天然h5,绝不添加任何添加剂(css)以及化学成分(js)(<( ̄ ﹌  ̄)我就是喜欢纯天然,不接受任何反驳) 关于本文 用原生的php和html做了一个登陆注册,大概是可以窥 ...

  6. 利用FutureTask进行超时设置方法

    public class Test {   public static void main(String[] args) { ExecutorService executor = Executors. ...

  7. 14,EasyNetQ-使用EasyNetQ.Hosepipe重新提交错误消息

    EasyNetQ队列管理实用程序. 用它从队列中抓取消息并重新发布. 还可以用它来检查错误队列消息并重试它们. 1,用法: EasyNetQ.Hosepipe.exe <command> ...

  8. BZOJ.2034.[2009国家集训队]最大收益(二分图匹配 贪心)

    题目链接 双倍经验:BZOJ.4276.[ONTAK2015]Bajtman i Okrągły Robin(然而是个权限题.区间略有不同) \(Description\) 有\(n\)个任务,完成一 ...

  9. 张量系列-Tensor(01)

    张量——N-dim 数组 1. 数组的创建 2. 符号数组的创建 3. 一维数组改变形状创建 4. 切片操作 5. 符号数组操作 6. 数组转化为列表 7. 维度为2的数组可以转化为矩阵  

  10. [HackerRank]Choosing White Balls

    [HackerRank]Choosing White Balls 题目大意: 有\(n(n\le30)\)个球排成一行,每个球的颜色为黑或白. 执行\(k\)次操作,第\(i\)次操作形式如下: 从\ ...