1. 25 年前的 ftape 驱动,为什么在现代内核上跑不起来
如果你手里有一台 QIC-80 磁带机,或者任何通过软驱控制器(FDC)通信的老式磁带设备,大概率听说过 ftape 这个驱动。它最后一次正经维护大概在 2000 年前后,之后就从内核主线里消失了。原因不复杂:磁带机本身被 USB、SCSI 和后来的云存储挤出了市场,没人再愿意维护一套直接操作 FDC I/O 端口、时序和中断的代码。
但问题在于,很多老磁带里的数据还在。QIC-80 在 1990 年代是个人和小型企业的常用备份介质,只要磁带没有物理损坏,里面的二进制内容是可以完整读出来的。ftape 的价值就在于它绕过专有格式,直接把磁带上的原始数据导出,后续再慢慢解析逻辑格式。这也是为什么有人宁愿留一台跑 CentOS 3.5 的老机器,也不愿意放弃它。
麻烦点在于,从 2.4 内核到 6.x 内核,内核 API 发生了大量变化:init_timer没了,file_operations结构体字段改了,register_chrdev的用法变了,内存分配和锁的接口也换了好几轮。一个 25 年前的驱动直接拿过来编译,报错能刷满整个终端。手动改不是不行,但你得同时熟悉 2.4 和 6.x 两套内核规范,学习成本极高。
这篇要做的,就是用 Claude Code 作为辅助工具,配合 TaoToken 统一 Key 通道,把 ftape 这类老驱动在现代内核上重新编译、加载、验证通信。我会给出可复制的settings.json和config.toml配置骨架、CC Switch 切换步骤,以及从编译到dmesg验证的完整命令和预期输出。适合有基本 C 语言和 Linux 模块经验、想跟做一遍的读者。
2. 前置准备:TaoToken 统一 Key 与 Claude Code 接入
Claude Code 本身是一个命令行编码助手,它需要模型 API 通道才能工作。TaoToken 在这里的角色是提供一个统一的 Key 和 API 入口,让你不用在多个模型供应商之间反复切换配置。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。
你需要先拿到一个可用的 API Key。进入控制台创建 Key 的页面在这里:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。创建之后复制出来,后面配置里会用到。注意 Key 只显示一次,丢了就重新建一个。
Claude Code 的接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面会说明当前支持的模型名和 base URL 格式。如果你用的是 Claude Code 的 Anthropic 兼容模式,参考这个页面:https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 。
环境上,你需要一台现代 Linux 发行版,我实测用的是 Xubuntu 24.04,内核 6.8。需要装好build-essential、linux-headers-$(uname -r)、git、curl。Claude Code 本身按官方方式安装即可,这里不展开安装步骤,重点放在配置和驱动编译上。
注意:加载和卸载内核模块需要 sudo 权限。Claude Code 不应该被赋予自动执行 sudo 操作的能力,敏感操作由你自己手动执行,把输出粘贴回去让它分析,这样更安全也更可控。
3. 可复制配置:settings.json 与 config.toml 骨架
Claude Code 的配置分两层:一层是它自身的settings.json,用来指定 API 通道和模型;另一层是项目级的config.toml,用来约束它在当前驱动仓库里的行为。下面是我实际用的骨架,你可以直接复制后替换 Key。
先看settings.json,一般放在~/.claude/settings.json或项目下的.claude/settings.json:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的TaoTokenKey", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" }, "permissions": { "allow": [ "Read", "Glob", "Grep", "Bash(gcc:*)", "Bash(make:*)", "Bash(ls:*)", "Bash(cat:*)", "Bash(dmesg:*)" ], "deny": [ "Bash(sudo:*)", "Bash(rm:*)", "Bash(insmod:*)", "Bash(rmmod:*)" ] } }这里的关键点:ANTHROPIC_BASE_URL指向 TaoToken 的 API 入口,不要加多余的路径后缀;ANTHROPIC_API_KEY填你刚创建的 Key;模型名按文档里当前可用的写。permissions.deny里明确禁掉了sudo、insmod、rmmod,防止它在迭代过程中自动加载模块或删文件。编译相关的make、gcc放开,方便它自己跑编译看报错。
再看项目级的config.toml,放在驱动仓库根目录,比如ftape/config.toml:
[project] name = "ftape-modernize" kernel_target = "6.8" source_root = "./" [build] makefile = "Makefile" module_name = "ftape" extra_cflags = ["-Wall", "-Wno-unused-variable"] [context] include_dirs = [ "/lib/modules/6.8.0-45-generic/build/include", "/lib/modules/6.8.0-45-generic/build/arch/x86/include" ] reference_logs = ["./logs/good-dmesg.txt"] [behavior] explain_before_edit = true max_iterations = 8include_dirs里的路径要用uname -r的实际结果替换。reference_logs指向你保存的“已知良好” dmesg 日志,后面排查通信问题时非常有用。explain_before_edit = true让它每次改代码前先解释意图,避免它一口气改一大片你没法审查。
配置完成后,用 CC Switch 切换通道。CC Switch 是一个管理多套 Claude Code 配置的工具,如果你装了它,操作大概是:
cc-switch list cc-switch use taotoken cc-switch current预期输出会显示当前激活的配置名和 base URL。如果没有装 CC Switch,直接确认settings.json里的ANTHROPIC_BASE_URL生效即可,可以用一个简单请求验证:
curl -s https://taotoken.net/api/v1/models \ -H "Authorization: Bearer sk-你的TaoTokenKey" | head -c 300返回模型列表的 JSON 片段就说明 Key 和通道都通了。这一步别跳过,通道没通后面所有编译辅助都是白费。
4. 编译验证:从 2.4 到 6.8 的模块构建与加载
配置通了之后,进入驱动仓库。假设你已经把 ftape 源码 clone 到本地:
git clone https://github.com/dbrant/ftape.git cd ftape先让 Claude Code 做一次“现状扫描”。在项目目录下启动它,输入类似这样的指令:
这个仓库是一个 Linux 内核驱动,原本只能在 2.4 内核下编译。 请先阅读源码,列出所有使用了已废弃内核 API 的位置, 按文件分组,并给出每个位置对应的现代内核等效写法。 不要直接改代码,先给清单。它会输出一份清单,比如init_timer要换成timer_setup,file_operations里的.ioctl要换成.unlocked_ioctl,register_chrdev要换成register_chrdev配合class_create等。你审查一遍,确认没有明显错误,再让它动手。
接下来是构建系统的现代化。原来的 ftape 需要放进完整内核树里编译,我们要的是独立可加载模块。让 Claude Code 生成一个独立的 Makefile:
obj-m += ftape.o ftape-objs := ftape-init.o fdc-internal.o ftape-internal.o KDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean然后编译:
make预期输出最后几行类似:
CC [M] /home/user/ftape/ftape-init.o CC [M] /home/user/ftape/fdc-internal.o CC [M] /home/user/ftape/ftape-internal.o LD [M] /home/user/ftape/ftape.o MODPOST /home/user/ftape/Module.symvers CC [M] /home/user/ftape/ftape.mod.o LD [M] /home/user/ftape/ftape.ko看到ftape.ko生成,说明编译阶段过了。如果报错,把完整报错粘贴给 Claude Code,让它对照内核头文件修正。这里有个坑:不同发行版的内核头文件路径可能不一样,KDIR要按实际调整,用ls /lib/modules/$(uname -r)/build确认存在。
编译通过后,手动加载模块。这一步必须你自己来:
sudo insmod ftape.ko lsmod | grep ftape dmesg | tail -30预期lsmod能看到 ftape 及其依赖模块。dmesg里会打印 ftape 的初始化日志。如果此时它报No such device or address或者ENXIO,别急,这正是原作者踩过的坑,下一节专门讲。
5. 常见报错排查:ENXIO、基地址与 dmesg 对比
最典型的报错是加载后dmesg出现:
ftape: fdc-internal: no such device or address ftape: probe failed with error -6-6就是ENXIO。原作者的定位过程很值得复现:问题出在fdc-internal.c里,当fdc->sra == 0xffff时函数直接返回-ENXIO。而所有基地址参数默认是-1,在配置函数里被转成了0xffff,导致检测失败。
排查步骤是这样的。先确认模块参数:
modinfo ftape.ko | grep parm你会看到类似parm: fdc_base:...的参数。默认值如果是-1,就需要在加载时显式指定。先查你的软驱控制器 I/O 端口:
cat /proc/ioports | grep -i floppy预期输出类似:
03f0-03f5 : floppy 03f7-03f7 : floppy那么基地址就是0x3f0。加载时传入:
sudo insmod ftape.ko fdc_base=0x3f0 dmesg | tail -20如果还是失败,把当前dmesg完整保存下来,和之前 CentOS 3.5 上成功读取时的日志做对比。让 Claude Code 做这个对比:
这是当前加载失败的 dmesg 输出:<粘贴> 这是已知成功的 dmesg 输出:<粘贴> 请逐行对比,找出第一处行为分叉的位置, 并指出对应源码里可能的原因。它通常能定位到具体函数和行号。我实测下来,除了基地址,还有两个高频问题:一是中断号没配对,二是 DMA 通道被现代内核的 floppy 驱动占用。前者通过modinfo看irq参数,后者可以先sudo rmmod floppy再加载 ftape 试试。
还有一个容易忽略的点:现代内核默认开启了CONFIG_STRICT_DEVMEM和 I/O 端口访问限制,老驱动直接inb/outb可能被拦。检查:
grep CONFIG_STRICT_DEVMEM /boot/config-$(uname -r)如果是=y,需要在模块里改用request_region正确申请端口,或者在内核启动参数里加iomem=relaxed(仅测试环境用)。这些改动都可以让 Claude Code 生成补丁,但你要自己审查每一处对 I/O 端口的操作。
排查过程中,dmesg -w实时看日志很有用,加载模块的同时观察输出,比事后tail更直观。每次改完代码重新make再insmod,记得先sudo rmmod ftape卸载旧模块,否则会报模块已加载。
6. 跑通之后:验证磁带通信与后续接入建议
当dmesg里出现类似下面的输出,说明模块已经识别到磁带机:
ftape: ftape module v5.10 loaded ftape: fdc detected at 0x3f0, irq 6, dma 2 ftape: QIC-80 drive detected接下来做一次实际读取验证。ftape 通常会提供字符设备节点,确认:
ls -l /dev/ftape*如果没有,手动创建:
sudo mknod /dev/ftape0 c 27 0 sudo chmod 660 /dev/ftape0然后用dd读一小段测试:
sudo dd if=/dev/ftape0 of=/tmp/test.bin bs=512 count=10 xxd /tmp/test.bin | head -20预期能看到磁带上的原始二进制内容,而不是全零或报 I/O 错误。如果读出来全是0x00,可能是磁带位置没回到起点,用 ftape 自带的mt类工具做 rewind,或者检查fdc-internal.c里的时序参数是否需要按你的硬件微调。
到这里,从配置到跑通的闭环就完成了。后续如果你要长期做这类内核驱动的现代化工作,建议把 Claude Code 的接入方式固定下来。日常对话和快速验证模型可以用模型对话入口:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。如果你像我一样要连续几个晚上跟驱动代码死磕,Coding Plan 更适合:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。接入文档和 API Key 管理分别在上面提到的 doc 和 api-keys 页面,配置骨架直接复用本文的settings.json和config.toml即可。
最后说一个我踩过的坑:Claude Code 在迭代编译错误时,有时会“自信地”改掉一些不该改的宏定义,导致编译过了但运行时行为不对。所以每次它改完,我都会用git diff看一遍改动范围,确认没有动到硬件时序相关的常量。内核模块这东西,编译通过只是第一步,真正跑起来还得靠 dmesg 和实际硬件说话。