Linux裝置驅動之USB hub驅動(續)
我們之前在分析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 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
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
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)
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? */
hub->limited_power = 1;
//如果本電源不供電
else
hub->limited_power = 0;
}
//如果hub 發生過電源保護,需要對hub power on
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dev_dbg (hub_dev, "overcurrent change\n");
msleep(500); /* Cool down */
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
hub_power_on(hub);
}
}
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_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = hub->hdev;
int i;
/* Disconnect all the children */
for (i = 0; i maxchild; ++i) {
if (hdev->children)
usb_disconnect(&hdev->children);
}
hub_quiesce(hub);
return 0;
}
hub_quiesce()是和hub_activate()相對應的一個函式, hub_activate()在前面已經分析過了,現在來對hub_quiesce()進行分析.
程式碼如下:
static void hub_quiesce(struct usb_hub *hub)
{
/* (nonblocking) khubd and related activity won't re-trigger */
hub->quiescing = 1;
hub->activating = 0;
/* (blocking) stop khubd and related activity */
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
if (hub->tt.hub)
cancel_work_sync(&hub->tt.kevent);
}
首先,它調hub->quiescing置為1,而activating置為0.這和hub_activate()剛好是相反的動作.之後,取消hub的中斷傳輸出URB.取得TT和LED的工作佇列.
我們在後面分析的HUB中斷URB傳輸,可以知道,如果將這個URB禁用,那麼,就不會將hub->event_list新增到hub_event_list.因此,也不會進入到hub_events()函式.
usb_disconnect()用來斷開某個裝置,程式碼如下:
void usb_disconnect(struct usb_device **pdev)
{
struct usb_device *udev = *pdev;
int i;
if (!udev) {
pr_debug ("%s nodev\n", __FUNCTION__);
return;
}
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
* this quiesces everyting except pending urbs.
*/
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
usb_lock_device(udev);
/* Free up all the children before we remove this device */
for (i = 0; i
if (udev->children)
usb_disconnect(&udev->children);
}
/* deallocate hcd/hardware state ... nuking all pending urbs and
* cleaning up all state associated with the current configuration
* so that the hardware is now fully quiesced.
*/
dev_dbg (&udev->dev, "unregistering device\n");
usb_disable_device(udev, 0);
usb_unlock_device(udev);
/* Unregister the device. The device driver is responsible
* for removing the device files from usbfs and sysfs and for
* de-configuring the device.
*/
device_del(&udev->dev);
/* Free the device number and delete the parent's children[]
* (or root_hub) pointer.
*/
release_address(udev);
/* Avoid races with recursively_mark_NOTATTACHED() */
spin_lock_irq(&device_state_lock);
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
usb_stop_pm(udev);
put_device(&udev->dev);
}
很容易看出.這個函式採用深度遍歷演算法,它依次遍歷udev->children[]下的子裝置,然後依然呼叫usb_disconnect().
這個函式中的另外幾個子函式有的在前面已經分析過,有的是裝置模型中的基礎函式.很有是跟PM相關的,在這裡就不做詳細分析,來看一下release_address()函式,顧名思意,它用來釋放裝置的地址,如下示:
static void release_address(struct usb_device *udev)
{
if (udev->devnum > 0) {
clear_bit(udev->devnum, udev->bus->devmap.devicemap);
udev->devnum = -1;
}
}
我們在分析UHCI中,有關root hub的初始化時說明,設各號都是儲存在bus->devmap陣列中的.在這裡,只需要將該裝置號在陣列中的某位清了即可.
hub_pre_reset()函式就分析到這裡了.
注意到這裡呼叫的put_device(&udev->dev)沒.根據Linux裝置模型的分析,這時它會呼叫跟它繫結的driver的remove()介面,對應的,這個函式會將操作回溯到usb_driver-> disconnect().可以自行查閱這個過程.
或許,有人的疑問又來了?要是這個usb_dev沒有跟usb_driver繫結怎麼辦呢?
不要忘記我們之前的分析了,對於usb_generic_driver這個驅動是會適用所有的usb_dev的.^_^,也是說,無論如何,usb_dev都會繫結到usb_generic_driver.
2: hub_port_connect_change()函式
這個函式是一個很核心的操作,它的程式碼如下:
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
int status, i;
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed (portstatus));
//HUB LED
if (hub->has_indicators) {
set_port_led(hub, port1, HUB_LED_AUTO);
hub->indicator[port1-1] = INDICATOR_AUTO;
}
/* Disconnect any existing devices under this port */
//如果對應埠已經有裝置連線,先將其斷開
if (hdev->children[port1-1])
usb_disconnect(&hdev->children[port1-1]);
//將hub_change_bits中的對應位清零,以免下次進來的時候,還會檢測到
//hub_port_logical_disconnect()對該值的設定
clear_bit(port1, hub->change_bits);
#ifdef CONFIG_USB_OTG
/* during HNP, don't repeat the debounce */
if (hdev->bus->is_b_host)
portchange &= ~USB_PORT_STAT_C_CONNECTION;
#endif
//連線發生改變
//連線反彈的處理,實際上就是除抖動
if (portchange & USB_PORT_STAT_C_CONNECTION) {
status = hub_port_debounce(hub, port1);
if (status
if (printk_ratelimit())
dev_err (hub_dev, "connect-debounce failed, "
"port %d disabled\n", port1);
goto done;
}
portstatus = status;
}
在這裡,我們忽略掉HUB LED燈的操作,然後,將HUB對應埠下面掛載的裝置斷開.經過前面的分析,進入到這個函式的可能有多種情況(在hub_events()中分析的五種情況).可以分為三大類:
一類是之前有連線之後沒聯接的,在這裡,將hub 對應埠下的裝置全部斷開是無可非議的.
第二類是之前沒有,之後有連線的,在這裡,if(hdev->children[port-1])的判斷是不會滿足的.
第三類是需要重置的埠,在這裡先將裝置斷開,然後再將它聯連上去好了.
接下來,將hub->change_bits的對應位清掉,該位是在函式hub_port_logical_disconnect()中被置的,在這裡將其清除,免得下次在進入hub_events()的時候,再次檢測到這個位發生改變.
忽略掉CONFIG_USB_OTG的處理,這個巨集我們在前面分析過很多次了,這裡不再贅述.
如果該埠的連線發生改變(從有連線到無接接,或者從無連線到有連線),就有一個除抖動的過程,usb2.0 spec上規定,除抖動的時間為100ms.
也許有人會有這樣的想法: 那檢測到移除了一個裝置,但它在100ms又插上去了,這裡適不適合這裡的抖動檢測的情況呢?
我們先從程式碼的流程看,檢測到連線發生改變,進入到hub_port_connect_change(),它首先就會將埠上的裝置移除.這樣,就算你在100ms上連線上去了,也得要再次建立.
從usb2.0的協議看來,裝置移除後,usb裝置裡儲存的資訊(例如選擇的配置,給它分配的地址)全部都丟失了,必須要重新進行配置過程才能夠使用.
在這裡,順便將hub_port_debounce()列出,來看一下具體的除抖過程是怎麼樣實現的.
static int hub_port_debounce(struct usb_hub *hub, int port1)
{
int ret;
int total_time, stable_time = 0;
u16 portchange, portstatus;
unsigned connection = 0xffff;
for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
ret = hub_port_status(hub, port1, &portstatus, &portchange);
if (ret
return ret;
if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&
(portstatus & USB_PORT_STAT_CONNECTION) == connection) {
stable_time += HUB_DEBOUNCE_STEP;
if (stable_time >= HUB_DEBOUNCE_STABLE)
break;
} else {
stable_time = 0;
connection = portstatus & USB_PORT_STAT_CONNECTION;
}
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}
if (total_time >= HUB_DEBOUNCE_TIMEOUT)
break;
msleep(HUB_DEBOUNCE_STEP);
}
dev_dbg (hub->intfdev,
"debounce: port %d: total %dms stable %dms status 0x%x\n",
port1, total_time, stable_time, portstatus);
if (stable_time
return -ETIMEDOUT;
return portstatus;
}
函式中的stable_time表示隱定的時間.在hub_events()的程式碼分析時,我們看到了,在檢測到連線狀態發生改變的時候,會發送Clear_Feature.因此,如果在這裡檢測到有USB_PORT_STAT_C_CONNECTION,就說明之後又有一次連線狀態發生改變了.
分析這個函式的時候,要注意有這樣的情況,埠的連線狀態,一直在波動,即時有連線,時末有連線.
還有注意, connection的初始值是0xffff, 所以(portstatus & USB_PORT_STAT_CONNECTION) == connection這個判斷是肯定不會滿足的,因為hub_port_status()取得的portstatus裡面還有一些保留位.所以,在第一次進入這個迴圈的時候,就會進入到else中,就會將stable_time置0,而connection也儲存了這一次的連線資訊.
如果埠維持前一個狀態,那迴圈中的流程就會滿足第一個if,在這個if的操作裡,會增加stable_time的值.
如果埠的狀態發生了改變,那迴圈中的流程就會滿足else,又將stable_time和connection初始化了.另外,要記得在狀態發生改變的時候,要傳送Clear_Feature,將狀態清除.
在函式裡,定義的測試時間是1500ms.如果在這個時間內,埠還末處於穩定狀態,就會返回-ETIMEDOUT.
如果已經處於穩定狀態了,就會返回穩定狀態下的portstatus.
/* Return now if nothing is connected */
//如果介面上沒有連線了,可以直接退出了
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
/* maybe switch power back on (e.g. root hub was reset) */
if ((wHubCharacteristics & HUB_CHAR_LPSM)
&& !(portstatus & (1
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
goto done;
return;
}
經過去抖後,埠穩定的處於斷開連線狀態.說明埠已經沒有裝置了.然後,再判斷hub是否有電源開關((wHubCharacteristics & HUB_CHAR_LPSM)
如果埠依然處理enable狀態,就會跳轉到標號done處,就埠disalbe.
//如果介面上面有了聯接,需要為聯接在埠上裝置建立連線
for (i = 0; i
struct usb_device *udev;
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
*/
udev = usb_alloc_dev(hdev, hdev->bus, port1);
if (!udev) {
dev_err (hub_dev,
"couldn't allocate port %d usb_device\n",
port1);
goto done;
}
usb_set_device_state(udev, USB_STATE_POWERED);
udev->speed = USB_SPEED_UNKNOWN;
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
/* set the address */
choose_address(udev);
if (udev->devnum
status = -ENOTCONN; /* Don't retry */
goto loop;
}
/* reset and get descriptor */
status = hub_port_init(hub, udev, port1, i);
if (status
goto loop;
/* consecutive bus-powered hubs aren't reliable; they can
* violate the voltage drop budget. if the new child has
* a "powered" LED, users should notice we didn't enable it
* (without reading syslog), even without per-port LEDs
* on the parent.
*/
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
&& udev->bus_mA
u16 devstat;
status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
&devstat);
if (status
dev_dbg(&udev->dev, "get status %d ?\n", status);
goto loop_disable;
}
le16_to_cpus(&devstat);
if ((devstat & (1
dev_err(&udev->dev,
"can't connect bus-powered hub "
"to this port\n");
if (hub->has_indicators) {
hub->indicator[port1-1] =
INDICATOR_AMBER_BLINK;
schedule_delayed_work (&hub->leds, 0);
}
status = -ENOTCONN; /* Don't retry */
goto loop_disable;
}
}
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
&& highspeed_hubs != 0)
check_highspeed (hub, udev, port1);
/* Store the parent's children[] pointer. At this point
* udev becomes globally accessible, although presumably
* no one will look at it until hdev is unlocked.
*/
status = 0;
/* We mustn't add new devices if the parent hub has
* been disconnected; we would race with the
* recursively_mark_NOTATTACHED() routine.
*/
spin_lock_irq(&device_state_lock);
if (hdev->state == USB_STATE_NOTATTACHED)
status = -ENOTCONN;
else
hdev->children[port1-1] = udev;
spin_unlock_irq(&device_state_lock);
/* Run it through the hoops (find a driver, etc) */
if (!status) {
status = usb_new_device(udev);
if (status) {
spin_lock_irq(&device_state_lock);
hdev->children[port1-1] = NULL;
spin_unlock_irq(&device_state_lock);
}
}
if (status)
goto loop_disable;
status = hub_power_remaining(hub);
if (status)
dev_dbg(hub_dev, "%dmA power budget left\n", status);
return;
loop_disable:
hub_port_disable(hub, port1, 1);
loop:
ep0_reinit(udev);
release_address(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
}
如果埠隱定處於連線狀態,那就需要連線埠下的裝置了.首先看到的是一個for迴圈,是用來配置裝置的兩種方式.我們知道,在配置裝置的時候,首先要去取裝置的描述符,這個過程是在ep0上完成的.而這個ep0支援的最大傳輸出資料又是在裝置描述符的bMaxPacketSize0中所定義的.
因此就對應有兩種處理方式:
第一種是傳輸8個位元組,取得描述符的前面一部份,從而就可以取得bMaxPacketSize0.此後再reset裝置,再根據這個bMaxPacketSize0的長度去取它的裝置描述符.
第二種是一次傳輸64位元組,取得裝置描述符的bMaxPacketSize0欄位
關於這兩種方式的描述,詳見fudan_abc的>.
有關這個for迴圈的作用就解釋到這裡.
在這段程式碼裡,它首先分配一個usb_dev的結構,然後將其置為USB_STATE_POWERED狀態.接著,為裝置指定一個地址.
然後就呼叫hub_port_init()對這個usb_dev結構進行一系的初始化,在這個函式中會處理:Get_Description,Set_address.等操作,這個函式接下來我們再詳細分析.
接著,將分配的struct usb_dev結構跟他的父結構關聯起來,也就是說新增到它的父結構的usb_dev-> children[]陣列.
最後再呼叫usb_new_device()來取這個裝置的配置項.這個函式我們在分析UHCI的時候已經分析過了.
中間是關於一些電流的判斷處理,這部份比較簡單,自行檢視就可以看懂,這裡不再分析.
注意,這裡在分配usb_dev結構的時候,跟root hub是不相同的,如下示:
udev = usb_alloc_dev(hdev, hdev->bus, port1)
在為root hub分配struct usb_dev的時候,它的第一個引數,也就是它的父結點是為NULL.
我們來觀察一下它在sysfs中的命名方式:
如下所示:
在沒有插入U盤之前:
[
/sys/bus/usb/devices
[[email protected] devices]# ls
1-0:1.0 usb1
[[email protected] devices]#
插入U盤之後:
[[email protected] devices]# ls
1-0:1.0 1-1 1-1:1.0 usb1
增加的兩個目是:
1-1和1-1:1.0
表示,U盤對應的裝置目錄是1-1.結合之前UHCI分析中,對usb_alloc_dev()應該很容易理解.
1-1:1.0 :只有這樣的目錄,表示該U盤只有一個介面,當前選取的是第0號設定項.
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
hcd->driver->relinquish_port(hcd, port1);
}
Done標號是對應上述處理失敗的處理,它禁用掉該埠(因為該埠沒有連線裝置或者是埠上的裝置配置失敗),如果是root hub,且USB控制器器驅動中又定義了relinquish_port.呼叫它.
照例,還是分析一下這個函式中涉及到的重要的子函式.
第一個要分析的函式是choose_address()
該函式用來為裝置選擇一個地址,程式碼如下所示:
static void choose_address(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;
/* If khubd ever becomes multithreaded, this will need a lock */
/* Try to allocate the next devnum beginning at bus->devnum_next. */
//從bus->devnum_next開始找到一個末被使用的位
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next);
//如果搜尋到了最末尾,(128是不能被佔用的),則從1起開始搜尋
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
//更新bus->devnum_next
bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
//如果找到了合適位,將該位設為佔用,然後更新udev->devnum為找到的裝置號
if (devnum
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
}
這個函式的原理我們在之前說過了多次,它是到所屬的usb bus的bus->devmap中找到沒有使用的那一位.在這裡設定bus->devnum_next項是一個搜尋的優化,它不必每次都從第1位起開始搜尋.最後將找到的值存放在udev->devnum中.
第二個要分析的函式是hub_port_disable().
這個函式將hub對應的埠禁用,程式碼如下:
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
{
struct usb_device *hdev = hub->hdev;
int ret = 0;
//將接在該埠下的裝置設為末連線
if (hdev->children[port1-1] && set_state)
usb_set_device_state(hdev->children[port1-1],
USB_STATE_NOTATTACHED);
//傳送enable 的Clear_Feature請求.
if (!hub->error)
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
if (ret)
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
port1, ret);
return ret;
}
該函式的邏輯很簡單,就是該端點下的聯接裝置斷開,如果埠有裝置連線的話.然後清除埠的enable.
第三個要分析的函式是hub_port_init().
將它列到最後,並不是因為它最輕微,而是因為它太複雜.^_^
程式碼分段分析如下:
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
static DEFINE_MUTEX(usb_address0_mutex);
struct usb_device *hdev = hub->hdev;
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
char *speed, *type;
int devnum = udev->devnum;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
*/
//設定port 的重置等待時間
if (!hdev->parent) {
delay = HUB_ROOT_RESET_TIME;
if (port1 == hdev->bus->otg_port)
hdev->bus->b_hnp_enable = 0;
}
/* Some low speed devices have problems with the quick delay, so */
/* be a bit pessimistic with those devices. RHbug #23670 */
if (oldspeed == USB_SPEED_LOW)
delay = HUB_LONG_RESET_TIME;
mutex_lock(&usb_address0_mutex);
/* Reset the device; full speed may morph to high speed */
//將port reset
retval = hub_port_reset(hub, port1, udev, delay);
if (retval
goto fail;
/* success, speed is known */
retval = -ENODEV;
//在裝置之前的設速已經確定的情況下
//如果裝置的速度發生了改變,肯定是發生了錯誤
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
dev_dbg(&udev->dev, "device reset changed speed!\n");
goto fail;
}
oldspeed = udev->speed;
首先為埠重置選擇一個合適的延時,即在這個延時過後,埠的Reset應該完成了.usb2.0 spec上規定,root hub的延時值是50ms,高速裝置是10ms,而低速裝置是100ms.從程式碼上看,這個延時都是從udev引數中來的,這個引數就是表示在埠上連線的裝置.其實,所謂的Reset埠,就是Reset埠上連線的裝置.
由於我們現在要對這個裝置進行配置,因此,先將它復原成初始值.
另外,如果重置之後,裝置的speed發生了變化,這肯定是錯誤的.
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
* it's fixed size except for full speed devices.
* For Wireless USB devices, ep0 max packet is always 512 (tho
* reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
*/
switch (udev->speed) {
case USB_SPEED_VARIABLE: /* fixed at 512 */
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
break;
case USB_SPEED_HIGH: /* fixed at 64 */
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
break;
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
/* to determine the ep0 maxpacket size, try to read
* the device descriptor to get bMaxPacketSize0 and
* then correct our initial guess.
*/
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
break;
case USB_SPEED_LOW: /* fixed at 8 */
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8);
break;
default:
goto fail;
}
根據裝置的speed來設定ep0的MaxPacketSize.這個只是spec上規定的值.另外對於Full Speed的設來說,它的MaxPacketSize有四種情況,即8.16.32和64實際的值要在裝置描述符的bMaxPacketSize0欄位才能知道.
type = "";
switch (udev->speed) {
case USB_SPEED_LOW: speed = "low"; break;
case USB_SPEED_FULL: speed = "full"; break;
case USB_SPEED_HIGH: speed = "high"; break;
case USB_SPEED_VARIABLE:
speed = "variable";
type = "Wireless ";
break;
default: speed = "?"; break;
}
dev_info (&udev->dev,
"%s %s speed %sUSB device using %s and address %d\n",
(udev->config) ? "reset" : "new", speed, type,
udev->bus->controller->driver->name, devnum);
這段程式碼無關緊要,只是打印出了一個Debug資訊,
/* Set up TT records, if needed */
if (hdev->tt) {
udev->tt = hdev->tt;
udev->ttport = hdev->ttport;
} else if (udev->speed != USB_SPEED_HIGH
&& hdev->speed == USB_SPEED_HIGH) {
udev->tt = &hub->tt;
udev->ttport = port1;
}
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
* Change it cautiously.
*
* NOTE: If USE_NEW_SCHEME() is true we will start by issuing
* a 64-byte GET_DESCRIPTOR request. This is what Windows does,
* so it may help with some non-standards-compliant devices.
* Otherwise we start with SET_ADDRESS and then try to read the
* first 8 bytes of the device descriptor to get the ep0 maxpacket
* value.
*/
for (i = 0; i
if (USE_NEW_SCHEME(retry_counter)) {
struct usb_device_descriptor *buf;
int r = 0;
#define GET_DESCRIPTOR_BUFSIZE 64
buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
if (!buf) {
retval = -ENOMEM;
continue;
}
/* Retry on all errors; some devices are flakey.
* 255 is for WUSB devices, we actually need to use
* 512 (WUSB1.0[4.8.1]).
*/
for (j = 0; j
buf->bMaxPacketSize0 = 0;
r = usb_control_msg(udev, usb_rcvaddr0pipe(),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
USB_DT_DEVICE
buf, GET_DESCRIPTOR_BUFSIZE,
USB_CTRL_GET_TIMEOUT);
switch (buf->bMaxPacketSize0) {
case 8: case 16: case 32: case 64: case 255:
if (buf->bDescriptorType ==
USB_DT_DEVICE) {
r = 0;
break;
}
/* FALL THROUGH */
default:
if (r == 0)
r = -EPROTO;
break;
}
if (r == 0)
break;
}
udev->descriptor.bMaxPacketSize0 =
buf->bMaxPacketSize0;
kfree(buf);
retval = hub_port_reset(hub, port1, udev, delay);
if (retval
goto fail;
if (oldspeed != udev->speed) {
dev_dbg(&udev->dev,
"device reset changed speed!\n");
retval = -ENODEV;
goto fail;
}
if (r) {
dev_err(&udev->dev, "device descriptor "
"read/%s, error %d\n",
"64", r);
retval = -EMSGSIZE;
continue;
}
#undef GET_DESCRIPTOR_BUFSIZE
}
for (j = 0; j
retval = hub_set_address(udev, devnum);
if (retval >= 0)
break;
msleep(200);
}
if (retval
dev_err(&udev->dev,
"device not accepting address %d, error %d\n",
devnum, retval);
goto fail;
}
/* cope with hardware quirkiness:
* - let SET_ADDRESS settle, some device hardware wants it
* - read ep0 maxpacket even for high and low speed,
*/
msleep(10);
if (USE_NEW_SCHEME(retry_counter))
break;
retval = usb_get_device_descriptor(udev, 8);
if (retval
dev_err(&udev->dev, "device descriptor "
"read/%s, error %d\n",
"8", retval);
if (retval >= 0)
retval = -EMSGSIZE;
} else {
retval = 0;
break;
}
}
這個for迴圈是一個很重要的操作,首先,我們來看一下USE_NEW_SCHEME巨集的定義.如下示:
((i) / 2 == old_scheme_first), old_scheme_first預設為0,也就是說,當i為0,1的時候,這個巨集會返回1.那就是說,對於之前分析的兩種機制,每種機制嘗試兩次.
區分一下這兩種機制的不同:
對於第一種機制,它先用64的buffer去取裝置描述符.而第二種機制,是以長度8的快取區,取裝置描述符的前半部份.
另外,第一種機制,去取裝置描述符之前沒有設定裝置的地址,因此使用地址0來表示裝置的地址,在程式碼中,用usb_rcvaddr0pipe()表示.而在第二種機制中,它在取裝置描述符之前已經設定了裝置的地址.
疑問:可能有人就有這樣的疑問,既然地址0可以表示沒有設定地址的裝置地址,那如果有多個沒有set address的裝置,這個地址0到底是表示那個裝置呢?
實際上,從程式碼上看,Linux是每開啟一個hub的埠就初始連在這個埠上的裝置.之後這連線上的裝置設定好地址之後再開啟hub的另外的埠進行配置,因此,在同一條usb bus上,不會出現多個末配置的活動裝置.
if (retval)
goto fail;
i = udev->descriptor.bMaxPacketSize0 == 0xff?
512 : udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed != USB_SPEED_FULL ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
dev_err(&udev->dev, "ep0 maxpacket = %d\n", i);
retval = -EMSGSIZE;
goto fail;
}
dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
ep0_reinit(udev);
}
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval descriptor)) {
dev_err(&udev->dev, "device descriptor read/%s, error %d\n",
"all", retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
}
retval = 0;
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
udev->devnum = devnum; /* for disconnect processing */
}
mutex_unlock(&usb_address0_mutex);
return retval;
}
在上面獲得的裝置描述符的bMaxPacketSize0欄位,也就是ep0的MaxPacketSize.但如果這個值不和我們之前根據spec為ep0設定的MaxPacketSize值相等,且不是Full speed的話,就會有錯誤了.因為只有Full Speed的裝置的ep0 的MaxPacketSize在spec上並沒有一個明確的定義值.
有了確定的ep0 的MaxPacketSize值,就可以取得完整的裝置描述符了.
第四個要分析的函式是hub_port_reset().
這個函式將埠重置並等待埠重置完成.程式碼如下:
static int hub_port_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay)
{
int i, status;
/* Block EHCI CF initialization during the port reset.
* Some companion controllers don't like it when they mix.
*/
down_read(&ehci_cf_port_reset_rwsem);
/* Reset the port */
//嘗試5次
for (i = 0; i
//傳送Reset 的Set_Feature
status = set_port_feature(hub->hdev,
port1, USB_PORT_FEAT_RESET);
//傳送錯誤
if (status)
dev_err(hub->intfdev,
"cannot reset port %d (err = %d)\n",
port1, status);
else {
//傳送Clear_Feature成功,等待埠重置完成
status = hub_port_wait_reset(hub, port1, udev, delay);
if (status && status != -ENOTCONN)
dev_dbg(hub->intfdev,
"port_wait_reset: err = %d\n",
status);
}
/* return on disconnect or reset */
switch (status) {
//成功
case 0:
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
udev->devnum = 0; /* Device now at address 0 */
/* FALL THROUGH */
//埠沒有連線
case -ENOTCONN:
//要傳送的裝置不存在
case -ENODEV:
clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_C_RESET);
/* FIXME need disconnect() for NOTATTACHED device */
usb_set_device_state(udev, status
? USB_STATE_NOTATTACHED
: USB_STATE_DEFAULT);
goto done;
}
dev_dbg (hub->intfdev,
"port %d not enabled, trying reset again...\n",
port1);
//將延遲設至最長,再試一次
delay = HUB_LONG_RESET_TIME;
}
dev_err (hub->intfdev,
"Cannot enable port %i. Maybe the USB cable is bad?\n",
port1);
done:
up_read(&ehci_cf_port_reset_rwsem);
return status;
}
這個函式的程式碼看清淅,首先將埠重置,然後等待埠重置完成.在成功返回或者是發錯致命錯誤的時候就會在清除掉RESET Feature,設定裝置狀態之後返回.這個所謂的致命包括:
1:傳送Clear_Feature時,返回-ENODEV,表示裝置不存在
2:在hub_port_wait_reset()後返回的-ENOTCONN,表示埠上末連線裝置.
另外,在這裡哆嗦的重複一句,只有在裝置有這個Feature的時候,才能Clear_Feature.在上面的程式碼中,只有程式碼中,如果Reset不成功,是不需要Clear USB_PORT_FEAT_C_RESET 這個Feature的.只有在已經設定成功的情況,才能將其Clear(-ENODEV的情況,無所謂,這個錯誤在submit urb前期就能測檢出來,不會跟硬體互動,而-ENOTCONN則表示埠Reset完成,但尚末檢測到連線裝置,這種情況下,也是需要Clear_Feature的).
另外,裡面還呼叫了一個子函式, hub_port_wait_reset().程式碼如下:
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay)
{
int delay_time, ret;
u16 portstatus;
u16 portchange;
//最長等待時間是500
for (delay_time = 0;
delay_time
delay_time += delay) {
/* wait to give the device a chance to reset */
msleep(delay);
/* read and decode port status */
ret = hub_port_status(hub, port1, &portstatus, &portchange);
if (ret
return ret;
/* Device went away? */
//埠已經沒有連線了,說明連線的裝置在某個時刻被撥下來了
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return -ENOTCONN;
/* bomb out completely if the connection bounced */
//連線狀態發生了改變,則說明連線狀態不穩定.因為斷開之後,再聯上是需要重新配置的
//退出
if ((portchange & USB_PORT_STAT_C_CONNECTION))
return -ENOTCONN;
/* if we`ve finished resetting, then break out of the loop */
//如果Reset已經完成,且埠處於enable狀態,設定speed成員就可以返回了
if (!(portstatus & USB_PORT_STAT_RESET) &&
(portstatus & USB_PORT_STAT_ENABLE)) {
if (hub_is_wusb(hub))
udev->speed = USB_SPEED_VARIABLE;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
else
udev->speed = USB_SPEED_FULL;
return 0;
}
/* switch to the long delay after two short delay failures */
//失敗兩次,將延時時間設為最長的時間
if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
delay = HUB_LONG_RESET_TIME;
dev_dbg (hub->intfdev,
"port %d not reset yet, waiting %dms\n",
port1, delay);
}
return -EBUSY;
}
注意到在上面為speed成員賦值的時候,出現了一個hub_is_wusb().該巨集用來判斷hcd是否是一個無線的USB主機控制器.如果hcd 是一個無線的,那其下的所有裝置的speed均為USB_SPEED_VARIABLE.這個是屬於usb2.5 spec裡面定義的.
到這裡,hub_thread()函式已經分析完了.它已經將hub下連線的所有新裝置都初始化並新增進了裝置模型.
5.2.3:HUB中斷URB傳輸完成的處理
在之前分析中斷URB初始化的時候,曾分析到,如果中斷URB傳輸完成,就會呼叫hub_irq().在分析這個函式之前,我們先從spec上了解一下,對於hub的中斷傳輸到底會傳些什麼樣的東西:
如下圖所示:
Bit0表示hub的連線狀態發生了改變,而bit1~bitN表示的是各埠連線狀態的改變.如果1表示改變,為0表示末改變.
現在可以看該函式的程式碼了,如下:
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = urb->context;
int status = urb->status;
int i;
unsigned long bits;
switch (status) {
case -ENOENT: /* synchronous unlink */
case -ECONNRESET: /* async unlink */
case -ESHUTDOWN: /* hardware going away */
return;
default: /* presumably an error */
/* Cause a hub reset after 10 consecutive errors */
dev_dbg (hub->intfdev, "transfer --> %d\n", status);
if ((++hub->nerrors error)
goto resubmit;
hub->error = status;
/* FALL THROUGH */
/* let khubd handle things */
case 0: /* we got data: port status changed */
bits = 0;
for (i = 0; i actual_length; ++i)
bits |= ((unsigned long) ((*hub->buffer)))
hub->event_bits[0] = bits;
break;
}
hub->nerrors = 0;
/* Something happened, let khubd figure it out */
kick_khubd(hub);
resubmit:
if (hub->quiescing)
return;
if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
&& status != -ENODEV && status != -EPERM)
dev_err (hub->intfdev, "resubmit --> %d\n", status);
}
從上面的程式碼可以看出,就將是設HUB中斷傳輸的資訊儲存在hub->event_bits中,然後又將此URB再次提交,再次提交的結果是,可以輪詢獲得hub的狀態,另外,還會呼叫kick_khubd().這樣, hub_events()就又會呼叫,又可以處理HUB埠的狀態改變.
六:小結
在本小結裡,對HUB的處理過程做了一個詳盡的分析,在這一節裡,也瞭解到了USB的驅動架構以及USB裝置的列舉過程.
在下一節裡,我們以特定的USB裝置分例,來分析USB驅動程式的架構.
相關推薦
Linux裝置驅動之USB hub驅動(續)
5.2.2:介面驅動中的hub_thread()函式 我們之前在分析usb_hub_init()的程式碼的時候,忽略掉了一部份. 程式碼片段如下所示: int usb_hub_init(void) { …… khubd_task = kthread_run(hub_thread, NULL, "
Linux裝置模型之tty&&uart驅動架構分析
五: uart_add_one_port()操作 在前面提到.在對uart裝置檔案過程中.會將操作轉換到對應的port上,這個port跟uart_driver是怎麼關聯起來的呢?這就是uart_add_ont_port()的主要工作了. 顧名思義,這個函式是在uart_driver增加一個port.程式碼如
linux裝置驅動之USB資料傳輸分析(之五)
也許,有人會有這樣的疑問: 對於控制傳輸,它不也是基於toggle的糾錯麼,為什麼它就不需要修改後續的包的toggle值呢? 這是因為,控制傳輸的toggle都是從1開始的,刪除掉當前的urb,也不會對後面的發包造成影響. 之後,處理完之後,將無用的td刪除. 跟
linux裝置驅動之USB主機控制器驅動分析 (一)
一:前言 Usb是一個很複雜的系統.在usb2.0規範中,將其定義成了一個分層模型.linux中的程式碼也是按照這個分層模型來設計的.具體的分為 usb裝置,hub和主機控制器三部份.在閱讀程式碼的時候,必須要參考相應的規範.最基本的就是USB2.0的spec.
Linux裝置模型之tty驅動架構分析
------------------------------------------ 本文系本站原創,歡迎轉載!轉載請註明出處:http://ericxiao.cublog.cn/------------------------------------------一:前言Tty這個名稱源於電傳打位元組的簡稱。
linux裝置模型之uart驅動架構分析
一:前言 接著前面的終端控制檯分析,接下來分析serial的驅動.在linux中,serial也對應著終端,通常被稱為串列埠終端.在shell上,我們看到的/dev/ttyS*就是串列埠終端所對應的裝置節點. 在分析具體的serial驅動之前.有必要先分析uart
Linux裝置驅動之button按鍵驅動學習與小結
button按鍵驅動,相對於前面的LED驅動來說。增加了中斷處理以及阻塞與非阻塞方式等新知識點。 先上學習的驅動程式碼。 核心:linux3.0 板子:fl2440 /***************************************************
linux驅動之USB驅動程式框架
USB驅動程式框架: app: ----------------------------------------------------------------------- USB裝置驅動程式
Linux驅動之usb滑鼠
/* * 參考drivers\hid\usbhid\usbmouse.c */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/
linux裝置模型之匯流排 裝置 和驅動
《Linux核心修煉之道》讀書筆記 1、 裝置模型的上層建築由匯流排(bus) 、裝置(device)、 驅動(device_driver)這3個數據結構構成,裝置模型表示了它們之間的連線關係。
tiny4412 裝置樹之SD卡驅動(三)
開發板:tiny4412(1611) 核心:linux4.4 編譯器:arm-none-linux-gnueabi-gcc (gcc version 4.8.3 20140320) 在linux核心中,SD卡屬於MMC子系統,簡單的介紹: http://blog.
使用USB gadget驅動測試USB晶片驅動功能
在核心中使能Mass Storage Gadget ,如下圖,配置路徑是Linux/arm64 4.14.0 Kernel Configuration → Device Drivers → USB support → USB Gadget Support ->Mass Stor
第18章 ARM Linux裝置樹之四(常用的OF API)
18.4 常用的OF API除了前文介紹的of_machine_is_compatible()、of_device_is_compatible()等常用函式以外,在Linux的BSP和驅動程式碼中,經常會使用到一些Linux中其他裝置樹的API,這些API通常被冠以of_字首
linux裝置模型之I2C子系統
=============================== 本文系本站原創,歡迎轉載! 轉載請註明出處:http://blog.csdn.net/gdt_a20 =============================== I2c子系統將i2c控制器(
linux裝置模型之spi子系統
=============================== 本文系本站原創,歡迎轉載! 轉載請註明出處:http://www.cnblogs.com/gdt-a20 =============================== 相比於前面介紹的i2c子系統,spi子系
linux裝置模型之mmc,sd子系統
struct mmc_host { 171 struct device *parent; 172 struct device class_dev; 173 int
linux裝置樹之gpio
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> #include &
SPI驅動之主控制器驅動程式
嵌入式微處理器訪問SPI裝置有兩種方式:使用GPIO模擬SPI介面的工作時序或者使用SPI控制器。使用GPIO模擬SPI介面的工作時序是非常容易實現的,但是會導致大量的時間耗費在模擬SPI介面的時序上,訪問效率比較低,容易成為系統瓶頸。這裡主要分析使用SPI控制器的情
linux裝置模型之bus,device,driver分析二
=============================== 本文系本站原創,歡迎轉載! 轉載請註明出處:http://blog.csdn.net/gdt_a20 =============================== 上篇分析了bus,driver的註冊過程
linux裝置模型之Class
參考:http://www.wowotech.net/device_model/class.html 剛開始寫字元裝置驅動程式的時候,老師教我們自動建立裝置節點,“要先建立類,在類下面建立裝置,類名字不重要“。 firstdrv_class