LINUX内核驱动的一处神来之笔

好的文字让人拍案叫绝,好的代码也如此。下面这段代码是很好的一个例子。不过,不知道有几个人能看出它的妙处?要说清楚它的妙处也不太容易。

这是Linux内核中USB XHCI驱动的一段代码。USB总线始于1995年,30年里一路走来,经历了很多个版本,从控制器角度来看,有OHCI、UHCI、EHCI、XHCI等。

今天广泛使用的是XHCI。XHCI控制器兼容USB 1.0、2.0和3.0定义的多种速度。或者说XHCI从功能角度替代了老的EHCI和UHCI。

考虑到硬件成本和使用场景,XHCI的端口可以具有不同的能力,比如幽兰代码本的USB口就有支持USB 3.0,也有只支持USB 2.0。

那么XHCI驱动是如何知道硬件到底支持哪些速度的端口呢?

简单说是通过XHCI的扩展能力描述表。细节在Intel公开的《XHCI规约》第七章里。

描述表的每一项格式如下:

简单说,每一项描述的是“从x口开始的n个口支持xxx速度”。驱动程序正是据此来初始化XHCI根集线器的每个端口。对于微软的驱动,做这件事的函数名叫RootHub_PrepareHardware()。对于Linux下的驱动,函数名叫xhci_setup_port_arrays().

函数开头的这段注释写的也非常好。

/* * Scan the Extended Capabilities for the "Supported Protocol Capabilities" that * specify what speeds each port is supposed to be.  We can't count on the port * speed bits in the PORTSC register being correct until a device is connected, * but we need to set up the two fake roothubs with the correct number of USB * 3.0 and USB 2.0 ports at host controller initialization time. */

正是在这个函数内部,有下面这段巧妙的代码。

offset = cap_start;    while (offset) {        xhci_add_in_port(xhci, num_ports, base + offset, cap_count);        if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports ==            num_ports)            break;        offset = xhci_find_next_ext_cap(base, offset,                        XHCI_EXT_CAPS_PROTOCOL);    }    if (xhci->usb2_rhub.num_ports == 0 && xhci->usb3_rhub.num_ports == 0) {        xhci_warn(xhci, "No ports on the roothubs?\n");        return -ENODEV;    }    xhci_dbg_trace(xhci, trace_xhci_dbg_init,               "Found %u USB 2.0 ports and %u USB 3.0 ports.",               xhci->usb2_rhub.num_ports, xhci->usb3_rhub.num_ports);

这段代码的while循环写的特别好,它遍历扩展能力表,根据表项描述来建立端口对象。把3.0的端口加到假的usb3_rhub下,2.0的口加到usb2_rhub下。

更巧妙的是循环中的那个break,当找到的端口数等于总端口数就自动退出了循环。这两行代码不一般,对于某些XHCI硬件,如果后面有更多表项,也可以照常工作。而Windows驱动则不然,它会遍历所有表项,如果发现表项中描述的端口号超过了端口数,那么就报告枚举失败,罢工而退。

在Linux的驱动里,真的就有一些缓解硬件不足的代码,比如:

/*         * Some hosts incorrectly use sub-minor version for minor         * version (i.e. 0x02 instead of 0x20 for bcdUSB 0x320 and 0x01         * for bcdUSB 0x310). Since there is no USB release with sub         * minor version 0x301 to 0x309, we can assume that they are         * incorrect and fix it here.         */        if (minor_revision > 0x00 && minor_revision < 0x10)            minor_revision <<= 4;        /*         * Some zhaoxin's xHCI controller that follow usb3.1 spec         * but only support Gen1.         */        if (xhci->quirks & XHCI_ZHAOXIN_HOST) {            tmp_minor_revision = minor_revision;            minor_revision = 0;        }

后半截看起来是弥补兆芯硬件的设计不足,如果xhci->quirks 里设置了 XHCI_ZHAOXIN_HOST,就自动调整minor_revision,这个版本可不是控制器的版本号,而是代表着控制器支持的USB标准版本号。这样单独为了兆芯的硬件而增加代码成本有点高,由此而看,上面的while循环中的break,不仅优雅,而且通用,真是妙极,神乎其技!

(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)

*************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

Image

也欢迎关注格友公众号

Image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值