Linux驱动中配置支持特定USB HUB

本文主要讲述在Linux中配置打开或关闭USB HUB功能。


支持特定的USB HUB

<kernel_dir>/drivers/usb/core/hub.c中的hub_probe()函数添加以下代码:
主要是通过pid和vid来进行过滤判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (hdev->parent) {
printk("busnum = %d, level = %d, vid = 0x%x, pid = 0x%x",
hdev->bus->busnum, hdev->level, hdev->descriptor.idVendor, hdev->descriptor.idProduct);
if ((2 == hdev->bus->busnum) && (1 == hdev->level)
&& ((0x214b == hdev->descriptor.idVendor)
&& (0x7000 == hdev->descriptor.idProduct))) {
printk("USB bus%d support level%d port%d external usb hub\n",
hdev->bus->busnum, hdev->level, hdev->portnum);
} else {
printk("USB bus%d ignore level%d port%d external hub\n",
hdev->bus->busnum, hdev->level, hdev->portnum);
return -ENODEV;
}
}

插入特定的USB HUB,打印的log如下:

1
2
3
4
[   13.913019] usb 2-1: new high-speed USB device number 2 using xxx-ehci
[ 14.050973] busnum = 2, level = 1, vid = 0x214b, pid = 0x7000USB bus2 support level1 port1 external usb hub
[ 14.060826] hub 2-1:1.0: USB hub found
[ 14.069704] hub 2-1:1.0: 4 ports detected

插入其他的USB HUB,打印log如下:

1
2
[  326.481019] usb 2-1: new high-speed USB device number 3 using xxx-ehci
[ 326.635857] busnum = 2, level = 1, vid = 0xbda, pid = 0x5411USB bus2 ignore level1 port1 external hub

这里的hdev变量用struct usb_device来表示,说明USB HUB本身也是属于USB设备。

  • hdev->parent:该成员用来判断这个USB设备不是ROOT HUB;
  • hdev->bus->busnum:表示当前处于哪个USB总线,更一般的说是哪个控制器控制的USB口;
  • hdev->level:表示当前的设备处于哪一层。ROOT HUB为第0层;
  • hdev->descriptor.idVendor:设备的VID;
  • hdev->descriptor.idProduct:设备的PID;

采用上面的方式是已正确完全枚举了一个USB HUB的USB设备,然后hub_probe()中将其他的USB HUB给过滤掉,使之不加载hub驱动。
我们接下来采用另外一种方式过滤USB HUB。


在枚举阶段过滤USB HUB

在枚举阶段中获取到设备描述符之后,我们就已经知道该USB设备的pid和vid了,此时就可以进行过滤。
<kernel_dir>/drivers/usb/core/hub.c中的usb_enumerate_device()函数调用usb_get_configuration()之后添加以下代码:

1
2
3
4
5
6
7
8
9
printk("in %s, line = %d, level =%d, vid = 0x%x, pid = 0x%x, DeviceClass = 0x%x\n",
__FUNCTION__,__LINE__, udev->level, udev->descriptor.idVendor,
udev->descriptor.idProduct, udev->descriptor.bDeviceClass);
if ((USB_CLASS_HUB == udev->descriptor.bDeviceClass)
&& (0x214b == udev->descriptor.idVendor)
&& (0x700 == udev->descriptor.idProduct)) {
printk("ignore this USB HUB\n");
return -ENOTSUPP;
}

这个方法是当枚举到特定的USB设备,那么就返回出错,表示该设备不能正常枚举。因此,得到的log如下:

1
2
3
4
[   57.233188] usb 2-1: new high-speed USB device number 3 using xxx-ehci
[ 57.373818] in usb_enumerate_device, line = 2439, level =1, vid = 0x214b, pid = 0x7000, DeviceClass = 0x9
[ 57.383445] ignore this USB HUB
[ 57.389030] hub 2-0:1.0: unable to enumerate USB device on port 1

插入其他的USB HUB设备,得到的log如下,表示可以正常被枚举:

1
2
3
4
[  270.948984] usb 2-1: new high-speed USB device number 4 using xxx-ehci
[ 271.099910] in usb_enumerate_device, line = 2439, level =1, vid = 0xbda, pid = 0x5411, DeviceClass = 0x9
[ 271.121961] hub 2-1:1.0: USB hub found
[ 271.127805] hub 2-1:1.0: 4 ports detected


完全不开放USB HUB功能

目前想到有两种方法实现:
(1)、
defconfig文件中打开宏CONFIG_USB_OTG_BLACKLIST_HUB。因为在hub_probe()中有这么一句判断:

1
2
3
4
5
6
#ifdef    CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
return -ENODEV;
}
#endif

通过hdev->parent来判断这是一个外接的USB HUB还是ROOT HUB,如果是外接的USB HUB,那么直接返回出错,不加载hub驱动。以此达到完全不开发USB HUB的功能,但是这个USB设备还是能正常被枚举上。

(2)、
通过struct usb_device中的level成员来判断当前的USB设备处于USB总线拓扑的哪个层级。USB ROOT HUB(level=0),第一个外接的USB设备的level=1,以此类推。
因此我们可以确定,当外接一个USB HUB,其level=1,在这个外接的USB HUB再接上USB设备,那么该USB设备的level=2。
此时可以在usb_enumerate_device()将连接在外接的USB HUB上的USB设备给屏蔽掉。
可以枚举到USB HUB,也正常加载了hub驱动,但是过滤掉这个USB HUB上的其他port口,不支持它的下一层级。

Title:Linux驱动中配置支持特定USB HUB

Author:Victor Huang

Time:2019-07-03 / 21:07

Link:http://wowothink.com/68203a0/

License: Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)