You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让自制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();]


解决方案

第一步:修正驱动代码中的错误

你的驱动存在两个关键问题,必须先修正才能正常工作:

  1. VID定义错误:USB_DEVICE宏需要的是16位整数类型的厂商ID(VID),而非字符串。你需要将USB_SKEL_VENDOR_ID改为数值型,比如使用未被注册的测试用VID(如0x1234,注意不要使用已被官方分配的VID)。
  2. 日志格式化错误: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。

第三步:验证流程

  1. 编译并加载Linux内核驱动模块:
    # 假设你有Makefile,编译驱动
    make
    # 加载模块
    sudo insmod usb_driver.ko
    # 查看模块是否加载成功
    lsmod | grep skeleton
    
  2. 将Arduino连接到Linux主机,查看内核日志:
    dmesg -w
    

如果设备枚举成功且VID/PID与驱动匹配,你会看到日志中打印o usb 7segmento foi conectado,说明skel_probe函数已被调用。


内容的提问来源于stack exchange,提问作者Guilherme Giácomo Simoes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 09:47:05