Linux学习第52天:Linux网络驱动实验(三):一往(网)情深

Linux版本号4.1.15   芯片I.MX6ULL                                 大叔学Linux    品人间百味  思文短情长


        许久没有更新,的确是最近有点懈怠了。没有任何借口,接受所有的批评。接下来无论如何也要坚持下去,不管处于什么境地、什么原因,没有任何借口。一时的中断可能都要重新开始学,教训啊。万里长征也算走过了八千里路,接下来一鼓作气,每天一更。

        先来一张本章的思维导图:

3.fec_netdev_ops操作集

        fec_probe 函数设置了网卡驱动的 net_dev_ops 操作集为 fec_netdev_ops,如下:

1 static const struct net_device_ops fec_netdev_ops = {
2 .ndo_open = fec_enet_open,
3 .ndo_stop = fec_enet_close,
4 .ndo_start_xmit = fec_enet_start_xmit,
5 .ndo_select_queue = fec_enet_select_queue,
6 .ndo_set_rx_mode = set_multicast_list,
7 .ndo_change_mtu = eth_change_mtu,
8 .ndo_validate_addr = eth_validate_addr,
9 .ndo_tx_timeout = fec_timeout,
10 .ndo_set_mac_address = fec_set_mac_address,
11 .ndo_do_ioctl = fec_enet_ioctl,
12 #ifdef CONFIG_NET_POLL_CONTROLLER
13 .ndo_poll_controller = fec_poll_controller,
14 #endif

        1)打开一个网卡的时候 fec_enet_open 函数就会执行

ret = fec_enet_clk_enable(ndev, true);//调用 fec_enet_clk_enable 函数使能 enet 时钟。
ret = fec_enet_alloc_buffers(ndev);/*调用 fec_enet_alloc_buffers 函数申请环形缓冲区 buffer,此函数里面会调用
fec_enet_alloc_rxq_buffers 和 fec_enet_alloc_txq_buffers 这两个函数分别实现发送队列和接收队
列缓冲区的申请。*/
fec_restart(ndev);/*重启网络,一般连接状态改变、传输超时或者配置网络的时候都会调用 fec_restart
函数。*/
ret = fec_enet_mii_probe(ndev);/*  重启网络,一般连接状态改变、传输超时或者配置网络的时候都会调用 fec_restart
函数。*/
29 napi_enable(&fep->napi);//调用 napi_enable 函数使能 NAPI 调度
30 phy_start(fep->phy_dev);//调用 phy_start 函数开启 PHY 设备。
31 netif_tx_start_all_queues(ndev);//调用 netif_tx_start_all_queues 函数来激活发送队列。

        2)关闭网卡的时候 fec_enet_close 函数就会执行

phy_stop(fep->phy_dev);//调用 phy_stop 函数停止 PHY 设备。
napi_disable(&fep->napi);//调用 napi_disable 函数关闭 NAPI 调度。
netif_tx_disable(ndev);//调用 netif_tx_disable 函数关闭 NAPI 的发送队列。
1fec_stop(ndev);//调用 fec_stop 函数关闭 I.MX6ULL 的 ENET 外设。
phy_disconnect(fep->phy_dev);//调用 phy_disconnect 函数断开与 PHY 设备的连接。
fec_enet_clk_enable(ndev, false);//调用 fec_enet_clk_enable 函数关闭 ENET 外设时钟。
fec_enet_free_buffers(ndev);//调用 fec_enet_free_buffers 函数释放发送和接收的环形缓冲区内存。

        3)I.MX6ULL 的网络数据发送是通过 fec_enet_start_xmit 函数来完成的,这个函数将上层传递过来的 sk_buff 中的数据通过硬件发送出去.

static netdev_tx_t fec_enet_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
/* 
skb 就是上层应用传递下来的要发送的网络数据
ndev 就是要发送数据的设备
*/
if (skb_is_gso(skb))/* 判断 skb 是否为 GSO(Generic Segmentation Offload),如果是 GSO 的话就通过fec_enet_txq_submit_tso 函数发送,如果不是的话就通过 fec_enet_txq_submit_skb 发送。*/
ret = fec_enet_txq_submit_tso(txq, skb, ndev);
else
ret = fec_enet_txq_submit_skb(txq, skb, ndev);
if (ret)
return ret;entries_free = fec_enet_get_free_txdesc_num(fep, txq);//获取剩余的发送描述符数量
if (entries_free <= txq->tx_stop_threshold)
netif_tx_stop_queue(nq);/*如果剩余的发送描述符的数量小于设置的阈值(tx_stop_threshold)的话就调用函数netif_tx_stop_queue 来暂停发送,通过暂停发送来通知应用层停止向网络发送 skb,发送中断中
会重新开启的。 */

        4)中断服务函数为 fec_enet_interrupt
 

int_events = readl(fep->hwp + FEC_IEVENT);//读取 NENT 的中断状态寄存器 EIR,获取中断状态,
writel(int_events, fep->hwp + FEC_IEVENT);//清除中断状态寄存器。
fec_enet_collect_events(fep, int_events);//统计都发生了哪些中断
if (napi_schedule_prep(&fep->napi)) {//调用 napi_schedule_prep 函数检查 NAPI 是否可以进行调度
/* Disable the NAPI interrupts */
writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);/*如果使能了相关中断就要先关闭这些中断,向 EIMR 寄存器的 bit23 写 1 即可关闭相关中断。*/
__napi_schedule(&fep->napi);/*调用__napi_schedule 函数来启动 NAPI 调度,这个时候 napi 的 poll 函数就会执行,在本网络驱动中就是 fec_enet_rx_napi 函数。*/}

        具体的网络数据收发是在 NAPI 的 poll 函数中完成的.


        5)fec_enet_interrupt 中断服务函数

        fec_enet_init 函数初始化网络的时候会调用 netif_napi_add 来设置 NAPI 的 poll 函数为
fec_enet_rx_napi。

1 static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
2 {
3 struct net_device *ndev = napi->dev;
4 struct fec_enet_private *fep = netdev_priv(ndev);
5 int pkts;
6 
7 pkts = fec_enet_rx(ndev, budget);//调用 fec_enet_rx 函数进行真正的数据接收。
8 
9 fec_enet_tx(ndev);//调用 fec_enet_tx 函数进行数据发送。
10
11 if (pkts < budget) {
12 napi_complete(napi);//调用 napi_complete 函数来宣布一次轮询结束,
13 writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);//设置 ENET 的 EIMR 寄存器,重新使能中断。
14 }
15 return pkts;
16 }

4.Linux内核PHY子系统与MDIO总线简析

1)PHY 设备

        使用 phy_device 结构体来表示 PHY 设备,结构体定义如下:

1 struct phy_device {
2 /* Information about the PHY type */
3 /* And management functions */
4 struct phy_driver *drv; /* PHY 设备驱动 */
5 struct mii_bus *bus; /* 对应的 MII 总线 */
6 struct device dev; /* 设备文件 */
7 u32 phy_id; /* PHY ID */
8 9
struct phy_c45_device_ids c45_ids;
10 bool is_c45;
11 bool is_internal;
12 bool has_fixups;
13 bool suspended;
14
15 enum phy_state state; /* PHY 状态 */
16 u32 dev_flags;
17 phy_interface_t interface; /* PHY 接口 */
18
19 /* Bus address of the PHY (0-31) */
20 int addr; /* PHY 地址(0~31) */
21
22 /*
23 * forced speed & duplex (no autoneg)
24 * partner speed & duplex & pause (autoneg)
25 */
26 int speed; /* 速度 */
27 int duplex; /* 双共模式 */
28 int pause;
29 int asym_pause;
30
31 /* The most recently read link state */
32 int link;
33
34 /* Enabled Interrupts */
35 u32 interrupts; /* 中断使能标志 */
36
37 /* Union of PHY and Attached devices' supported modes */
38 /* See mii.h for more info */
39 u32 supported;
40 u32 advertising;
41 u32 lp_advertising;
42 int autoneg;
43 int link_timeout;
44
45 /*
46 * Interrupt number for this PHY
47 * -1 means no interrupt
48 */
49 int irq; /* 中断号 */
50
51 /* private data pointer */
52 /* For use by PHYs to maintain extra state */
53 void *priv; /* 私有数据 */
54
55 /* Interrupt and Polling infrastructure */
56 struct work_struct phy_queue;
57 struct delayed_work state_queue;
58 atomic_t irq_disable;
59 struct mutex lock;
60 struct net_device *attached_dev; /* PHY 芯片对应的网络设备 */
61 void (*adjust_link)(struct net_device *dev);
62 };

一个 PHY 设备对应一个 phy_device 实例,然后需要向 Linux 内核注册这个实例。使用
phy_device_register 函数完成 PHY 设备的注册:

int phy_device_register(struct phy_device *phy)//phy: 需要注册的 PHY 设备。

PHY 设备的注册过程一般是先调用 get_phy_device 函数获取 PHY 设备.
 

1 struct phy_device *get_phy_device(struct mii_bus *bus, int addr,
bool is_c45)
2 {
3 struct phy_c45_device_ids c45_ids = {0};
4 u32 phy_id = 0;
5 int r;
6
7 r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);/*调用 get_phy_id 函数获取 PHY ID,也就是读取 PHY 芯片的那两个 ID 寄存器,得到 PHY 芯片 ID 信息。*/
8 if (r)
9 return ERR_PTR(r);
10
11 /* If the phy_id is mostly Fs, there is no device there */
12 if ((phy_id & 0x1fffffff) == 0x1fffffff)
13 return NULL;
14
15 return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);/*调用 phy_device_create 函数创建 phy_device,此函数先申请 phy_device 内存,然后初始化 phy_device 的各个结构体成员,最终返回创建好的 phy_device。 phy_device_register 函数注册的就是这个创建好的 phy_device。*/
16 }

2)PHY驱动

        PHY 驱动使用结构体 phy_driver 表示.

①、注册 PHY 驱动 --- phy_driver_register 函数

        注册phy驱动的时候会设置驱动的总线为mdio_bus_type,也就是MDIO总线.

int phy_driver_register(struct phy_driver *new_driver)

②、连续注册多个 PHY 驱动 --- phy_drivers_register

int phy_drivers_register(struct phy_driver *new_driver, int n)

③、卸载 PHY 驱动

void phy_driver_unregister(struct phy_driver *drv)

3)MDIO 总线

         设备和驱动就是 phy_device 和phy_driver。总线就是 MDIO 总线

        MDIO 总线最主要的工作就是匹配 PHY 设备和 PHY 驱动。

if (phydrv->match_phy_device)return phydrv->match_phy_device(phydev);
/*
检查 PHY 驱动有没有提供匹配函数 match_phy_device,如果有的话就直接调
用 PHY 驱动提供的匹配函数完成与设备的匹配。
*/

        对比 PHY 驱动和 PHY 设备中的 phy_id 是否一致,这里需要与PHY 驱动里面的 phy_id_mask 进行与运算,如果结果一致的话就说明驱动和设备匹配。


 4)通用 PHY 驱动 --- Generic PHY

rc = phy_drivers_register(genphy_driver,
ARRAY_SIZE(genphy_driver));

5)LAN8720A 驱动

        配置路径如下:

-> Device Drivers
        -> Network device support
                -> PHY Device support and infrastructure
                        -> Drivers for SMSC PHYs

93 }, {
94 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
95 .phy_id_mask = 0xfffffff0,
96 .name = "SMSC LAN8710/LAN8720",
97
98 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
99 | SUPPORTED_Asym_Pause),
100 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
101
102 /* basic functions */
103 .config_aneg = genphy_config_aneg,
104 .read_status = lan87xx_read_status,
105 .config_init = smsc_phy_config_init,
106 .soft_reset = smsc_phy_reset,
107
108 /* IRQ related */
109 .ack_interrupt = smsc_phy_ack_interrupt,
110 .config_intr = smsc_phy_config_intr,
111
112 .suspend = genphy_suspend,
113 .resume = genphy_resume,
114
115 .driver = { .owner = THIS_MODULE, }
116 } };

五、网络驱动实验测试

1.LAN8720 PHY驱动测试

2.通用PHY驱动测试

3.DHCP功能配置

        udhcpc 命令
        在 busybox 源码中找到 examples/udhcp/simple.script,将其拷贝到开发板/usr/share/udhcpc 目录下(如果没有的话请自行创建此目录),拷贝完成以后将根文件系统下的 simple.script 并且重命名为 default.script,命令如下:
cd busybox-1.29.0/examples/udhcp
cp simple.script /home/zuozhongkai/linux/nfs/rootfs/usr/share/udhcpc/default.script //拷贝并重
命名

六、单网卡使用

1.只使用ENET2网卡

1)屏蔽或删除掉 fec2 节点内容

        首先找到 fec1 节点,然后将其中的 status 属性改为“disabled”即可。

1 &fec1 {
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_enet1
4 &pinctrl_enet1_reset>;
5 phy-mode = "rmii";
6 phy-handle = <&ethphy0>;
7 phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
8 phy-reset-duration = <200>;
9 status = "disabled";
10 };

2)修改 ENET1 对应的 fec1 节点信息。

11 mdio {
12 #address-cells = <1>;
13 #size-cells = <0>;
14
15 ethphy0: ethernet-phy@0 {
16 compatible = "ethernet-phy-ieee802.3-c22";
17 reg = <0>;
18 };

 3)屏蔽或删除掉 ENET2 对应的 pinctrl 节点

4)在 ENET1 网卡对应的 pinctrl 节点中添加 MDIO 和 MDC 引脚配置

MX6UL_PAD_GPIO1_IO07__ENET1_MDC 0x1b0b0
MX6UL_PAD_GPIO1_IO06__ENET1_MDIO 0x1b0b0

2.只使用ENET1网卡

总结:

关于 Linux 的网络驱动,整体比较复杂,但是实际使用起来确实非常简单的,尤其是对这种内置 MAC+外置 PHY 的网络方案而言,几乎不需要我们修改驱动,因为内核已经继承了通用 PHY 驱动了。
 


本笔记为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

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

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

相关文章

鼠标与键盘交互设计

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在海龟绘图中&#xff0c;也支持与鼠标或键盘的交互操作。它提供了监听键盘按键事件、鼠标事件以及定时器等方法&#xff0c;下面分别进行介绍。 1键…

背包模型——AcWing 423. 采药

背包模型 定义 背包模型是一种常见的算法问题模型&#xff0c;它主要涉及将一些物品放入一个容量有限的背包中&#xff0c;以达到某种最优目标&#xff0c;如最大化价值或最小化重量等。 运用情况 常用于资源分配、项目选择、货物装载等实际问题中。例如&#xff0c;在选择…

AWS云中的VPC启用流日志保存S3(AWS中国云)

问题 需要在AWS中国云中对VPC启用流日志操作。 步骤 创建s3桶 这里设置一个s3桶名&#xff0c;创建即可。如果出现已存在具有相同名称的存储桶错误&#xff0c;就换个桶名再试一试吧。 启用vpc流日志 找到vpc流日志入口操作&#xff0c;如下图&#xff1a; 设置vpc流日志…

『亚马逊云科技产品测评』程序员最值得拥有的第一台专属服务器 “亚马逊EC2实例“

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 引言 自2006年8月9日&#xff0c;在搜索引擎大会&#xff08;SES San Jo…

嵌入式EMC

名词介绍&#xff1a; EMC&#xff1a;兼容&#xff0c;设备间相互兼容在共同环境下工作的能力。包括&#xff1a;EMI和EMS EMI&#xff1a;interference干扰&#xff0c;自身对其他设备的干扰 EMS&#xff1a;susceptibility抗干扰能力 EMS包含以下&#xff1a; 浪涌&…

Java文件操作小项目-带GUI界面统计文件夹内文件类型及大小

引言 在Java编程中&#xff0c;文件操作是一项基本且常见的任务。我们经常需要处理文件和文件夹&#xff0c;例如读取、写入、删除文件&#xff0c;或者遍历文件夹中的文件等。本文将介绍如何使用Java的File类和相关API来统计一个文件夹中不同类型文件的数量和大小。 准备工作…

c++基本数据类型和计算(三)习题讲解

1.【单选题】 int a450; int main() { int a100; { int a 999; std::cout << a; } } A.999 B.100 C.450 解析&#xff1a;这道题考察作用域&#xff0c;本身遵守就近原则&#xff0c;所以选A。 2.【单选题】 int a; { int b 100; } ab<&l…

MySQL 基础概念

MySQL逻辑架构 MySQL 服务器逻辑架构图 最上层的服务并不是MySQL所独有的&#xff0c;大多数基于网络的客户端/服务器的工具或者服务都有类似的架构&#xff0c;比如连接管理、授权认证、安全等等。 大多数MySQL的核心服务都在第二层&#xff0c;包括查询解析、分析、优化、…

手持小风扇品牌有哪些?分享口碑最好的五款手持小风扇

手持小风扇在炎热的夏季成为了许多人解暑的好帮手。它们不仅轻便便携&#xff0c;随时随地都能为我们带来清凉和舒适。然而&#xff0c;市场上手持小风扇的品牌繁多&#xff0c;让人眼花缭乱。为了帮助大家做出更明智的选择&#xff0c;接下来我们将分享口碑最好的五款手持小风…

vscode关于java扩展

包含IDEA同款代码提示&#xff0c;vscode的debug&#xff0c;除了创建类要手动新建java文件以外&#xff0c;其他都还行&#xff0c;满足初学者要求。

Python+Pytest+Allure+Yaml接口自动化测试框架详解

PythonPytestAllureYaml接口自动化测试框架详解 编撰人&#xff1a;CesareCheung 更新时间&#xff1a;2024.06.20 一、技术栈 PythonPytestAllureYaml 版本要求&#xff1a;Python3.7.0,Pytest7.4.4,Allure2.18.1,PyYaml6.0 二、环境配置 1、安装python3.7&#xff0c;并配置…

C++编程逻辑讲解step by step:根据关键词自动判断文章的类别,去掉无关词后统计文章中每个单词出现的频率。

题目分析 根据关键词自动判断文章的类别。共2篇文章&#xff08;用char s1[]和char s2[]保存&#xff09;&#xff0c;去掉无关词后&#xff08;用char irrelevant[][10]保存&#xff09;&#xff0c;统计文章里面每个单词出现的频率&#xff1a;关键词个数去掉无关词后的文章…

前端JS必用工具【js-tool-big-box】学习,数值型数组的正向排序和倒向排序

这一小节&#xff0c;我们说一下前端 js-tool-big-box 这个工具库&#xff0c;添加的数值型数组的正向排序和倒向排序。 以前呢&#xff0c;我们的数组需要排序的时候&#xff0c;都是在项目的utils目录里&#xff0c;写一段公共方法&#xff0c;弄个冒泡排序啦&#xff0c;弄…

已解决java.security.GeneralSecurityException: 安全性相关的通用异常的正确解决方法,亲测有效!!!

已解决java.security.GeneralSecurityException: 安全性相关的通用异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 解决思路 解决方法 确定具体异常类型 检查输入参数 验证算法支持性 调整安全策略 确保资源可…

uniapp 小程序 堆叠轮播图 左滑 右滑 自动翻页 点击停止自动翻页

uniapp 小程序 堆叠轮播图 左滑 右滑 自动翻页 点击停止自动翻页 超过指定时间未点击滑动 则继续开始滚动 直接上代码 componentSwiper.vue 需要注意页面切换时清除计时器 <template><view><view class"swiperPanel" touchstart"startMove"…

Verilog刷题笔记48——FSM1型异步复位

题目: 解题&#xff1a; module top_module(input clk,input areset, // Asynchronous reset to state Binput in,output out);// parameter A0, B1; reg state, next_state;always (*) begin // This is a combinational always block// State transition logiccase(…

VMware vSphere 8.0 Update 3 发布下载 - 企业级工作负载平台

VMware vSphere 8.0 Update 3 发布下载 - 企业级工作负载平台 vSphere 8.0U3 | ESXi 8.0U3 & vCenter Server 8.0U3 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-vsphere-8-u3/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&am…

【yolov8:报错AttributeError: ‘Segment‘ object has no attribute ‘detect‘】

今天在运行yolov8的Segment模式的val模型测验时报错&#xff1a; AttributeError: ‘Segment’ object has no attribute ‘detect’ 原因是yolov8的版本问题&#xff0c;更新至最新版就可以了&#xff1a; 输入ultralytics查看版本 卸载当前的老版本 然后下载最新版本就可以…

【前端】Vue项目和微信小程序生成二维码和条形码

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是前端菜鸟的自我修养&#xff01;今天给大家分享Vue项目和微信小程序如何生成二维码和条形码&#xff0c;介绍了JsBarcode、wxbarcode等插件&#xff0c;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01…

为什么要学Java?

想要自己教会自己java&#xff0c;从小白成长到架构师。实现硬实力就业&#xff01; 因为Java是全球排名第一的编程语言&#xff0c;Java工程师也是市场需求最大的软件工程师&#xff0c;选择Java&#xff0c;就是选择了高薪。 为什么Java应用最广泛&#xff1f; 从互联网到…