标题:
Linux那些事儿之我是Hub(11)While You Were Sleeping(四)
[打印本页]
作者:
bingchentiao
时间:
2013-5-16 11:29
标题:
Linux那些事儿之我是Hub(11)While You Were Sleeping(四)
我们说过
,hub
里面的中断端点是
IN
的
,
不是
OUT
的
.
但这并不说明凡是中断传输数据一定是从设备到主机
,
没这种说法
,
别起哄
.
不过
hub
需要的确实只是
IN
的传输
.
首先
,
每一个男人都应该知道
,
中断是由设备产生的
.
在
usb
的世界里两个重要角色
,
主机
,
设备
.
主机就像一个人民公仆
,
设备就像人民群众
,
公仆常常日理万机
,
或者日理万鸡
,
他也许不会去理会每一个子民的水深火热
,
群众如果要想引起公仆的注意
,
只能做一些有创意的事情
,
他们知道
,
像许茹芸唱的那样
,”
没有星星的夜里
,
我用泪光吸引你
”,
其实是没有意义的
,
他们知道只有一些诸如山西黑砖窑事件
,
如无锡绿藻泛滥事件
,
如九江大桥坍塌事件
,
如唐山的黑军车事件
,
如安徽的粽子事件
,
才是有意义的
,
一次事件就可以引发公仆的一次中断
,
会引发一些事情
(
数据传输
).
那么什么是
interval
呢
?interval
就是间隔期
,
啥叫间隔期
?
首先你要明白
,
中断传输绝对不是一种周期性的传输
,
你说黑砖窑事件会不会一个月来一次
?
绿藻泛滥事件会不会
?
黑军车事件会不会
?
肯定不会对不对
.
真要是每个月总有几天发生这些事件
,
那大家都别活了
,
也甭买安尔乐护舒宝了
.
那为什么还要有一个间隔期呢
?
实际上是这样的
,
尽管中断本身不会定期发生
,
但是有一个事情是周期性的
,
对于
IN
的中断端点
,
主机会定期向设备问寒问暖
,
就好比一个父母官定期微服私访
,
去民间了解民情
,
那么如果人民生活很艰苦
,
饱受通货膨胀之苦
,
买不起房子
,
人民哭爹叫娘的
,
父母官就会知道
,
然后就会进行处理
,
会想办法去拯救万民于水火之中
,
会严惩万恶的房地产商
.
只要每一个当官的都能这样做
,
何愁天下不太平
,
人民不安居不乐业
.
那么具体来讲
,
在
usb
的世界里
,
体现的是一种设计者们美好的愿望
.
就是说
,
设计者们眼看着现实世界中的公仆们都很让人失望
,
所以在设计
spec
的时候是这么规定的
,host
必须要体恤民情
,
而且
,
很重要的一点
,
人民可以提出自己的愿望
,
比如你希望父母官多久来探望你一次
,
一个端点的端点描述符里
bInterval
这么一项写的很清楚
,
就是她渴望的总线访问周期
,
或者说她期望
host
隔多久就能看望一下她
.
设备要进行中断传输
,
需要提交一个
urb,
里面注明这个探访周期
,
这算是一种民意
,
而
host
答应不答应呢
,
这个在
host controller
的驱动程序里才知道
,
我们不管
,host
当然有一个标准
,
中断传输可以用在低速
/
全速
/
高速设备中
,
而高速传输可以接受在每个微帧有多达
80%
的时间是进行定期传输
,(
包括中断传输和等时传输
),
而全速
/
低速传输则可以接受每个帧最多有
90%
的时间来进行定期传输
.
所以呢
,host
那边会统一来安排
,
他会听取每一个群众
(
设备
)
的意见或者说愿望
,
然后他来统一安排
,
日程安排得过来就给安排
,
安排不过来那么就返回错误
.
这些都是
host controller
驱动做的
,
暂且不表
.
那么如果说
host
同意
,
或者说
host controller
接受了群众的意见
,
比如你告诉他你希望他每周来看你一次
,
那好
,
他每周来一次
,
来问候你一下
,
问你有没有什么困难
(
中断
).
从技术的角度来讲
,
就是
,host controller
定期给你发送一个
IN token,
就是说发送这么一个包
,
而你如果有中断等在那里
,
那你就告诉她
,
你有了
(
中断
).
同时你会把与这个中断相关的数据发送给主机
,
这就是中断传输的数据阶段
,
显然
,
这就是
IN
方向的传输
.
然后主机接收到数据之后他会发送回来一个包
,
向你确认一下
.
那么如果
host controller
发送给你一个
IN token
的时候
,
你没有中断
,
那怎么办呢
?
你还是要回应一声
,
说你没有
.
当然还有另一种情况是
,
你可能人不在家
,
你出差去了
,
那么你可以在家留个条
,
告诉人家你没法回答
.
以上三种情况
,
对应专业术语来说就是
,
第一种
,
你回应的是
DATA,
主机回应的是
ACK
或者是
Error.
第二种
,
你回应的是
NAK,
第三种
,
你回应的是
STALL.
咱们别玩深沉的
,
没必要说这些专业术语
.
就这么说吧
,
用一段电话对白来解释中断传输的过程中
,
设备需要做些什么
:
对话一
:
问
:
复旦有处女吗
?
答
:
有
.
在女生肚子里
. (DATA)
对话二
:
问
:
复旦有处女吗
?
答
:
没有
. (NAK)
对话三
:
问
:
复旦有处女吗
?
答
:
对不起
,
您所呼叫的用户不在服务区
,
暂时无法接通
. (STALL)
三种情况
,
你都会有回应
,
但是意义明显不同
.
顺便解释一下
OUT
类型的中断端点是如何进行数据传输
,
虽然
Hub
里根本就没有这种款式的端点
.
分三步走
,
第一步
,host
发送一个
OUT token
包
,
然后第二步就直接发送数据包
,
第三步
,
设备回应
,
也许回应
ACK,
表示成功接收到了数据
,
也许回应
NAK,
表示失败了
,
也许回应
STALL,
表示设备端点本身有问题
,
传输没法进行
.
Ok,
现在可以回到刚才那个
interval
的问题来了
,
因为不同速度的
interval
的单位不一样
,
所以同样一个数字表达的意思也不一样
,
那么对于高速设备来说
,
比如它的端点的
bInterval
的值为
n,
那么这表示它渴望的周期是
2
的
(n-1)
次方个微帧
,
比如
n
为
4,
那么就表示
2
的
3
次方个微帧
,
即
8
个
125
微秒
,
换句话说就是
1
毫秒
.
对于高速设备来说
,spec
里规定
,n
的取值必须在
1
到
16
之间
,
而对于全设备来说
,
其渴望周期在
spec
里有规定
,
必须是在
1
毫秒到
255
毫秒之间
,
对于低速设备来说
,
其渴望周期必须在
10
毫秒到
255
毫秒之间
.
可见
,
对于全速
/
低速设备来说
,
不存在这种指数关系
,
所以
urb->interval
直接被赋值为
bInterval,
而高速设备由于这种指数关系
,bInterval
的含义就不是那么直接
,
而是表示那个幂指数
.
而
start_frame
是专门给等时传输用的
,
所以我们不用管了
,
这里当然直接设置为
-1
即可
.
终于
,
我们明白了这个中断传输
,
明白了这个
usb_fill_int_urb()
函数
,
于是我们再次回到
hub_configure()
函数中来
,830
和
831
行
,
这个没什么好说的
,usb-storage
里面也就这么设的
,
能用
DMA
传输当然要用
DMA
传输
.
834
行
,has_indicators
不用说了
,
刚刚才介绍的
,
有就设置为了
1,
没有就是
0,
不过
hub
驱动里提供了一个参数
,
叫做
blinkenlights,
指示灯这东西有两种特性
,
一个是亮
,
一个是闪
,
我们常说的一闪一闪亮晶晶
,
有灯了
,
亮了
,
但是不一定会闪
,
所以
blinkenlights
就表示闪不闪
,
这个参数可以在我们加载模块的时候设置
,
默认值是
0,
在
drivers/usb/core/hub.c
中有定义
:
90 /* cycle leds on hubs that aren't blinking for attention */
91 static int blinkenlights = 0;
92 module_param (blinkenlights, bool, S_IRUGO);
93 MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
以上都是和模块参数有关的
.
如果这两个条件都满足
,
就设置
hub->indicator [0]
为
INDICATOR_CYCLE.
这么设置有什么用
,
咱们以后会看到
.
不过老实说
,
指示灯这东西怎么工作根本不是我们感兴趣的
,
作为一个有志青年我们更应该关心关心国家大事
,
关心关心巴以冲突
,
关心关心韩国人质
,
哪有闲功夫去理睬这种指示灯的事呢
.
837
行
,hub_power_on(),
这个函数的意图司马昭之心
,
路人皆知
.
无非就是相当于打开电视机开关
.
不过这里涉及到一个重要的函数
,
暂且不细讲
,
稍后会专门讲
.
838
行
, hub_activate().
在讲这个函数之前
,
先看一下
hub_configure()
中剩下的最后几行
,hub_activate()
之后就
return 0,
返回了
.841
行的
fail
是行标
,
之前那些出错的地方都有
goto fail
语句跳转过来
,
而且错误码也记录在了
ret
里面
,
于是返回
ret.
好
,
让我们来看一下
hub_activate().
这个函数不长
,
依然来自
drivers/usb/core/hub.c:
514 static void hub_activate(struct usb_hub *hub)
515 {
516 int status;
517
518 hub->quiescing = 0;
519 hub->activating = 1;
520
521 status = usb_submit_urb(hub->urb, GFP_NOIO);
522 if (status < 0)
523 dev_err(hub->intfdev, "activate --> %d/n", status);
524 if (hub->has_indicators && blinkenlights)
525 schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
526
527 /* scan all ports ASAP */
528 kick_khubd(hub);
529 }
quiescing
和
activating
就是两个标志符
.activating
这个标志的意思不言自明
,
而
quiescing
这个标志的意思就容易让人疑惑了
,
永垂不朽的国产软件金山词霸告诉我们
,quiescing
是停顿
,
停止
,
停息的意思
,
咋一看
activating
和
quiescing
就是一对反义词
,
可是如果真的是起相反的作用
,
那么一个变量不就够了么
?
可以为
1,
可以为
0,
不就表达了两种意思了吗
?
嗯
,
套用小龙人的歌词
,
就不告诉你
,
就不告诉你
.
至少现在不用告诉你
,
后面用上了再说
.
512
行
,
这个我们太熟悉了
,
非常熟悉
,
相当熟悉
.
前面我们调用
usb_fill_int_urb()
填充好了一个
urb,
这会儿就该提交了
,
然后
host controller
就知道了
,
然后如果一切顺利的话
,host controller
就会定期来询问
hub,
问它有没有中断
,
有的话就进行中断传输
,
这个我们前面讲过了
.
524
行
,
又和刚才一样的判断
,
不过这次判断条件满足了以后就会执行一个函数
,schedule_delayed_work(),
终于看到这个函数被调用了
,
不枉费我们前面专门分析的蝴蝶效应吧
,
前面分析清楚了
,
这里一看我们就知道
,
延时调用
,
调用的是
leds
对应的那个
work
函数
,
即我们当初注册的那个
led_work().
这里
LED_CYCLE_PERIOD
就是一个宏
,
表明延时多久
,
这个宏在
drivers/usb/core/hub.c
里定义好了
,
208 #define LED_CYCLE_PERIOD ((2*HZ)/3)
关于这个指示灯的代码我们以后再分析
,
趁着年轻
,
我们应该赶紧把该做的事情做了
,
像指示灯相关的代码以后再看也不迟
,
我们真正需要花时间关注的是
hub
作为一个特殊的
usb
设备它是如何扮演好连接主机和设备的作用的
.
席慕蓉说
:
这个世界上有许多事情
,
你以为明天一定可以再继续做的
,
有很多人
,
你以为一定可以再见到面的
.
于是
,
在你暂时放下手
,
或者暂时转过身的时候
,
你心中所想的
,
只是明日又将重聚的希望
.
有时候
,
甚至连这种希望都感觉不到
.
因为
,
你以为日子既然这样一天一天过来
,
当然也应该就这样一天一天过去
.
昨天
,
今天
,
明天应该是没有什么不同的
.
但是
,
就会有那么一次
,
在你一放手
,
一转身的一刹那
,
有的事情就完全改变了
.
太阳落下去
,
而在它重新开始以前
,
有些人有些事就从此和你永别
.
唉
,
怎么说着说着这么伤感
,
继续看
,528
行
,kick_khubd(hub),
来自
drivers/usb/core/hub.c,
316 static void kick_khubd(struct usb_hub *hub)
317 {
318 unsigned long flags;
319
320 /* Suppress autosuspend until khubd runs */
321 to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
322
323 spin_lock_irqsave(&hub_event_lock, flags);
324 if (list_empty(&hub->event_list)) {
325 list_add_tail(&hub->event_list, &hub_event_list);
326 wake_up(&khubd_wait);
327 }
328 spin_unlock_irqrestore(&hub_event_lock, flags);
329 }
这才是我们真正期待的一个函数
,
看见
wake_up()
函数了吧
,
一看到这里就知道怎么回事了吧
,
先看
321
行
,int pm_usage_cnt
是
struct usb_interface
的一个成员
,pm
就是电源管理
,usage_cnt
就是使用计数
,
这里的意图很明显
,hub
要使用了
,
就别让电源管理把它给挂起来了
,
不明白
?
用过笔记本吧
?2005
年进
Intel
的时候每人发一本
IBM T42,
我发现很多时候我合上笔记本它就会自动进入休眠
,
这叫
autosuspend,
可是有时候我会发现我合上笔记本以后
,
笔记本并没有休眠
,
后来总结出来规律了
,
下
A
片的时候基本上计算机是不会进入睡眠的
,
理由很简单
,
它发现你有活动着的网络线程
,
那时候用的是
Windows XP,
当然
Windows
也知道计数
,
别把人家想的那么傻
.
不过
,
这里
,
我们计数的目的不是记录网络线程
,
而是告诉
usb core,
咱们拒绝自动挂起
,
具体的处理会由
usb core
来统一操作
,
不用咱们瞎操心
,usb core
那边自然会判断
pm_usage_cnt
的
,
只要我们这里设置了就可以了
.
324
到
327
行这段
if
判断语句
,
很显然
,
现在我们是第一次来到这里
,
不用说
,hub->event_list
是空的
,
所以
,
条件满足
,
于是
list_add_tail()
会被执行
,
关于队列操作函数
,
咱们也讲过了
,
所以这里也不用困惑了
,
就是往那个总的队列
hub_event_list
里面加入
hub->event_list,
然后调用
wake_up(&khubd_wait)
去唤醒那个昏睡了
N
多年的
hub_thread().
如我们前面说过的那样
,
从此
hub_events()
函数将再次被执行
.
323
行和
328
行
,
关于自旋锁
,
老规矩
,
放到最后面去讲
.
统一讲
.
至此为止
,
整个关于
hub
的配置就讲完了
,
从现在开始
,hub
就可以正式上班了
.
而我们也终于完成了一个目标
,
唤醒了那个该死的
hub_thread(),
进入
hub_events().
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0