Linux下的USB HUB驱动
[日期:2012-07-29]
来源:Linux社区
作者:zhengmeifu
[字体:大 中 小]
在这里,顺带提一下HUB的指示灯问题.
Hub描述符的wHubCharacteristics的bit7来描述设备是否支持显示灯.为1表示在下游的连接端口上支持显示灯,为0则不支持.
如果Hub支持指示灯,则将hub->has_indicators置为1.另外,HUB的指示灯是否起作用,还由一个参数决定,在代码中,大家也看到,这个参数是blinkenlights.这个参数定义如下:
static int blinkenlights = 0;
module_param (blinkenlights, bool, S_IRUGO);
MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
这是一个可调的模块参数.如果要显示灯起作用,必须要将其置为1才可以.我们可以用下面两种方法来设置这个参数:
1:如果模块没有编译进kernel,可以在插入模块的时候,加上这个参数:
modprobe usbcore blinkenlights=1
2:如果模块已经编进kernel,那在kernel的启动参数上加上如下参数:
usbcore.blinkenlights=1
在usb2.0 spec中,对不同情况下的灯颜色都做了定义.
在代码中,灯的显示交给了一延迟的工作队列进行处理,初始化如下所示:
INIT_DELAYED_WORK(&hub->leds, led_work); (在hub_probe()函数中)
在这里,并不打算详细分析这一部份,有兴趣的可以跟踪下去看下.
那函数后面初始化的URB是用来干什么的呢?我们将这个URB的初始化部份单独列出如下:
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
if (maxp > sizeof(*hub->buffer))
maxp = sizeof(*hub->buffer);
我们从此可以看出,这个URB是作用于ep0之外的另一个端点,而且传输数据的长度最大为sizeof(*hub->buffer)
Hub->buffer被定义如下:
struct usb_hub {
……
char (*buffer)[8];
……
}
由此可以看出buffer是指向一个8元素字符数组的指针,sizeof(*hub->buffer)等于0.
关于传输的数据长度,代码中有一段注释,这段注释说,spec上规定的长度是(PORTS+1+7)/8.而linux中,对每个hub上挂有认为最多的端口进行处理,因些,就是(31+1+7)/8 = 5
为什么这里要是8呢?
因为usb2.0 spec上规定,ep0的最大发包长度,可能为8.16.32.64.512.所以选择比5要大的最小值8.
另外,我们注意到,URB完成之后,所要调用的函数是hub_irq().如下所示:
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
UHCI必须要知道HUB的端口的一些连接状态,因此,需要HUB周期性的上报它的端口连接状态.这个URB就是用来做这个用途的.UHCI周期性的发送IN方向中断传输传输给HUB.HUB就会通过这个URB将端口信息发送给HUB.
那这个轮询周期是多长呢?根据我们之前分析的UHCI的知识,它的调度周期是由endpoint的bInterval 字段所决定的.
现在,我们慢慢来接触到hub的一些核心处理了.整理一下心情,继续看代码.^_^
接下来,我们要看到的第一个函数是hub_power_on().代码如下:
static void hub_power_on(struct usb_hub *hub)
{
int port1;
//从连接端口从开机到准备好的时间
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
//wHubCharacteristics字段
u16 wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
/* Enable power on each port. Some hubs have reserved values
* of LPSM (> 2) in their descriptors, even though they are
* USB 2.0 hubs. Some hubs do not implement port-power switching
* but only emulate it. In all cases, the ports won't work
* unless we send these messages to the hub.
*/
//开关模式
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
dev_dbg(hub->intfdev, "enabling power on all ports\n");
else
dev_dbg(hub->intfdev, "trying to enable port power on "
"non-switchable hub\n");
//给每一个端口发送PORT_POWER的Set_Feature消息
for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
/* Wait at least 100 msec for power to become stable */
//等待端口可以正常工作,至少100 ms
msleep(max(pgood_delay, (unsigned) 100));
}
这里就是给每个接口发送PORT_POWER的Set_Feature消息,告之可起来工作了,然后等待端口可以工作.
另外要分析的函数是hub_activate().代码如下:
static void hub_activate(struct usb_hub *hub)
{
int status;
hub->quiescing = 0;
hub->activating = 1;
//提交urb
status = usb_submit_urb(hub->urb, GFP_NOIO);
if (status < 0)
dev_err(hub->intfdev, "activate --> %d\n", status);
if (hub->has_indicators && blinkenlights)
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
/* scan all ports ASAP */
kick_khubd(hub);
}
首先,是在hub的两个字段进行赋值操作,hub-> quiescing 和 hub->activating表示分别表示hub处理暂停和活跃状态.注意,在这里,不要和接口的mark_active()设置的intf->is_active相混淆.
然后,将hub->urb提交.开始调度Led的工作队列.
最后,流程转入kick_khubd().代码如下:
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
/* Suppress autosuspend until khubd runs */
//pm_usage_cnt设置为1,防止autosuspend
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
在这个函数中,先将接口的pm_usage_cnt置1,此后,该接口就不能SUSPEND.
然后,将该hub添加进hub_event_list链表,并唤醒Khubd_wait等待队列.
hub_event_list和Khubd_wait到底代表着什么呢?它后面的参数又是什么呢?接着往下看