从CH341到串口通信:USB总线驱动中的serial层架构剖析

1. 从一根USB转串口线说起:CH341的“自我介绍”

如果你玩过单片机、树莓派,或者调试过路由器、工控板,那你抽屉里大概率躺着一两根USB转串口的小玩意儿。它们长得像U盘,一头是USB-A口,另一头是几根杜邦线,用来连接设备的串口引脚。这东西的核心,往往就是一颗小小的芯片,比如我们今天要聊的CH341

CH341是沁恒微电子推出的一款非常经典的USB转串口芯片。它便宜、稳定、出货量巨大,几乎成了电子爱好者和嵌入式开发者的“标配”。当你把它插上电脑的USB口,在Linux系统里,它可能就变成了 /dev/ttyUSB0 这个设备文件。你可以用 minicomscreen 或者自己写的程序打开这个文件,像读写普通文件一样和远端的单片机“聊天”。这个过程看似简单,背后却是一场跨越了USB总线驱动serial抽象层TTY子系统三个“王国”的精密协作。

我自己在调试一块STM32核心板时就遇到过坑。插上CH341的转换器,dmesg 里能看到设备识别了,但就是没有 /dev/ttyUSB0 出现。当时一头雾水,只能凭感觉瞎试,一会儿怀疑内核没编译驱动,一会儿怀疑权限问题。后来才明白,问题出在内核的serial层模块没有自动加载。这个经历让我下定决心,必须把从USB插口到 /dev/ttyUSB0 这条路上的每一道关卡都搞清楚。今天,我就以CH341这个“老朋友”为线索,带你深入Linux内核,看看数据究竟是怎么“过五关斩六将”,最终变成我们能用的串口的。我们会重点关注那个关键的“粘合层”——usb-serial层,看它如何巧妙地桥接起USB的“高速世界”和TTY的“字符流世界”。

2. 第一站:CH341设备驱动的“标准动作”

让我们先从最具体的代码开始。在Linux内核源码里(比如 drivers/usb/serial/ch341.c),你能找到CH341的驱动程序。它看起来大概是这样的:

static struct usb_serial_driver ch341_device = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "ch341-uart",
    },
    .id_table = ch341_id_table,
    .num_ports = 1,
    .open = ch341_open,
    .close = ch341_close,
    .write = ch341_write,
    .read_bulk_callback = ch341_read_bulk_callback,
    .attach = ch341_attach,
    .port_probe = ch341_port_probe,
    .port_remove = ch341_port_remove,
};

这就是一个 usb_serial_driver 结构体的实例。你可以把它理解为CH341芯片的“人格画像”或“操作手册”,专门告诉内核:“嘿,我是CH341,我长这样(id_table),我能干这些活(open, write等函数)”。

.id_table 是关键,它是一张“身份证”列表:

static const struct usb_device_id ch341_id_table[] = {
    { USB_DEVICE(0x4348, 0x5523) }, // 厂商ID: 0x4348, 产品ID: 0x5523
    { USB_DEVICE(0x1a86, 0x7523) }, // 另一个常见的CH341变体
    { } /* Terminating entry */
};

当内核的USB核心检测到一个新插入的USB设备时,会拿着它的厂商ID和产品ID,去所有已注册驱动的 id_table 里挨个比对。一旦匹配上,就说明“哦,这个设备归你管了!”

那么,这个“操作手册”是怎么交到内核手里的呢?在驱动文件的最后,你会看到一行宏:


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值