把 USB 打印机改成全屋无线打印机:斐讯 N1 + CUPS + brlaser
家里这台 Brother HL-2240D 是纯 USB 打印机:没有网口,没有 Wi-Fi,也不支持 AirPrint。原来要打印就得开一台连着它的电脑。这篇文章记录把它改造成打印服务器的完整过程:打印机 USB 线插在 斐讯 N1 上,N1 上跑 CUPS,全屋 Windows、Android、MacBook 直接打,出门在外通过 Tailscale 也能打。
适用场景:
- 你有一台 只有 USB 口的老打印机,想让全屋设备(包括手机)都能无线打印;
- 家里有 斐讯 N1 或其他常驻局域网的 ARM 小主机(Armbian/Debian);
- 打印机型号老旧、官方没有 ARM 驱动,想走开源驱动路线。
选型:cudy 还是 N1
家里两台候选设备:Cudy TR3000(OpenWrt,常驻路由)和斐讯 N1(Armbian,常驻服务节点)。对比结论:
| 项目 | 斐讯 N1 | Cudy TR3000 |
|---|---|---|
| 系统 | Armbian / Debian 12 | OpenWrt |
| CPU / 内存 | 4 核,1.8 GiB | 2 核,约 486 MiB |
| 可用存储 | 约 4.2 GiB | 约 129 MiB |
| 打印软件 | CUPS、Avahi、驱动生态完整 | 基本只有 p910nd 这类极简方案 |
| 现有职责 | 代理、FRP、HAProxy、监控 | 路由、AdGuard、Tailscale |
| 打印服务挂掉的影响 | 只影响打印 | 可能波及路由和 DNS |
选 N1。除了资源和软件生态,还有故障域的考虑:打印服务崩了不应该影响上网出口。Cudy 保持原样,一点没动。
驱动路线:为什么要自己编译 brlaser
确认型号 HL-2240D 后,查到三个关键事实:
- 它只有 USB 2.0,支持自动双面,不支持 AirPrint,也不支持 IPP-over-USB(排除
ipp-usb方案); - Debian 12 仓库里的
printer-driver-brlaser 6-3没有 HL-2240D 的型号条目——“仓库里有 brlaser 这个包”不等于“它支持你的型号”,要打开brlaser.drv.in确认; - 新版 brlaser 6.2.8 里有
HL-2240D series条目,并且明确启用了双面打印。
所以路线是:CUPS + Avahi 用发行版仓库的,brlaser 用 Debian 源码包里的 6.2.8 固定版本自己编译。不选 p910nd(RAW 9100 端口):它要求每台客户端自己装驱动,手机基本没法用。
本文实际时间线与较短路径
本文实际时间线
- 选型对比,定 N1;确认打印机型号和驱动支持
- 打印机接上 N1,确认识别
- 备份现状,安装 CUPS、Avahi 和编译依赖
- 编译安装 brlaser 6.2.8,跑通驱动自测
- 创建共享队列(A4、默认单面)
- 收紧访问范围:先限制
192.168.2.0/24,再放行 Tailscale,再补 IPv6 - MacBook 打印卡在“保留以进行认证”,定位出 IPv6 问题,最终改成
@IF(eth0)双栈放行 - 清理中间备份,固定 N1 的 IP(路由器 DHCP 保留)
- 排查“Windows 找不到打印机”,确认是那台电脑的网络路径问题
读者可采用的较短路径
如果你照着做,第 6、7 步可以直接采用最终配置(本文 Step 4 给出的就是最终版),不需要经历“先限 IPv4、再补 Tailscale、再补 IPv6”的反复。
环境与前提
网络拓扑:
| 设备 | 角色 | 地址 |
|---|---|---|
| Redmi AX6(OpenWrt 24.10.1) | 主路由,DHCP 池 192.168.2.100–249 |
192.168.2.1 |
| Cudy TR3000 | OpenWrt 路由(本文不动它) | 192.168.2.2 |
| 斐讯 N1 | 打印服务器,Armbian / Debian 12,千兆有线 | DHCP,后保留为 192.168.2.3 |
| HL-2240D | USB 打印机,接 N1 | 无 |
所有要打印的客户端和 N1 在同一个 192.168.2.0/24 网段,IPP 和 mDNS 可以直接通信,不需要在路由器上做任何 mDNS 转发。
执行位置:N1,通过 SSH 登录。 先确认系统能看到打印机:
lsusb
usb-devices | sed -n '/Vendor=04f9/,+12p'
dmesg | grep -Ei "usblp|Brother" | tail
检查点:
lsusb里出现04f9:0040(Brother 的 HL-2240D);usb-devices显示设备类为打印机,内核绑定usblp驱动。
如果这里看不到打印机,先解决 USB 线缆、端口或供电问题,不要继续装软件。
Step 1:备份现状并安装软件包
执行位置:N1。 创建权限 0700 的备份目录,记录安装前的软件包和配置基线:
backup=$(mktemp -d /root/printing-backup-XXXXXX)
chmod 700 "$backup"
dpkg-query -W > "$backup/dpkg.before.txt"
systemctl is-enabled cups avahi-daemon > "$backup/services.before.txt" 2>&1 || true
[ -d /etc/cups ] && cp -a /etc/cups "$backup/cups.before"
echo "BACKUP=$backup"
N1 上原来没装过 CUPS,/etc/cups 可能不存在,属于正常。然后只安装打印所需组件,不升级其他系统包:
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
cups cups-client cups-filters ghostscript avahi-daemon \
build-essential cmake libcups2-dev libcupsimage2-dev
其中 build-essential 到 libcupsimage2-dev 是下一步编译 brlaser 的依赖。安装完成后 CUPS 和 Avahi 会自动启动。
Step 2:编译安装 brlaser 6.2.8
执行位置:N1。 从 Debian 源码池下载 6.2.8 的上游源码包,校验哈希后编译:
build_dir=$(mktemp -d /tmp/brlaser-build-XXXXXX)
curl -fL --retry 3 -o "$build_dir/brlaser_6.2.8.orig.tar.gz" \
https://deb.debian.org/debian/pool/main/b/brlaser/brlaser_6.2.8.orig.tar.gz
printf "%s %s\n" \
"16dae855aa7fff0eef0c05398fab37678243d7d610fa5f9af0d3a2cc9bf08cb0" \
"$build_dir/brlaser_6.2.8.orig.tar.gz" | sha256sum -c -
tar -xzf "$build_dir/brlaser_6.2.8.orig.tar.gz" -C "$build_dir"
cmake -S "$build_dir/brlaser-6.2.8" -B "$build_dir/build" -DCMAKE_BUILD_TYPE=Release
cmake --build "$build_dir/build" -j4
ctest --test-dir "$build_dir/build" --output-on-failure
cmake --install "$build_dir/build"
哈希值对应本文使用的这一份
brlaser_6.2.8.orig.tar.gz。如果版本更新了,以你自己下载后算出的为准,并核对 drv 文件里仍有 HL-2240D 条目。
检查点:
sha256sum -c输出OK;ctest全部通过(本文实测 4 项自测);- 确认驱动已就位并认识这台打印机:
sha256sum /usr/lib/cups/filter/rastertobrlaser /usr/share/cups/drv/brlaser.drv
lpinfo -m | grep -F "HL-2240D"
应看到:
drv:///brlaser.drv/br2240d.ppd Brother HL-2240D series, using Owl-Maintain/brlaser v6.2.8
这一行就是 Debian 12 仓库旧版 brlaser 里没有、必须升级才有的一条。
Step 3:创建共享打印队列
执行位置:N1。 开启共享(不开远程管理),把 USB 设备建成队列:
cupsctl --share-printers --no-remote-admin --no-remote-any
lpadmin -p Brother_HL_2240D -E \
-v "usb://Brother/HL-2240D%20series" \
-m "drv:///brlaser.drv/br2240d.ppd" \
-D "Brother HL-2240D" -L "n1 USB" \
-o printer-is-shared=true \
-o PageSize=A4 -o Duplex=None
cupsaccept Brother_HL_2240D
cupsenable Brother_HL_2240D
说明:
- 设备 URI 来自
lpinfo -v | grep usb的输出(usb://Brother/HL-2240D%20series); - 默认纸张 A4、默认单面,客户端需要双面时自己选;
Duplex=None只是默认值,驱动的sides-supported仍包含长边/短边双面。
检查点:
ss -lntp | grep 631
lpstat -p Brother_HL_2240D -l
lpstat -v Brother_HL_2240D
- 631 端口应监听在
0.0.0.0和[::](如果只看到127.0.0.1:631,说明共享没生效,局域网客户端会连不上); - 打印机状态
idle; accepting jobs,选项里有Brother HL-2240D series, using Owl-Maintain/brlaser v6.2.8和双面能力。
Step 4:收紧访问范围并绑定 Avahi 接口
执行位置:N1。 cupsctl --share-printers 默认的访问规则是 Allow @LOCAL,会跟着接口走,Tailscale 网卡也算“本地”。这里改成显式放行,最终 /etc/cups/cupsd.conf 的 <Location /> 块如下:
<Location />
Order allow,deny
Allow @IF(eth0)
Allow from 100.64.0.0/10
Allow [fd7a:115c:a1e0::]/48
</Location>
三条规则的含义:
Allow @IF(eth0):放行 eth0 上的所有地址(IPv4 和 IPv6 都覆盖)。N1 换了局域网地址、或者家里换了 IPv6 前缀,都不用改配置。这是排错之后确定的最终写法,见后文;Allow from 100.64.0.0/10:Tailscale 的 IPv4 地址段;Allow [fd7a:115c:a1e0::]/48:Tailscale 的固定 IPv6 ULA 段,注意 IPv6 要用方括号写法(见问题 2)。
<Location /admin> 保持默认(只允许本机),远程管理不开。
再改 /etc/avahi/avahi-daemon.conf,让 mDNS 只在有线口发布:
allow-interfaces=eth0
use-ipv4=yes
use-ipv6=yes
use-ipv6=yes 必须保留:macOS 发现打印机后会优先走 IPv6,把 Avahi 的 IPv6 关了会直接触发问题 1。
改完校验并重启:
cupsd -t && systemctl restart cups avahi-daemon
systemctl is-active cups avahi-daemon
cupsd -t 报错时不要强行重启,它阻止的是带着坏配置重启服务;运行中的旧服务不受影响,先修配置。
Step 5:服务端验证
执行位置:N1。 三项检查:
# 1) HTTP 层:局域网地址和 Tailscale 地址都应返回 200
curl -sS -o /dev/null -w "%{http_code}\n" http://192.168.2.3:631/printers/Brother_HL_2240D
curl -sS -o /dev/null -w "%{http_code}\n" http://100.100.1.8:631/printers/Brother_HL_2240D
# 2) IPP 层:读打印机属性
ipptool -tv ipp://127.0.0.1/printers/Brother_HL_2240D \
/usr/share/cups/ipptool/get-printer-attributes.test | grep -E \
"(successful-ok|printer-make-and-model|document-format-supported|sides-supported|media-default)"
# 3) 实打一张测试页,等队列清空
lp -d Brother_HL_2240D -o media=A4 -o sides=one-sided /usr/share/cups/data/testprint
watch -n 2 'lpstat -W not-completed -o Brother_HL_2240D'
本文实测的关键属性:
printer-make-and-model:Brother HL-2240D series, using Owl-Maintain/brlaser v6.2.8;- 文档格式包含 PDF、PWG Raster、Apple Raster,客户端不用装任何驱动,渲染由服务端完成;
sides-supported:单面、长边双面、短边双面;media-default:iso_a4_210x297mm;- mDNS 名称:
Brother HL-2240D @ armbian。
测试页发出后队列回到 idle,/var/log/cups/error_log 里没有 filter 报错,纸上真出了字,服务端就完成了。
执行位置:MacBook。 打开“系统设置 → 打印机与扫描仪”,打印机列表应自动出现 Brother HL-2240D @ armbian(Bonjour 发现),直接添加即可。打印一页确认。
Step 6:固定打印机地址(DHCP 保留,不是静态地址)
N1 原来的地址 192.168.2.164 是 DHCP 动态分配的。客户端按 IP 添加过打印机的话,地址一变全部失效,所以要固定。在主路由上做 DHCP 保留,而不是在 N1 上写静态 IP:保留方式回退容易,也不会和地址池冲突。
先确认想用的地址空闲。本文选 192.168.2.3(在 .100–.249 池之外):
# 在任意局域网机器上执行
ip neigh show 192.168.2.3 # 期望 FAILED 或无结果
ping -c 3 -W 1 192.168.2.3 # 期望不通
执行位置:主路由(OpenWrt)。 先备份,再加保留租约(MAC 换成你自己的):
cp -p /etc/config/dhcp /root/dhcp.before-n1-192.168.2.3
uci set dhcp.n1=host
uci set dhcp.n1.name='n1'
uci set dhcp.n1.mac='06:01:85:67:4d:6e'
uci set dhcp.n1.ip='192.168.2.3'
uci commit dhcp
/etc/init.d/dnsmasq reload
执行位置:N1。 重新获取租约(N1 用 NetworkManager,会有几秒断网):
nmcli connection down "Wired connection 1" && sleep 2
nmcli connection up "Wired connection 1"
ip -4 -brief address show dev eth0
检查点:
- 路由器
/tmp/dhcp.leases里该 MAC 对应192.168.2.3,ARP 表一致; - N1 拿到
.3,cups avahi-daemon tailscaled frpc-de frpc-hk haproxy microsocks beszel全部active,打印服务的安装没有碰到原有服务; cupsd.conf用的是@IF(eth0),地址变了不需要改打印配置;- MacBook 上
armbian.local已解析到新地址。
之前手动按 .164 添加过打印机的 Windows/Android,把地址改成 .3 或删掉重加;Bonjour 和 Tailscale 方式不受影响。
客户端接入
统一使用这个队列地址:
ipp://192.168.2.3/printers/Brother_HL_2240D
macOS
Bonjour 自动发现(见 Step 5)。注意 macOS 添加后本地队列的 URI 是 ipps://armbian.local.:631/...:走 mDNS 名字和 IPv6,还带 TLS。问题 1 就出在这个 URI 上。
Windows
Windows 的“自动搜索”搜不到这台打印机,多半不是服务端问题,先查网络路径。
排查方法(PowerShell):
ipconfig # 看本机在这个网络里的地址,是否在 192.168.2.0/24
Test-NetConnection 192.168.2.3 -Port 631 # 测 631 是否可达
同网段时,手动添加:设置 → 蓝牙和其他设备 → 打印机和扫描仪 → 添加设备 → 手动添加 → “按名称选择共享打印机”,填:
ipp://192.168.2.3/printers/Brother_HL_2240D
或用 PowerShell(Windows 11 支持 -IppURL 参数,见微软文档):
Add-Printer -IppURL "http://192.168.2.3:631/printers/Brother_HL_2240D"
Windows 会用它内置的 IPP 类驱动,不需要在服务端或客户端装 Brother 驱动。
不在同网段的机器,走 Tailnet:地址换成 ipp://100.100.1.8/printers/Brother_HL_2240D(服务端已放行 100.64.0.0/10 和 Tailscale 的 IPv6 段)。我写这篇时,家里的 ThinkBook 正处于后一种状态:它的 WLAN 地址不在家里这个网段,631 端口直连不通,只能通过它自己的 Tailnet 地址访问 n1,所以自动搜索必然搜不到,手动添加按上面两条路径之一即可。
Android
系统自带的“默认打印服务”会通过 mDNS 发现 _ipp._tcp 服务,同一网段下在 设置 → 打印 里应能直接看到打印机。没看到就在打印服务里按 IP 手动添加,队列地址同上。出门在外与 Windows 同理,用 Tailscale 地址手动添加。mDNS 不跨 Tailnet,自动发现在外面永远搜不到,这是协议决定的,不是故障。
问题与修复
1) macOS 显示“保留以进行认证”,不是密码问题
现象:MacBook 打印时任务卡住,队列里显示“保留以进行认证”(cups-held-for-authentication),但 N1 根本没设认证,服务端日志里也没有 401。
定位:在 MacBook 上执行 lpstat -v,看到本地队列 URI 是 ipps://armbian.local.:631/printers/Brother_HL_2240D:macOS 解析 armbian.local 时优先选了 IPv6 地址;而当时的 CUPS 规则只放行了 IPv4 网段,服务器返回 403,macOS 把 403 显示成“需要认证”。
修复:访问规则改用 Allow @IF(eth0)(双栈全覆盖),Avahi 保持 use-ipv6=yes。修复后任务不需要任何密码,直接打完。
教训:CUPS 的 403 会被客户端包装成认证错误,遇到“要密码”先查服务端 access_log 有没有 403,再看客户端队列实际用的 URI 和地址族。
2) cupsd.conf 拒绝裸 IPv6 CIDR
修上面的问题时,我先写的是 Allow from 2001:db8::/64 这种形式,cupsd -t 直接报错并阻止重启。CUPS 的 IPv6 地址必须写成方括号形式:Allow [2001:db8::]/64。不过最终根本不需要写死任何前缀,@IF(eth0) 一步到位。
3) A4 默认值第一次没生效
lpadmin 建队列时带 -o PageSize=A4,但 lpoptions -p Brother_HL_2240D -l 里默认仍是 Letter。重新执行一次 lpadmin -p Brother_HL_2240D -o PageSize=A4 -o Duplex=None 并复查即可。建完队列后务必用 lpoptions 确认默认纸张,不要假设参数一次到位。
4) CUPS 安装后自己退出过一次
安装初期 CUPS 曾自动退出、由 systemd 拉起一次。之后多次重启服务和两轮实际打印都没有复现,error_log 里只有一条当时的 Scheduler shutting down due to program error。保持现状观察,没有进一步动作。
5) “仓库里有包”不等于“支持你的型号”
Debian 12 的 printer-driver-brlaser 6-3 确实存在,但打开 6-3 的 brlaser.drv.in 里没有 HL-2240D;6.2.8 的同一文件 里才有。装驱动前先确认 drv/PPD 里有你的型号条目,否则 CUPS 会装上一个“能跑但不认识打印机”的通用配置。
最终状态与安全边界
写这篇时打印服务已稳定运行,复测数据:
cupsd+avahi-daemon合计常驻内存约 16 MB,N1 还有 1.3 GiB 富余;- CUPS 日志共 12 KB,Debian 每日轮转、保留 7 份;
- 打印队列
idle,服务enabled,N1 重启后自动恢复。
安全边界:
- 只开放 TCP 631 和 UDP 5353,仅限局域网网段和 Tailnet;
- CUPS Web 管理界面只监听 localhost,远程管理关闭(
--no-remote-admin); - 不做端口转发、不进 FRP、不向公网发布;
- 不加 Samba 或 WSD 之类的额外发现服务,IPP + mDNS 覆盖了全部实际客户端。
所有安装前基线和最终配置都留在 N1 的 /root/printing-backup-*/ 目录里,连同 brlaser 的来源和哈希记录,出问题可以按文件回滚。