当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]Linux网络驱动程序功能分析

1.驱动模块的加载和卸载

  如果网络设备(包括wireless)是PCI规范的,则先是向内核注册该PCI设备(pci_register_driver),然后由pci_driver数据结构中的probe函数指针所指向的侦测函数来初始化该PCI设备,并且同时注册和初始化该网络设备。

  如果网络设备(包括wireless)是PCMCIA规范的,则先是向内核注册该PCMCIA设备(register_pccard_driver),然后driver_info_t数据结构中的attach函数指针所指向的侦测函数来初始化该PCMCIA设备,并且同时注册和初始化该网络设备。

  static int __init tg3_init(void)

  {

  //先注册成PCI设备,并初始化,如果是其他的ESIA,PCMCIA,用其他函数

  return pci_module_init(&tg3_driver);

  }

  static void __exit tg3_cleanup(void)

  {

  pci_unregister_driver(&tg3_driver);//注销PCI设备

  }

  module_init(tg3_init); //驱动模块的加载

  module_exit(tg3_cleanup); //驱动模块的卸载

  申明为PCI设备:

  static struct pci_driver tg3_driver = {

  .name = DRV_MODULE_NAME,

  .id_table = tg3_pci_tbl, //此驱动所支持的网卡系列,vendor_id, device_id

  .probe = tg3_init_one, //初始化网络设备的回调函数

  .remove = __devexit_p(tg3_remove_one), //注销网络设备的回调函数

  .suspend = tg3_suspend, //设备挂起函数

  .resume = tg3_resume //设备恢复函数

  };

  2.PCI设备探测函数probe,初始化网络设备

  static int __devinit tg3_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)

  {

  //初始化设备,使I/O,memory可用,唤醒设备

  pci_enable_device(pdev);

  //申请内存空间,配置网卡的I/O,memory资源

  pci_request_regions(pdev, DRV_MODULE_NAME);

  pci_set_master(pdev);

  //设置DMA属性

  pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff);

  //网卡 I/O,memory资源的启始地址

  tg3reg_base = pci_resource_start(pdev, 0);

  //网卡I/O,memory资源的大小

  tg3reg_len = pci_resource_len(pdev, 0);

  //分配并设置网络设备

  dev = alloc_etherdev(sizeof(*tp));

  //申明为内核设备模块

  SET_MODULE_OWNER(dev);

  //初始化私有结构中的各成员值

  tp = dev->priv;

  tp->pdev = pdev;

  tp->dev = dev;

  ……

  //锁的初始化

  spin_lock_init(&tp->lock);

  //映射I/O,memory地址到私有域中的寄存器结构

  tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len);

  dev->irq = pdev->irq;

  //网络设备回调函数赋值

  dev->open = tg3_open;

  dev->stop = tg3_close;

  dev->get_stats = tg3_get_stats;

  dev->set_multicast_list = tg3_set_rx_mode;

  dev->set_mac_aDDRess = tg3_set_mac_addr;

  dev->do_ioctl = tg3_ioctl;

  dev->tx_timeout = tg3_tx_timeout;

  dev->hard_start_xmit= tg3_start_xmit;

  //网卡的MAC地址赋值dev->addr

  tg3_get_device_address(tp);

  //注册网络设备

  register_netdev(dev);

  //把网络设备指针地址放入PCI设备中的设备指针中

  pci_set_drvdata(pdev, dev);

  }

  3.注销网络设备

  static void __devexit tg3_remove_one(struct pci_dev *pdev)

  {

  struct net_device *dev = pci_get_drvdata(pdev);

  //注销网络设备

  unregister_netdev(dev);

  //取消地址映射

  iounmap((void *) ((struct tg3 *)(dev->priv))->regs);

  //释放网络设备

  kfree(dev);

  //释放PCI资源

  pci_release_regions(pdev);

  //停用PCI设备

  pci_disable_device(pdev);

  //PCI设备中的设备指针赋空

  pci_set_drvdata(pdev, NULL);

  }

  4.打开网络设备

  static int tg3_open(struct net_device *dev)

  {

  //分配一个中断

  request_irq(dev->irq, tg3_interrupt, SA_SHIRQ, dev->name, dev);

  /* int request_irq(unsigned int irq,

  void (*handler)(int irq, void *dev_id, struct pt_regs *regs),

  unsigned long irqflags,

  const char * devname,

  void *dev_id);

  irq是要申请的硬件中断号。在Intel平台,范围0--15。handler是向系统登记的中断处理函数。这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,device id,寄存器值。dev_id就是下面的request_irq时传递给系统的参数dev_id。irqflags是中断处理的一些属性。比较重要的有SA_INTERRUPT,标明中断处理程序是快速处理程序(设置SA_INTERRUPT)还是慢速处理程序(不设置SA_INTERRUPT)。快速处理程序被调用时屏蔽所有中断。慢速处理程序不屏蔽。还有一个SA_SHIRQ属性,设置了以后运行多个设备共享中断。dev_id在中断共享时会用到。一般设置为这个设备的device结构本身或者NULL。中断处理程序可以用dev_id找到相应的控制这个中断的设备,或者用rq2dev_map找到中断对应的设备。*/

  //初始化硬件

  tg3_init_hw(tp);

  //初始化收包和发包的缓冲区

  tg3_init_rings(tp);[!--empirenews.page--]
//初始化定时器

  init_timer(&tp->timer);

  tp->timer.expires = jiffies + tp->timer_offset;

  tp->timer.data = (unsigned long) tp;

  tp->timer.function = tg3_timer; //超时回调函数

  add_timer(&tp->timer);

  //允许网卡开始传输包

  netif_start_queue(dev);

  }

  5.关闭网络设备

  static int tg3_close(struct net_device *dev)

  {

  //停止网卡传输包

  netif_stop_queue(dev);

  netif_carrier_off(tp->dev);

  //去除定时器

  del_timer_sync(&tp->timer);

  //释放收包和发包的缓冲区

  tg3_free_rings(tp);

  //释放中断

  free_irq(dev->irq, dev);

  }

  [NextPage]

  6.硬件处理数据包发送

  static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)

  {

  len = (skb->len - skb->data_len);

  //以DMA方式向网卡物理设备传输包。如果是wireless的话,需要根据802.11协议及硬件的规范从新填充

  //硬件帧头,然后提交给硬件发送。

  mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE);

  tp->tx_buffers[entry].skb = skb;

  pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping);

  //硬件发送

  tg3_set_txd(tp, entry, mapping, len, base_flags, mss_and_is_end);

  //记录发包开始时间

  dev->trans_start = jiffies;

  }

  7.中断处理收包,发包

  static void tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs)

  {

  //如果要收包

  tg3_rx(tp);

  //如果要发包

  tg3_tx(tp);

  }

  8.发包

  static void tg3_tx(struct tg3 *tp)

  {

  struct tx_ring_info *ri = &tp->tx_buffers[sw_idx];

  struct sk_buff *skb = ri->skb;

  //以DMA方式向网卡传输包完毕

  pci_unmap_single(tp->pdev, pci_unmap_addr(ri, mapping),

  (skb->len - skb->data_len), PCI_DMA_TODEVICE);

  ri->skb = NULL;

  dev_kfree_skb_irq(skb);

  }

  9.收包

  static int tg3_rx(struct tg3 *tp, int budget)

  {

  struct sk_buff *copy_skb;

  //分配一个包

  copy_skb = dev_alloc_skb(len + 2);

  copy_skb->dev = tp->dev;

  //修改包头空间

  skb_reserve(copy_skb, 2);

  //加入数据到包中

  skb_put(copy_skb, len);

  //以DMA方式从网卡传输回数据

  pci_dma_sync_single(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);

  memcpy(copy_skb->data, skb->data, len);

  skb = copy_skb;[!--empirenews.page--]
 //解析包的协议

  skb->protocol = eth_type_trans(skb, tp->dev);

  //把包送到协议层

  netif_rx(skb);

  //记录收包时间

  tp->dev->last_rx = jiffies;

  }

  10.读取包的网卡收发包的状态,统计数据

  static struct net_device_stats *tg3_get_stats(struct net_device *dev)

  {

  //从硬件相关的寄存器读取数据,累加

  //stats->rx_packets, stats->tx_packets, stats->rx_bytes, stats->tx_bytes等

  }

  11.用户的ioctl命令系统调用

  static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)

  {

  struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;

  switch(cmd) {

  //ethtool程序命令的调用

  case SIO*HTOOL:

  return tg3_ethtool_ioctl(dev, (void *) ifr->ifr_data);

  //mii程序命令的调用

  case SIOCGMIIREG: {

  err = tg3_readphy(tp, data->reg_num & 0x1f, &mii_regval)

  data->val_out = mii_regval;

  return err;

  }

  ……

  }

  }

  12.PCI设备的挂起和恢复函数

  static int tg3_suspend(struct pci_dev *pdev, u32 state)

  {

  //停用网卡的中断寄存器

  tg3_disable_ints(tp);

  //停止网卡收发包

  netif_device_detach(dev);

  //停止网卡某些硬件,fireware的一些功能

  tg3_halt(tp);

  //设置网卡的电源状态

  tg3_set_power_state(tp, state);

  }

  static int tg3_resume(struct pci_dev *pdev)

  {

  //恢复网卡电源

  tg3_set_power_state(tp, 0);

  //允许网卡收发包

  netif_device_attach(dev);

  //初始化收发包的缓冲区

  tg3_init_rings(tp);

  //初始化网卡硬件

  tg3_init_hw(tp);

  //打开网卡中断寄存器

  tg3_enable_ints(tp);

  }

  13.参数设置

  在驱动程序里还提供一些方法供系统对设备的参数进行设置和读取信息。一般只有超级用户(root)权限才能对设备参数进行设置。设置方法有:

  tg3_set_mac_addr (dev->set_mac_address)

  当用户调用ioctl类型为SIOCSIFHWADDR时是要设置这个设备的mac地址。一般对mac地址的设置没有太大意义的。

  dev->set_config()

  当用户调用ioctl时类型为SIOCSIFMAP时,系统会调用驱动程序的set_config方法

  用户会传递一个ifmap结构包含需要的I/O、中断等参数。

  总结:

  所有的Linux网络驱动程序遵循通用的接口。设计时采用的是面向对象的方法。一个设备就是一个对象(net_device 结构),它内部有自己的数据和方法。一个网络设备最基本的方法有初始化,发送和接收。

  Linux网络驱动程序的体系结构可以划分为四层:

  网络协议接口,网络设备接口,设备驱动功能,网络设备和网络媒介层

  网络驱动程序,最主要的工作就是完成设备驱动功能层。在Linux中所有网络设备都抽象为一个接口,这个接口提供了对所有网络设备的操作集合。由数据结构struct net_device来表示网络设备在内核中的运行情况,即网络设备接口。它既包括纯软件网络设备接口,如环路(Loopback),也包括硬件网络设备接口,如以太网卡。而由以dev_base为头指针的设备链表来集体管理所有网络设备,该设备链表中的每个元素代表一个网络设备接口。数据结构net_device中有很多供系统访问和协议层调用的设备方法,包括初始化,打开和关闭网络设备的open和stop函数,处理数据包发送的hard_start_xmit函数,以及中断处理函数等。

  网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭