linux hub设备,Linux设备驱动之USB hub驱动(续)

5.2.2:接口驱动中的hub_thread()函数我们之前在分析usb_hub_init()的代码的时候,忽略掉了一部份.代码片段如下所示:int usb_hub_init(void){……khubd_task = kthread_run(hub_thread, NULL, "khubd");……}Kthread_run()是kernel中用来启动一个新kernel线程的接口,它所要执行的函数就...
摘要由CSDN通过智能技术生成

5.2.2:接口驱动中的hub_thread()函数

我们之前在分析usb_hub_init()的代码的时候,忽略掉了一部份.

代码片段如下所示:

int usb_hub_init(void)

{

……

khubd_task = kthread_run(hub_thread, NULL, "khubd");

……

}

Kthread_run()是kernel中用来启动一个新kernel线程的接口,它所要执行的函数就是后面跟的第一个参数.在这里,也就是hub_thread().另外,顺带提一句,要终止kthread_run()创建的线程,可以调用kthread_stop().

Hub_thread()的代码如下:

static int hub_thread(void *__unused)

{

set_freezable();

do {

hub_events();

wait_event_freezable(khubd_wait,

!list_empty(&hub_event_list) ||

kthread_should_stop());

} while (!kthread_should_stop() || !list_empty(&hub_event_list));

pr_debug("%s: khubd exiting\n", usbcore_name);

return 0;

}

在上面的代码中, kthread_should_stop()用来判断是否有kthread_stop()将其终止.

在这里,我们终止看到,我们在前面要唤醒的等待队列khubd_wait,也就是在这个地方了.

这个函数的核心处理是hub_events().分段分析代码,如下:

static void hub_events(void)

{

struct list_head *tmp;

struct usb_device *hdev;

struct usb_interface *intf;

struct usb_hub *hub;

struct device *hub_dev;

u16 hubstatus;

u16 hubchange;

u16 portstatus;

u16 portchange;

int i, ret;

int connect_change;

/*

*  We restart the list every time to avoid a deadlock with

* deleting hubs downstream from this one. This should be

* safe since we delete the hub from the event list.

* Not the most efficient, but avoids deadlocks.

*/

while (1) {

/* Grab the first entry at the beginning of the list */

//如果hub_event_list为空,退出

spin_lock_irq(&hub_event_lock);

if (list_empty(&hub_event_list)) {

spin_unlock_irq(&hub_event_lock);

break;

}

//取hub_event_list中的后一个元素,并将其断链

tmp = hub_event_list.next;

list_del_init(tmp);

hub = list_entry(tmp, struct usb_hub, event_list);

kref_get(&hub->kref);

spin_unlock_irq(&hub_event_lock);

hdev = hub->hdev;

hub_dev = hub->intfdev;

intf = to_usb_interface(hub_dev);

dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",

hdev->state, hub->descriptor

? hub->descriptor->bNbrPorts

: 0,

/* NOTE: expects max 15 ports... */

(u16) hub->change_bits[0],

(u16) hub->event_bits[0]);

/* Lock the device, then check to see if we were

* disconnected while waiting for the lock to succeed. */

usb_lock_device(hdev);

//如果hub断开了,继续hub_event_list中的下一个

if (unlikely(hub->disconnected))

goto loop;

/* If the hub has died, clean up after it */

//设备没有连接上

if (hdev->state == USB_STATE_NOTATTACHED) {

hub->error = -ENODEV;

//将下面的子设备全部disable

hub_pre_reset(intf);

goto loop;

}

/* Autoresume */

ret = usb_autopm_get_interface(intf);

if (ret) {

dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);

goto loop;

}

/* If this is an inactive hub, do nothing */

//hub 暂停

if (hub->quiescing)

goto loop_autopm;

//hub 有错误发生?

if (hub->error) {

dev_dbg (hub_dev, "resetting for error %d\n",

hub->error);

ret = usb_reset_composite_device(hdev, intf);

if (ret) {

dev_dbg (hub_dev,

"error resetting hub: %d\n", ret);

goto loop_autopm;

}

hub->nerrors = 0;

hub->error = 0;

}

首先,从hub_event_list摘下第一个元素,根据我们之前在接口驱动probe过程的kick_khubd()函数分析中,有将hub->event_list添加到hub_event_list.因此,就可以顺藤摸瓜找到hub,再根据hub结构,找到接口结构和所属的usb 设备结构.

然后,进行第一个重要的判断.如果hub被断开了,则,断开hub下面所连接的所有端口,这是在hub_pre_reset()中完成的.

最后,进行第二个重要的判断,如果hub发生了错误,则reset它下面的所有端口,这是在usb_reset_composite_device()中完成的.

/* deal with port status changes */

//遍历hub中的每一个port

for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {

{

if (test_bit(i, hub->busy_bits))

continue;

connect_change = test_bit(i, hub->change_bits);

if (!test_and_clear_bit(i, hub->event_bits) &&

!connect_change && !hub->activating)

continue;

//Get_Port_Status:取得端口状态.

//会取得port的改变值和状态值

ret = hub_port_status(hub, i,

&portstatus, &portchange);

if (ret < 0)

continue;

//如果对应端口没有在设备树上,且端口显示已经连接上

//将connect_change置为1

if (hub->activating && !hdev->children[i-1] &&

(portstatus &

USB_PORT_STAT_CONNECTION))

connect_change = 1;

//端口的连接状态发生了改变.需要发送Clear_Feature

if (portchange & USB_PORT_STAT_C_CONNECTION) {

clear_port_feature(hdev, i,

USB_PORT_FEAT_C_CONNECTION);

connect_change = 1;

}

//端口的状态从enable 变为了disable

if (portchange & USB_PORT_STAT_C_ENABLE) {

if (!connect_change)

dev_dbg (hub_dev,

"port %d enable change, "

"status %08x\n",

i, portstatus);

clear_port_feature(hdev, i,

USB_PORT_FEAT_C_ENABLE);

/*

* EM interference sometimes causes badly

* shielded USB devices to be shutdown by

* the hub, this hack enables them again.

* Works at least with mouse driver.

*/

//端口已经被停止了,且端口已经被连在设备树中.

//需要重启一下此端口

if (!(portstatus & USB_PORT_STAT_ENABLE)

&& !connect_change

&& hdev->children[i-1]) {

dev_err (hub_dev,

"port %i "

"disabled by hub (EMI?), "

"re-enabling...\n",

i);

connect_change = 1;

}

}

//Resume完成

if (portchange & USB_PORT_STAT_C_SUSPEND) {

clear_port_feature(hdev, i,

USB_PORT_FEAT_C_SUSPEND);

//如果端口连接了设备,就将设备唤醒

if (hdev->children[i-1]) {

ret = remote_wakeup(hdev->

children[i-1]);

if (ret < 0)

connect_change = 1;

}

//如果端口没有连接设备,就将端口禁用

else {

ret = -ENODEV;

hub_port_disable(hub, i, 1);

}

dev_dbg (hub_dev,

"resume on port %d, status %d\n",

i, ret);

}

//有过流保护,需要对hub power on

if (portchange & USB_PORT_STAT_C_OVERCURRENT) {

dev_err (hub_dev,

"over-current change on port %d\n",

i);

clear_port_feature(hdev, i,

USB_PORT_FEAT_C_OVER_CURRENT);

hub_power_on(hub);

}

//Reset状态已经完成了

if (portchange & USB_PORT_STAT_C_RESET) {

dev_dbg (hub_dev,

"reset change on port %d\n",

i);

clear_port_feature(hdev, i,

USB_PORT_FEAT_C_RESET);

}

if (connect_change)

hub_port_connect_change(hub, i,

portstatus, portchange);

}

这段代码就是最核心的操作了,首先要说明的是,在struct usb_dev中,有一个struct usb_device *children[USB_MAXCHILDREN]的成员,它是表示对应端口序号上所连接的usb设备.

在这里,它遍历hub上的每一个端口,如果端口的连接会生了改变(connect_change等于1)的情况,就会调用hub_port_connect_change().我们来看一下,什么情况下, hub_port_connect_change才会被设为1.

1:端口在hub->change_bits中被置位.搜索整个代码树,发生在设置hub->change_bits的地方,只有在hub_port_logical_disconnect()中手动将端口禁用,会将对应位置1.

2:hub上没有这个设备树上没有这个端口上的设备.但显示端口已经连上了设备

3:hub这个端口上的连接发生了改变,从端口有设备连接变为无设备连接,或者从无设备连接变为有设备连接.

4:hub的端口变为了disable,此时这个端口上连接了设备,但被显示该端口已经变禁用,需要将connect_change设为1.

5:端口状态从SUSPEND变成了RESUME,远程唤醒端口上的设备失败,就需要将connect_change设为1.

另外hub_port_connect_change()函数我们放在后面再来讨论

//对HUB的处理

/* deal with hub status changes */

//如果hub状态末变化,不需要做任何处理

if (test_and_clear_bit(0, hub->event_bits) == 0)

;   /* do nothing */

//Get_hub_status 失败?

else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)

dev_err (hub_dev, "get_hub_status failed\n");

else {

//这里是对应hub 状态发生了改变,且Get_hub_status正常返回的情况

//如果hub的本地电源供电发生了改变

if (hubchange & HUB_CHANGE_LOCAL_POWER) {

dev_dbg (hub_dev, "power change\n");

clear_hub_feature(hdev, C_HUB_LOCAL_POWER);

//如果是本地电源供电

if (hubstatus & HUB_STATUS_LOCAL_POWER)

/* FIXME: Is this always true?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值