标题:
Linux那些事儿之我是Hub(24)所谓的热插拔
[打印本页]
作者:
bingchentiao
时间:
2013-5-17 08:45
标题:
Linux那些事儿之我是Hub(24)所谓的热插拔
你问我这世界
,
最远的地方在哪里
,
我将答案抛向蓝天之外落在你心底
,
你问我这世界
,
最后的真爱在哪里
,
我把线索指向大海之外直达我怀里
.
你问我
hub_irq()
这个函数
,
最终是被谁调用
,
我却只能说我既没有答案也没有线索
.当然,你要是问我芙蓉姐姐还能红多久,我倒是可以很爽快的告诉你,你知道永远有多远吗?
我们曾经在
hub_configure
中讲过中断传输
,
当时调用了
usb_fill_int_urb()
函数
,
并且把
hub_irq
作为一个参数传递了进去
,
最终把
urb->complete
赋值为
hub_irq.
然后
,
主机控制器会定期询问
hub,
每当
hub
端口上有一个设备插入或者拔除时
,
它就会向主机控制器打小报告
.(
怎么哪都有人打小报告啊
,
当初在
Intel
不知道哪位哥们缺德
,
跟老板说我做事情只关注结果不管过程
,
只要问题解决了就好
,
根本不管问题是怎么被解决的
.
害得我在
Intel
差点没过试用期
.)
具体来说
,
从硬件的角度看
,
就是
hub
会向
host controller
返回一些信息
,
或者说
data,
这个
Data
被称作
”Hub and Port Status Change Bitmap”,
而从软件角度来看
,host controller
的驱动程序接下来会在处理好这个过程的
urb
之后
,
调用该
urb
的
complete
函数
,
对于
hub
来说
,
这个函数就是
hub_irq().
337 /* completion function, fires on port status changes and various faults */
338 static void hub_irq(struct urb *urb)
339 {
340 struct usb_hub *hub = urb->context;
341 int status;
342 int i;
343 unsigned long bits;
344
345 switch (urb->status) {
346 case -ENOENT: /* synchronous unlink */
347 case -ECONNRESET: /* async unlink */
348 case -ESHUTDOWN: /* hardware going away */
349 return;
350
351 default: /* presumably an error */
352 /* Cause a hub reset after 10 consecutive errors */
353 dev_dbg (hub->intfdev, "transfer --> %d/n", urb->status);
354 if ((++hub->nerrors < 10) || hub->error)
355 goto resubmit;
356 hub->error = urb->status;
357 /* FALL THROUGH */
358
359 /* let khubd handle things */
360 case 0: /* we got data: port status changed */
361 bits = 0;
362 for (i = 0; i < urb->actual_length; ++i)
363 bits |= ((unsigned long) ((*hub->buffer)
))
364 << (i*8);
365 hub->event_bits[0] = bits;
366 break;
367 }
368
369 hub->nerrors = 0;
370
371 /* Something happened, let khubd figure it out */
372 kick_khubd(hub);
373
374 resubmit:
375 if (hub->quiescing)
376 return;
377
378 if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
379 && status != -ENODEV && status != -EPERM)
380 dev_err (hub->intfdev, "resubmit --> %d/n", status);
381 }
你问这个参数
urb
是哪个
urb?
告诉你
,
中断传输就是只有一个
urb,
不是说像
bulk
传输那样每次开启一次传输都要有申请一个
urb,
提交
urb,
对于中断传输
,
一个
urb
就可以了
,
反复利用
,
所以我们只有一次调用
usb_fill_int_urb()
函数
.
这正体现了中断交互的周期性
.
340
行
,
当初我们填充
urb
的时候
,urb->context
就是赋的
hub,
所以现在这句话就可以获得我们的那个
hub.
345
行开始判断
urb
的状态
,
前三种都是出错了
,
直接返回
.
351
的
default
和
case 0.
这段代码是我认为最有技术含量的一段代码
.
我不知道我是该恨谭浩强该是该恨我自己
,
我记得我在谭浩强的书上看到的
default
总是在各个
case
之后
,
结果我以为这里
default
不管
case
等于
0
与否都会执行
,
结果半天没看懂
,
后来我明白了
,
其实当
urb->status
为
0
的时候
,default
那一段是不会执行的
.
所以这段代码就很好理解了
.
一开始
hub->error
为
0,hub->nerrors
也为
0,
所以
default
这一段很明显
,goto resubmit,
即
,
我们允许年轻人犯错误
,
并且可以允许犯十次
,
错了没有关系
,
只要不在同一条阴沟里翻船翻十次就可以了
,
我相信这对大多数人来说都足够了
,
唯一例外的大概是中国男足了
.
每次阴沟里翻船
,
爬起来一看
,
咦
,
怎么还是上次那条阴沟啊
?resubmit
那一段就是重新调用了一次
usb_submit_urb()
而已
.
当然
,
还判断了
hub->quisecing.
这个变量初始值为
1,
但是我们前面在
hub_activate
里把它设置为了
0,
有一个函数会把它设置为
1,
这个函数就是
hub_quiesce(),
而调用后者的只有两个函数
,
一个是
hub_suspend,
一个是
hub_pre_reset().
于是
,
这里的意思就很明确了
,
如果
hub
被挂起了
,
或者要被
reset
了
,
那么就不用重新提交
urb
了
,hub_irq()
函数直接返回吧
.
再看
case 0,urb->status
为
0,
说明这个
urb
被顺利的处理了
,
即
host controller
获得了他想要的数据
,
即那个
”Hub and Port Status Change Bitmap”,
因为我们当初调用
usb_fill_int_urb
的时候
,
把
*hub->buffer
传递给了
urb->transfer_buffer,
所以这个数据现在就在
hub->buffer
中
,
我们来看这个
bitmap
是什么样子
,usb spec
中给出了这样一幅图
:
首先这幅图为我们解答了一个很大的疑惑
.
当时在
hub_events()
中您大概还有一些地方不是很清楚
.
现在我们可以来弄清楚它们了
.
我们回过头来看
,struct usb_hub
中
,unsigned long event_bits[1],
首先这是一个数组
,
其次这个数组只有一个元素
,
而这一个元素恰恰就是对应这里的这个
Bitmap,
即所谓的位图
,
每一位都有其作用
.
一个
unsigned long
至少
4
个字节
,
即
32
个
bits.
所以够用了
,
而我们看到这张图里
,bit0
和其它的
bit
是不一样的
,bit 0
表示
Hub
有变化
,
而其它
bit
则具体表示某一个端口有没有变化
,
即
bit 1
表示端口
1
有变化
,bit 2
表示端口
2
有变化
,
如果一个端口没有变化
,
对应的那一位就是
0.
所以我们可以回到
hub_events()
函数中来
,
看看当时我们是如何判断
hub->event_bits
的
,
当时我们有这么一小段
,
2685 /* deal with port status changes */
2686 for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
2687 if (test_bit(i, hub->busy_bits))
2688 continue;
2689 connect_change = test_bit(i, hub->change_bits);
2690 if (!test_and_clear_bit(i, hub->event_bits) &&
2691 !connect_change && !hub->activating)
2692 continue;
看到了吗
?
循环指数
i
从
1
开始
,
有多少端口就循环多少次
,
而对
event_bits
的测试
,
即
2690
判断的是
bitmap
中
bit 1,bit 2,…,bit N,
而不需要判断
bit 0.
反过来
,
如果具体每个端口没有变化
,
而变化的是
hub
的整体
,
比如
,Local Power
有变化
,
比如
Overcurrent
有变化
,
我们则需要判断的是
bit 0.
即当时我们在
hub_events()
中看到的下面这段代码
.
2776 /* deal with hub status changes */
2777 if (test_and_clear_bit(0, hub->event_bits) == 0)
2778 ; /* do nothing */
2779 else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
2780 dev_err (hub_dev, "get_hub_status failed/n");
2781 else {
2782 if (hubchange & HUB_CHANGE_LOCAL_POWER) {
2783 dev_dbg (hub_dev, "power change/n");
2784 clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
2785 if (hubstatus & HUB_STATUS_LOCAL_POWER)
2786 /* FIXME: Is this always true? */
2787 hub->limited_power = 0;
2788 else
2789 hub->limited_power = 1;
2790 }
2791 if (hubchange & HUB_CHANGE_OVERCURRENT) {
2792 dev_dbg (hub_dev, "overcurrent change/n");
2793 msleep(500); /* Cool down */
2794 clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
2795 hub_power_on(hub);
2796 }
2797 }
而这样我们就很清楚
hub_events()
的整体思路了
,
判断每个端口是否有变化
,
如果有变化就去处理它
,
没有变化也没有关系
,
接下来判断是否
hub
整体上有变化
,
如果有有变化
,
那么也去处理它
.
满足了个人利益
,
满足了集体利益
,
这不正体现了我们社会主义制度的优越性么
?
关于这个
actual_length,
其实你不傻的话这个根本就不用我啰嗦了
.
因为每一个
hub
的
port
是不一样的
,
所以这张
bitmap
的长度就不一样
,
比如说你是
16
个
port,
那么这个
bitmap
最多就只要
16+1
个
bit
就足够了
.
而
actual_length
就是
3,
即
3
个
bytes.
因为
3
个
bytes
等于
24
个
bits,
足以容纳
16+1
个
bits
了
.
而
struct usb_hub
中
,buffer
是这样一个成员
,char (*buffer)[8],
所以
3
个
bytes
就意味着这个
buffer
的前三个元素里承载着我们的希望
.
这样我们就不难理解这里这个
hub->event_bits
是如何变成为这张
bitmap
的了
.
369
行
,
把
nerrors
清零吧
,
过去的事情就让它过去吧
,
让我们忘记过去
,
展望未来
.
其实这道理不用我说
,
小刚那首
<<
忘记
>>
就说的很清楚了
:
有太多往事就别喝下太少酒精
,
太珍惜生命就别随便掏心
,
舍不得看破就别张开眼睛
,
想开心就要舍得伤心
;
有太多行李就别单独旅行
,
不能够离开就不要接近
,
舍不得结束就别开始一段感情
,
想忘记就要一切归零
.
谢谢林夕
,
用这么好的歌词为我们诠释了
Linux
内核代码
.
最关键的当然还是
372
行
,
再次调用
kick_khubd()
函数
,
于是会再一次触发
hub_events().
而
hub_irq()
函数也就到这里了
.
这个函数不长
,
可是很重要
,
其中最重要的正是最后这句
kick_khubd().
而这也就是所谓的热插拔的实现路径
,
要知道我们上次分析
kick_khubd
的时候是在
hub
初始化的时候
,
即那次针对的情况是设备一开始就插在了
hub
上
,
而这里再次调用
kick_khubd
才是真正的在使用过程中
,
突然间
hub
有了变化的情况
,
应该说这个后者才是真正有技术含量的冬冬
.
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0