一、前言
不知道大家在學(xué)習(xí)Linux的時候有沒有過這樣的疑問,為什么我們通過同一個接口接入的不同USB設(shè)備,我們的電腦都可以識別到呢?為什么Linux電腦不會把鼠標(biāo)識別成鍵盤呢?帶著這些疑問我們一起來看一下USB的識別和加載過程。
二、USB設(shè)備的識別過程
當(dāng)我們插入一個USB設(shè)備時,Linux內(nèi)核會自動檢測并加載相應(yīng)的驅(qū)動程序,使設(shè)備能夠正常工作。下面我們將深入探討USB設(shè)備在Linux系統(tǒng)中的識別和加載過程。
USB控制器是一個硬件設(shè)備,用于控制USB總線上的設(shè)備。當(dāng)你插入一個USB設(shè)備時,USB控制器會檢測到電壓變化并發(fā)出一個中斷信號。這個中斷信號被送到處理器上的USB控制器中斷線上,告訴Linux內(nèi)核有新的USB設(shè)備插入。
2.2 內(nèi)核檢測并加載驅(qū)動程序
當(dāng)內(nèi)核接收到USB控制器發(fā)出的中斷信號時,它會調(diào)用USB子系統(tǒng)中的usbcore模塊,該模塊負(fù)責(zé)檢測新的USB設(shè)備并加載相應(yīng)的驅(qū)動程序。usbcore模塊首先會檢測設(shè)備的描述符,這個描述符包括設(shè)備的廠商ID、產(chǎn)品ID、類別碼等信息。
如果已經(jīng)存在一個匹配的驅(qū)動程序,那么usbcore模塊就會加載這個驅(qū)動程序。如果沒有匹配的驅(qū)動程序,則會嘗試加載一個通用的驅(qū)動程序,這個驅(qū)動程序能夠支持大多數(shù)USB設(shè)備。
2.3 驅(qū)動程序向USB子系統(tǒng)注冊
一旦正確的驅(qū)動程序被加載,它會向USB子系統(tǒng)注冊并告訴它自己可以處理哪些設(shè)備。這一步通常包括向內(nèi)核注冊USB設(shè)備的類別(如存儲設(shè)備、輸入設(shè)備等)。
這個過程包括了向內(nèi)核注冊一個新的USB設(shè)備驅(qū)動程序,并在該驅(qū)動程序中指定設(shè)備的廠商ID、產(chǎn)品ID等信息。一旦驅(qū)動程序被成功注冊,USB子系統(tǒng)就可以將設(shè)備與正確的驅(qū)動程序進(jìn)行匹配。
2.4 USB子系統(tǒng)創(chuàng)建設(shè)備節(jié)點
USB子系統(tǒng)接下來會為設(shè)備創(chuàng)建一個設(shè)備節(jié)點。設(shè)備節(jié)點是一個特殊的文件,在/dev目錄下,它允許用戶空間程序與設(shè)備通信。設(shè)備節(jié)點的名稱通常是由內(nèi)核根據(jù)設(shè)備的廠商ID、產(chǎn)品ID和序列號等信息動態(tài)生成的。
設(shè)備節(jié)點的創(chuàng)建是通過udev守護(hù)進(jìn)程實現(xiàn)的,這個守護(hù)進(jìn)程會監(jiān)視系統(tǒng)中的設(shè)備插拔事件,并自動創(chuàng)建或刪除相應(yīng)的設(shè)備節(jié)點。創(chuàng)建設(shè)備節(jié)點之后,內(nèi)核就可以將設(shè)備的訪問權(quán)限分配給用戶空間程序。
2.5 驅(qū)動程序初始化設(shè)備
驅(qū)動程序被通知有新的設(shè)備插入后,它會對設(shè)備進(jìn)行初始化。初始化可能包括設(shè)置設(shè)備的傳輸速率、分配內(nèi)存緩沖區(qū)等。設(shè)備初始化完成后,驅(qū)動程序會向USB子系統(tǒng)報告設(shè)備已準(zhǔn)備好。
2.6 用戶空間程序打開設(shè)備:
最后,用戶空間程序可以打開設(shè)備節(jié)點并與設(shè)備通信。設(shè)備節(jié)點的權(quán)限通常被設(shè)置為只允許root用戶或在相關(guān)組中的用戶訪問。用戶空間程序可以使用系統(tǒng)調(diào)用(如read和write)向設(shè)備發(fā)送命令和接收數(shù)據(jù)。
通過這個過程,Linux系統(tǒng)可以自動識別設(shè)備并加載相應(yīng)的驅(qū)動程序,使設(shè)備可以正常工作。這也是為什么當(dāng)我們插入一個USB設(shè)備時,我們不需要手動安裝任何驅(qū)動程序或執(zhí)行任何其他操作就可以直接開始使用設(shè)備。
當(dāng)你插入一個USB設(shè)備時,Linux系統(tǒng)會自動執(zhí)行上述步驟,從而自動識別設(shè)備并加載相應(yīng)的驅(qū)動程序,使設(shè)備可以正常工作。下面我們從代碼的層面來分析一下該過程。
三、代碼實現(xiàn)講解
下面我通過一些示例代碼,講解一下USB設(shè)備在Linux系統(tǒng)中的識別和加載過程。這些示例代碼只是講解一下原理,實際代碼將會更加復(fù)雜。
3.1 檢測設(shè)備插入
當(dāng)USB設(shè)備插入到系統(tǒng)中時,會產(chǎn)生一個中斷信號,這個信號會被處理器上的USB控制器中斷線捕獲,并由內(nèi)核的USB子系統(tǒng)處理。下面是一個示例代碼,演示如何檢測USB設(shè)備的插入和拔出事件:
#include#include intmain(){ libusb_device**devs; libusb_context*ctx=NULL; intr=libusb_init(&ctx); if(r0)?{ ????????printf("Failed?to?initialize?libusb "); ????????return?1; ????} ????//?掃描USB總線并列出所有連接的設(shè)備 ????ssize_t?cnt?=?libusb_get_device_list(ctx,?&devs); ????if?(cnt?0)?{ ????????printf("Failed?to?get?device?list "); ????????return?1; ????} ????//?遍歷設(shè)備列表,檢測插入和拔出事件 ????for?(int?i?=?0;?i?
這段代碼使用了libusb庫,這是一個C語言庫,用于訪問USB設(shè)備。它提供了一個用于初始化USB子系統(tǒng)和掃描USB總線的API,以及用于訪問USB設(shè)備的API。
3.2 加載驅(qū)動程序
一旦檢測到設(shè)備插入,USB子系統(tǒng)會嘗試加載一個適當(dāng)?shù)尿?qū)動程序。下面是一個示例驅(qū)動程序代碼,它負(fù)責(zé)支持USB存儲設(shè)備(例如U盤):
#include#include staticstructusb_device_idstorage_devices[]={ {USB_DEVICE(0xabcd,0x1234)}, {USB_DEVICE(0xffff,0xffff)}, {} }; MODULE_DEVICE_TABLE(usb,storage_devices); staticintstorage_probe(structusb_interface*interface,conststructusb_device_id*id){ //初始化設(shè)備并注冊 return0; } staticvoidstorage_disconnect(structusb_interface*interface){ //釋放設(shè)備 } staticstructusb_driverstorage_driver={ .name="usb-storage", .probe=storage_probe, .disconnect=storage_disconnect, .id_table=storage_devices, }; module_usb_driver(storage_driver);
這段代碼演示了一個簡單的驅(qū)動程序,它可以處理USB存儲設(shè)備的插入和拔出事件。在加載驅(qū)動程序時,內(nèi)核將搜索已加載的驅(qū)動程序列表,以查找與設(shè)備匹配的驅(qū)動程序。
如果找到了匹配的驅(qū)動程序,內(nèi)核將使用該驅(qū)動程序來管理該設(shè)備。如果沒有找到匹配的驅(qū)動程序,內(nèi)核將不會加載任何驅(qū)動程序。
3.3 設(shè)備注冊
一旦找到了與設(shè)備匹配的驅(qū)動程序,驅(qū)動程序?qū)⒈患虞d并啟動,它將嘗試對設(shè)備進(jìn)行初始化,并將其注冊到內(nèi)核。下面是一個示例代碼,演示如何初始化USB存儲設(shè)備并將其注冊到內(nèi)核:
staticintstorage_probe(structusb_interface*interface,conststructusb_device_id*id){ structusb_device*dev=interface_to_usbdev(interface); //獲取設(shè)備描述符 structusb_device_descriptordesc; intr=usb_get_descriptor(dev,USB_DT_DEVICE,0,&desc,sizeof(desc)); if(r0)?{ ????????printk(KERN_ERR?"Failed?to?get?device?descriptor "); ????????return?r; ????} ????//?打印設(shè)備信息 ????printk(KERN_INFO?"USB?storage?device?detected:?Vendor?ID=0x%04x,?Product?ID=0x%04x ",?desc.idVendor,?desc.idProduct); ????//?初始化設(shè)備并注冊到內(nèi)核 ????//?... ????return?0; }
上面這段示例代碼使用了內(nèi)核的usb_get_descriptor()函數(shù)來獲取設(shè)備描述符,并使用printk()函數(shù)將設(shè)備信息記錄到內(nèi)核日志中。
當(dāng)驅(qū)動程序?qū)⒄{(diào)用設(shè)備初始化函數(shù)并將其注冊到內(nèi)核,但是由于設(shè)備初始化和注冊的過程因設(shè)備而異,因此這里省略了這部分代碼。
3.4 設(shè)備訪問
一旦設(shè)備已經(jīng)被注冊到內(nèi)核,用戶空間程序就可以通過設(shè)備節(jié)點來訪問設(shè)備。在Linux系統(tǒng)中,設(shè)備節(jié)點是一種特殊的文件,可以通過標(biāo)準(zhǔn)文件I/O函數(shù)來訪問。下面是一個示例代碼,演示如何打開并讀取USB存儲設(shè)備:
#include#include #include intmain(){ //打開設(shè)備節(jié)點 intfd=open("/dev/sdb",O_RDONLY); if(fd0)?{ ????????printf("Failed?to?open?device "); ????????return?1; ????} ????//?讀取設(shè)備數(shù)據(jù) ????char?buf[1024]; ????ssize_t?n?=?read(fd,?buf,?sizeof(buf)); ????if?(n?0)?{ ????????printf("Failed?to?read?device "); ????????close(fd); ????????return?1; ????} ????//?關(guān)閉設(shè)備節(jié)點 ????close(fd); ????return?0; }
這段代碼使用了標(biāo)準(zhǔn)的文件I/O函數(shù)來訪問設(shè)備節(jié)點。在這個例子中,設(shè)備節(jié)點的路徑是/dev/sdb,這是一個典型的USB存儲設(shè)備節(jié)點。接下來,程序?qū)⒃O(shè)備節(jié)點作為文件打開,并使用read()函數(shù)從設(shè)備中讀取數(shù)據(jù)。一旦完成數(shù)據(jù)的讀取,程序?qū)㈥P(guān)閉設(shè)備節(jié)點并退出。
四、結(jié)語
Linux系統(tǒng)識別USB設(shè)備的過程可以分為四個步驟:設(shè)備連接、驅(qū)動匹配、設(shè)備注冊和設(shè)備訪問。當(dāng)用戶將USB設(shè)備插入計算機(jī)時,內(nèi)核將通過USB總線來檢測設(shè)備的插入事件,并嘗試查找與設(shè)備匹配的驅(qū)動程序。一旦找到了匹配的驅(qū)動程序,驅(qū)動程序?qū)⒈患虞d并啟動,它將嘗試對設(shè)備進(jìn)行初始化,并將其注冊到內(nèi)核。一旦設(shè)備已經(jīng)被注冊到內(nèi)核,用戶空間程序就可以通過設(shè)備節(jié)點來訪問設(shè)備。
在Linux系統(tǒng)中,驅(qū)動程序是非常重要的組成部分,它們負(fù)責(zé)管理和控制系統(tǒng)中的各種設(shè)備。對于USB設(shè)備而言,內(nèi)核提供了一個通用的USB驅(qū)動框架,它可以自動檢測和加載驅(qū)動程序,并為用戶提供了一個簡單而強(qiáng)大的USB設(shè)備訪問接口。通過深入理解USB驅(qū)動程序的工作原理,我們可以更好地理解Linux系統(tǒng)中設(shè)備管理的內(nèi)部機(jī)制,這對于開發(fā)和調(diào)試設(shè)備驅(qū)動程序非常有幫助。
審核編輯:湯梓紅
-
控制器
+關(guān)注
關(guān)注
112文章
16332瀏覽量
177803 -
usb
+關(guān)注
關(guān)注
60文章
7936瀏覽量
264462 -
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209322 -
Linux系統(tǒng)
+關(guān)注
關(guān)注
4文章
593瀏覽量
27392 -
電腦
+關(guān)注
關(guān)注
15文章
1692瀏覽量
68780
發(fā)布評論請先 登錄
相關(guān)推薦
評論