实验二十二 移植网卡驱动------四处齐动,把 MAE0621A 装进内核
对应课件:《第5章 移植Linux内核》5.4 节步骤 6(Slide 52~56)
系列说明 :本系列基于华清远见 FS-MP1A(STM32MP157A)开发板,对应课件《第5章 移植Linux内核》。为什么第 5 章要专门移植网卡?因为后面的开发、调试全靠网络挂载根文件系统 (改一次文件不用烧盘),网线就是生命线。eMMC 那篇(实验二十一)驱动现成、只补设备树;本篇不一样------驱动代码也要自己放 (资料包里的
maxio.c/phy_device.c,后者还要覆盖内核同名文件),加上 Makefile、Kconfig、menuconfig、设备树,四处齐动,是第 5 章改动最多的一篇,也是最像"真实工作"的一篇。前置:实验二十一(eMMC 节点已通,重编译流程已熟)。
一、改动全景:四个文件,各管一件事
| # | 改哪里 | 干什么 | 性质 |
|---|---|---|---|
| 1 | drivers/net/phy/ 目录 |
放入 maxio.c、phy_device.c 两个驱动源文件 |
加代码 |
| 2 | drivers/net/phy/Makefile |
加一行编译项,让 .c 被编译成 .o |
构建入口 |
| 3 | drivers/net/phy/Kconfig |
加一个配置项,让 menuconfig 里出现它的开关 | 配置入口 |
| 4 | arch/arm/boot/dts/stm32mp15xx-fsmp1x.dtsi |
加 ðernet0 节点,告诉内核网卡接在哪 |
硬件描述 |
四者的关系:没有 1,2 无米下锅;没有 2,1 编不进;没有 3,menuconfig 里找不到开关(也就打不开 1、2 的总闸);没有 4,驱动编进去了也不知道网卡在哪根总线上。少一处就白跑一次编译。
先弄清驱动角色 :我们板子的以太网是"MAC + PHY"两级结构------MAC 控制器(
ethernet0,ST 芯片内置,内核自带stmmac驱动,设备树里声明即可)+ PHY 芯片 MAE0621A (板子上一颗独立小芯片,需要专门驱动)。第 3 章 F-5 给 U-Boot 换过它的驱动(u-boot-网卡-MAE0621A驱动.rar,放的是phy.c/maxio.c/dwc_eth_qos.c),本篇换的是内核版 驱动(kernel-网卡驱动.zip,放的是phy_device.c/maxio.c)------课件 Slide 52 特别提醒:"注意不是用于 u-boot 的驱动"。两个包里虽然都叫maxio.c,内容也各是各的(编译体系都不同),两套代码、两个世界,别拿错包。
二、实验环境(实际)
| 项目 | 实际值 |
|---|---|
| 操作位置 | Ubuntu 虚拟机,实验二十/二十一那棵源码树 |
| 素材 | kernel-网卡驱动.zip(18,708 字节,md5 d75517d6942d13263c1d9a165aff043b)------结构是两层目录:kernel-网卡驱动/MAE0621A驱动/ 下两个文件:maxio.c(6,654 字节)、phy_device.c(64,547 字节) |
| 板子 | 本篇不开 |
开工自检(10 秒) :
git status --short里只剩实验二十一的预期改动(dtsi/.config 类,干净更好);ls drivers/net/phy/能看到几十个.c文件、Makefile/Kconfig在列。
三、课件 ↔ 步骤对应表
| 课件 Slide | 内容 | 对应步骤 |
|---|---|---|
| 52 | 为什么移植网卡;从出厂源码包取 MAE0621A 的 phy_device.c/maxio.c 放入 drivers/net/phy | 步骤 1 |
| 53 | phy/Makefile 加 obj-$(CONFIG_MAXIO_PHY) += maxio.o |
步骤 2 |
| 54 | phy/Kconfig 加 config MAXIO_PHY 配置项 |
步骤 3 |
| 55 | menuconfig:Device Drivers → Network device support → PHY → 勾 <*> MAXIO PHYS |
步骤 4 |
| 56 | dtsi 末尾加 ðernet0 节点 + 重编译 make -j4 uImage dtbs LOADADDR=0xC2000040 |
步骤 5~6 |
本篇动作 → 后面谁用 → 现在含糊的后果
| 本篇动作 | 后面哪一篇要用 | 现在含糊的后果 |
|---|---|---|
| 内核里装好 MAE0621A + ethernet0 节点 | 实验二十三点火后内核日志应出现 PHY 报上名来;第 6 章用 NFS 挂根文件系统时全靠这块网卡 | 网卡没通,第 6 章的 NFS 根起不来,整个"网络调试"玩法作废 |
| Makefile/Kconfig/menuconfig 三件套的改法 | 第 7 章起写自己的驱动时一模一样的三件套(加代码→Makefile→Kconfig→menuconfig) | 只会抄不会懂,后面每加一个驱动都要现查 |
| "四处齐动"的完整性检查习惯 | 以后任何"内核不支持某硬件"的移植都这么拆解 | 漏一处就白编一次(十几分钟起步),还查不出原因 |
本篇需要的资料:kernel-网卡驱动.zip(见上表)。注意与第 3 章 F-5 的 U-Boot 驱动包区分(见"先弄清驱动角色"一段)。
四、实验步骤
步骤 1:放入驱动源文件(Slide 52)
把 kernel-网卡驱动.zip 拷进虚拟机共享目录,然后在源码树里解包------注意 zip 里套了两层目录 (kernel-网卡驱动/MAE0621A驱动/),用 unzip -j 直接拍平到目标目录最省事:
bash
cd <共享目录>/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/linux-5.4.31/drivers/net/phy/ # 内核源码顶层之下的 phy 目录(新开终端就要重新 cd)
unzip -j -o <共享目录>/kernel-网卡驱动.zip -d . # zip 路径按你的共享目录改
就地验证(三条):
bash
ls -l maxio.c phy_device.c
# maxio.c 6,654 字节;phy_device.c 64,547 字节(内核自带那份是 64,147 字节------被覆盖的就是它)
cp phy_device.c /tmp/phy_device.c.orig # 覆盖已经发生,留一份内核自带的原版备查
phy_device.c名字撞车、必须覆盖,为什么? 内核自带的drivers/net/phy/phy_device.c是 MDIO 总线扫 PHY 的通用代码。华清改过三个地方(拿/tmp/phy_device.c.orig与 zip 版diff一看便知):① 读 PHY ID 之前先往
MII_PHYSID2寄存器写一次 0 ------注释原话 "An MDIO connects to multiple PHYs requiring write before read":MAE0621A 的脾气是先写后读才肯报 ID ,不写就探测不到。这段代码包在#ifdef CONFIG_MAXIO_PHY里------步骤 3 要定义的那个配置开关,管的不只是 maxio.c 编不编译,还管这段"怪脾气补丁"编不编译 ,三处改动咬合就在这;② 读 ID 失败时返回
-ENODEV而不是-EIO------注释 "returning -ENODEV doesn't stop bus scanning",让 MDIO 总线把所有地址扫完而不是半途而废;③ 一处设备生命周期小修正(
device_initialize时机提前)。所以覆盖不是可选项,是课件方案的必需环节;
.orig备份的价值是"以后启动异常时能 diff 出厂商到底动了什么"。

图:实测------步骤 1 全程:
cd进drivers/net/phy/(绿箭头"前往 phy 目录");unzip -j -o <共享目录>/kernel-网卡驱动.zip -d .把共享目录里的网卡驱动包直接解压进当前目录 (inflating: ./maxio.c、inflating: ./phy_device.c两行 = 两个文件各就各位);ls -l对账 6,654 / 64,547 字节 (7月7日 2022 = 素材包自带的时间戳);cp phy_device.c /tmp/phy_device.c.orig完成备份(cp成功一声不吭)。
步骤 2:Makefile 加编译项(Slide 53)
步骤 1 把你带进了 drivers/net/phy/ 深处,先退回内核源码根目录 ------从这里到顶层正好三级(phy → net → drivers → 顶层),.. 写三次:
bash
cd ../../..
pwd # 末段应是 linux-5.4.31 ------ 回到内核源码根目录
此后直到本篇结束都在根目录里操作(grep/nano/make 的相对路径全部以它为基准,别再往下钻)。然后打开 Makefile:
bash
nano drivers/net/phy/Makefile
Ctrl+W 搜 XGENE 回车定位,在第 44 行 obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o 附近加一行:
makefile
obj-$(CONFIG_MAXIO_PHY) += maxio.o
改完 Ctrl+O 回车保存(这台 Ubuntu 的 nano 是 4.8,底栏就是 ^O 写入;新版 nano 的 Ctrl+S 未默认绑定,按了可能没反应------以 ^O 写入 为准)、Ctrl+X 退出。改好的样子:

图:实测(顶替课件 Slide 53)------nano 4.8 里改好的样子:绿框即新增的
obj-$(CONFIG_MAXIO_PHY) += maxio.o,插在obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o之后,绿箭头指向它;底部"已写入 97 行" = 保存成功。
就地验证:
bash
grep -n "MAXIO" drivers/net/phy/Makefile # 应打出新加的一行
这行的意思:如果 .config 里 CONFIG_MAXIO_PHY=y,就把 maxio.c 编进内核(=m 则编成模块)。CONFIG_MAXIO_PHY 这个名字是我们下一步在 Kconfig 里自己定义的------Makefile 与 Kconfig 靠这个名字对上号,拼错一个字母就断链。
步骤 3:Kconfig 加配置项(Slide 54)
打开 Kconfig:
bash
nano drivers/net/phy/Kconfig
Ctrl+W 搜 RENESAS 回车定位(保存退出键同上),在 config REALTEK_PHY(第 452 行)与 config RENESAS_PHY(第 457 行)附近加:
kconfig
config MAXIO_PHY
tristate "MAXIO PHYS"
---help---
Supports the Maxio MAEXXXX PHY.

图:实测(顶替课件 Slide 54)------nano 4.8 里改好的样子:绿框即新增的
config MAXIO_PHY三行(tristate与---help---用 Tab 缩进),插在config REALTEK_PHY之后、config RENESAS_PHY之前,与正文指引的位置一致;标题栏"已更改"表示截图时还没保存。config后面的MAXIO_PHY就是步骤 2 Makefile 引用的那个名字,也是步骤 1 里phy_device.c那段#ifdef认的名字。
步骤 4:menuconfig 勾选(Slide 55)
bash
make menuconfig
路径(三层子菜单,逐层进):
Device Drivers --->
[*] Network device support --->
-*- PHY Device support and infrastructure --->
<*> MAXIO PHYS ← 新出现的项,勾成 <*>(编进内核)
按键逐步走(menuconfig 三招在实验二十一步骤 2 演示过,这里按本篇路径再走一遍):
- 进第一层 :方向键把光标移到
Device Drivers --->行,按回车; - 进第二层 :方向键移到
Network device support --->(确认它已是[*]),按回车; - 进第三层 :方向键移到
PHY Device support and infrastructure --->(-*-状态),按回车; - 勾选 :方向键移到
< > MAXIO PHYS行,按空格键 ------[ ]变<*>即勾上(或直接按 Y ,效果相同;多按一次空格会切回[ ],按错了就再按回来); - 退出并保存 :连按两次 Esc 逐层退出(也可以每层选
<Exit>),回到最外层再退时会问Do you wish to save your new configuration?------方向键选 Yes 回车。不保存 = 白勾。

图:课件 Slide 55------PHY 设备菜单实拍:
<*> Realtek PHYs(已选)、标红新增的<*> MAXIO PHYS(已选) 、< > Driver for Renesas PHYs(未选)------勾完之后你的屏幕就是这一页的样子。
这次是真的要勾 :与实验二十一的 SDMMC(default y天生已开)不同,MAXIO_PHY是我们自己在 Kconfig 里新定义的选项,默认不开 ------menuconfig 里不勾,前两步全白搭。路径前两级显示[*]/-*-是因为MAXIO_PHY依赖它们(menuconfig 里按/搜MAXIO能看到依赖链),依赖不满足时这一项根本不出现。
就地验证:
bash
grep MAXIO .config # 应打出 CONFIG_MAXIO_PHY=y
步骤 5:设备树加 ethernet0 节点(Slide 56)
打开设备树文件(实验二十一改过的同一个文件):
bash
nano arch/arm/boot/dts/stm32mp15xx-fsmp1x.dtsi
Ctrl+W 搜 vrefbuf 回车------跳到文件末尾区域,在文件末尾 (&vrefbuf 段之后)添加:
c
/*网卡*/
ðernet0 {
status = "okay";
pinctrl-0 = <ðernet0_rgmii_pins_a>;
pinctrl-1 = <ðernet0_rgmii_pins_sleep_a>;
pinctrl-names = "default", "sleep";
phy-mode = "rgmii-id";
max-speed = <1000>;
phy-handle = <&phy0>;
mdio0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dwmac-mdio";
phy0: ethernet-phy@0 {
reg = <0>;
};
};
};
改完 Ctrl+O 回车保存、Ctrl+X 退出,然后对照课件图检查一遍插入效果:

图:课件 Slide 56------
stm32mp15xx-fsmp1x.dtsi文件末尾(课件作者文件里第 194~212 行)的新增内容截图,与我们抄的一致。

图:实测(对照上图课件)------nano 4.8 里改好的样子:绿框即新增的
/*网卡*/ ðernet0 { ... }整段,插在&vrefbuf段之后、文件末尾,绿箭头指向它。phy0: ethernet-phy@0 { reg = <0>; }里的 0 就是 PHY 的 MDIO 地址(F-5 在 U-Boot 侧见过的同一个概念)。
逐行读关键属性(对照实验十六内核日志里 stm32-dwmac 5800a000.ethernet 那一串):
| 属性 | 意思 |
|---|---|
phy-mode = "rgmii-id" |
MAC 与 PHY 之间用 RGMII 千兆接口;-id 表示时钟延迟在数据线内嵌、由接收端补------硬件接法决定的写法,照抄即可 |
max-speed = <1000> |
最高千兆(实际跑多快由 PHY 自协商决定,实验十三见过全过程) |
phy-handle = <&phy0> + mdio0 里的 ethernet-phy@0 { reg = <0>; } |
MAC 通过 MDIO 总线找 PHY,PHY 地址 0 ------与 F-5 在 U-Boot 里那次同源(地址由板上接线决定,两边看到的是同一颗芯片);snps,dwmac-mdio 是 stmmac 内置 MDIO 总线的标准 compatible |
ethernet0_rgmii_pins_a 等引脚组 |
定义在 SoC 级 stm32mp15-pinctrl.dtsi(0020 补丁带来的,实验二十一的五个 sdmmc2 引脚组也在那个文件),照抄引用即可 |
改完 git diff 过一眼。
步骤 6:重编译(Slide 56 末)
bash
make -j4 uImage dtbs LOADADDR=0xC2000040
增量编译,几分钟(OOM 就 -j2)。验证:两个产物时间戳更新、新 uImage 字节数记下;git diff --stat 查看本篇改动------注意 maxio.c 是新文件(未跟踪),不会出现在 git diff --stat 里 (实测只列出 4 项:fsmp1x.dtsi、Kconfig、Makefile、phy_device.c),新增文件要用 git status --short 看。
验收预告(实验二十三) :用这对新文件点火,内核日志应出现
stmmac探测 5800a000.ethernet、PHY 枚举(MAE0621A 报上名来)------之后ping通 Ubuntu、第 6 章 NFS 挂根才有戏。
步骤 7:git 收官提交(把实验二十一 + 本篇一起提交)
实验二十一(eMMC)与本篇(网卡)改的是同一个 fsmp1x.dtsi、同属"第 5 章驱动移植"一组改动,两篇一起提交一条 (实验二十一篇尾有说明)。提交前 git status --short 应能看到:maxio.c(新增)、phy_device.c(修改)、phy/Makefile、phy/Kconfig、fsmp1x.dtsi(含实验二十一的 sdmmc2 与本篇的 ethernet0 两段)与 .config(被 .gitignore 忽略、不入列)。显式列出提交(不用 git add -A,实验十一的教训):
bash
git add drivers/net/phy/maxio.c drivers/net/phy/phy_device.c drivers/net/phy/Makefile drivers/net/phy/Kconfig arch/arm/boot/dts/stm32mp15xx-fsmp1x.dtsi
git commit -m "FS-MP1A eMMC 与网卡驱动(实验二十一/二十二)"
git log --oneline | head -3 # 顶部应是这条,其下是实验二十的提交
git status --short # 应无输出 = 工作区干净
提交后版本串的 -dirty 消失(g<新哈希> 仍在);照例增量重编一次 make uImage dtbs LOADADDR=0xC2000040,实验二十三点火对账以最新字节数为准。
实际执行结果(2026-09-26 实测):

图:实测------步骤 6 重编译收尾与步骤 7 提交全程:重编译打出
Image Name: Linux-5.4.31-g381ee3922-dirty(版本串已从实验二十的g77fcad789前进到 g381ee3922 ,-dirty= 实验二十一/二十二改动当时未提交)、Data Size: 7315136、Load Address/Entry Point均c2000040,绿箭头"重编译成功"指Kernel: arch/arm/boot/uImage is ready;随后git diff --stat显示 4 files changed, 41 insertions(+), 6 deletions(-)(maxio.c 是未跟踪文件不在此列 );git add五文件、git commit打出[WORKING 8dd24eeb2] FS-MP1A eMMC 与网卡驱动(实验二十一/二十二)、5 files changed, 294 insertions(+), 6 deletions(-) (create mode ×1 = maxio.c 首次入库);git log --oneline三层叠加 =8dd24eeb2(实验二十一/二十二)→381ee3922(实验二十)→77fcad789(new kernel 基线);收尾git status --short无输出 = 工作区干净。
五、注意事项
phy_device.c覆盖内核自带同名文件 ------按课件做,但先留.orig备份(步骤 1 已带命令);三处改动的出处见步骤 1 的解读框。- Makefile 的
CONFIG_MAXIO_PHY与 Kconfig 的config MAXIO_PHY必须一字不差 (含大小写),这是一对"暗号";phy_device.c里那段#ifdef CONFIG_MAXIO_PHY认的也是它------三处咬合,一处拼错全盘哑火。 - Kconfig 的缩进用 Tab :
tristate/---help---行前的制表符是语法的一部分,用空格可能报 Kconfig 解析警告。 - menuconfig 退出选 Save ,
grep MAXIO .config复核=y。 - dtsi 的 ethernet0 节点加在文件末尾 ,别插进别的节点中间;
#address-cells/#size-cells两行是 mdio0 子总线的编址声明,照抄即可。 - 重编译必须带 LOADADDR(实验二十/二十一同款注意事项)。
- 别拿成 U-Boot 版驱动包:文件名相近(都有 maxio.c),但 U-Boot 版三件套(phy.c/maxio.c/dwc_eth_qos.c)属于 U-Boot 的驱动体系------编译报头文件/函数不存在时,第一反应核对 md5 与文件清单。
六、验证点一览
| 验证点 | 命令 | 通过的样子 | 在哪一步敲 |
|---|---|---|---|
| 驱动文件到位 | ls -l maxio.c phy_device.c(phy 目录里) |
6,654 / 64,547 字节各一份 | 步骤 1 |
| 原版已备份 | ls -l /tmp/phy_device.c.orig |
64,147 字节(内核自带那份) | 步骤 1 |
| Makefile 编译项 | grep -n "MAXIO" drivers/net/phy/Makefile |
obj-$(CONFIG_MAXIO_PHY) += maxio.o 一行 |
步骤 2 |
| Kconfig 配置项 | grep -n -A1 "MAXIO" drivers/net/phy/Kconfig |
config MAXIO_PHY + tristate "MAXIO PHYS" |
步骤 3 |
| 配置已勾 | grep MAXIO .config |
CONFIG_MAXIO_PHY=y |
步骤 4 |
| 设备树节点 | grep -A 18 "网卡" arch/arm/boot/dts/stm32mp15xx-fsmp1x.dtsi |
ethernet0 节点整段,在文件末尾 | 步骤 5 |
| 重编完成 | ls -l arch/arm/boot/uImage arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb |
实测通过(2026-09-26) :时间戳 21:53/23:54 更新;uImage 头 Data Size: 7315136 |
步骤 6 |
| 改动范围清白 | git diff --stat + git status --short |
diff 4 项(未跟踪的 maxio.c 不进 diff)+ status 可见 maxio.c;提交后两者都干净 | 步骤 6~7 |
| git 收官提交 | `git log --oneline | head -3` | 实测通过(2026-09-26) :8dd24eeb2 → 381ee3922 → 77fcad789 三层叠加 |
不达标时的排查:
| 现象 | 先查什么 |
|---|---|
| menuconfig 里找不到 MAXIO PHYS | Kconfig 的 config MAXIO_PHY 拼写、缩进是不是 Tab、文件保存没有;前两级依赖(Network device support / PHY infrastructure)是否开着 |
勾了但 .config 里是 # CONFIG_MAXIO_PHY is not set |
依赖不满足------menuconfig 里按 / 搜 MAXIO 看依赖链 |
编译报 maxio.c 里的头文件/函数错误 |
源文件拿错包(拿成 U-Boot 版了?)------核对 md5 与文件名 |
编译报 phy_maxio_init 一类符号缺失 |
maxio.c 没拷到位、或 Makefile 那行没加------ls 与 grep 两把尺子各过一遍 |
| 覆盖没生效(PHY 仍探测不到) | diff /tmp/phy_device.c.orig drivers/net/phy/phy_device.c 应能看到步骤 1 解读框里的三处差异;看不到 = 拷贝被原版盖回去了 |
| 点火后网卡仍不通 | (实验二十三验收)先看内核日志有没有 stmmac/PHY 枚举行,再回本篇查设备树 |
七、实验完成标志
maxio.c(6,654 字节)、phy_device.c(64,547 字节)已放入drivers/net/phy/,内核原版已备份/tmp/phy_device.c.orig(步骤 1 实测,截图入档)drivers/net/phy/Makefile已加obj-$(CONFIG_MAXIO_PHY) += maxio.o,grep打出第 45 行(步骤 2 实测,截图入档)drivers/net/phy/Kconfig已加config MAXIO_PHY三行(Tab 缩进、插在 REALTEK 与 RENESAS 之间),grep可见(步骤 3 实测,截图入档)- menuconfig 已勾
<*> MAXIO PHYS,.config里CONFIG_MAXIO_PHY=y(步骤 4) stm32mp15xx-fsmp1x.dtsi末尾已加ðernet0节点(步骤 5 实测,截图入档)make -j4 uImage dtbs LOADADDR=0xC2000040重编译成功:uImage 头Data Size: 7315136、版本串Linux-5.4.31-g381ee3922-dirty(步骤 6 实测,截图入档)- git 收官提交完成:哈希 8dd24eeb2 (5 files changed, 294 insertions(+), 6 deletions(-)),
git log三层叠加、工作区干净(步骤 7 实测,截图入档)
八、下一步:运行内核
两块短板都补齐了:eMMC 认得出、网卡通了。下一篇(实验二十三,第 5 章收官)用我们自己编的 uImage 与 dtb 点火:先复现"无根文件系统"的老结局(顺便看清两块盘的盘号),再用 bootargs 挂上出厂根文件系统走进 Linux 命令行------第 3 章以来"bootm 找不到内核"与"内核找不到根"两大悬案,至此全部了结。