5.2.2:介面驅動中的hub_thread()函式
我們之前在分析usb_hub_init()的**的時候,忽略掉了一部份.
**片段如下所示:
int usb_hub_init(void)
kthread_run()是kernel中用來啟動乙個新kernel執行緒的介面,它所要執行的函式就是後面跟的第乙個引數.在這裡,也就是hub_thread().另外,順帶提一句,要終止kthread_run()建立的執行緒,可以呼叫kthread_stop().
hub_thread()的**如下:
static int hub_thread(void *__unused)
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)
//取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_inte***ce(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)
/* autoresume */
ret = usb_autopm_get_inte***ce(intf);
if (ret)
/* if this is an inactive hub, do nothing */
//hub 暫停
if (hub->quiescing)
goto loop_autopm;
//hub 有錯誤發生?
if (hub->error)
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 descriptor->bnbrports; i++)
//埠的狀態從enable 變為了disable
if (portchange & usb_port_stat_c_enable)
}//resume完成
if (portchange & usb_port_stat_c_suspend)
//如果埠沒有連線裝置,就將埠禁用
else
dev_dbg (hub_dev,
"resume on port %d, status %d\n",
i, ret);
}//有過流保護,需要對hub power on
if (portchange & usb_port_stat_c_overcurrent)
//reset狀態已經完成了
if (portchange & usb_port_stat_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)
dev_err (hub_dev, "get_hub_status failed\n");
else
//如果hub 發生過電源保護,需要對hub power on
if (hubchange & hub_change_overcurrent)
}hub->activating = 0;
/* if this is a root hub, tell the hcd it's okay to
* re-enable port-change interrupts now. */
if (!hdev->parent && !hub->busy_bits[0])
usb_enable_root_hub_irq(hdev->bus);
loop_autopm:
/* allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list))
usb_autopm_enable(intf);
loop:
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
} /* end while (1) */
}處理完hub上的port之後,就要來處理hub本身的狀態改變了,結合**中的注釋應該很容易看懂,在這裡主要是清除hub的對應feature.
之後,將 hub->activating設為了0,如果hub是root hub,需要重新開啟root hub的中斷.
這個函式到這裡就完成了.不過,其中的幾個子函式,涉及到的操作很重要,現分析如下:
1: hub_pre_reset()函式.
該函式在裝置斷開連線的時候,將其下掛載的所有子裝置全部登出掉,**如下所示:
static int hub_pre_reset(struct usb_inte***ce *intf)
hub_quiesce(hub);
return 0;
}
Linux裝置驅動之《字元裝置驅動》
linux裝置中最大的特點就是裝置操作猶如檔案操作一般,在應用層看來,硬體裝置只是乙個裝置檔案。應用程式可以像操作檔案一樣對硬體裝置進行操作,如open close read write 等。下面是乙個字元裝置驅動程式的簡單實現test.c 模組分析 1.初始化裝置驅動的結構體 struct fil...
Linux裝置驅動之字元裝置驅動
一 linux裝置的分類 linux系統將裝置分成三種基本型別,每個模組通常實現為其中某一類 字元模組 塊模組或網路模組。這三種型別有 字元裝置 字元裝置是個能夠像位元組流 類似檔案 一樣被訪問的裝置,由字元裝置驅動程式來實現這種特性。字元裝置可以通過檔案系統節點來訪問,比如 dev tty1等。這...
linux驅動之塊裝置驅動
塊裝置驅動的系統架構 塊裝置註冊過程 1,註冊裝置塊驅動程式 register blkdev 2,初始化請求佇列 blk init queue 3,指明扇區的大小 blk queue logical block size dev queue,sect size 4,申請乙個gendisk結構,初始化...