标题:
Linux那些事儿之我是Hub(21)八大重量级函数闪亮登场(五)
[打印本页]
作者:
bingchentiao
时间:
2013-5-17 08:40
标题:
Linux那些事儿之我是Hub(21)八大重量级函数闪亮登场(五)
八大函数已经看了一半
,
剩下一半
,Linux
十六岁了
,
十六岁的季节
,
一半是诗
,
一半是梦
,
一首浸透着生命的诗
,
一个温馨的少年梦
.
十六岁的天空
,
一半绚丽
,
一半深沉
,
一种缀满五彩缤纷云霞般的绚丽
,
一种风也洒脱
,
雨也豪迈的深沉
.
让我们继续下一半
,
我知道你和我一样
,
也感觉到了一些疲倦
,
这个时候
,
正是体现我们作为社会主义有志青年的关键时刻
,
让我们以黄健翔为榜样
,
一边高喊张靓颖万岁
,
一边像男人
_
婆
_
一样去战斗
!
2514
行至
2536
行
,
整个这一块代码是专门为了处理
Hub
的
,
网友
”
洞房不败
”
质疑我
,
这不废话么
,
现在讲的就是
hub
驱动
,
不是处理
hub
难道还是为了处理显示器的
?
错
,
我的意思是说
,2514
行这个
if
语句判断的是
,
接在当前
Hub
端口的设备不是别的普通设备
,
恰恰也正是另一个
Hub,
即所谓的级联
.udev->bus_mA
刚刚在
2493
行那里设置的
,
即
hub
那边能够提供的每个端口的电流
.
在当年那个
hub_configure
函数里面我们曾经设置了
hub->mA_per_port,
如果它小于等于
100mA,
说明设备供电是存在问题的
,
电力不足
.
那么这种情况我们首先要判断接入的这个
Hub
是不是也得靠总线供电
,
如果是
,
那就麻烦了
.
所以这里再次调用
usb_get_status(),
虽说我们把这个函数列入八大重量级函数之一
,
但是实际上我们前面已经讲过这个函数了
,
所以这里不必进入函数内部
,
只是需要知道
usb_get_status
这么一执行
,
正常的话
,
这个子
hub
的状态就被记录在
devstat
里面了
,
而
2525
行的意思就是如果这个设备不能自力更生
,
那么我们就打印一条错误信息
,
然后
goto loop_disable,
关闭这个端口
,
结束这次循环
.
2529
行至
2533
行又是指示灯相关的代码
,
当初我们在讲蝴蝶效应的时候就已经说得很清楚了
,
此刻
schedule_delayed_work(&hub->leds,0)
函数这么一执行
,
就意味着当初注册的
led_work()
函数将会立刻被调用
.
这个函数其实挺简单的
,
代码虽然不短
,
但是都是幼稚的代码
.
考虑到
usb_get_status()
我们不用再贴出来了
,
所以这里就干脆顺便看一下这个幼稚函数吧
.
208 #define LED_CYCLE_PERIOD ((2*HZ)/3)
209
210 static void led_work (struct work_struct *work)
211 {
212 struct usb_hub *hub =
213 container_of(work, struct usb_hub, leds.work);
214 struct usb_device *hdev = hub->hdev;
215 unsigned i;
216 unsigned changed = 0;
217 int cursor = -1;
218
219 if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing)
220 return;
221
222 for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
223 unsigned selector, mode;
224
225 /* 30%-50% duty cycle */
226
227 switch (hub->indicator
) {
228 /* cycle marker */
229 case INDICATOR_CYCLE:
230 cursor = i;
231 selector = HUB_LED_AUTO;
232 mode = INDICATOR_AUTO;
233 break;
234 /* blinking green = sw attention */
235 case INDICATOR_GREEN_BLINK:
236 selector = HUB_LED_GREEN;
237 mode = INDICATOR_GREEN_BLINK_OFF;
238 break;
239 case INDICATOR_GREEN_BLINK_OFF:
240 selector = HUB_LED_OFF;
241 mode = INDICATOR_GREEN_BLINK;
242 break;
243 /* blinking amber = hw attention */
244 case INDICATOR_AMBER_BLINK:
245 selector = HUB_LED_AMBER;
246 mode = INDICATOR_AMBER_BLINK_OFF;
247 break;
248 case INDICATOR_AMBER_BLINK_OFF:
249 selector = HUB_LED_OFF;
250 mode = INDICATOR_AMBER_BLINK;
251 break;
252 /* blink green/amber = reserved */
253 case INDICATOR_ALT_BLINK:
254 selector = HUB_LED_GREEN;
255 mode = INDICATOR_ALT_BLINK_OFF;
256 break;
257 case INDICATOR_ALT_BLINK_OFF:
258 selector = HUB_LED_AMBER;
259 mode = INDICATOR_ALT_BLINK;
260 break;
261 default:
262 continue;
263 }
264 if (selector != HUB_LED_AUTO)
265 changed = 1;
266 set_port_led(hub, i + 1, selector);
267 hub->indicator
= mode;
268 }
269 if (!changed && blinkenlights) {
270 cursor++;
271 cursor %= hub->descriptor->bNbrPorts;
272 set_port_led(hub, cursor + 1, HUB_LED_GREEN);
273 hub->indicator[cursor] = INDICATOR_CYCLE;
274 changed++;
275 }
276 if (changed)
277 schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
278 }
注意了
,
刚才我们进入这个函数之前
,
我们设置了
hub->indicator[port1-1]
为
INDICATOR_AMBER_BLINK,
而眼下这个函数从
222
行开始主循环
,
有多少个端口就循环多少次
,
即遍历端口
.227
行就判断了
,
对于咱们的这个情形
,
很显然
,selector
被设置为
HUB_LED_AMBER,
这个宏的值为
1.
而
mode
被设置为
INDICATOR_AMBER_BLINK_OFF.
然后
266
行
,set_port_led(),
182 /*
183 * USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7
184 * for info about using port indicators
185 */
186 static void set_port_led(
187 struct usb_hub *hub,
188 int port1,
189 int selector
190 )
191 {
192 int status = set_port_feature(hub->hdev, (selector << 8) | port1,
193 USB_PORT_FEAT_INDICATOR);
194 if (status < 0)
195 dev_dbg (hub->intfdev,
196 "port %d indicator %s status %d/n",
197 port1,
198 ({ char *s; switch (selector) {
199 case HUB_LED_AMBER: s = "amber"; break;
200 case HUB_LED_GREEN: s = "green"; break;
201 case HUB_LED_OFF: s = "off"; break;
202 case HUB_LED_AUTO: s = "auto"; break;
203 default: s = "??"; break;
204 }; s; }),
205 status);
206 }
看到调用
set_port_feature
我们就熟悉了
,USB_PORT_FEAT_INDICATOR
对应
usb spec
中的
PORT_INDICATOR
这个
feature,
咱们传递进来的是
Amber,
即
Selector
为
1.
于是指示灯会亮
Amber,
即琥珀色
.
这里我们看到有两个
Mode,
一个是
Automatic,
一个是
Manual,Automatic
就是灯自动闪
,
自动变化
,
而
Manual
基本上就是说我们需要用软件来控制灯的闪烁
.
我们选择的是后者
,
所以
265
行我们就设置
changed
为
1.
这样
,
我们将走到
276
行
, schedule_delayed_work()
再次执行
,
但这次执行的时候
,
第二个参数不再是
0,
而是
LED_CYCLE_PERIOD,
即
0.66HZ.
而我们现在的
hub->indicator[port1-1]
和刚才进来的时候相反
,
是
INDICATOR_AMBER_BLINK_OFF,
于是你会发现下次咱们进来又会变成
INDICATOR_AMBER_BLINK,
如此反复
.
这就意味着
,
这个函数接下来将以这个频率被调用
,
也就是说指示灯将以
0.66HZ
的频率亮了又灭灭了又亮
,
一闪一闪亮晶晶
.
不过我需要说的是
,
注意我们刚才是如何进入到这个函数的
,
是因为我们遇见了电力不足的情况进来的
,
所以这并不是什么好事
,
通常琥珀色的灯亮了话
,
说明硬件方面有问题
.
就比如我们这里的电的问题
.
按
spec
规定
,
琥珀色亮而不闪
,
表明是错误环境
,
琥珀色又亮又闪
,
表明硬件有问题
,
只有绿色才是工作状态
,
如果绿色闪烁
,
那说明软件有问题
.
接下来我们把第六个函数也讲掉
.check_highspeed(),
看了名字基本就知道是干嘛的了
.usb spec
里面规定
,
一个设备如果能够进行高速传输
,
那么它就应该在设备描述符里的
bcdUSB
这一项写上
0200H.
所以这里的意思就是说如果一个设备可以进行高速传输
,
但它现在却处于全速传输的状态
,
并且
,highspeed_hubs,
这个变量干嘛的
?drivers/usb/core/hub.c
中定义的一个静态变量
.static unsigned highspeed_hubs,
不要说你是第一次见它
,
当年我们在
hub_probe()
里面就和它有过一面之缘
.
让我们把思绪拉回到
hub_probe
中去
,
当时我们就判断了如果
hdev->speed
是
USB_SPEED_HIGH,
则
highspeed_hubs++,
所以现在这里的意思就很明确了
,
如果有高速的
hub,
而你又能进行高速传输
,
而你偏偏还要进行全速传输
,
那么你就是生得贱
!
这种情况下
,
调用
check_highspeed.
2340 static void
2341 check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
2342 {
2343 struct usb_qualifier_descriptor *qual;
2344 int status;
2345
2346 qual = kmalloc (sizeof *qual, GFP_KERNEL);
2347 if (qual == NULL)
2348 return;
2349
2350 status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0,
2351 qual, sizeof *qual);
2352 if (status == sizeof *qual) {
2353 dev_info(&udev->dev, "not running at top speed; "
2354 "connect to a high speed hub/n");
2355 /* hub LEDs are probably harder to miss than syslog */
2356 if (hub->has_indicators) {
2357 hub->indicator[port1-1] = INDICATOR_GREEN_BLINK;
2358 schedule_delayed_work (&hub->leds, 0);
2359 }
2360 }
2361 kfree(qual);
2362 }
还是那句话
,
有些事情知道多了才觉得太残酷
.
我原以为我可以瞒过去
,
看来是不行了
, struct usb_qualifier_descriptor
这个结构体牵出了另外一个概念
,Device Qualifier descriptor.
首先这个结构体定义于
include/linux/usb/ch9.h
中
:
348 /* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */
349 struct usb_qualifier_descriptor {
350 __u8 bLength;
351 __u8 bDescriptorType;
352
353 __le16 bcdUSB;
354 __u8 bDeviceClass;
355 __u8 bDeviceSubClass;
356 __u8 bDeviceProtocol;
357 __u8 bMaxPacketSize0;
358 __u8 bNumConfigurations;
359 __u8 bRESERVED;
360 } __attribute__ ((packed));
当我们设计
usb 2.0
的时候我们务必要考虑与过去的
usb 1.1
的兼容
,
高速设备如果接在一个旧的
hub
上面
,
总不能说用不了吧
?
所以
,
如今的高速设备通常是可以工作于高速也可以不工作于高速
,
即可以调节
,
接在高速
hub
上就按高速工作
,
如果不然
,
那么就按全速的方式去工作
.
关键得看环境
,
放眼古代
,
三字经中有
”
昔孟母择邻处子不学断机杼
”
的佳话
,
展望今天
,
也许我长在中国是一个优秀的大学生
,
但是也许生在日本我就是一个纯粹的变态
,
环境改变人嘛
.
那么这一点是如何实现的呢
?
首先
,
在高速和全速下有不同设备配置信息的高速设备必须具有一个
device_qualifier
描述符
,
怎么称呼呢
,
你可以叫它设备限定符描述符
,
不过这个叫法过于别扭
,
所以我们直接用英文
,
就叫
device qualifier
描述符
.
它干嘛用的呢
?
它描述了一个高速设备在进行速度切换时所需改变的信息
.
比如
,
一个设备当前工作于全速状态
,
那么
device qualifier
中就保存着信息记录这个设备工作在高速状态的信息
,
反之如果一个设备当前工作于高速状态
,
那么
device qualifier
中就包含着这个设备工作于全速状态的信息
.
Ok,
这里我们看到
,
首先定义一个
device qualifier
描述符的指针
qual,
然后为其申请内存空间
,
然后
usb_get_descriptor
去获得这个描述符
,
这个函数的返回值就是设备返回了多少个
bytes.
如果的确是
device qualifier
描述符的大小
,
那么说明这个设备的确是可以工作在高速状态的
,
因为全速设备是没有
device qualifier
的
,
只有具有高速工作能力的设备才具有
device qualifier
描述符
,
而对于全速设备
,
在收到这么一个请求之后
,
返回的只是错误码
.
所以这里的意思就是说如果你这个设备确实是能够工作在高速的
,
然而你却偏偏工作于全速
,
并且我们刚才调用
check_highspeed()
之前也看到了
,
我们已经判断出设备是工作于全速而系统里有高速的
hub,
那么至少这说明你这个设备不正常
,
所以剩下的代码就和刚才那个闪琥珀色的道理一样
,
只不过这次是闪绿灯
,
通常闪绿灯的意思是软件问题
.
好了
,
又讲完一个函数
.
至此我们已经过五关斩六将
,
八个函数讲完了六个
.
你也许觉得这些函数很枯燥
,
觉得读代码很辛苦
,
不过很不幸
,
我得告诉你
,
其实真正最重要的函数是第七个
.usb_new_device.
这个函数一结束你就可以用
lsusb
命令看到你的设备了
.
正如
<<
这个杀手不太冷
>>
中的对白
,
人生本就是苦还是只有童年苦
?
生命就是如此
.
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0