usb声卡驱动(一):USB描述符
usb聲卡驅動(一)
前面看了內核的啟動,接下來就是驅動的學習。
正好手邊有一個USB聲卡,就準備以此為基礎,進行usb聲卡驅動的學習。
因此,在學些usb聲卡之前,先看看usb驅動。然后再是alsa驅動,然后再是兩者的結合
usb的關鍵數據結構
任何usb設備,都有一段數據,用來描述自己。比如自己有什么功能,自己的廠商ID是多少等等
有個組織,定義了這段數據的組織形式和意義,這段數據稱為USB描述符。這個組織叫USB-IF(USB Implementers Forum)
USB描述符的組織形式
usb描述符,邏輯上分成三個層級:配置,接口,端點
一個usb設備描述符,可能包含多個配置,一個配置可能包含多個接口,一個接口可能包含多個端點。
上述每一個邏輯體,在linux中都有一個數據結構與之對應。
配置描述符:
struct usb_config_descriptor {__u8 bLength;//描述符的長度__u8 bDescriptorType;//描述符的類型,有兩種值:USB_DT_CONFIG,USB_DT_OTHER_SPEED_CONFIG (表示高速設備操作在低速或者全速模式時的配置信息)__le16 wTotalLength;//所有描述符的總長度__u8 bNumInterfaces;//配置包含的接口數目__u8 bConfigurationValue;//表示這個配置的一個數字。使用該值,調用SET_CONFIGURATION請求來設置此配置為當前配置__u8 iConfiguration;//描述配置信息的字符串描述符的索引值__u8 bmAttributes;//表示配置的一些特點,如bit6為1表示self-power;bit5為1表示支持遠程喚醒__u8 bMaxPower;//設備正常運作時,從hub分得的最大電流,單位2mA。//那么當設備請求的電流,大于hub能給予的最大值時,hub就會直接拒絕//而保存hub當前能給出的最大電流保存在struct usb_device的bus_mA中。 } __attribute__ ((packed));接口描述符:
struct usb_interface_descriptor {__u8 bLength;//該描述符的長度__u8 bDescriptorType;//描述符的類型__u8 bInterfaceNumber;//每個配置里面可以包含多個接口,這個值可以表示對應的接口索引號__u8 bAlternateSetting;//接口使用的可選設置號。默認為0__u8 bNumEndpoints;//接口擁有的端點個數,不包括0端點(后續說明原因)//下面三個,用于表示這個接口,具備的功能。由于各個功能其實有很多共同點,因此將其抽象出三個層級:class,subclass,protocol.//同一個class下面,可以有很多subclass,同一個subclass下,也可以根據protocol的不同,分成很多的設備__u8 bInterfaceClass;__u8 bInterfaceSubClass;__u8 bInterfaceProtocol;__u8 iInterface;//接口對應的字符串描述符的索引值 } __attribute__ ((packed));端點描述符:
struct usb_endpoint_descriptor {__u8 bLength;//描述符的長度__u8 bDescriptorType;//端點的類型__u8 bEndpointAddress;//該字段含有,端點方向信息,地址信息,端點號信息__u8 bmAttributes;//bit1,bit0 表示傳輸類型:00控制類型,01登時,10批量,11中斷__le16 wMaxPacketSize;//端點一次處理的最大字節數__u8 bInterval;//希望主機輪詢自己的時間間隔。/* NOTE: these two are _only_ in audio endpoints. *//* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. *///下面兩個字段,用于在音頻設備中,主要用于等時同步端點,會在后面介紹audio協議的時候,引入介紹__u8 bRefresh;__u8 bSynchAddress; } __attribute__ ((packed));除了上面的三個描述以外,還有一個重要的描述,那就是設備描述符:
struct usb_device_descriptor {__u8 bLength;//描述符的長度__u8 bDescriptorType;//描述符的類型__le16 bcdUSB;//USB spc的版本號//和接口描述符中的class,subclass,protocol意義類似__u8 bDeviceClass;__u8 bDeviceSubClass;__u8 bDeviceProtocol;__u8 bMaxPacketSize0;//端點0一次可以處理的最大字節數.因為端點0沒有對應的描述符,所以,將端點0的相關信息,放在了設備描述符中__le16 idVendor;//廠商ID__le16 idProduct;//產品ID__le16 bcdDevice;//設備版本號__u8 iManufacturer;//制造商對應的字符串描述符的索引值__u8 iProduct;//產品對應的字符串描述符的索引值__u8 iSerialNumber;//序列號對應的字符串描述符的索引值__u8 bNumConfigurations;//該設備當前速度模式下的配置數量 } __attribute__ ((packed));現在我們知道了usb設備大概有哪些信息,接下來查看一下,應該怎么才能獲取到上面的這些
USB描述符的獲取
當一個新的設備,插入USB 總線后,這些信息該如何獲取呢?
第一步:如何檢測到硬件已經被插入
常規的usb接口,有四根線,gnd線,vcc線,D+線,D-線。在hub端D+,D-分別接了一個15k的下拉電阻到地。
而在設備端,D+或者D-端接了一個1.5k的上拉電阻。低速設備接在D-端。高速和全速設備接在D+端。
當設備插入hub時,hub能通過D+,D-上面的變化來區分設備的類型。
第二步:如何區分全速設備和高速設備
對于全速設備和高速設備而言,他們的上拉電阻都接在了D+端。為了進一步區分這兩種設備。需要進行一定的通信。
為了簡化通信的細節,現在大致描述如下:
當設備進入復位狀態之后,設備持續一段時間的向hub發送信號。這個信號就是給D-持續輸出17.87mA的電流。
hub因為有自己的電阻,所以,它能檢查到一定的電壓,大概為800mV
hub在檢查到持續了一段時間的800mV的電壓之后,就知道,哦,原來這是一個高速設備。
hub在接下來100us內,進行響應。告訴設備,我已經知道你是高速設備了,并且我也切換到了高速模式下了。
設備在接受到hub的響應之后,就將自己切換到高速設備的電路上。
上面所述的5個步驟,在電氣信號上面的詳細描述,可以參考:
https://blog.csdn.net/flydream0/article/details/71512852
第三步:獲取設備描述符
現在,設備已經連上,接下來,就是獲取設備的信息(設備描述符)。但是在此之前,需要明白一個東西:那就是usb的數據包,到底是怎么組織的。
usb數據包的組織
我們都知道電信號只能傳遞0和1的邏輯值。在usb的世界里,將這些0和1排列組合,組成7種基本的信息,稱為域,分別叫做:同步域(SYNC),標識域(PID),地址域(ADDR),端點域(ENDP),幀號域(FRAME),數據域(DATA),校驗域(CRC)
在這些域的上層,則定義4種包:令牌包,數據包,握手包,特殊包。這些包都是由上面介紹的域組合而成。
令牌包有四種,分別為:輸入,輸出,設置,幀起始。前面三種的域的組成情況一樣,為SYNC+PID+ADDR+ENDP+CRC.第四種的域組成為,SYNC+PID+FRAME+CRC
數據包有兩種,分別為:DATA0,DATA1。他們的域組成一樣都為:SYNC+PID+DATA+CRC
握手包,只有一種,它的域組成為:SYNC+PID
現在有了這些包之后,我們使用這4種包來定義各種不同的事務。目前就定義了3種事務,稱為:IN事務,OUT事務,SETUP事務。
每種事務,都由三個階段組成:令牌包階段,數據包階段,握手包階段
令牌包階段:啟動輸入、輸出、設置事務
數據包階段:按照輸入、輸出發送相應的數據
握手包階段:返回數據的接收情況
現在知道了3種事務之后,就可以使用這3中事務,進行傳輸了,傳輸也分成了4種:中斷傳輸,批量傳輸,同步傳輸,控制傳輸
中斷傳輸:由OUT事務和IN事務構成
批量傳輸:由OUT事務和IN事務構成
同步傳輸:由OUT事務和IN事務構成
控制傳輸:由SETUP事務,(OUT事務,IN事務)構成
現在知道了上面的東西,就可以進一步知道,怎么獲取設備描述符了。
注意:上面只是介紹了各個數據包的組成成分,對于這些成分的具體二進制值沒有介紹。因為我關心的是整個過程的理解,而不是具體的二進制是什么樣子的。如果需要查看具體的二進制,可以參考對應的文章
在設備才連上hub時,此時設備還處于一種默認的狀態,它沒有地址,為了能夠響應主機發出的請求。它將地址0作為默認地址。
那么獲取設備描述的過程,大致描述如下:
第一階段
首先使用一個Get_Descriptor這個請求。這個請求使用的是SETUP事務,它由令牌包,數據包,握手包組成.
然后再次發一個數據輸入的請求。這個請求使用的是IN事務,它同樣由令牌包,數據包,握手包組成。
然后再次發送一個數據輸出請求——用于通知設備,Get_Descriptor請求的狀態。這次使用的是OUT事務,它同樣有三個階段
現在主機已經擁有了usb設備的描述符信息。
第二階段
已經知道了部分數據之后,需要為該設備分配地址。
發出一個Set_Address請求。這個請求跟第一階段的第一步幾乎一樣,使用的是SETUP事務
本次的數據為地址,但是地址已經放在了Set_Address請求中了。所以不需要傳輸數據。
為了獲得Set_Address請求是否成功,需要接受設備的響應。因此發送一個數據輸入請求,即跟第一階段的第二步一樣。
第三階段
當一切正常之后,就使用新的地址,重新獲取設備描述符。獲取設備描述符的過程見第一階段,長度稍有不同,不過不影響整個過程的理解
除了設備描述符以外,還需要獲取其他的描述符。如配置描述符,接口描述符,端點描述符,字符串描述符等。
根據這些描述符的內容,選擇不同的驅動程序
至此,整個設備才算是完完全全的能被使用了。
注意,注意:上面所有的步驟,建立在兩個前提下。1.默認所有的操作都正常;2.描述過程時使用的是數據包中的更加抽象的語言。并沒有引入具體的二進制數值
本篇相當于,usb設備的一個枚舉過程。
接下來一篇,用于說明,音頻設備描述符的相關細節
總結
以上是生活随笔為你收集整理的usb声卡驱动(一):USB描述符的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 创建线程thread_初学Ja
- 下一篇: 网络违法信息巡查上报系统