快三年的时间没有在这里写过文章了。这三年,一直都很忙,被公司的各种项目弄的团团转。
2011年年初从上海回成都,一个人在成都的BIOS圈子里孤军作战,整个公司只有一个x86硬件,只有我一个BIOS研发,就这样默默的做项目,我们两个人,以底层小员工的心态去做,一直以为会有新的领导出现,来为自己的项目决定方向,来管理自己的工作内容,一切都只是以为。
2013年,x86硬件扩充到7个人,BIOS扩充到3个人,再也没有人来领导我,从此我开始领导别人,我们都还那么的年轻不成熟。一心想把手头的项目做好,做好后能够改善大家的待遇。最后的结果是少数项目赚了钱,都拿去投更多垃圾项目了,所以每年年终总结,公司都穷困潦倒,要靠政府的救济金过日子(研发补助费)。
在成都的这几年,主攻工控项目,做了X86的很多平台,比如Menlow,CedarTrail,BayTrail,Quark,ChiefRiver, SharkBay, 工控行业的各种尺寸接口规格也都做过了。付出了很多,但是没有赚到钱。当初从台企跳到民企的初衷是想把从台湾人那偷学的知识拿来报答祖国人民,现在发现这种想法很幼稚,民企做事情太三心二意,有头无尾,口口声声说要做物联网,招了一年了物联网部门都只有一个员工。给不起钱,招不到人都只是借口,关键问题是有没有想发展的决心。说是主攻物联网,却要做车机,要做平板,医疗机,什么都想抓住,什么都没做成功。觉得BOSS和我老爸很像,喜欢跟风,却什么都做的一般。
我觉的做事情最好先坚持把一样做好,持续改进,等达到行业领先后,再扩宽比较好,只可惜BOSS不这么想,他只要宽,不想做好,因此接了无数的项目把我们淹死。
因为BOSS的意图太明显了,做宽了就把公司卖掉,只管他捞钱,不管我们的死活。所以今年有很多部门领导辞职了,这其中有曾经和我并肩作战的X86硬件同学。
他们都走了,我很失落,于是我开始好好学习,用学习来赶走失落的心情,或许我也能再谋一个好东家。
好了,不说了,开始讲Bug吧,程序员的生活每天就是解无数的Bug。如果没有Bug,就没有我们活着的价值了。
Intel Atom TunnelCreek平台上,我们引出了两个TYPEA的USB插槽,Port2插槽插上USB HUB,在windriver启动时,会概率性的死在:
[ 0.613091] RPC: Registered tcp NFSv4.1 backchannel transportmodule.]
Port1插槽的硬件位置为:EHCI 2/8/3 Port1Port2插槽的硬件位置为:EHCI 2/2/3 Port3
死机的现象很奇怪,port1很稳定,port2有问题,客户帮忙在Windriver里追出死机的位置如下:
当IDP USB driver往OHCI发送Ownership Request请求时会挂掉
if (control & OHCI_CTRL_IR) {
int wait_time = 500; /* arbitrary; 5 seconds */
writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
writel(OHCI_OCR, base + OHCI_CMDSTATUS); <----- BIOS hangs after this line
while (wait_time > 0 &&
readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
wait_time -= 10;
msleep(10);
}
if (wait_time <= 0)
dev_warn(&pdev->dev, "OHCI: BIOS handoff failed"
" (BIOS bug?) %08x\n",
readl(base + OHCI_CONTROL));
}
之前以为是硬件上的port1和port2有差异,找HW分析了很久后认为他们没有差别。于是放弃,老实看BIOS的代码。
后来发现BIOS在OHCI控制器的Ownership change时有一个SMI的处理程序,于是去看处理程序,发现处理事情所做的事情如下:
1. 把OHCI controller 上所有的port都disconnect,然后Stop HC,最后reset HC。
2. 把EHCI Controller上所有的port都disconnect,然后Stop HC,最后reset HC。
即把BIOS以前对USB所做过的所有事情都清理干净,然后把权利让给OS下的USB驱动。
于是我编译调试版本的BIOS去查死在哪里,结果调试版本没有这个bug出现,只有release版本有问题,那问题显然是延时不够。
如果所接的USB HUB是high speed设备,那它只能工作在highspeed模式,因此这个bug肯定和EHCI的处理有关。
于是我仔细查看EHCI那边的程序,发现stop HC时只给了1ms的延时,而USB spec里对于很多种延时的情况都规定了10ms以上的时间。于是把这个时间改成10ms,就解决了问题。
这种问题对于OS来说,很难追,因为SMI对于OS来讲是透明的,SMI的处理程序结果不会返回给操作系统,驱动根本不知道发生了什么事情。一旦死机了,连错误都不会报告。OS最恨X86的这种机制吧。这是首次解决此类问题,所以记录一下,以后再遇到OS下相关的bug时要往SMI方向想。
这里记录一下从《Linux那些事儿之我是USB Hub驱动》偷学回来的知识,对看懂BIOS的USB部分代码很有帮助。
USB初始化需要延时的地方有:
1、Power On
2、Reset
3、确认插入
4、确认拔出
5、所有的USB命令都需要设置一个timeout的超时机制
这些延时的最小延时有些是USB spec规定的,有些是设备厂商规定的,可以从设备的描述符里读取。
2.0的USB HUB只能工作在2.0模式,由EHCI控制器负责控制,如果这个HUB下接了Low speed或者full speed的设备,会使用硬件的TT(Transaction Translate)进行事务转换。
HUB为它的每个port提供的电流为100MA到500MA之间,可以下命令去读设备的状态来得知,设备是使用HUB供电,还是设备自身有电源插座。HUB每为一个接在它上面的设备供电后,都要计算剩余电流,如果剩余电流不能满足新插入的设备的要求,就会报错。
USB HUB驱动初始化流程:
1. 读取HUB的描述符,从而知道HUB有几个Port,HUB的速度,HUB所需要的电流,以及HUB的power on延时时间等。
2. 把HUB的所有Port的power使能,以便后来检测哪些Port上插了设备的,只有先给电才能知道有没有设备attach。
3. 把port上的设备reset,reset后进入enable状态。
4. 为port上的设备分配设备号,并发送请求USB_REQ_SET_ADDRESS,把分配到的设备号写到设备。
设备号分配规则为:
用一个128bit的变量devmap来记录有没有设备,如果有找到设备就从Bit0开始置1,第一个设备置bit0,第二个设备置bit1,依此类推,用的第几个bit,设备号就是几,比如用的第8个bit来记录查找到的第8个设备,那这个设备的设备号devnum就是8. 如果128个bit用完了,就从bit0开始从头再来。
4. 读取port上的设备的描述符。
5. 调用Linux内核函数Add_Device,向全世界通报这个设备的存在,为这个设备寻找匹配的驱动。
HUB驱动的使命到这里就结束了,它只负责报告设备,并不负责设备的初始化,HUB报告后,OS会为这个设备寻找最匹配的驱动,然后由设备的驱动程序最终去完成设备的初始化动作。