如何在Linux HCI命令中针对特定USB蓝牙适配器(指定厂商ID和产品ID)排除Read_Local_Extended_Features命令
我完全理解你的困扰——UGREEN CM749蓝牙适配器在Linux 6.15.7内核下的初始化超时问题,全局注释Read_Local_Extended_Features虽然能解决,但会影响所有蓝牙设备,显然不是长期方案。下面是只针对特定VID/PID设备跳过该命令的上游友好修改方案,基于你提供的信息和内核蓝牙子系统的代码逻辑:
问题背景回顾
UGREEN CM749(VID 0x33fa,PID 0x0010)和CM748的行为与Linux内核的HCI初始化流程不兼容:内核发送Read_Local_Extended_Features后,紧接着发送Read_Buffer_Size,而适配器此时还未完成前一个命令的响应,导致后者超时。Windows 11会跳过对CM748的Read_Local_Extended_Features命令,我们可以对齐这个行为,只针对这两款设备做特殊处理。
核心修改思路
我们不需要全局修改初始化阶段的命令列表,而是在hci_read_local_ext_features_1_sync对应的底层函数中,添加USB VID/PID的判断逻辑:如果当前设备是目标UGREEN适配器,直接返回0(不执行该HCI命令),否则保持原有逻辑不变。
具体代码修改
修改内核源码net/bluetooth/hci_sync.c文件,步骤如下:
1. 添加USB头文件依赖
在文件顶部的#include区域添加:
#include <linux/usb.h>
2. 修改hci_read_local_ext_features_sync函数
找到你提到的hci_read_local_ext_features_sync函数,添加VID/PID判断逻辑:
static int hci_read_local_ext_features_sync(struct hci_dev *hdev, u8 page) { struct hci_cp_read_local_ext_features cp; struct usb_device *usb_dev; // 仅处理USB蓝牙设备,检查是否是目标UGREEN适配器 if (hdev->dev && hdev->bus == HCI_USB) { usb_dev = to_usb_device(hdev->dev); // 匹配UGREEN CM749/CM748的VID和PID if (usb_dev->descriptor.idVendor == 0x33fa && usb_dev->descriptor.idProduct == 0x0010) { BT_DBG("Skipping Read Local Extended Features for UGREEN CM749/CM748"); return 0; } } // 原有逻辑:仅当设备支持扩展特性时才执行命令 if (!lmp_ext_feat_capable(hdev)) return 0; memset(&cp, 0, sizeof(cp)); cp.page = page; return __hci_cmd_sync_status(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp, HCI_CMD_TIMEOUT); }
替代方案:修改初始化阶段的命令列表
如果你更倾向于在初始化阶段直接跳过该命令,可以修改hci_init_stages数组,添加一个条件判断的初始化函数,替代原有的HCI_INIT(hci_read_local_ext_features_1_sync)项:
// 新增一个封装函数 static int hci_cond_read_local_ext_features_sync(struct hci_dev *hdev) { struct usb_device *usb_dev; if (hdev->dev && hdev->bus == HCI_USB) { usb_dev = to_usb_device(hdev->dev); if (usb_dev->descriptor.idVendor == 0x33fa && usb_dev->descriptor.idProduct == 0x0010) { BT_DBG("Skipping Read Local Extended Features init for UGREEN device"); return 0; } } // 非目标设备,执行原有命令 return hci_read_local_ext_features_1_sync(hdev); } // 修改初始化阶段的数组项 static const struct hci_init_stage hci_init_stages[] = { // ... 其他初始化项 ... /* HCI_OP_READ_LOCAL_EXT_FEATURES */ HCI_INIT(hci_cond_read_local_ext_features_sync), // ... 其他初始化项 ... };
验证方法
- 编译并安装修改后的内核,重启系统
- 插入UGREEN CM749适配器,用
lsusb确认设备被识别:lsusb | grep 33fa:0010 - 执行初始化命令,确认无超时错误:
sudo hciconfig hci0 up - 验证蓝牙功能正常:使用KDE/Plasma蓝牙守护进程或
bluetoothctl扫描、连接设备
上游友好性说明
这种修改方案只针对特定VID/PID的设备做特殊处理,不会影响其他蓝牙适配器的正常工作,符合Linux内核的“quirk”(硬件兼容性特殊处理)设计原则。你可以基于这个修改提交补丁到蓝牙子系统,同时附上Windows的行为对比作为依据,这样被上游接受的概率会更高。
内容来源于stack exchange




