从零构建:基于RK3568 USB Gadget的IP-KVM核心功能实现
1. RK3568与IP-KVM的奇妙组合
第一次听说IP-KVM这个概念时,我正为远程管理机房服务器发愁。传统的KVM切换器价格昂贵,而基于树莓派的PiKVM项目让我眼前一亮。不过树莓派现在价格飞涨,我开始寻找替代方案,RK3568开发板就这样进入了我的视野。
RK3568是瑞芯微推出的一款中高端ARM处理器,四核Cortex-A55架构,主频可达2GHz,性能完全碾压树莓派4。更重要的是它原生支持USB Gadget功能,这意味着我们可以把它变成一个"USB外设",模拟键盘、鼠标甚至U盘。想象一下,把开发板通过USB线连接到目标主机,就能通过网络远程控制这台主机,这就是IP-KVM的核心魔法。
我手头的这块RK3568开发板是Firefly的ROC-RK3568-PC,带有一个USB3.0 OTG接口。OTG(On-The-Go)技术让这个接口可以在主机(host)和设备(device)模式间切换,这正是实现USB Gadget的关键。相比树莓派需要额外芯片支持OTG,RK3568原生支持让整个方案更加稳定可靠。
2. 内核配置:打好基础
2.1 编译前的准备工作
拿到开发板第一件事就是确认内核配置。我建议直接从官方SDK开始,避免自己从头编译的麻烦。Rockchip的Linux SDK已经包含了大部分我们需要的内容,但还需要确认几个关键配置:
cd ~/rk3568_linux_sdk make menuconfig在图形界面中,我们需要确保以下选项被启用:
- CONFIG_USB_CONFIGFS=y
- CONFIG_USB_LIBCOMPOSITE=y
- CONFIG_USB_CONFIGFS_F_HID=y
- CONFIG_USB_CONFIGFS_MASS_STORAGE=y
这些配置项让内核支持USB Gadget功能框架和具体的HID(人机接口设备)、UMS(USB大容量存储)功能。如果你像我一样喜欢直接修改配置文件,可以编辑kernel/arch/arm64/configs/rockchip_linux_defconfig,确保这些选项都是"y"。
2.2 编译与烧录
配置完成后,编译内核就很简单了:
./build.sh kernel这个命令会在kernel目录下生成新的内核镜像。我建议先备份原来的boot.img,然后再用新的内核替换:
cp out/target/product/rk3568/boot.img boot.img.bak ./mkimage.sh烧录新固件到开发板时,记得让开发板进入loader模式(通常是通过按住某个按键再上电)。我用的工具是RKDevTool,界面虽然复古但很稳定。烧录完成后重启开发板,我们可以通过以下命令确认HID模块是否加载成功:
ls /dev/hid*如果看到/dev/hidg0和/dev/hidg1这样的设备节点,说明内核配置已经正确生效了。
3. USB Gadget配置实战
3.1 理解configfs机制
Linux内核通过configfs来动态配置USB Gadget功能,这比传统的静态编译灵活多了。简单来说,configfs提供了一个虚拟文件系统,我们可以通过创建目录和写入文件来定义USB设备的功能。
在RK3568上,相关操作都在/sys/kernel/config/usb_gadget/目录下进行。我习惯先创建一个名为"rockchip"的gadget:
cd /sys/kernel/config/usb_gadget/ mkdir rockchip cd rockchip接下来需要定义这个USB设备的基本信息,包括厂商ID、产品ID等。Rockchip的默认脚本会处理这些,但了解原理很重要:
echo 0x2207 > idVendor # Rockchip的厂商ID echo 0x310c > idProduct # 自定义产品ID mkdir strings/0x409 # 英文语言支持 echo "123456789" > strings/0x409/serialnumber echo "Rockchip" > strings/0x409/manufacturer echo "RK3568 HID Gadget" > strings/0x409/product3.2 配置HID功能
现在来到最核心的部分——配置键盘和鼠标功能。我们需要创建两个HID功能实例:
mkdir functions/hid.usb0 # 键盘 mkdir functions/hid.usb1 # 鼠标键盘的配置相对复杂,需要定义HID报告描述符。这个描述符告诉主机如何解析我们发送的数据。下面是我调试多次后确定的最佳配置:
# 键盘配置 echo 1 > functions/hid.usb0/protocol # 键盘协议 echo 1 > functions/hid.usb0/subclass # Boot接口子类 echo 8 > functions/hid.usb0/report_length # 报告长度8字节 # 键盘HID描述符 echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc鼠标的配置稍微简单些:
# 鼠标配置 echo 2 > functions/hid.usb1/protocol # 鼠标协议 echo 1 > functions/hid.usb1/subclass echo 3 > functions/hid.usb1/report_length # 3字节报告 # 鼠标HID描述符 echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x02\\x81\\x06\\xc0\\xc0 > functions/hid.usb1/report_desc3.3 集成UMS功能
一个完整的IP-KVM还需要模拟U盘功能,用于远程安装系统等场景。添加UMS功能很简单:
mkdir functions/mass_storage.0 echo /dev/mmcblk1p4 > functions/mass_storage.0/lun.0/file # 使用SD卡的第四个分区这里我使用的是SD卡的一个分区作为虚拟U盘的内容。你也可以使用镜像文件:
dd if=/dev/zero of=/usbdisk.img bs=1M count=1024 mkfs.vfat /usbdisk.img echo /usbdisk.img > functions/mass_storage.0/lun.0/file4. 功能整合与启动脚本
4.1 创建配置描述
现在我们需要把各个功能整合到一个USB配置中:
mkdir configs/c.1 mkdir configs/c.1/strings/0x409 echo "HID+UMS Config" > configs/c.1/strings/0x409/configuration # 将功能添加到配置 ln -s functions/hid.usb0 configs/c.1/ ln -s functions/hid.usb1 configs/c.1/ ln -s functions/mass_storage.0 configs/c.1/4.2 自动化脚本
手动输入这么多命令太麻烦了,我创建了一个启动脚本/usr/local/bin/start_gadget.sh:
#!/bin/bash cd /sys/kernel/config/usb_gadget/ mkdir -p rockchip cd rockchip echo 0x2207 > idVendor echo 0x310c > idProduct mkdir -p strings/0x409 echo "123456789" > strings/0x409/serialnumber echo "Rockchip" > strings/0x409/manufacturer echo "RK3568 HID Gadget" > strings/0x409/product mkdir -p functions/hid.usb0 echo 1 > functions/hid.usb0/protocol echo 1 > functions/hid.usb0/subclass echo 8 > functions/hid.usb0/report_length echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc mkdir -p functions/hid.usb1 echo 2 > functions/hid.usb1/protocol echo 1 > functions/hid.usb1/subclass echo 3 > functions/hid.usb1/report_length echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x02\\x81\\x06\\xc0\\xc0 > functions/hid.usb1/report_desc mkdir -p functions/mass_storage.0 echo /dev/mmcblk1p4 > functions/mass_storage.0/lun.0/file mkdir -p configs/c.1 mkdir -p configs/c.1/strings/0x409 echo "HID+UMS Config" > configs/c.1/strings/0x409/configuration ln -s functions/hid.usb0 configs/c.1/ ln -s functions/hid.usb1 configs/c.1/ ln -s functions/mass_storage.0 configs/c.1/ udevadm settle -t 5 || : ls /sys/class/udc > UDC记得给脚本执行权限:
chmod +x /usr/local/bin/start_gadget.sh5. 功能测试与验证
5.1 基本功能测试
配置完成后,把开发板通过USB线连接到一台电脑上。在Linux主机上,可以运行lsusb查看设备:
lsusb -d 2207:310c应该能看到类似这样的输出:
Bus 003 Device 007: ID 2207:310c Rockchip RK3568 HID Gadget在Windows设备管理器中,应该能看到新添加的HID键盘和鼠标设备。
5.2 发送键盘鼠标事件
测试键盘功能,我们可以直接向/dev/hidg0写入数据。我找到了一个很好的测试工具hid_gadget_test:
// hid_gadget_test.c #include <linux/input.h> #include <linux/uinput.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> int main(int argc, char *argv[]) { int fd = open("/dev/hidg0", O_RDWR); if (fd < 0) { perror("Unable to open device"); return 1; } char buf[8] = {0}; buf[0] = 0x02; // Left Shift buf[2] = 0x04; // 'a'键 write(fd, buf, 8); memset(buf, 0, 8); write(fd, buf, 8); // 释放所有键 close(fd); return 0; }编译并运行这个程序,连接的电脑上应该能看到输入了一个大写的"A"。
5.3 鼠标移动测试
测试鼠标功能类似,向/dev/hidg1写入数据:
int fd = open("/dev/hidg1", O_RDWR); char buf[3] = {0x00, 0x10, 0x10}; // 鼠标右下方移动 write(fd, buf, 3); close(fd);这段代码会让鼠标指针向右下方移动。实际项目中,我们需要将这些操作与网络接口对接,实现真正的远程控制。
6. 常见问题排查
6.1 USB设备未被识别
如果电脑没有识别到USB设备,首先检查内核日志:
dmesg | grep gadget常见问题包括:
- 内核配置不正确,缺少必要的模块
- USB线不支持数据传输(有些线只能充电)
- OTG模式未正确启用
6.2 HID功能不正常
如果键盘或鼠标工作不正常,检查:
- HID报告描述符是否正确
/dev/hidg*设备节点是否存在- 写入的数据格式是否符合报告描述符定义
6.3 UMS功能问题
U盘功能常见问题:
- 指定的存储设备或镜像文件不存在
- 文件系统不被Windows识别(建议使用FAT32)
- 分区被目标主机挂载(需要先卸载)
7. 进阶优化方向
7.1 性能优化
默认配置可能无法满足高频率的鼠标移动需求。我们可以调整USB配置提高性能:
echo 512 > configs/c.1/MaxPower # 提高供电需求 echo high > configs/c.1/bmAttributes # 启用远程唤醒7.2 多配置支持
有时候我们需要不同的功能组合,可以创建多个配置:
mkdir configs/c.2 echo "HID Only" > configs/c.2/strings/0x409/configuration ln -s functions/hid.usb0 configs/c.2/ ln -s functions/hid.usb1 configs/c.2/然后通过脚本切换活动配置。
7.3 与PiKVM项目集成
完成这些基础工作后,我们可以考虑集成PiKVM的软件部分,实现完整的IP-KVM方案。主要包括:
- 视频采集(通过HDMI采集卡)
- 网络服务(提供Web控制界面)
- 电源管理(控制目标主机开关机)
RK3568的强大性能完全可以胜任这些任务,而且成本远低于商业KVM解决方案。
