news 2026/5/16 11:13:14

usb 串行口驱动庖丁解牛2: 实现/dev/ttyUSB0设备文件框架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
usb 串行口驱动庖丁解牛2: 实现/dev/ttyUSB0设备文件框架

author: hjjdebug
date: 2026年 05月 15日 星期五 18:17:21 CST
descrip: descrip: usb 串行口驱动庖丁解牛2: 实现/dev/ttyUSB0设备文件框架


文章目录

  • 1. 代码说明
  • 2 代码
  • 3 测试:
  • 4. 带hack代码:
  • 5. 测试:

1. 代码说明

由于看pl2303真实驱动表示看不懂,所以才通过一个系列来彻底解剖它的架构.
结合庖丁解牛1来看本博客庖丁解牛2.
用一个宏 module_usb_serial_driver 包装了模块初始化及逆初始化函数.

  1. 宏的展开
    module_usb_serial_driver(serial_drivers, pl2303_id_table);
    展开结果:
    static int usb_serial_module_init(void) { return usb_serial_register_drivers(serial_drivers, “pl2303”, pl2303_id_table); }
    static __inittest(void) { return usb_serial_module_init; }
    int init_module(void)attribute((alias(“usb_serial_module_init”)));;
    static void usb_serial_module_exit(void) { usb_serial_deregister_drivers(serial_drivers); }
    static __exittest(void) { return usb_serial_module_exit; }
    void cleanup_module(void)attribute((alias(“usb_serial_module_exit”)));;;

意义: 这个宏包含了2个函数,
一个公开的导出函数init_module, 它实际上就是usb_serial_module_init
另一个公开导出函数cleanup_module, 它实际地址就是usb_serial_module_exit 函数
你可以通过nm pl2303.o 文件得到证实.
第2, 它实现了这2个函数, 分别是调用
usb_serial_register_drivers(serial_drivers, “pl2303”, pl2303_id_table); //调用了底层的注册函数
usb_serial_deregister_drivers(serial_drivers); 调用了底层的注销函数

在驱动中,如果你没有定义probe函数,绑定成功后,它就不调用probe 函数了, 但如果定义了.calc_num_ports,
它会调用这个函数, 返回1,它会创建一个/dev/ttyUSB0, 返回2,它会创建2个文件. /dev/ttyUSB0,/de/ttyUSB1
显然,一个硬件应该只返回1.

  1. /dev/ttyUSB0 设备节点文件的生成.
    如此简单, 你要在driver 变量中定义.calc_num_ports 函数, 返回1就可以了.
    不过这个文件节点只是个壳子,还不能正常工作, 打开,读写都不会正常,因为目前还没有函数支撑它.

2 代码

$ cat pl2303.c#include<linux/init.h>#include<linux/module.h>#include<linux/usb.h>#include<linux/tty.h>#include<linux/usb/serial.h>// PL2303 VID/PID#definePL2303_VID0x067B#definePL2303_PID0x2303// 设备匹配表staticconststructusb_device_idpl2303_id_table[]={{USB_DEVICE(PL2303_VID,PL2303_PID)},{}};MODULE_DEVICE_TABLE(usb,pl2303_id_table);// 端口数量计算staticintpl2303_calc_num_ports(structusb_serial*serial,structusb_serial_endpoints*epds){if(!epds->bulk_in||!epds->bulk_out){dev_err(&serial->interface->dev,"缺少批量端点\n");return-ENODEV;}dev_info(&serial->interface->dev,"端点解析完成,单端口PL2303\n");return1;}// 驱动单体定义staticstructusb_serial_driverpl2303_serial_driver={.driver={.name="pl2303-step2",},.id_table=pl2303_id_table,.calc_num_ports=pl2303_calc_num_ports,};// 5.4内核强制要求:驱动指针数组staticstructusb_serial_driver*constserial_drivers[]={&pl2303_serial_driver,NULL};// 官方标准注册module_usb_serial_driver(serial_drivers,pl2303_id_table);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("PL2303 第二步 5.4内核 实现端口计算,实现生成设备文件");

3 测试:

拔出USB设备
[ 27.863495] usb 1-1: USB disconnect, device number 2
[ 27.866327] pl2303-step2 ttyUSB0: pl2303-step2 converter now disconnected from ttyUSB0
[ 27.871035] pl2303 1-1:1.0: device disconnected
插入USB设备
[ 46.132395] usb 1-1: new full-speed USB device number 3 using uhci_hcd
[ 46.284119] usb 1-1: New USB device found, idVendor=067b, idProduct=2303, bcdDevice= 2.02
[ 46.288389] usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[ 46.292541] pl2303 1-1:1.0: 端点解析完成,单端口PL2303 **********
[ 46.294571] pl2303 1-1:1.0: pl2303-step2 converter detected
[ 46.296963] usb 1-1: pl2303-step2 converter now attached to ttyUSB0

实验成功.
我们插入usb设备,自动执行注册的pl2303_calc_num_ports 函数, 返回1, 就生成了/dev/ttyUSB0.
我们只打印了一行信息,其它是系统打印的.

老式 PL2303 端点有错位问题
所以, 在端口计数函数里判断是否触发 Hack
设定设备特征标记 quirks, 以此变量来区分是否需要走Hack函数
所谓hack 就是黑客, 所谓quirks, 就是怪僻,嗜好,特征. 为啥加这段代码, 就是因为设备端设计不好,需要驱动端为它擦屁股. 这样才能走顺. 以后慢慢搞清了数据交互,会更清晰.

4. 带hack代码:

$ cat pl2303.c#include<linux/tty.h>#include<linux/init.h>#include<linux/module.h>#include<linux/usb.h>#include<linux/usb/serial.h>// PL2303 基础ID#definePL2303_VID0x067B#definePL2303_PID0x2303// 设备特殊特性标记#definePL2303_QUIRK_ENDPOINT_HACKBIT(0)staticconststructusb_device_idpl2303_id_table[]={{USB_DEVICE(PL2303_VID,PL2303_PID)},{}};MODULE_DEVICE_TABLE(usb,pl2303_id_table);// 私有数据结构体,存放quirks标记structpl2303_private_data{unsignedlongquirks;//quirks, 怪僻, 嗜好. 用这个字节标记是否需要hack等};// 端点Hack核心函数:跨接口抓取中断输入端点, hack 是黑客的意思, 破解的意思,需要修复一些东西staticintpl2303_endpoint_hack(structusb_serial*serial,structusb_serial_endpoints*epds){structusb_device*dev=serial->dev;structusb_interface*intf=serial->interface;structusb_host_interface*iface_desc;structusb_endpoint_descriptor*ep;unsignedinti;pr_info("come to endpoint_hack.");// 如果串行口的接口是接口0,不需要调整,直接返回if(intf==dev->actconfig->interface[0])return0;// 当前不是接口0,才需要去接口0捞端点, 这是pl2303设备描述符混乱造成的,// 我们在这里给他顺过来. 把描述符存入对应中断入地址dev_info(&intf->dev,"启用PL2303端点Hack,跨接口查找中断端点\n");iface_desc=dev->actconfig->interface[0]->cur_altsetting;for(i=0;i<iface_desc->desc.bNumEndpoints;i++){ep=&iface_desc->endpoint[i].desc;if(!usb_endpoint_is_int_in(ep))continue;// 存入中断端点数组if(epds->num_interrupt_in<ARRAY_SIZE(epds->interrupt_in))epds->interrupt_in[epds->num_interrupt_in++]=ep;}return0;}// 计算端口数量,同时判断执行端点补丁staticintpl2303_calc_num_ports(structusb_serial*serial,structusb_serial_endpoints*epds){structpl2303_private_data*priv;unsignedlongquirks=0;// 临时标记启用端点hack,实际可按芯片型号区分quirks|=PL2303_QUIRK_ENDPOINT_HACK;// 分配私有数据并保存标记priv=devm_kzalloc(&serial->interface->dev,sizeof(*priv),GFP_KERNEL);if(!priv)return-ENOMEM;priv->quirks=quirks;usb_set_serial_data(serial,priv);// 满足条件则执行端点hackif(quirks&PL2303_QUIRK_ENDPOINT_HACK)pl2303_endpoint_hack(serial,epds);// 基础端点校验if(!epds->bulk_in||!epds->bulk_out){dev_err(&serial->interface->dev,"缺少批量收发端点\n");return-ENODEV;}dev_info(&serial->interface->dev,"端点解析完成,单USB串行口就绪\n");// 固定返回1 代表1路串行口return1;}// 串口驱动结构体staticstructusb_serial_driverpl2303_serial_driver={.driver={.name="pl2303-step3",},.id_table=pl2303_id_table,.calc_num_ports=pl2303_calc_num_ports,};// 驱动数组 匹配内核参数要求staticstructusb_serial_driver*constserial_drivers[]={&pl2303_serial_driver,NULL};module_usb_serial_driver(serial_drivers,pl2303_id_table);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("PL2303 版本三:端点Hack + 私有quirks标记");

5. 测试:

/host # insmod pl2303.ko
[ 3518.926739] usbcore: registered new interface driver pl2303
[ 3518.928630] usbserial: USB Serial support registered for pl2303-step3
[ 3518.930731] come to endpoint_hack. *************************
[ 3518.930734] pl2303 1-1:1.0: 端点解析完成,单USB串行口就绪
[ 3518.933934] pl2303 1-1:1.0: pl2303-step3 converter detected
[ 3518.936012] usb 1-1: pl2303-step3 converter now attached to ttyUSB0

没有打印出 “启用PL2303端点Hack,跨接口查找中断端点”, 说明接口是0,还没有走到其它端口.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 11:13:02

FanControl风扇控制:3分钟掌握专业级Windows散热管理终极指南

FanControl风扇控制&#xff1a;3分钟掌握专业级Windows散热管理终极指南 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Tren…

作者头像 李华
网站建设 2026/5/16 11:12:13

【RT-DETR实战】041、损失函数改进:解耦分类与回归头

从一次深夜调试说起 上周在部署RT-DETR到边缘设备时遇到一个诡异现象:模型在COCO上mAP不错,但在我们的产线检测场景中,误检率突然飙升。 查看错误样本发现,大量背景区域被识别为“产品”,但定位框却胡乱飘在图像角落。直觉告诉我,分类和回归任务在互相干扰——这是典型…

作者头像 李华
网站建设 2026/5/16 11:12:06

航班数据爬虫实战:从反爬策略到开源框架flightclaw解析

1. 项目概述&#xff1a;一个为飞行数据而生的开源爬虫利器如果你曾经尝试过从各大航空公司的官网、票务平台或者航班信息网站上批量抓取航班数据&#xff0c;你大概率会和我一样&#xff0c;经历过一段相当“痛苦”的时光。这些网站的反爬机制层出不穷&#xff0c;动态加载、数…

作者头像 李华
网站建设 2026/5/16 11:12:05

3PEAK思瑞浦 TPA1862-VR MSOP8 精密运放

特性 供电电压:4.5V至40V或2.25V至20V 偏移电压:最大30伏 差分输入电压范围至电源轨&#xff0c;可作为比较器工作 输入轨至-Vs&#xff0c;轨到轨输出: 驱动任意电容负载 带宽:6MHz&#xff0c;斜率:5V/us 优异的EMI抑制性能:1GHz时为85dB 过温保护 低噪声:1kHz时8nV/√Hz 2kV…

作者头像 李华
网站建设 2026/5/16 11:11:05

从Places365到MIT67:手把手教你用PyTorch微调ResNet-50进行场景迁移学习

从Places365到MIT67&#xff1a;深度迁移学习实战与ResNet-50微调策略 在计算机视觉领域&#xff0c;迁移学习已成为解决小样本分类问题的黄金标准。当面对像MIT67这样包含67类室内场景、每类仅80张训练图像的中等规模数据集时&#xff0c;直接从头训练深度神经网络往往难以达到…

作者头像 李华