简介:本资源为基于Linux DSA框架的YT9215 switch驱动源码包,面向嵌入式网络开发与交换机调试人员。驱动加载后会生成lan网卡用于获取各网口状态,实际数据通信则依赖eth网卡,并可通过VLAN划分实现每个接口独立管理,具体划分方式可参考包内patch说明。压缩包共3个文件,包含1个patch、1个h头文件与1个c源文件,整体约12KB,分别用于设备树配置、寄存器定义与驱动核心逻辑实现,结构精简便于快速移植与二次开发。目前已有455人学习下载。读者可从中获取DSA框架下交换机驱动的完整实现思路、网口状态与数据通道分离的设计方法,以及VLAN隔离配置的参考范例,适合需要调试YT9215或类似交换芯片的开发者对照使用。
1. yt9215 switch 驱动:从一颗车规交换芯片到能跑通的以太网口
第一次拿到 yt9215 这块板子的时候,我盯着丝印看了半天——它是一颗车规级以太网交换芯片,常见于车载 T-BOX、域控制器和工业网关里,负责把几路 MAC 出来的流量做二层转发。标题里的「switch 驱动」不是指游戏机手柄,也不是编程语言里的 switch 语句,而是给这颗交换芯片写/移植 Linux 内核态的 switchdev 或 DSA 驱动,让内核能识别它的端口、VLAN、FDB,最终让ip link能看到几个能收发包的以太网口。这件事的受众很明确:做车载网关、工业交换机、边缘盒子的嵌入式工程师,手里有 yt9215 的板子或者原理图,需要把它在 Linux 上跑起来。它解决的核心问题是——芯片原厂给的往往是一份 SDK 或者一段裸寄存器操作,而你要的是标准内核网络栈能用的接口。这篇笔记就按我实际落地的顺序,把选型、设备树、驱动骨架、调试和踩坑讲清楚。
2. 先搞清楚 yt9215 在 Linux 里该走哪条驱动路线
在动手写一行代码之前,得先决定这颗芯片用哪种框架接进内核。选错了框架,后面全是返工。yt9215 是一颗带 CPU 接口(常见是 PCIe 或者 RGMII/SPI 管理口)的交换芯片,内部有多个 PHY 口和一个上行口。Linux 里管交换芯片主要有三条路:纯 PHY 驱动、DSA(Distributed Switch Architecture)、switchdev。三条路的抽象层级和适用场景完全不同,选型直接决定你后面要写多少代码。
2.1 DSA、switchdev、纯 PHY 三条路线的取舍
纯 PHY 驱动只适合「芯片就是个透明 PHY」的场景,比如你只用一个口,芯片内部交换逻辑不用。yt9215 明显不是这种,它有多口转发能力,用纯 PHY 等于把交换功能浪费了。
DSA 是内核为「一个交换芯片挂在一个 MAC 后面、内部多个口」这种拓扑设计的框架。它的特点是每个物理口在 Linux 里表现为一个独立的 netdev(比如 lan1、lan2),内核通过 tag 协议在 CPU 口和交换芯片之间传递元数据。DSA 的好处是用户态完全无感,ip link、bridge命令直接能用,VLAN 也能配。缺点是它假设 CPU 口和交换芯片之间是「一个 MAC 对多个口」的固定拓扑,如果你的 yt9215 是通过 PCIe 挂上去的、或者有多个 CPU 口,DSA 的模型就不太贴合。
switchdev 是更底层的模型,它把交换芯片的转发面(FDB、VLAN、ACL)通过 notifier 暴露给内核,让内核的 bridge 子系统去下发。switchdev 更灵活,适合多 CPU 口、复杂 offload 的场景,但代价是驱动要自己处理的东西多,调试也更麻烦。
我一般的判断标准是:如果 yt9215 是单 CPU 口、内部就是标准多口交换,优先 DSA,因为社区支持好、调试工具全;如果是 PCIe 挂载或者多 CPU 口,走 switchdev。下面这张表是我选型时实际对比的维度。
| 维度 | DSA | switchdev | 纯 PHY |
|---|---|---|---|
| 适用拓扑 | 单 CPU 口 + 多 PHY 口 | 多 CPU 口 / PCIe 挂载 | 单口透明 |
| 用户态可见 | 每口一个 netdev | 通常一个 netdev + offload | 一个 netdev |
| VLAN 配置 | bridge vlan直接可用 | 需驱动实现 notifier | 不支持 |
| 调试难度 | 低,工具成熟 | 中高 | 低 |
| 代码量 | 中 | 高 | 低 |
2.2 从芯片手册确认 CPU 接口和寄存器基址
选完框架,下一步是翻 yt9215 的 datasheet,确认三件事:CPU 接口类型、寄存器访问方式、端口映射。CPU 接口决定了你在设备树里怎么写节点——如果是 RGMII 管理口,通常走 MDIO 访问寄存器;如果是 PCIe,那驱动要注册 PCI driver,用 BAR 映射寄存器空间。
寄存器基址和偏移是写驱动的命根子。yt9215 一般会有一个全局控制寄存器块、每个端口的寄存器块、以及 VLAN/FDB 表。你要从手册里找到这些块的基址和访问宽度(8/16/32 位)。我踩过的坑是:手册里写的是「寄存器偏移」,但实际访问要加上一个 base,这个 base 在 PCIe 模式下是 BAR0 的地址,在 MDIO 模式下是 PHY 地址加寄存器号。搞错这个,读出来的全是 0xFFFF。
端口映射也要确认:芯片内部 port 0 到 port N 分别对应板子上的哪个物理口,哪个是 CPU 口。这个信息通常在手板的原理图或者芯片的 port map 表里。写驱动时,port 号和 netdev 的对应关系就靠这张表。
2.3 最小可跑的驱动骨架长什么样
确认完硬件信息,可以先搭一个最小骨架,目标不是功能全,而是能 probe 成功、能读到芯片 ID。下面这段是我常用的 platform driver 骨架,假设 yt9215 通过 MDIO 管理、寄存器用 32 位访问。
#include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/mdio.h> #include <linux/regmap.h> #define YT9215_CHIP_ID_REG 0x00 #define YT9215_CHIP_ID_VAL 0x9215 struct yt9215_priv { struct device *dev; struct regmap *regmap; struct mii_bus *bus; int mdio_addr; }; static const struct regmap_config yt9215_regmap_cfg = { .reg_bits = 16, /* 寄存器地址 16 位 */ .val_bits = 32, /* 寄存器数据 32 位 */ .max_register = 0xFFFF, }; static int yt9215_probe(struct platform_device *pdev) { struct yt9215_priv *priv; u32 id; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = &pdev->dev; /* 从设备树拿 MDIO 总线句柄和 PHY 地址 */ priv->bus = devm_mdio_find_bus(&pdev->dev); if (!priv->bus) return -EPROBE_DEFER; ret = of_property_read_u32(pdev->dev.of_node, "reg", &priv->mdio_addr); if (ret) return ret; /* 读芯片 ID,确认硬件在线 */ ret = mdiobus_read(priv->bus, priv->mdio_addr, YT9215_CHIP_ID_REG); if (ret < 0) return ret; id = ret & 0xFFFF; if (id != YT9215_CHIP_ID_VAL) { dev_err(priv->dev, "chip id mismatch: 0x%x\n", id); return -ENODEV; } platform_set_drvdata(pdev, priv); dev_info(priv->dev, "yt9215 probed, id=0x%x\n", id); return 0; } static const struct of_device_id yt9215_of_match[] = { { .compatible = "vendor,yt9215" }, { } }; MODULE_DEVICE_TABLE(of, yt9215_of_match); static struct platform_driver yt9215_driver = { .probe = yt9215_probe, .driver = { .name = "yt9215", .of_match_table = yt9215_of_match, }, }; module_platform_driver(yt9215_driver); MODULE_LICENSE("GPL");这段代码的逻辑是:probe 时先拿 MDIO 总线,再从设备树读 PHY 地址,然后读芯片 ID 寄存器做校验。参数上,reg_bits=16和val_bits=32要按手册改,如果 yt9215 是 16 位数据宽度就改成 16。YT9215_CHIP_ID_REG和YT9215_CHIP_ID_VAL是占位,实际值从手册拿。devm_mdio_find_bus要求设备树里 MDIO 控制器已经就绪,否则返回-EPROBE_DEFER,这是正常的延迟探测,不是错误。
提示:如果 probe 一直 defer,先确认 MDIO 控制器驱动有没有加载,用
ls /sys/class/mdio_bus/看总线在不在。
3. 设备树、MDIO 与端口注册:把芯片接进内核网络栈
骨架跑通只说明你能读到芯片,真正让它变成能用的网口,还得写设备树、注册 MDIO、把端口挂到 DSA 或 switchdev 上。这一章是落地的主体,每一步都有具体的文件要改、命令要跑。
3.1 设备树里 yt9215 节点的正确写法
设备树是驱动和硬件之间的合同。yt9215 的节点要挂在 MDIO 控制器下面,标明 compatible、reg(PHY 地址)、以及端口信息。下面是一个 DSA 模式下的典型写法。
&mdio { yt9215: switch@0 { compatible = "vendor,yt9215"; reg = <0>; #address-cells = <1>; #size-cells = <0>; ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; label = "cpu"; ethernet = <&fec1>; phy-mode = "rgmii-id"; }; port@1 { reg = <1>; label = "lan1"; phy-mode = "internal"; }; port@2 { reg = <2>; label = "lan2"; phy-mode = "internal"; }; }; }; };关键点:reg = <0>是 MDIO 总线上的 PHY 地址,要和硬件跳线一致。port@0是 CPU 口,ethernet指向 SoC 的 MAC 节点,phy-mode要按实际走线填,RGMII 一般用rgmii-id(内部延迟)。port@1、port@2是外部口,phy-mode = "internal"表示用芯片内部 PHY。如果端口映射和手册不一致,ip link出来的口会错位,甚至 probe 失败。
3.2 MDIO 读写与寄存器访问的封装
DSA 框架下,MDIO 读写由框架帮你做,但你要实现ds->ops里的phy_read和phy_write。如果是 switchdev 或者自己写,就得封装一套寄存器访问函数。下面是我常用的封装,基于 regmap。
static int yt9215_reg_read(struct yt9215_priv *priv, u32 reg, u32 *val) { int ret; ret = regmap_read(priv->regmap, reg, val); if (ret) dev_err(priv->dev, "read reg 0x%x failed: %d\n", reg, ret); return ret; } static int yt9215_reg_write(struct yt9215_priv *priv, u32 reg, u32 val) { int ret; ret = regmap_write(priv->regmap, reg, val); if (ret) dev_err(priv->dev, "write reg 0x%x failed: %d\n", reg, ret); return ret; } /* 读端口状态,bit0 表示 link up */ static int yt9215_port_link_status(struct yt9215_priv *priv, int port) { u32 val; int ret; ret = yt9215_reg_read(priv, YT9215_PORT_STATUS_REG(port), &val); if (ret) return ret; return !!(val & BIT(0)); }regmap_read/write的好处是它帮你处理了总线锁和缓存,不用自己加 mutex。YT9215_PORT_STATUS_REG(port)是个宏,按端口号算寄存器偏移,具体公式从手册拿。BIT(0)是 link 状态位,实际位定义要对照手册,有的芯片 link 和 speed 在不同位段。
3.3 端口注册与 netdev 创建
DSA 模式下,端口注册由框架在ds->ops->setup里回调,你只需要填ds->num_ports和每个端口的phy_mode。switchdev 模式下要自己调register_netdev。下面是一个 switchdev 风格的端口注册片段。
static int yt9215_register_ports(struct yt9215_priv *priv) { struct net_device *ndev; int i, ret; for (i = 0; i < priv->num_ports; i++) { ndev = alloc_etherdev(sizeof(struct yt9215_port)); if (!ndev) return -ENOMEM; ndev->netdev_ops = &yt9215_netdev_ops; ndev->ethtool_ops = &yt9215_ethtool_ops; SET_NETDEV_DEV(ndev, priv->dev); ret = register_netdev(ndev); if (ret) { free_netdev(ndev); return ret; } priv->ports[i].ndev = ndev; } return 0; }alloc_etherdev分配 netdev,netdev_ops里至少要有ndo_open、ndo_stop、ndo_start_xmit。SET_NETDEV_DEV把 netdev 和设备树节点关联,这样ip link能看到正确的父设备。register_netdev成功后,ip link就能看到 lan1、lan2 这些口了。如果注册失败,先看dmesg里的错误码,常见的是-EEXIST(名字冲突)或者-ENOMEM。
注意:DSA 模式下不要自己调
register_netdev,框架会帮你做,重复注册会导致内核 panic。
4. 避坑与排查:yt9215 驱动调试中最容易翻车的 5 个点
驱动写完了不代表能跑,调试阶段才是血泪经验的集中地。下面这 5 个坑是我在 yt9215 上实际踩过的,每个都按「现象 → 原因 → 解决」写,你遇到类似症状可以直接对号入座。
4.1 probe 成功但ip link看不到端口
现象:dmesg里能看到yt9215 probed,但ip link只有 lo 和 eth0,没有 lan1、lan2。
原因:端口注册没执行,或者 DSA 的ds->num_ports设成了 0。DSA 框架在dsa_register_switch时才会创建 netdev,如果setup回调里没填num_ports,框架就认为没有端口。
解决:在setup回调里确认ds->num_ports = priv->num_ports,并且每个 port 的phy_mode都填了。用cat /sys/kernel/debug/dsa/下的节点看框架识别到几个口。
4.2 端口能 up 但收不到包
现象:ip link set lan1 up成功,ethtool lan1显示 link up,但 ping 不通,tcpdump也看不到包。
原因:CPU 口的 tag 协议没配对。DSA 在 CPU 口和交换芯片之间用 tag 传递端口信息,如果 tag 协议和芯片实际用的不一致,包会被丢弃。
解决:确认ds->tag_protocol和芯片手册一致,常见的是DSA_TAG_PROTO_EDSA或DSA_TAG_PROTO_8021Q。用tcpdump -i eth0 -e看 CPU 口有没有带 tag 的包进来。
4.3 寄存器读出来全是 0xFFFF
现象:probe 时读芯片 ID 返回 0xFFFF,或者读任何寄存器都是 0xFFFF。
原因:MDIO 地址错了,或者寄存器访问宽度不对。0xFFFF 通常是总线没响应,MDIO 从设备没应答。
解决:先用mdio-tools或者mii-tool扫一遍 MDIO 总线,确认 yt9215 的 PHY 地址。然后检查reg_bits和val_bits是否和手册一致。如果是 PCIe 模式,确认 BAR 映射成功,用lspci -vv看 BAR 地址。
4.4 VLAN 配置后流量不通
现象:bridge vlan add配了 VLAN,但带 tag 的包进不来。
原因:芯片的 VLAN 表没下发,或者端口默认 VLAN 没设对。DSA 的 VLAN 操作会回调port_vlan_add,如果驱动没实现这个回调,配置就只停留在软件层。
解决:实现ds->ops->port_vlan_add和port_vlan_del,在里面写芯片的 VLAN 表寄存器。用bridge vlan show确认软件层配置,再读芯片 VLAN 表确认硬件层。
4.5 卸载模块后重新加载失败
现象:rmmod yt9215后再insmod,probe 返回-EBUSY。
原因:上一次卸载时资源没释放干净,比如 MDIO 总线没注销、netdev 没 unregister、中断没 free。
解决:在remove回调里按注册的逆序释放资源,unregister_netdev、mdiobus_unregister、free_irq一个都不能少。用cat /proc/interrupts确认中断有没有残留。
5. 进阶:用 ethtool 和 debugfs 验证 yt9215 的转发面
驱动跑通只是第一步,真正要上线还得能验证转发面是否正确。我一般用两个工具:ethtool看端口状态和统计,debugfs看芯片内部的 FDB 和 VLAN 表。下面这段是我常用的验证脚本,配合ethtool -S和 debugfs 节点,能快速定位转发问题。
#!/bin/bash # yt9215 转发面验证脚本 SW=lan1 CPU=eth0 echo "=== 端口状态 ===" ethtool $SW | grep -E "Link|Speed|Duplex" echo "=== 端口统计 ===" ethtool -S $SW | grep -E "rx_packets|tx_packets|rx_errors|tx_errors" echo "=== FDB 表 ===" cat /sys/kernel/debug/yt9215/fdb 2>/dev/null || echo "fdb node not found" echo "=== VLAN 表 ===" cat /sys/kernel/debug/yt9215/vlan 2>/dev/null || echo "vlan node not found" echo "=== CPU 口 tag 抓包(3 秒) ===" timeout 3 tcpdump -i $CPU -e -c 10 2>/dev/null这个脚本的逻辑是:先看端口物理状态,再看统计计数有没有增长,然后读 FDB 和 VLAN 表确认硬件表项,最后抓 CPU 口的包看 tag 格式。ethtool -S的计数如果rx_errors持续增长,说明有 CRC 或者对齐错误,要查 phy-mode 和时钟延迟。debugfs 节点需要你在驱动里用debugfs_create_file创建,把 FDB 和 VLAN 表 dump 出来,这是排查转发问题最直接的手段。
我自己的习惯是:每次改完驱动,先跑这个脚本,确认端口 up、统计增长、FDB 有表项,再去测实际流量。这样能把问题范围缩小到「硬件没通」还是「转发逻辑错」。yt9215 这颗芯片本身不复杂,复杂的是设备树和 tag 协议的匹配,把这两块吃透,剩下的就是体力活。希望帮到你。
本文还有配套的精品资源,点击获取