好的文字让人拍案叫绝,好的代码也如此。下面这段代码是很好的一个例子。不过,不知道有几个人能看出它的妙处?要说清楚它的妙处也不太容易。
这是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,不仅优雅,而且通用,真是妙极,神乎其技!
(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)
*************************************************
正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生
扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

也欢迎关注格友公众号

1127

被折叠的 条评论
为什么被折叠?



