简介加密芯片ATSHA204A采用SHA-256与AES-128算法在Linux系统中承担设备身份认证与数据安全保护任务。这套驱动源码包面向嵌入式Linux驱动开发工程师、系统安全相关研发人员及硬件学习爱好者完整提供了驱动与内核交互所需的头文件、核心逻辑实现、I2C通信层及SHA204辅助层适合作为对照芯片手册学习真实Linux驱动框架、I2C设备和字符设备注册流程的参考。压缩包共12个文件以6个头文件和4个C文件为主体附带Makefile与Kconfig可在内核配置中灵活选择编译方式结构简洁便于阅读和二次移植。当前已有485人学习使用。通过学习源码可掌握ATSHA204A命令封装、校验返回码、ioctl接口设计等细节并了解驱动如何向上层暴露安全操作能力为后续在产品项目中集成加密芯片或独立开发安全驱动打下基础。1. 加密芯片 ATSHA204A Linux 驱动源码为什么设备商都卡在这一关做嵌入式 Linux 项目的朋友应该都有过这种经历硬件选型时加密芯片选的是 Microchip ATSHA204A数据手册翻得滚瓜烂熟结果到了移植驱动环节发现官方只给了单片机例程和 Windows 下的工具Linux 下要么自己写、要么去翻一堆年代久远的补丁。这份 ATSHA204A Linux 驱动源码要解决的就是这个断档问题——它提供了一整套基于 I2C 总线的字符设备驱动框架把芯片内部的随机数生成、MAC 认证、配置区读写这些能力暴露成标准的 ioctl 接口应用层不用碰 I2C 协议细节几行代码就能完成一次硬件级安全认证。适合正在做设备认证、耗材防伪、固件防抄板方案的工程师也适合想把硬件根密钥真正用起来、而不是只在固件里存一段假加密字符串的开发者。2. 先把存储分区和命令族吃透驱动逻辑的根基在芯片内部2.1 四个存储区域与权限模型写驱动前先搞清谁能写、谁能读ATSHA204A 内部不是一块简单的 EEPROM它把存储空间分成 Config、Data、OTP、SRAM 四个区。驱动源码里所有读写逻辑都是围绕这四个区的权限模型设计的如果对这个理解不到位调试 CheckMAC 失败时你会完全摸不着头脑。Config 区一共 88 字节存放芯片序列号、I2C 地址配置、安全设置位、锁定状态等。这个区最大的特点是一旦你把 Config 区 lock 住里面的关键字节就永远改不了了。驱动源码里的写配置、读序列号功能本质上就是在跟这个区打交道。Data 区才是真正存业务密钥的地方总共 4096 位512 字节分成 16 个 slot每个 slot 32 字节。OTP 区 64 字节一次性可编程驱动如果读到一个非 0xFF 的 OTP 字节说明已经被写过。SRAM 是临时区掉电即失Nonce 命令生成的随机数临时值就存放在这里。这里有个新手最容易忽略的点Data 区的每个 slot 都有独立的权限配置写在 Config 区对应字节里。比如一个 slot 可以被配置成 永远不可读、只能用于 MAC 运算另一个 slot 可以配置成 可写不可读。这份驱动源码在初始化时会读取 Config 区的权限配置动态决定 ioctl 的读写命令是否放行。你在应用层发现 Write 命令返回错误码 0x0FCommand execution error时先别急着怀疑驱动八成是 slot 权限配成了只允许 MAC 运算。另外一个关键是锁定状态。驱动源码里通常会带一个 lock 命令的实现对应芯片的 0x15 命令Lock。Config 区锁定后整个芯片进入生产姿态所有配置字节冻结Data 区锁定后slot 权限同时冻结但 Data 区内容仍可按权限写入。开发阶段我习惯只锁 Config、不锁 Data等量产前再一次性锁 Data 区。2.2 命令族与指令字节ioctl 背后对应的是哪条硬件指令ATSHA204A 的指令集是固定的那十几条驱动源码的功能映射表如下。这份驱动把每条指令封装成一个内核函数应用层通过 ioctl 命令字触发。命令名指令字节驱动内对应函数用途Write0x12sha204a_write写 Data / Config / OTP 区Read0x02sha204a_read读存储区Random0x1Bsha204a_random生成 32 字节随机数Nonce0x16sha204a_nonce生成临时随机数存入 SRAMMAC0x08sha204a_mac基于密钥计算 MAC 摘要CheckMAC0x28sha204a_checkmac校验对方提供的 MAC 值DeriveKey0x1Csha204a_derivekey从现有密钥派生新密钥HMAC0x11sha204a_hmac计算 HMAC 摘要Lock0x17sha204a_lock锁定 Config / Data 区Pause0x01sha204a_pause多芯片共总线时选片驱动源码里有一个参数需要特别注意MAC 命令中有一个 mode 字节bit 0 决定要不要把 Nonce 值包含进 MAC 计算bit 3 决定是不是要输出密钥所在 slot 的完整摘要。很多人在应用层对接失败就是因为 host 端和芯片端 MAC 计算的 mode 不一致。驱动源码在 ioctl 参数里直接暴露了 mode 字段你在用户态传什么驱动就原样传给芯片——这里没有任何黑匣子操作调试时两边对照 datasheet 即可。2.3 I2C 通信特性ATSHA204A 不是标准 SMBus 设备ATSHA204A 走的是 I2C 接口但它有两个不能忽略的脾气。第一它的 I2C 从机地址不是 0x48、0x50 这种常见地址默认是 0x647 位地址。用 i2cdetect 扫描时会看到 0x64 出现在列表里。但这不是固定的Config 区里有 I2C 地址配置字节可以改。驱动源码默认写死 0x64如果你的板子改过地址记得把驱动里的 SHA204A_I2C_ADDR 宏一并改掉不然 probe 阶段直接失败。第二ATSHA204A 支持时钟拉伸clock stretching。芯片在完成命令计算后会通过拉低 SCL 让主机等待。Linux I2C 子系统默认支持这个行为但前提是你的 I2C adapter 驱动没关闭相关标志位。之前遇到过一版老的内核某些平台 I2C 控制器在 dmesg 里报 bus busy 错误后来查出来是 adapter 的 quirks 标志不允许时钟拉伸。驱动源码里对这个问题没有特殊处理因为正常内核的 i2c-core 已经支持了——你如果遇上 I2C 传输超时优先查硬件平台适配层的 quirks。另外ATSHA204A 有一条专用的唤醒时序在发送任何命令前需要把 SDA 拉低至少 60 微秒再释放芯片才从睡眠状态醒过来。驱动源码的sha204a_wake函数就是用 GPIO 模拟这个时序。有些移植版本会直接用 i2c 的 SMBus 写一个字节来唤醒这也能用但实测在长 I2C 总线上偶尔会唤醒失败还是 GPIO 拉低最稳。3. 移植到你的板子设备树、Makefile 与加载验证3.1 设备树节点怎么配compatible 与 reg 缺一不可拿到这份驱动源码第一步是把它接到你的内核设备树里。ATSHA204A 挂在 I2C 总线上所以设备树节点是 i2c 总线节点的子节点。常见写法是i2c1 { status okay; clock-frequency 100000; sha204a64 { compatible microchip,atsha204a; reg 0x64; reset-gpios gpio3 17 GPIO_ACTIVE_LOW; }; };reg 0x64必须和芯片实际 I2C 地址一致前面说过默认是 0x64。reset-gpios不是必需的——ATSHA204A 本身没有 reset 引脚但有些板子会给芯片供电加一个 GPIO 可控的 LDO用来做硬件复位驱动里一般会留一个复位接口。如果你板子没有这个控制直接删掉这行就行。compatible 字符串要跟驱动源码里的of_device_id匹配。绝大多数驱动源码里写的是 microchip,atsha204a下载后建议先 grep 一下确认不一致就改成一致否则内核不会执行 probe 回调。3.2 Makefile 与交叉编译一个 5 分钟搭好的编译环境这份驱动源码是内核模块编译时拿你的嵌入式 Linux 项目的内核源码树来编。Makefile 按内核模块标准写法组织KERNEL_SRC ? /lib/modules/$(shell uname -r)/build CROSS_COMPILE ? arm-linux-gnueabihf- ARCH ? arm obj-m sha204a_drv.o sha204a_drv-objs : sha204a_core.o sha204a_i2c.o sha204a_cmd.o all: $(MAKE) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) -C $(KERNEL_SRC) M$(PWD) modules clean: $(MAKE) -C $(KERNEL_SRC) M$(PWD) clean编译很简单指定好内核源码目录和交叉编译工具链执行make。三行命令搞定export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf- make KERNEL_SRC/path/to/kernel驱动源码分了三个 .c 文件sha204a_core.c管字符设备和 ioctl 分发sha204a_i2c.c管 I2C 收发与唤醒时序sha204a_cmd.c管命令帧组装和 CRC 校验。这样拆的好处是你要换 I2C 控制器实现只改sha204a_i2c.c要加新命令只动sha204a_cmd.c核心的 file_operations 结构体基本不动。3.3 加载与探测dmesg 和 i2cdetect 交叉验证先加载模块确认没有报错insmod sha204a_drv.ko dmesg | tail -30正常情况 dmesg 里能看到sha204a 1-0064: chip found, serial number xx...类似的信息。如果没看到 probe 成功的日志先用 i2cdetect 确认芯片是否在总线上i2cdetect -y -r 1这时输出的地址表里应该能看到64如果没被 i2c-tools 自动规避。-r参数很关键ATSHA204A 不支持 SMBus 的 quick read 操作不加-r、默认用 quick write 方式扫描时芯片会因为收到非法命令进入错误状态扫描结果可能是一片空白或者地址变成UU被驱动占用。我遇到过好几次这个问题后来习惯直接-r就没翻过车。确认芯片在线后检查/dev下是否生成了设备节点ls -l /dev/sha204a*驱动源码里如果用的是动态分配主设备号内核会通过misc_register自动创建设备节点。如果/dev下没有检查设备树 compatible 是否匹配、模块有没有被正确绑定不要怀疑是别的问题——百分之七八十是设备树节点没配对。4. 源码解析从 open 到 ioctl 的完整调用链4.1 file_operations 与 ioctl 命令分发驱动源码的用户态入口是标准的字符设备操作。核心结构体长这样static const struct file_operations sha204a_fops { .owner THIS_MODULE, .open sha204a_open, .release sha204a_release, .unlocked_ioctl sha204a_ioctl, };sha204a_open函数里通常只做两件事递增打开计数、获取 I2C adapter 引用。因为 ATSHA204A 是独占设备同一时刻只允许一个进程打开避免两条命令交叉把状态机搞乱。驱动源码里用atomic_read判断是否已被占用非 0 直接返回-EBUSY。ioctl 是真正的命令入口。驱动源码定义的一套 ioctl 命令字一般是SHA204A_IOC_RANDOM、SHA204A_IOC_MAC、SHA204A_IOC_CHECKMAC、SHA204A_IOC_WRITE、SHA204A_IOC_READ这种风格对应芯片指令。命令字用_IOR/_IOW宏定义保证和文件系统的其他 ioctl 不冲突。这里有一个内核编程的习惯要说一下unlocked_ioctl和ioctl的区别。新内核里已经删了老的ioctl字段只有unlocked_ioctl。驱动源码如果是从老补丁移植来的你要是看到ioctl赋值编不过去手动改成unlocked_ioctl就行这是内核 2.6.36 以后的规定。4.2 命令帧封装与 CRC不按 datasheet 来就会撞墙ATSHA204A 的命令帧格式是固定的 5 字节头部加参数所有命令帧都要走一遍 CRC16 校验。驱动源码里sha204a_cmd.c一般会提供两个函数sha204a_crc16和sha204a_calccrc。static uint16_t sha204a_crc16(uint16_t crc, uint8_t byte) { uint16_t crc16 crc; uint8_t i; crc16 ^ byte; for (i 0; i 8; i) { if (crc16 0x0001) crc16 (crc16 1) ^ 0x8005; else crc16 crc16 1; } return crc16; }CRC 多项式是 0x8005初始值是 0x0000这是 ATSHA204A 专用的 CRC16 变体跟标准 Modbus 的 0xA001 反射多项式不是一回事。很多人在自己写应用层协议对接时套用了 libcrc 里的 CRC16/Modbus结果算出来跟芯片返回的校验对不上——这块在驱动源码内部已经处理好了但你要是想基于这份源码做二次开发记得保留原实现别拿通用 CRC 库去替换。命令帧的发送流程是这样的驱动先发 wake 时序然后写命令帧再等待芯片处理完毕最后读响应帧。响应帧的第一字节是状态字节0x00 表示成功其他都是错误码。常见错误码里面 0x0F 是命令执行失败0x01 是校验错误0x03 是芯片还在忙。调试时看这个字节基本能定位问题方向。一个值得注意的细节是ATSHA204A 的命令执行时间不是固定的。Random 命令几十毫秒MAC 命令可能上百毫秒。驱动源码一般会用 I2C 的轮询方式等待先读一字节如果读到 0x00 说明命令执行完毕否则延时再读。这个过程不能死等源码里sha204a_poll_status会限时重试超时返回-ETIMEDOUT。你要是发现随机数生成命令经常超时检查一下 I2C 时钟频率100kHz 和 400kHz 下的命令耗时差别不小。4.3 内核态与用户态的边界为什么敏感计算必须在芯片里一定要理解ATSHA204A 的安全模型是「密钥永远不出芯片」。驱动源码里所有涉及密钥的操作MAC、CheckMAC、DeriveKey、HMAC都是把参数传给芯片由芯片内部取用存储在 Data 区 slot 里的密钥计算结果通过 I2C 返回。驱动本身不读取密钥也没有任何接口能直接读出 slot 里的密钥字节——除非你在 Config 区里把该 slot 配成了可读。这是整个安全方案的地基。如果驱动给用户态提供了 Read slot 的能力那整个方案就失去了意义因为攻击者只要从应用层读一遍 slot密钥就泄露了。所以使用这份驱动源码时你应该确认sha204a_read函数在 ioctl 层面对 Data 区 slot 地址做了限制凡是被配置成不安全类型的 slotread 一律返回-EPERM。源码里常见做法是在驱动初始化时读取 Config 区建立一张 slot 权限表后续每次读写都查表拦截。我见过一种典型的误用开发者在调试阶段为了图省事把某个 slot 配成可读写等产品上线也没改。到最后整机被抄板检查才发现密钥早就能被读出来。这是驱动层面防不了的事情属于应用层的低级失误。所以我的习惯是开发阶段用开发密钥上线前生成新密钥并锁定所有敏感 slot这个流程驱动无法替代。5. 编译加载避坑五个最常见的翻车现场5.1 现象insmod 成功但 /dev 下没有设备节点原因设备树 compatible 与驱动of_device_id不匹配probe 根本没执行或者用的老内核里misc_register失败没报出来。解决dmesg | grep sha204a看内核有没有打印匹配信息。没有的话打开设备树节点对应目录ls /sys/bus/i2c/devices/1-0064/这个目录存在说明硬件枚举成功再检查目录下的modalias里的 compatible 字符串是否跟驱动里一致。不一致就改设备树一致就查驱动 init 函数的注册顺序。5.2 现象i2cdetect -y -r 1 扫不到 0x64原因地址被改过不是默认值或者 I2C 总线号不对最隐蔽的是——芯片在睡眠状态里i2cdetect 的扫描命令没走唤醒时序芯片不响应。解决先用 GPIO 手动拉低 SDA 60us 唤醒再重新扫描。命令如下gpioset gpiochip3 171 # 先释放 sleep 0.01 gpioset gpiochip3 170 # 拉低实际输出的是 I2C SDA 线 sleep 0.1 gpioset gpiochip3 171 # 释放完成唤醒时序但要注意 SDA 是 I2C 总线上的开漏信号用 GPIO 置低直接拉可能导致总线上同时出现两个主机在操作。更稳的做法是在驱动源码里加一个调试 ioctl专门触发sha204a_wake函数从内核态操作。改完重新编译加载再跑 i2cdetect。如果还是没有用示波器抓 SDA 波形看唤醒时序的拉低时间够不够 60 微秒——很多 GPIO 操作接口有延迟实际拉低时间可能只有 20 微秒这就是玄学现场抓波形最靠谱。5.3 现象Random 命令返回 0x0F 命令执行错误原因芯片还没完全唤醒命令帧第一个字节被芯片当成无效数据丢弃后续字节错位芯片解析出非法命令。解决在每条命令前强制插入唤醒时序而不是只在初始化时唤醒一次。ATSHA204A 在空闲 10 毫秒后会自动进入睡眠状态所以每次 ioctl 调用前都要先唤醒。驱动源码sha204a_i2c.c里会有一个sha204a_send函数这个函数的第一步是发 wake第二步才是写命令帧。如果移植时把 wake 挪到了 open 里执行一次后面所有命令都会间歇性失败——这是最常见的移植翻车点。5.4 现象I2C 传输偶尔报 timeout 且重试后正常原因ATSHA204A 在执行命令时会拉长 SCL如果 I2C adapter 不支持时钟拉伸或者总线上挂了其他不支持拉伸的设备就会出现偶发超时。解决把 I2C 时钟频率降到 100kHz同时确认适配器驱动没有主动禁用时钟拉伸。在内核配置里检查CONFIG_I2C_GPIO或对应平台控制器的相关项。如果是 GPIO 模拟的 I2C打开CONFIG_I2C_GPIO后天然支持拉伸问题基本就消失了。另外总线上最好只挂 ATSHA204A 一个设备至少别挂 EEPROM——EEPROM 的 ACK 回得快ATSHA204A 的命令计算时间长两者并发容易造成总线锁死。5.5 现象普通用户运行应用层程序报 Permission denied原因设备节点默认权限是 root 才能访问这在内核里通过misc_register创建设备节点时用的默认 mode 决定。解决在驱动源码里找到miscdevice结构体的mode字段改成 0666或者用 udev 规则给/dev/sha204a*授权。我一般用后者因为产品环境里设备节点权限应该收敛而不是放开KERNELsha204a*, MODE0660, GROUPsecurity把运行安全服务的用户加进security组这样既避免 root 直跑应用又不会出现权限问题。注意改 udev 规则后执行udevadm control --reload和udevadm trigger才会生效。6. 应用层对接技巧用一套随机数回环流程验证驱动完整可用6.1 用户态调用的组织方式驱动是否能正常工作最终要在应用层验证。用户态程序通过 ioctl 向驱动发命令这段代码是这套方案最值得收进你项目里的部分#include stdio.h #include fcntl.h #include sys/ioctl.h #include sha204a_ioctl.h int main(void) { int fd open(/dev/sha204a, O_RDWR); if (fd 0) { perror(open); return -1; } struct sha204a_random_arg { uint8_t num_bytes; uint8_t random[32]; } rnd_arg { 32, {0} }; if (ioctl(fd, SHA204A_IOC_RANDOM, rnd_arg) 0) { perror(ioctl random); close(fd); return -1; } printf(random[0..3]: %02x %02x %02x %02x\n, rnd_arg.random[0], rnd_arg.random[1], rnd_arg.random[2], rnd_arg.random[3]); close(fd); return 0; }这个调用方式里有个细节值得注意ioctl 第三个参数传的是用户态结构体指针驱动在内核态用copy_from_user把参数拷贝进来、用copy_to_user把随机数写回去。这套源码包的定义里random字段是输出参数用户态不需要预置任何内容。如果你想在调试时省事直接在用户态多调几次 Random每次输出 32 字节肉眼就能判断随机数质量——连续调用生成的数不重复说明驱动到芯片的链路是通的。6.2 完整的回环验证Random → Nonce → MAC驱动不能只测随机数因为那只是芯片最基础的功能。真正要验证的是密钥操作链路。在没有预置密钥的前提下可以先做一组回环流程来验证 MAC 计算是否可靠# 先向 slot 3 写入测试密钥32 字节全为 0xAA ioctl /dev/sha204a SHA204A_IOC_WRITE slot3 dataaa...aa # 生成随机数 ioctl /dev/sha204a SHA204A_IOC_RANDOM outrandom.bin # 用随机数作为 Nonce 输入 ioctl /dev/sha204a SHA204A_IOC_NONCE datarandom.bin # 计算 MAC模式选包含 Nonce 的模式 ioctl /dev/sha204a SHA204A_IOC_MAC slot3 mode0x01 outmac.bin如果每一步都返回 0说明芯片内的 SRAM、Data 区、MAC 引擎全部工作正常。这里有一个判断小技巧把同一份 Nonce 数据重新传一次再算一次 MAC两次结果应该完全相同。如果两次 MAC 不一致说明 Nonce 操作没有正确更新 SRAM驱动在 Nonce 命令的参数传递上有问题重点检查命令帧长度对不对。6.3 出厂姿态与开发姿态的切换驱动最终交付时要确保芯片处于生产锁定状态。Config 区和 Data 区锁定后芯片的序列号、密钥、权限配置全部冻结任何人都无法通过 I2C 改写。我一般会在出厂测试固件里加一个自检阶段先读序列号再执行一次 MAC 回环确认密钥存在最后执行 Lock 命令锁定 Data 区。锁定之后再做一次 CheckMAC 验证——这一步很关键因为锁定后某些 slot 的读权限会立即被收紧如果自检逻辑还按开发时的权限去读 slot会直接失败。从那以后我每移植一个平台都会强制走一遍「写开发密钥 → 回环验证 → 锁定 → 锁定后验证」的流程漏掉哪一步都会在生产阶段付出十倍的时间代价。希望这套驱动源码和这篇实战笔记能帮你在 ATSHA204A 的方案落地上少走几步弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?