简介:这是一份面向ADM6996交换机芯片的驱动源码压缩包,适合嵌入式网络设备驱动开发工程师,以及需要基于该芯片完成系统适配、交换功能定制或调试相关硬件的中高级技术人员。包内共2个文件:ADM6996.c为驱动实现源文件,涉及芯片初始化、寄存器读写、数据通路收发等核心逻辑;ADM6996.h为配套头文件,定义了接口函数、数据结构与常量宏,方便上层模块调用或二次封装。压缩包仅1KB,代码虽精简,但完整呈现了驱动的基本骨架,便于逐行阅读、移植或整合进现有内核模块。已有229人学习下载,使用时可结合ADM6996数据手册理解寄存器布局,并配合EEPROM配置信息完成芯片启动参数烧写,确保交换机正确运行。开发者可借此掌握的不仅是ADM6996的软件接口,更是一套网络交换设备驱动的编写与排错思路,在此基础上继续扩展VLAN、端口管理、中断处理等功能时也有迹可循。
1. adm6996.tar.gz 里装的是什么:老交换机芯片的“后悔药”和入门钥匙
拆过老旧网络设备的人大多遇到过这种场面:板子上躺着一颗 ADM6996,丝印清晰,但厂商提供的驱动要么是闭源二进制、要么是一个解压后不知道从哪里下手的 tar.gz。这颗芯片是多年前很常见的五口百兆交换芯片,出现在各种 SOHO 路由器、工业交换机、物联网网关的网口背板上。你手上的 adm6996.tar.gz 通常是一套 Linux 下的驱动源码包,里面包含芯片初始化、PHY 管理、VLAN 表配置和端口镜像这几块代码。它能解决的核心问题只有一个:让板子上这颗交换芯片从“上电即通”的傻瓜模式,变成你可以控制 VLAN 隔离、端口镜像和 QoS 的网络设备。适合的是拿到板子想让网口真正按业务转发、而不是只会插上就亮的嵌入式工程师,也适合在旧设备上做二次开发和改造的人。
2. 认识 ADM6996:先从 tar.gz 里读出芯片的“黑匣子”
2.1 解包与文件布局:源码里哪几类文件直接决定能否移植成功
拿到 adm6996.tar.gz 之后,不建议急着丢进交叉编译链里跑,先解包看目录结构。常见的做法是放到工作目录下先列一遍文件类型,搞清楚里面是完整驱动还是只有寄存器头文件和示例。
mkdir -p ~/work/adm6996 && cd ~/work/adm6996 tar xzvf /path/to/adm6996.tar.gz file * | grep -E "text|source|script" | head -20 find . -maxdepth 2 -type f | sort解包后重点找三类文件:带_reg.h或_def.h后缀的寄存器定义头文件、带adm6996_main.c或adm6996_mdio.c的驱动主体、以及 README 或 INSTALL 文本。file命令能快速确认哪些是真正的 C 源码,哪些可能是换行符被改坏的文本文件。这个步骤的价值在于判断源码包的完整度:如果连寄存器定义头文件都缺,说明这个包拿到手就是残缺的,后边所有工作都要从逆向寄存器开始,工时预算要重新评估。
多数情况下,驱动主体会以mii.c或platform_driver的形式挂在 mdio 总线上,通过读写 MDIO/MDC 引脚访问芯片内部寄存器。源码包里往往还会带一个adm6996_switch.c这样的文件,里面实现了switchdev或procfs接口的封装。解包后先确认有没有struct platform_driver和struct mii_bus这两个关键结构体定义,它们决定了你后续编写的设备树节点和板级初始化代码长什么样。
2.2 看懂寄存器地图:VLAN、镜像与 QoS 的落点
ADM6996 的寄存器空间分两段:前 32 个是 PHY 寄存器,后面是 Switch Control Registers。驱动里所有功能最终都是写这几个寄存器实现的。不要试图记住每个寄存器的地址,重点记住三类:全局控制寄存器、VLAN 表寄存器、端口映射寄存器。
grep -n "VLAN\|PORT_MAP\|MIRROR\|QOS" adm6996_reg.h | head -40输出里会看到类似ADM6996_VLAN_TABLE、ADM6996_PORT_MAP这样的宏定义。这些宏的偏移量和解包前的数据手册寄存器地址一一对应。读懂这张寄存器表之后,你要明白几个关键逻辑:ADM6996 端口编号通常从 0 开始,端口 4 或 5 可能被设计成 CPU 口(接 SoC 的 MAC);VLAN 表是按哈希索引组织的,查表结果是端口位图;端口镜像的本质是把源端口收到的报文复制一份从镜像端口发出去,硬件上通过一个 mirror 控制寄存器开关。这些理解决定了你后边写配置工具时是直接写寄存器,还是调用驱动已有的 API。
3. 把解出来的驱动跑在目标板上:从交叉编译到最小配置
3.1 编译前必改的三个地方:Makefile、芯片型号选择与 PHY 地址
源码包解出来后直接make大概率会失败,因为 Makefile 里的交叉编译前缀和目标架构是写给作者自己的板子用的。常见需要改的是三处:CROSS_COMPILE、ARCH、以及CONFIG_ADM6996_PHY_ADDR。如果你把驱动编进内核而不是作为模块,还需要确认KERNELDIR指向你正在使用的内核源码树。
CROSS_COMPILE := arm-linux-gnueabihf- ARCH := arm KERNELDIR := /home/user/kernel/linux-4.14 obj-m := adm6996_drv.o adm6996_drv-objs := adm6996_core.o adm6996_mdio.o adm6996_vlan.o all: $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules宏CONFIG_ADM6996_PHY_ADDR如果不确定,可以先不定义,很多驱动会在probe时通过读取芯片 ID 寄存器自动识别 PHY 地址。mii-tool或phy_read能读到的典型 ID 寄存器的值与数据手册中ADM6996_ID的定义做比对。交叉编译前缀一定要和你的工具链一致,arm-linux-gnueabihf-是常见的硬浮点版本,如果你的根文件系统是软浮点编的,带上-mfloat-abi=softfp可能反而引发内核模块和你用户态库的 ABI 不匹配。模块方式编译时建议先make -C $(KERNELDIR) modules_prepare确认内核源码已经配置过,否则会出现一堆找不到generated/autoconf.h的报错。
3.2 注册成 MII 设备的最小路径:平台设备 + mdio 总线
编译通过只是第一步,真正麻烦的是让内核在启动时找到这颗芯片。ADM6996 走的是 mdio 总线,SoC 的 MAC 控制器会注册一个mii_bus,交换芯片挂在某个 PHY 地址上。驱动需要做的是在这个 mii_bus 上扫描并绑定 ADM6996,而不是直接注册一个平台设备。但很多源码包默认是平台设备模型,这时需要做一层适配。
static struct plat_adm6996_data adm6996_plat_data = { .phy_addr = 0x01, .cpu_port = 0x04, .vlan_enable = 1, }; static struct platform_device adm6996_device = { .name = "adm6996", .id = -1, .dev = { .platform_data = &adm6996_plat_data, }, }; static int __init adm6996_board_init(void) { return platform_device_register(&adm6996_device); } arch_initcall(adm6996_board_init);这段代码的关键在于phy_addr与cpu_port两个参数。phy_addr是芯片在 mdio 总线上的地址,一般由板级原理图决定,常见取值是 0x00 到 0x1F;cpu_port是连接 SoC 网口的端口号,如果接错,后面所有 VLAN 转发测试都会失败。arch_initcall保证它在网卡驱动注册之前被调用,否则可能出现 mdio 总线还没注册导致 probe 失败的情况。编译进内核时还要在 Kconfig 里增加对应的配置项,或者直接把这个文件的initcall改成module_init并用 insmod 加载,这样调试时可以快速改参数重载。
3.3 上电后的首个自检命令:看芯片是否被正确锁定
驱动加载成功后,第一件事是查看网络接口是否出现。因为 ADM6996 通常接到 SoC 的 MAC 上,所以你会看到 eth0(或 eth1)的 link 状态变为 UP。注意这个 link 不代表整个交换芯片已经工作了,它只是 MAC 到 PHY 之间的链路握手成功。
ifconfig eth0 up mii-tool eth0 cat /sys/class/net/eth0/phy_link_speed dmesg | grep -i adm6996如果mii-tool输出negotiated 100baseTx-HD之类的结果,说明 PHY 通信正常。如果输出no link,则要检查 PHY 地址对不对,常见做法是用mdio-tool或者直接写一个读取 PHY ID 寄存器的小工具,把 0x00~0x1F 地址全扫一遍,看哪个地址能读回0x0290或源码头文件里定义的厂商 ID。dmesg里如果出现adm6996 detect failed,基本可以断定 PHY 地址或 MII 总线号写错了。
4. 调通交换机的“本职工作”:VLAN、端口隔离与镜像的实际参数
4.1 配置 VLAN 的最小寄存器写序列
很多拿到源码包的人第一个疑问是:为什么默认状态下所有端口互通,配置 VLAN 之后反而全都不通了。这里要理解 ADM6996 的 VLAN 表工作方式:它是基于哈希查找的端口映射表,CPU 口默认不在任何 VLAN 里,一旦你把业务端口划进某个 VLAN,CPU 口也必须加进去,否则 SoC 发出的报文无法被交换芯片识别,表现为“可以收到外部包,但 ping 不通网关”。
static void adm6996_vlan_add(u16 vid, u8 port_mask) { u32 reg_val; /* 禁止 VLAN 表更新期间查表 */ adm6996_write_reg(ADM6996_VLAN_CTRL, 0x02); reg_val = (vid << 16) | (port_mask << 8) | 0x01; adm6996_write_reg(ADM6996_VLAN_TABLE_REG, reg_val); /* 先读后写,确保本次修改生效 */ reg_val = adm6996_read_reg(ADM6996_VLAN_TABLE_REG); adm6996_write_reg(ADM6996_VLAN_CTRL, 0x00); }上面代码里0x02是置位 VLAN 表更新模式,0x01是触发一次表项写入。vid是 VLAN ID,port_mask的每个 bit 对应一个物理端口,比如 0x13 表示端口 0、1、4 在同一个 VLAN。不同源码包对ADM6996_VLAN_TABLE_REG偏移的定义可能不一致,常见命名是ADM6996_VLAN_TABLE0到TABLE3。业务上一定要把 CPU 口算进 mask 里,这是几乎所有初次接触的人都会踩的坑。配置完 VLAN 后验证方式很简单:两个业务口互 ping 通、和 CPU 互 ping 通,但跨 VLAN 互 ping 不通。如果全部 ping 不通,优先检查 port_mask 的位顺序是否和芯片默认端口编号一致,有的版本端口 0 对应最靠近电源灯的网口,有的反过来。
4.2 端口镜像:让抓包器看到另一个口
端口镜像常用于故障排查和流量审计。ADM6996 支持把一个或几个源端口的流量复制到镜像端口,但镜像端口本身不能承载业务流量,否则会出现帧风暴。
static int adm6996_set_mirror(int src_port, int mirror_port) { u32 val; if (src_port == mirror_port) { pr_err("src and mirror port must be different\n"); return -EINVAL; } /* 设置镜像端口和源端口 */ val = (mirror_port << 8) | (1 << src_port); adm6996_write_reg(ADM6996_MIRROR_REG, val); /* 使能镜像功能 */ val = adm6996_read_reg(ADM6996_MIRROR_CTRL); val |= 0x80; adm6996_write_reg(ADM6996_MIRROR_CTRL, val); return 0; }src_port作为 bit 位放进低 8 位,mirror_port放进高字节。使能位是0x80,表示镜像功能开启。镜像口一般选择接一个不带 VLAN 的独立网口,用笔记本电脑抓包。查问题时常见的困惑是为什么 mirror 口只能抓到广播包抓不到单播包——这通常是因为源端口和镜像端口必须在同一个 VLAN 域里,ADM6996 的镜像实现是查表复制,VLAN 不匹配直接丢弃副本。另一个常被忽略的地方是镜像方向,这个寄存器通常同时控制 RX 和 TX,无法单独镜像一个方向,如果你只需要看上行流量,必须在应用层过滤。
4.3 和现有网络设备对接时的兼容性设置
在实际组网中,ADM6996 所在的设备经常要接到别的交换机上,比如接到 H3C 或华为的接入交换机的 trunk 口。这种情况下 ADM6996 侧的 VLAN 设置要特别注意帧格式。如果对端交换机 trunk 口封装的是 802.1Q,你这边端口必须是 tagged 模式,否则对端会直接丢弃帧,现象就是灯亮着但 ping 不通。
# 在 Linux 侧创建带 VLAN 的接口 ip link add link eth0 name eth0.100 type vlan id 100 ip link set eth0.100 up ip addr add 192.168.100.2/24 dev eth0.100常见做法是把 ADM6996 的 CPU 口设成 tagged,业务口设成 untagged。Linux 侧再用ip link创建对应的 VLAN 子接口。如果对端设备是华为或锐捷,还要注意链路两端的协商模式:ADM6996 这边固定成 100M 全双工,对端如果设置成自协商,可能会出现协商失败变成半双工,性能断崖式下降。遇到这种无法协商的情况,可以在对端交换机上强制端口速率,或者在 ADM6996 驱动初始化时直接把自动协商关掉,写死速度和双工模式。
5. adm6996 常见问题与避坑:功耗、镜像、自协商与环路的现场排查
5.1 现象:驱动加载正常,但两个业务口互相不通
板子上电,dmesg 里也看到了adm6996 driver initialized,两个端口插上电脑,灯都亮,但互 ping 不通。从原理上看,问题基本出现在端口映射寄存器没有初始化。很多源码包默认把端口映射表清零了,意味着每个端口不在任何 VLAN 里,交换芯片不知道把帧往哪转发。解决方法是重新初始化所有端口的默认转发位图:端口 0 应该发给端口 1、2、3、4、5,端口 1 发给 0、2、3、4、5,依此类推。有的驱动把这个放在adm6996_reset_switch()里,有的放在init_port_map()里,如果没执行,整个芯片就是一个空壳。在probe函数最后打一条日志,确认port_map寄存器读回的值不是 0。
5.2 现象:打开镜像后,业务口的吞吐量骤降
抓包需求很多,但开了镜像口之后,原来能跑满 100M 的业务口变成只有 30M。原因是 ADM6996 的镜像实现会把所有流量从 mirror 口复制一份,如果 mirror 口以半双工模式连到分析设备,复制过程会消耗内部带宽。这个不是 bug,是芯片设计的硬限制。我的习惯是镜像功能只在排障时临时开,调完立刻关掉,不要做成常开功能。如果你的业务就是需要做流量审计,那就得换带独立镜像处理能力的交换芯片,别在 ADM6996 上死磕。
5.3 现象:反复重启后配置丢失
设备断电重启,VLAN 设置全部没了,又回到所有端口互通状态。这是因为 ADM6996 内部寄存器是易失性的,所有配置都在 RAM 里,掉电即失。源码包里的驱动如果没做配置持久化,重上电后只有probe里的默认值。解决思路有两种:如果用的是 Linux,在用户态写一个启动脚本,通过驱动导出的 sysfs 节点或 ioctl 重新下发 VLAN 配置;如果用的是 RTOS,就要在初始化函数里把配置表放到 Flash 里,上电时先读 Flash 再写寄存器。很多源码包确实带一个adm6996_config.c文件,里面定义了默认配置数组,你要做的就是确认这个数组的内容符合你的业务需求。
5.4 现象:接到原有的交换机上后整个网络时通时断
把 ADM6996 的设备接到现网的交换机后,整个链路不稳定,Ping 延迟忽高忽低。优先看网线对端交换机的端口日志,八成是协商成了 10M 半双工,或者对端端口做了风暴控制,把这台设备发的未知单播帧误判成广播风暴给丢弃了。解决方法是对端交换机端口配置undo negotiation auto(华为或 H3C 指令场景)强制 100M 全双工,或者在本侧驱动里关闭自协商。另一种可能性是环路:ADM6996 的默认配置不参与 STP,如果你的组网里出现了冗余链路,广播帧会无限循环,直接导致整个局域网瘫痪。排查时拔掉一根网线测试即可确认。
5.5 现象:同一版本源码,换一批板子后 PHY 地址读不到了
原来的板子 PHY 地址是 0x01,新打样的板子驱动加载失败。原因是硬件改版把 ADM6996 的 PHY 地址引脚电平改了,MDIO 地址从 0x01 变成了 0x05。这类问题在量产时经常遇到,属于典型的“硬件改版、软件滞后”事故。常见做法是不要写死 PHY 地址,而是让驱动在probe阶段扫描整个 MDIO 地址空间,通过读芯片 ID 寄存器来确认是不是 ADM6996。扫描代码很少,但能省下大量硬件和软件联调的沟通成本。
6. 把“不通”变成“预期内”:留好读回接口和验证脚本
先给驱动加一个简单的寄存器读回接口,我一般喜欢暴露成/sys/kernel/debug/adm6996/reg这样的 debugfs 节点。读回的价值在于:写寄存器人人会,读寄存器才能确认硬件真的按你的意思工作了。下面这组命令可以作为每次调整后的标准验证流程:
# 打印所有端口 link 状态 cat /sys/kernel/debug/adm6996/link_status # 读取 VLAN 表前 4 项 echo 0 > /sys/kernel/debug/adm6996/vlan_index cat /sys/kernel/debug/adm6996/vlan_table # 读取端口映射寄存器 cat /sys/kernel/debug/adm6996/port_map配合 tcpdump 做转发验证时,将笔记本插到镜像口,执行tcpdump -i eth0 -e -n vlan 100,观察抓包内容里是否出现业务口的 MAC 和目标 MAC。如果抓到的全是广播帧,没有源端口发来的单播帧,那就回到第 4 章检查 port_mask。链路吞吐的验证不建议直接用 iperf,老芯片的收发缓存很小,大包小包混杂时候会掉包,相对可靠的做法是用ping -s 1400 -c 200看丢包率,再配合netstat -i查看 RX-DRP 和 TX-DRP 计数。如果ping -s 1400不丢但ping -f丢包严重,优先级队列配置就要单独调。
有一件事我吃过亏:调试完直接把方案移交,结果一个月后现场反馈“交换机偶尔死机”。后来发现得意忘形地忽略了板子上的散热设计,ADM6996 在全速转发时发热明显,寄存器温度超过阈值后芯片内部锁相环失锁导致转发异常。从那以后我的习惯是每次拿到新方案,先让它满负荷跑 24 小时,中途人工断几次电模拟现场误操作。这套验证流程看起来不聪明,但确实帮我减少了百分之八十的售后往返。希望帮到你。
本文还有配套的精品资源,点击获取