如何让自制USB设备向Linux内核发送VID与PID触发probe函数
我正在制作一款简易实验性USB设备,同时开发对应的Linux内核模块驱动。
我的usb_driver.h代码如下:
#ifndef __USB_DRIVER_H__ #define __USB_DRIVER_H__ #define USB_SKEL_VENDOR_ID "7SEG_DRIVER" #define USB_SKEL_PRODUCT_ID 0x0001 static int skel_probe(struct usb_interface *intf, const struct usb_device_id *id); static void skel_disconnect(struct usb_interface *intf); static struct usb_device_id skel_table[] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } }; static struct usb_driver skel_driver = { .name = "skeleton", .probe = skel_probe, .disconnect = skel_disconnect, .id_table = skel_table, }; #endif
我的usb_driver.c代码如下:
#include "usb_driver.h" MODULE_AUTHOR("SEU NOME <seu_email@email.com>"); MODULE_DESCRIPTION("Introducao a criacao de driver pra linux"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0"); MODULE_DEVICE_TABLE(usb, skel_table); static int skel_probe(struct usb_interface *intf, const struct usb_device_id *id) { pr_info("o usb 7segmento foi conectado"); return 0; } static void skel_disconnect(struct usb_interface *intf) { pr_info("o usb 7segmento foi desconectado"); } static int __init usb_skel_init(void) { //registrando este driver com subsistema USB int result = usb_register(&skel_driver); if(result < 0) { pr_err("usb_register falhou para o driver %s. Erro numero %s" , skel_driver.name, result); return -1; } return 0; } static void __exit usb_skel_exit(void) { usb_deregister(&skel_driver); pr_info("Modulo removido\n"); } module_init(usb_skel_init); module_exit(usb_skel_exit);
我的USB设备结构非常简单:仅为工作指示灯LED连接了Vcc与GND引脚,同时将D+与D-引脚连接至Arduino以传输数据,但我尚未编写Arduino端代码。
我的疑问是:如何让该USB设备向Linux内核发送Vendor ID和Product ID,从而使内核调用我驱动中的skel_probe函数?即实现如下流程:[DEVICE USB] ========> [KERNEL] ===============> [call skel_probe();]
第一步:修正驱动代码中的错误
你的驱动存在两个关键问题,必须先修正才能正常工作:
- VID定义错误:
USB_DEVICE宏需要的是16位整数类型的厂商ID(VID),而非字符串。你需要将USB_SKEL_VENDOR_ID改为数值型,比如使用未被注册的测试用VID(如0x1234,注意不要使用已被官方分配的VID)。 - 日志格式化错误:
pr_err中打印错误码时使用了%s,但错误码是整数,应该改为%d。
修正后的usb_driver.h关键部分:
// 改为数值型VID,与Arduino端保持一致 #define USB_SKEL_VENDOR_ID 0x1234 #define USB_SKEL_PRODUCT_ID 0x0001
修正后的usb_driver.c中错误处理部分:
pr_err("usb_register falhou para o driver %s. Erro numero %d", skel_driver.name, result);
第二步:编写Arduino端代码,实现自定义USB设备枚举
USB设备插入Linux后,内核会发起枚举流程,读取设备描述符中的VID和PID,再与驱动的id_table匹配。你需要在Arduino端配置对应的VID/PID,并让设备能正确响应内核的枚举请求。
分情况处理:
情况1:使用自带USB控制器的Arduino(如Leonardo、Micro,基于ATmega32U4)
这类板子本身集成USB控制器,可直接通过代码修改VID/PID:
#include <Arduino.h> #include <USBAPI.h> // 与驱动中定义的VID/PID完全一致 #define MY_VID 0x1234 #define MY_PID 0x0001 // 自定义设备描述符中的字符串(可选) #define STRING_PRODUCT L"7Segment Device" #define STRING_MANUFACTURER L"My Vendor" void setup() { // 覆盖默认VID/PID USB_VID = MY_VID; USB_PID = MY_PID; // 初始化USB设备 USB.begin(); // 初始化指示灯 pinMode(LED_BUILTIN, OUTPUT); } void loop() { // 维持USB连接状态 USB.Task(); // 指示灯闪烁,标识设备运行 digitalWrite(LED_BUILTIN, HIGH); delay(500); digitalWrite(LED_BUILTIN, LOW); delay(500); }
情况2:使用USB转串口芯片的Arduino(如Uno、Nano,基于ATmega328P)
这类板子的USB功能由额外的芯片(如ATmega16U2)实现,需要重写该芯片的固件来设置自定义VID/PID。你可以使用dfu-programmer工具刷入修改过的USB固件,固件中需配置与驱动一致的VID和PID。
第三步:验证流程
- 编译并加载Linux内核驱动模块:
# 假设你有Makefile,编译驱动 make # 加载模块 sudo insmod usb_driver.ko # 查看模块是否加载成功 lsmod | grep skeleton - 将Arduino连接到Linux主机,查看内核日志:
dmesg -w
如果设备枚举成功且VID/PID与驱动匹配,你会看到日志中打印o usb 7segmento foi conectado,说明skel_probe函数已被调用。
内容的提问来源于stack exchange,提问作者Guilherme Giácomo Simoes

