【网络BSP开发经验】Linux gmac驱动调试(FH8626)

news2024/11/18 22:33:57

文章目录

  • Linux网络设备驱动简介
  • Linux网卡驱动
    • 网络协议接口层
    • 网络设备接口层
    • 设备驱动功能层
    • 网络设备与媒介层
    • linux驱动数据结构
    • linux驱动注册过程
    • 网络设备驱动的注册与注销
    • linux驱动数据包收发流程
  • Linux PHY驱动
    • MDIO接口
    • PHY简介
    • PHY关联过程
    • PHY状态机
    • 对端MAC情况(接switch芯片)的处理
  • 总结

Linux网络设备驱动简介

网络设备驱动是linux内核中三大类设备驱动之一,它用来完成高层网络协议的底层数据传输及设备控制。
网络设备与其他两种设备的区别:
网络接口不存在于linux的文件系统中,及/dev下没有设备文件,用户通过套接口socket函数使用网络。
网络除了响应来自内核的请求外,还需要处理外界的异步数据除了数据处理,网络设备还要完成地址设置、配置网络参数及流量统计等管理任务。
提到网卡不得不提一下网络分层模型,在互联网发展过程中出现过两种协议分层模型,及OSI模型和TCP/IP参考模型,但事实上被采用的是TCP/IP模型。
ISO模型和TCP/IP模型:

根据TCP/IP参考模型,网络设备及其驱动程序实际上完成的是最底层的主机到网络层。

在linux中,网络驱动子系统被设计成与协议无关,网络驱动只需负责具体的数据收发过程,而上层协议相关内容,由内核的网络协议栈完成。

Linux网卡驱动

linux网络设备驱动层次结构为

在这里插入图片描述

网络协议接口层

给上层协议提供统一的数据包收发接口,无论上层是ARP协议还是IP协议,都通过dev_queue_xmit函数发送数据,对数据包的接收通过netif_rx函数实现,函数原型:

dev_queue_xmit(struct sk_buff *skb)

netif_rx(struct sk_buff *skb)

sk_buff结构体:套接字缓冲区,用于在linux网络子系统中各层间传输数据,发送数据时内核协议栈将建立好的sk_buff交给网络驱动部分,当网络设备接收到数据,网络驱动要数据转换为sk_buff传给协议栈。

网络设备接口层

为千变万化的网络设备定义统一的、抽象的数据结构net_device结构体,以不变应万变,实现多种硬件在软件层次上的统一,包含网络设备的属性描述和操作接口。
net_device结构体在内核中指代一个网络设备,网络设备驱动只需填充其结构体并注册到内核就可以实现内核与具体硬件操作函数的挂接。
net_device结构体很复杂,它包含网络设备的属性描述和操作接口。

设备驱动功能层

对应net_device结构体中的设备驱动功能函数,例如xxx_open()、xxx_stop()、xxx_tx()等。
另一个主题部分是中断处理函数,它负责接收硬件上的数据包并传给上层协议,主要函数有xxx_interrupt()和xxx_rx(),前者完成中断类型判断及处理,后者则将从硬件获得的数据进行封包并交给上层。

网络设备与媒介层

对应实际的硬件设备,对设备操作进行更一般的描述主要是一些访问网络设备内部寄存器的操作。具体的接口函数与特定的硬件紧密相关。
物理网卡通常包括PHY和MAC两个控制器,在OSI七层模型中,PHY指物理层,定义数据收发所需要的电气特性;MAC对应数据链路层,提供寻址机构、数据帧的构建、数据差错检查、传送控制。

linux驱动数据结构

在linux系统中所有的网络设备都被抽象为一个接口,这个接口提供了对所有网络设备的操作集合。
数据结构 struct net_device 就是网络设备接口,它既包含了纯软件网络设备接口,如环路(lo),又包含了硬件网络设备接口,如以太网(ethX),我们构建网络设备驱动的核心就是构建此数据结构。
该结构体只有部分成员会被驱动程序用到,其它成员仅提供给内核使用。
net_device部分成员介绍
char name[]:网络设备名称,名称字符串末尾的数字表示统一类型的多个适配器。
以下是一些网络设备的命名:

  • ethN 以太网接口包括10Mbps/100Mbps/1000Mbps
  • wifi0 无线网络接口
  • pppN ppp网络接口
  • isdnN ISDN网络接口
  • lo 回送网络接口

struct net_device_ops *netdev_ops

网络设备方法操作集,该数据结构定义了针对当前设备的一组操作集合,

比如ndo_open、ndo_stop和ndo_start_xmit等。

  • struct netdev_hw_addr_list uc:网络设备的单播MAC地址列表。
  • struct netdev_hw_addr_list mc:网络设备多播MAC地址列表。
  • struct netdev_hw_addr_list dev_addrs:网络设备硬件地址链表,net_device通过该成员将当前设备添加到链表中。
  • struct netdev_queue rx_queue:网络设备接收队列。
  • struct netdev_queue *_tx:网络设备发送队列。
  • watchdog_timeo:用于设定网络设备在传输数据包时传输超时的到期时间。

设备方法net_device_ops

网络设备最核心的功能是收发数据包,此外还需要提供配置与统计功能,这些被称之为设备方法
设备方法的实现依赖于具体的硬件环境,该部分正好对应了网卡驱动中的设备功能层。
从不同的硬件设备中提取具有共性的东西进行统一描述和操作,形成了net_device_ops设备方法,以下是linux中该结构体的部分成员:

在这里插入图片描述

针对网络设备操作集,我们需要根据设备的实际功能选择性的实现接口函数。
介绍几个接口函数:

int (*ndo_init)(struct net_device *dev):为具体硬件的初始化提供接口

int (*ndo_open)(struct net_device *dev)、int (*ndo_stop)(struct net_device *dev):使用ifconfig打开或关闭网络接口时,以上函数最终被调用。

netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,struct net_device *dev):

完成数据包的发送任务,skb为待发送的数据包,dev则为本次用来发送网络数据包的设备对象
提到数据的发送不得不提数据包的接收,由于网络设备接收数据是随机的,它采用中断的方式让驱动程序响应。

linux驱动注册过程

net_device对象的分配和释放由于net_device结构体比较复杂,申请时需要进行一系列的初始化,所以我们使用内核提供的接口来完成申请和释放任务。
接口宏:
alloc_netdev():申请一个net_device对象并初始化。

alloc_etherdev():申请一个具体针对以太网卡设备的net_device对象同时初始化。

free_netdev():释放alloc_netdev等分配的系统资源。

网络设备驱动的注册与注销

int register_netdev(struct net_device *dev) 该函数完成注册网络设备驱动到linux内核,该函数会为设备分配一个接口名称,然后进行设备的注册。

int unregister_netdev(struct net_device *dev)注销设备驱动,释放占用的系统资源。

linux驱动数据包收发流程

数据收发流程概述:
发送:发送数据时,linux内核的网络处理模块必须建立一个包含要传输的数据包的sk_buff,然后将sk_buff递交给下层,各层在sk_buff中添加不同的协议头直至交给网络设备发送。
接收:当网络设备从媒介收到数据包后,它必须将数据转换为sk_buff数据结构并传递给上层,各层剥去相应的协议头直至交给用户。
相关数据结构sk_buff
sk_buff称为“套接字缓冲区”,用于在linux网络子系统中各层之间传递数据。是Linux网络子系统数据传递的“中枢神经”,是IP层与链路层交流的桥梁。 sk_buff的申请和释放:

struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)

struct sk_buff *dev_alloc_skb(unsigned int length)

void kfree_skb(struct sk_buff *skb)

void dev_kfree_skb(struct sk_buff *skb)

sk_buff成员:

sk_buff_data_t tail;

sk_buff_data_t end;
unsigned char *head, *data;

head指向已分配空间的头,end指向尾,这俩值是不变的;data和tail由模块根据需要进行修改,如图:
在这里插入图片描述

数据包的发送流程

在这里插入图片描述

数据包的接收流程

在这里插入图片描述

在这里插入图片描述

Linux PHY驱动

MDIO接口

phy驱动往往是在注册MDIO总线的过程中顺便创建的,然后通过MDIO_BUS, 进行phy_driver 和 phy_device的匹配,匹配的依据就是phy的id,这种一般每一种phy芯片都是不一样的。

在这里插入图片描述

在这里插入图片描述

static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
	struct phy_device *phydev = to_phy_device(dev);
	struct phy_driver *phydrv = to_phy_driver(drv);

	return ((phydrv->phy_id & phydrv->phy_id_mask) ==
		(phydev->phy_id & phydrv->phy_id_mask));
}

PHY简介

phy 的驱动代码
drivers/net/phy/*

设计 phy 驱动框架的目的
大部分网络设备由向 MAC 层提供接口的多个寄存器的集合组成,MAC 层通过 PHY 与物理链路连接。

phy自身要解决与对端网络链接时链路参数协商的功能,并且提供一个寄存器接口让驱动来确定当前选择的配置,同时驱动也能够配置那些 phy 运行的设置。

phy与网络设备有明确的区别,并符合寄存器的标准布局。常见的网卡设备设计中会在网卡驱动中集成 phy 的管理代码。这样的方式产生了一大堆不能复用的代码。同时,在一些具有多个以太网控制器并连接到相同总线的设备上,要确保安全的使用总线相对困难。

phy 也是一种设备,用于访问 phy 的管理总线实际上也是一种总线,phy 抽象层就是这样对待它们的。这样做的好处如下:

增加代码的可复用性
提高整个代码的可维护性
加速新驱动、新系统开发的时间

有时在启动过程中,网络驱动需要建立网络设备与 phy 设备之间的连接。在这种情况下,phy 的总线和驱动及其它所有相关的部分都需要被加载,phy 进入就绪状态等待连接。

在这个条件下,有几种不同的连接到 phy 的方式。

  • phy 抽象层控制一切,并且只在链路状态改变的时候调用网络驱动,网卡能够做出反应
  • phy 抽象层控制除中断外的所有状态
  • phy 抽象层控制一切状态,但是每秒在网络驱动中检测一次状态,这样的过程允许网络驱动在 phy 抽象层反应之前做出响应
  • phy 抽象层只提供函数库,网络设备自行调用这些函数来更新状态并且配置 phy

PHY关联过程

让 phy 抽象层控制一切
如果你选择使用第一种方式,连接到 phy 非常简单,主要有以下过程:

首先你需要一个对链接状态改变做出响应的函数,这个函数的原型如下:

static void fh_gmac_adjust_link(struct net_device *dev);

然后你需要知道连接到网卡设备的 phy 的设备名称。这个名称看上去像 “0:00” 这种格式,第一个数字是总线 id,第二个数字是在总线上的 phy 地址。一般而言,总线负责唯一标识自己的 id。

这种phy设备产生与系统注册mdio总线的过程中,系统注册mdio总线具有如下流程:

1、调用 mdiobus_alloc 分配一个mii_bus数据结构。

2、填充mii_bus数据,包括read/write函数。

3、mdiobus_register注册mii_bus总线,探测设备并注册一个phy设备,
这个phy设备记录了phy的id信息。

在每一条注册的总线上都会对0-31号地址进行扫描,如果有设备就会注册phy_device。

现在,可以调用如下函数连接到 phy:

phydev = phy_connect(dev, phy_name, &fh_gmac_adjust_link, interface);

phydev 是一个指向 phy_device 结构体的指针,该结构体代表了 phy。如果 phy_connect 执行成功,它将会返回这个指针,这里的 dev 参数是指向你的网络设备的。一旦完成,这个函数将会启动 phy 的软件状态机并且注册 phy 的中断,如果 phy 支持的话。phydev 结构体将会提供当前状态信息,尽管在这个点 phy 还没有真正运行。

PHY状态机

enum phy_state {
    PHY_DOWN = 0,  //0 down 如关闭网卡,ifconfig eth0 down
    PHY_STARTING,   //1 PHY芯片OK了,但驱动还没有准备好
    PHY_READY,      //2 phy设备注册成功
    PHY_PENDING,    //3 phy芯片挂起
    PHY_UP,         //4 开启网卡,ifconfig eth0 up
    PHY_AN,         //5 网卡自动协商
    PHY_RUNNING,    //6 网卡上已插入网线、并建立物理连接
    PHY_NOLINK,     //7 断网,如拔掉网线
    PHY_FORCING,    //8 自动协商标识未被使能,就强制执行自动协商(读取phy寄存器、并设置通讯速率、半双工或全双工模式、等)
    PHY_CHANGELINK, //9 当连接时,会换到PHY_RUNNING,当断网时,会切到PHY_NOLINK
    PHY_HALTED,     //10 即phy挂起
    PHY_RESUMING    //11 即phy恢复
};

在这里插入图片描述

PHY状态切换

在这里插入图片描述

主要说明如下:

  1. 当phy_device处于PHY_UP状态时,则phy_device状态机处理接口,设置needs_aneg为1,调用phy_start_ange接口,进入PHY_AN状态或PHY_FORCING状态,进行phy_device的状态的协商;
  2. 在PHY_AN状态时,则phy_state_machine判断是否需要将状态跳转至PHY_RUNNING或PHY_NOLINK;
  3. 在PHY_RUNNING状态时,若phy_device不支持中断,则将其状态跳转至PHY_CHANNGELINK状态,由phy_state_machine接口来轮询判断phy_device的状态是否跳转至PHY_RUNNING/PHY_NOLINK;

而像PHY_DOWN、PHY_START、PHY_READY、PHY_UP、PHY_HALTED、

PHY_RESUMING这几个状态,一般由外部模块调用相应的接口进行跳转,而非phy_state_machine自动流转。

针对net_device也有相应的状态定义,如下图所示主要涉及如下几个状态

enum netdev_state_t {

__LINK_STATE_START,

__LINK_STATE_PRESENT,

__LINK_STATE_NOCARRIER,

__LINK_STATE_LINKWATCH_PENDING,

__LINK_STATE_DORMANT,

};
  1. 当net_device注册至系统时,即设置__LINK_STATE_PRESENT;
  2. 当执行ifconfig xxx up,启用一个net_device时,则将其状态设置为__LINK_STATE_START(记住此时并不代表phy已完成协商且处于linkup状态),并在调用net_device->netdev_ops->ndo_open时, 会启动phy状态机,状态机中会设置__LINK_STATE_NOCARRIER;
  3. 当调用phy_connect完成phy_device与net_device的绑定以及启动phy_device的状态机后,且phy_device跳转至PHY_RUNNING的同时,清除__LINK_STATE_NOCARRIER位,此时方才表明可进行数据通信。

在这里插入图片描述

对端MAC情况(接switch芯片)的处理

有一种情况就是gmac对端并没有接一个phy芯片,而是接到了一个switch上,而这个switch芯片直接与cpu gmac对接,没有Phy中转,这种情况下一般是将一端配置成phy模式,作为时钟的输出端,另外一段就需要关闭时钟。下图是大体框图:

在这里插入图片描述

这个情况理论上 需要将gmac驱动中的phy指向 ,switch的smi接口中的cpu_port。但是在部分芯片中这个phy并不是标准的phy寄存器组织方式,导致gmac对接无法直接进入link状态,为了解决这个问题,我们需要虚拟一个mdio总线,保证cpu与switch连接端口对应的phy一直处于Link状态。

在这里插入图片描述

总结

我们说的Linux的网络设备驱动,可以分为四个部分,网络协议接口层,网络设备接口层,设备驱动功能层,硬件结构层。而贯穿这个四个层,我们在网络设备驱动编写的过程,首先申请的也是这么一个net_device结构体,在这个结构体中就包括DMA通道,中断号,设备的MAC地址以及操作函数,而这个操作函数就是整个网络设备驱动功能层的集合,像这个open,stop等函数,在open中就去实现这个硬件初始化,申请中断号,IO区域,DMA通道等。还有一个比较重要的结构就是缓存区,我们用结构体skb_buff 表示,在Linux中网络数据包就是以这种结构进行传输的,且相关的协议头的添加以及去除,也是通过操作这个结构体来完成的。此外调试gmac往往最需要关注phy驱动,因为一般很少有机会自己写gmac驱动,芯片厂商都会提供,我们需要重点关注cpu与phy或者switch之间的时钟,以及gmac关联的phy设备所处的状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/946408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

便携式水质检测仪都测哪些水中指标

水质检测仪分为实验室(台式)和户外使用的便携式多参数水质检测仪。 便携式的有哪些特点? 相对于实验室的水质分析设备,便携式水质多参数分析仪体积小巧,结构简单,户外使用更加便捷,功能更丰富。…

OLED透明屏暗斑问题解析:原因、解决方案与行业趋势

OLED透明屏作为一项创新技术,广泛应用于广告、零售和汽车等领域,其高透明度和出色的显示效果备受青睐。 然而,一些用户反映在使用过程中出现了暗斑问题,影响了显示效果。 那么,在这篇文章中,尼伽将和大家…

RT_Thread内核机制学习(六)互斥量

互斥量 互斥量是特殊的信号量,资源个数只能是0、1,实现了优先级继承。 互斥量优点:谁拥有谁释放,优先级继承。 信号量的缺点:谁都可以释放信号量、优先级反转。 HP被MP抢占,优先级反转。 互斥量实现了优…

MySQL备份和还原

前言 mysql日志默认保存在/usr/local/mysql/data 常见的日志: 1、错误日志 2、一般查询日志 3、二进制日志 4、中继日志 5、重做日志 6、回滚日志 7、慢查询日志 配置文件位置 vim /etc/my.cnf 错误日志,用来记录当MySQL启动、停止或运行时发生的错误信…

Java 数据结构使用学习

Set和List的区别 Set 接口实例存储的是无序的&#xff0c;不重复的数据。List 接口实例存储的是有序的&#xff0c;可以重复的元素。 Set 检索效率低下&#xff0c;删除和插入效率高&#xff0c;插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。 List 和数…

Linux-crontab使用问题解决

添加定时进程 终端输入&#xff1a; crontab -e选择文本编辑方式&#xff0c;写入要运行的脚本&#xff0c;以及时间要求。 注意&#xff0c;如果有多个运行指令分两种情况&#xff1a; 1.多个运行指令之间没有耦合关系&#xff0c;分别独立&#xff0c;则可以直接分为两个…

C++学习记录——이십구 异常

文章目录 1、异常概念2、实际用法3、C标准库的异常体系4、重新抛出异常5、优缺点 1、异常概念 C语言处理错误有assert&#xff0c;返回错误码来处理错误的方式&#xff0c;不过release模式下assert无效&#xff0c;错误码需要程序员自己去查看是什么错误。 C认为应当能给到程…

LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略

LLMs之Code&#xff1a;Code Llama的简介、安装、使用方法之详细攻略 导读&#xff1a;2023年08月25日(北京时间)&#xff0c;Meta发布了Code Llama&#xff0c;一个可以使用文本提示生成代码的大型语言模型(LLM)。Code Llama是最先进的公开可用的LLM代码任务&#xff0c;并有潜…

会玩这10个Linux命令,一定是个有趣的IT男!

Linux当中有很多比较有趣的命令&#xff0c;可以动手看看&#xff0c;很简单的。 1、rev命令 一行接一行地颠倒所输入的字符串。 运行&#xff1a; $rev 如输入&#xff1a;shiyanlou shiyanlou 2、asciiview命令 1)先安装aview $sudo apt-get install aview 2)再安装…

第八周第二天学习总结 | MySQL入门及练习学习第四天

实操练习&#xff1a; 1.建立一个员工表和与之对应的部门表 2.建立外键约束 3.使用多表查询&#xff0c;直接查询部门表和员工表 发现&#xff1a;有很多多余的因笛卡尔乘积而带来的多余输出内容 我想要的到简单明了的数据结果&#xff0c;要消除多于因笛卡尔乘积带来的输出…

3d激光slam建图与定位(2)_aloam代码阅读

1.常用的几种loam算法 aloam 纯激光 lego_loam 纯激光 去除了地面 lio_sam imu激光紧耦合 lvi_sam 激光视觉 2.代码思路 2.1.特征点提取scanRegistration.cpp&#xff0c;这个文件的目的是为了根据曲率提取4种特征点和对当前点云进行预处理 输入是雷达点云话题 输出是 4种特征点…

【Apollo学习笔记】——规划模块TASK之PATH_ASSESSMENT_DECIDER

文章目录 前言PATH_ASSESSMENT_DECIDER功能简介PATH_ASSESSMENT_DECIDER相关信息PATH_ASSESSMENT_DECIDER总体流程1. 去除无效路径2. 分析并加入重要信息给speed决策SetPathInfoSetPathPointType 3. 排序选择最优的路径4. 更新必要的信息 前言 在Apollo星火计划学习笔记——Ap…

信息系统项目管理师(第四版)教材精读思维导图-第七章项目立项管理

请参阅我的另一篇文章&#xff0c;综合介绍软考高项&#xff1a; 信息系统项目管理师&#xff08;软考高项&#xff09;备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 本章思维导图PDF格式 本章思维导图XMind源文件 ​ 目录 7.1 项目建议与立项申请 7.2 项目可…

【洛谷】P1873 EKO / 砍树

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1873 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 整体思路&#xff1a;二分答案 设置一个变量highest来记录最高的树的高度&#xff0c;sum记录切下的木头的长度。令左边界l0&#xff0c…

java八股文面试[多线程]——公平锁

一个线程启动时刚好碰到另外的线程释放锁&#xff0c;则该线程会获取到锁&#xff0c;其他等待队列中的线程不会获取到锁。好处&#xff1a;减少线程状态切换&#xff08;不用在start()之后进入阻塞&#xff09;&#xff0c;提高吞吐量。 非公平锁 非公平锁是多个线程加锁时直接…

高通开发系列 - QTI守护进程服务介绍

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回:专栏总目录 目录 代码位置和依赖关系功能介绍代码逻辑讲解外设节点关注的目录socket服务端初始化DPM客户端监听守护关键的数据结构体…

C# char曲线控件

一、char曲线显示随机数数据 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Syst…

【JS案例】JS实现手风琴效果

JS案例手风琴 &#x1f31f;效果展示 &#x1f31f;HTML结构 &#x1f31f;CSS样式 &#x1f31f;实现思路 &#x1f31f;具体实现 1.绑定事件 2.自定义元素属性 3.切换菜单 &#x1f31f;完整JS代码 &#x1f31f;写在最后 &#x1f31f;效果展示 &#x1f31f;HTML…

如何使用pytest进行自动化测试

Pytest作为广泛使用的Python测试框架之一&#xff0c;可以用于单元测试、功能测试、性能测试等场合。自动化测试是功能测试的一种形式&#xff0c;可以使用Pytest编写并管理自动化测试用例&#xff0c;再执行相应的自动化测试。 功能测试通常包括接口测试和Web测试两种类型&am…

服务器端使用django websocket,客户端使用uniapp 请问服务端和客户端群组互发消息的代码怎么写的参考笔记

2023/8/29 19:21:11 服务器端使用django websocket,客户端使用uniapp 请问服务端和客户端群组互发消息的代码怎么写 2023/8/29 19:22:25 在服务器端使用Django WebSocket和客户端使用Uniapp的情况下&#xff0c;以下是代码示例来实现服务器端和客户端之间的群组互发消息。 …