寫在前面:
前兩天拿到一個產品,也就是一speakerphone;以前自己也參加過音頻類USB設備的研發,當然只是單獨的speaker或microphone,也曾經想過怎麼讓同一USB設備即作speaker輸出又作microphone輸入,只不過沒有具體去實現。當拿到這個speakerphone後就有一種去實現這樣一個USB設備的衝動,於是就有了這篇文章.......
先給出完整的usb描述符供參考:
const unsigned char Demo_DeviceDescriptor[] =
{
//Device:USB1.10,Vid=0x0435,Pid=0x2430,bNumConfigurations = 0x01,
0x12, //Length
0x01, //DescriptorType
0x10,0x01, //bcdUSB
0x00, //DeviceClass
0x00, //DeviceSubClass
0x00, //DeviceProtocol
0x08, //bMaxPacketSize 8
0x35,0x04, //idVendor........
0x30,0x24, //idProduct........
0x01,0x00, //bcdDevice
1, //iManufacturer
2, //iProduct
3, //iSerialNumber
0x01 //bNumConfigurations
};
/* USB Configuration Descriptor */
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
const unsigned char Demo_ConfigDescriptor[] =
{
//Configuration:wTotalLength = 0x00be,NumInterfaces = 0x03,
0x09, //Length
0x02, //DescriptorType : ConfigDescriptor
0xbe,0x00, //TotalLength:0x00be
0x03, //NumInterfaces:3
0x01, //ConfigurationValue
0x00, //Configuration String
0x80, //Attributes:Bus Power
0xfa, //MaxPower = 0xfa*2ma
//standard interface AC descriptor(Interface 0, Alternate Setting 0):
//bNumEndpoints = 0x00,bInterFaceClass = 0x01(audio),bInterfaceSubClass = 0x01(audio ctl),
0x09, //Length
0x04, //DescriptorType:Inerface
0x00, //InterfaceNum:0
0x00, //AlternateSetting:0
0x00, //NumEndpoint:0
0x01, //InterfaceClass:audio
0x01, //InterfaceSubClass:audio ctl
0x00, //InterfaceProtocol
0x00, //Interface String
//class-specific AC interface descriptor,audio interface(0x24),audio control header(0x01),
//Total Length 0x0048,Number of streaming interface 2,interfaceNr 2,1
0x0a, //Length
0x24, //DescriptorType:audio interface descriptor
0x01, //DescriptorSubType:audio control header
0x00,0x01, //bcdADC:audio Device Class v1.00
0x48,0x00, //TotalLength:0x0048
0x02, //InCollection:2 AudioStreaming interface
0x02, //InterfaceNr(2) - AS #1 id AudioStreaming interface 2 belongs to this AudioControl interface
0x01, //InterfaceNr(1) - AS #2 id AudioStreaming interface 1 belongs to this AudioControl interface
//USB Microphone IT:audio interface descriptor,audio control input terminal(0x02),terminal id 0x01,
//Microphone(0x0201),Input Terminal(0x02),2 channel:Left Front,Right Front
0x0c, //Length
0x24, //DescriptorType:audio interface descriptor
0x02, //DescriptorSubType:Input Terminal
0x01, //TerminalID:0x01
0x01,0x02, //TerminalType:USB Microphone
0x00, //AssocTerminal
0x02, //NrChannels:2 channel
0x03,0x00, //ChannelConfig:Left Front,Right Front,
0x00, //ChannelName String
0x00, //Terminal String
//Audio Feature Unit Descriptor:audio interface descriptor,feature_unit(0x06),terminal id 0x02,
//SourceId 0x01,ControlSize 0x01,Mute,Volume,
0x0a, //Length
0x24, //DescriptorType:audio interface descriptor
0x06, //DescriptorSubType:Audio Feature Unit
0x02, //UnitID:0x02
0x01, //SourceID:1 #Microphone IT
0x01, //ControlSize:1 byte
0x01, //Controls:Mute
0x02, //Controls(0):Volume
0x02, //Controls(1):Volume
0x00, //Feature String
//USB Streaming OT:audio interface descriptor,audio control output terminal(0x03),terminal id 0x03,
//USB Streaming(0x0101),Output Terminal(0x03),SourceId 0x02,
0x09, //Length
0x24, //DescriptorType:audio interface descriptor
0x03, //DescriptorSubTYpe:Output Terminal
0x03, //TerminalID:0x03
0x01,0x01, //TerminalType:USB Streaming
0x00, //AssocTerminal:ID 0
0x02, //SourceID:2 #Feature UNIT
0x00, //Terminal String
//USB USB Streaming IT:audio interface descriptor,audio control input terminal(0x02),terminal id 0x04,
//USB Streaming(0x0101),Input Terminal(0x02),2 channel:Left Front,Right Front
0x0c, //Length
0x24, //DescriptorType:audio interface descriptor
0x02, //DescriptorSubType:Input Terminal
0x04, //TerminalID:0x04
0x01,0x01, //TerminalType:USB Streaming
0x00, //AssocTerminal
0x02, //NrChannels:2 channel
0x03,0x00, //ChannelConfig:Left Front,Right Front,
0x00, //ChannelName String
0x00, //Terminal String
//Audio Feature Unit Descriptor:audio interface descriptor,feature_unit(0x06),terminal id 0x05,
//SourceId 0x04,ControlSize 0x01,Mute,Volume,
0x0a, //Length
0x24, //DescriptorType:audio interface descriptor
0x06, //DescriptorSubType:Audio Feature Unit
0x05, //UnitID:0x05
0x04, //SourceID:4 #USB Streaming IT
0x01, //ControlSize:1 byte
0x01, //Controls:Mute,
0x02, //Controls(0):Volume
0x02, //Controls(1):Volume
0x00, //Feature String
//USB Speaker OT:audio interface descriptor,audio control output terminal(0x03),terminal id 0x06,
//USB Speaker(0x0301),Output Terminal(0x03),SourceId 0x05,
0x09, //Length
0x24, //DescriptorType:audio interface descriptor
0x03, //DescriptorSubTYpe:Output Terminal
0x06, //TerminalID:0x06
0x01,0x03, //TerminalType:Speaker
0x00, //AssocTerminal:
0x05, //SourceID:5 #Feature UNIT
0x00, //Terminal String
//-------------------Microphone interface---------------------//
//standard interface AS descriptor(Interface 1, Alternate Setting 0):
//bNumEndpoints = 0x00,bInterFaceClass = 0x01(audio),bInterfaceSubClass = 0x02(audio stream),
0x09, //Length
0x04, //DescriptorType:Interface
0x01, //InterfaceNum:1
0x00, //AlternateSetting:0
0x00, //NumEndpoint:0
0x01, //InterfaceClass:audio
0x02, //InterfaceSubClass:audio streaming
0x00, //InterfaceProtocol
0x00, //Interface String
//standard interface AS descriptor(Interface 1, Alternate Setting 1):
//bNumEndpoints = 0x01,bInterFaceClass = 0x01(audio),bInterfaceSubClass = 0x02(audio stream),
0x09, //Length
0x04, //DescriptorType:Interface
0x01, //InterfaceNum:1
0x01, //AlternateSetting:1
0x01, //NumEndpoint:1
0x01, //InterfaceClass:audio
0x02, //InterfaceSubClass:audio streaming
0x00, //InterfaceProtocol
0x00, //Interface String
//Audio Streaming Interface Descriptor:AS_GENERAL(0x01),
//TerminalLink 0x03,PCM(0x0001)
0x07, //Length
0x24, //DescriptorType:audio interface descriptor
0x01, //DescriptorSubType:AS_GENERAL
0x03, //TerminalLink:#3USB USB Streaming OT
0x01, //Delay:1
0x01,0x00, //FormatTag:PCM
//Type 1 Format type descriptor:FORMAT_TYPE(0x02),FORMAT_TYPE_I(0x01),
//physical channels 0x02,two byte per audio subframe(0x02),16bit,
//32K(0x007d00)
0x0b, //Length
0x24, //DescriptorType:audio interface descriptor
0x02, //DescriptorSubType:Format_type
0x01, //FormatType:Format type 1
0x02, //NumberOfChanne:2
0x02, //SubframeSize:2byte
0x10, //BitsResolution:16bit
0x01, //SampleFreqType:One sampling frequency.
0x00,0x7d,0x00, //32K(0x007d00)
//Endpoint 1 - Standard Descriptor:Input Endpoint1
//Isochronous,Synchronization Type(Asynchronous),MaxPacketSize 0x0084,
0x07, //Length
0x05, //DescriptorType:endpoint descriptor
0x81, //EndpointAddress:Input endpoint 1
0x05, //Attributes:0x05,Isochronous,Synchronization Type(Asynchronous).........
0x84,0x00, //MaxPacketSize:0x0084=........
0x01, //Interval
//Endpoint - Audio Streaming Descriptor:
//Audio Endpoint descriptor,General,
0x07, //Length
0x25, //DescriptorType:audio endpoint descriptor
0x01, //DescriptorSubType:audio endpiont general
0x00, //Attributes:0x00........
0x00, //LockDelayUnits
0x00,0x00, //LockDelay
//-------------------Speaker interface---------------------//
//standard interface AS descriptor(Interface 2, Alternate Setting 0):
//bNumEndpoints = 0x00,bInterFaceClass = 0x01(audio),bInterfaceSubClass = 0x02(audio stream),
0x09, //Length
0x04, //DescriptorType:Interface
0x02, //InterfaceNum:2
0x00, //AlternateSetting:0
0x00, //NumEndpoint:0
0x01, //InterfaceClass:audio
0x02, //InterfaceSubClass:audio streaming
0x00, //InterfaceProtocol
0x00, //Interface String
//standard interface AS descriptor(Interface 2, Alternate Setting 1):
//bNumEndpoints = 0x01,bInterFaceClass = 0x01(audio),bInterfaceSubClass = 0x02(audio stream),
0x09, //Length
0x04, //DescriptorType:Interface
0x02, //InterfaceNum:2
0x01, //AlternateSetting:1
0x01, //NumEndpoint:1
0x01, //InterfaceClass:audio
0x02, //InterfaceSubClass:audio streaming
0x00, //InterfaceProtocol
0x00, //Interface String
//Audio Streaming Interface Descriptor:AS_GENERAL(0x01),
//TerminalLink 0x04,PCM(0x0001)
0x07, //Length
0x24, //DescriptorType:audio interface descriptor
0x01, //DescriptorSubType:AS_GENERAL
0x04, //TerminalLink:#4 USB Streaming IT
0x01, //Delay:1
0x01,0x00, //FormatTag:PCM
//Type 1 Format type descriptor:FORMAT_TYPE(0x02),FORMAT_TYPE_I(0x01),
//physical channels 0x02,two byte per audio subframe(0x02),16bit,
//32K(0x007d00)
0x0b, //Length
0x24, //DescriptorType:audio interface descriptor
0x02, //DescriptorSubType:Format_type
0x01, //FormatType:Format type 1
0x02, //NumberOfChanne:2
0x02, //SubframeSize:2byte
0x10, //BitsResolution:16bit
0x01, //SampleFreqType:One sampling frequency.
0x00,0x7d,0x00, //32K(0x007d00)
//Endpoint 2 - Standard Descriptor:Output Endpoint2
//Isochronous,Synchronization Type(Asynchronous),MaxPacketSize 0x0084,
0x07, //Length
0x05, //DescriptorType:endpoint descriptor
0x02, //EndpointAddress:Output endpoint 2
0x05, //Attributes:0x05,Isochronous,Synchronization Type(Asynchronous).........
0x84,0x00, //MaxPacketSize:0x0084=.....
0x01, //Interval
//Endpoint - Audio Streaming Descriptor:
//Audio Endpoint descriptor,General,
0x07, //Length
0x25, //DescriptorType:audio endpoint descriptor
0x01, //DescriptorSubType:audio endpiont general
0x00, //Attributes:0x00.............
0x00, //LockDelayUnits
0x00,0x00, //LockDelay
};
/* USB String Descriptor (optional) */
const unsigned char Demo_StringLangID[] =
{
0x04,
0x03,
0x09,
0x04
};
const unsigned char Demo_StringVendor[] =
{
0x26, //Length
0x03, //DescriptorType
'D', 0, 'e', 0, 'm', 0, 'o', 0, '-', 0, 's', 0, 'p', 0, 'e', 0,
'r', 0, 'k', 0, 'e', 0, 'r', 0, 'p', 0, 'h', 0, 'o', 0, 'n', 0,
'e', 0, '1', 0
};
const unsigned char Demo_StringProduct[] =
{
0x1c, //Length
0x03, //DescriptorType
'S', 0, 'p', 0, 'e', 0, 'a', 0, 'k', 0, 'e', 0,
'r', 0, 'p', 0, 'h', 0, 'o', 0, 'n', 0, 'e', 0, '2', 0
};
const unsigned char Demo_StringSerial[] =
{
0x1c, //Length
0x03, //DescriptorType
'S', 0, 'p', 0, 'e', 0, 'a', 0, 'k', 0, 'e', 0,
'r', 0, 'p', 0, 'h', 0, 'o', 0, 'n', 0, 'e', 0, '2', 0
};
相關說明:
這裏就不針對整個描述符去作具體的分析,其實註釋已經寫的很詳細了。這裏只是對一些細節性的進行說明
1.根據USB描述符可以得到其拓撲圖:
2.描述符的分層組織結構
3.設備描述符裏採用的Vendor和Product ID號是隨便寫的一個用於測試。
4.設備描述符下包含一個配置描述符,配置描述符下包含了3組接口,IF0作爲音頻控制接口,IF1作爲microphone接口,IF2作爲speaker接口。
5.在IF0的定義中沒有包含任何端點,所以與音頻相關的控制信息將通過默認的控制端點0來進行信息的交付。也就是說端點0除了響應默認的控制事務(Set interfance等等)以外還需要響應音頻類相關控制事務(set cur,get max等等),音量調節就是通過控制端點0來完成的。
6.IF0中的Feature Unit定義了音量調節方式,mute和Volume,其中ID號爲2的Unit控制microphone,ID 5控制speaker。具體關係常見其拓撲圖。
7.IF0中兩個IT(Input Terminal)定義了音頻流邏輯通道數及其具體組成形式,在該示例中包含了2個邏輯通道,分別是LEFT&RIGHT,如果需要更多的通道可以通過修改相應描述符單元得到(同時注意修改相應的Streaming描述符中物理通道數,相應USB endpoint支持的包字節大小)。
8. IF1和IF2中都定義了2個setting,其中setting 0都不包含傳輸endpoint,setting 1都包含一個同步傳輸endpoint。這裏要說明的是之所以都要定義一個不含有任何傳輸endpoint(除了默認端點0)的setting,是由usb audio設備特性決定的,該setting不可省略,該setting用於在設備沒有被使用時作爲usb設備的設置。如果沒有利用該usb設備進行放音,和錄音則主機使用的接口是IF(1,0)(接口1的設置0)和IF(2,0)(接口2的設置0),如果開始放音則使用的接口是IF(1,0)和IF(2,1),如果即用於放音又用於錄音則使用IF(1,1)和IF(2,1)。這裏敘述的有些累贅,其實大家可以利用USB協議相關分析軟件得到其具體交互過程。
9.在IF1和IF2中定義了音頻流格式爲PCM格式(更多關於格式的信息參考USB audio相關文檔)。支持一個採樣頻點:32K,如果需要更多的採樣頻點的支持,可以通過修改相應字段得到(注意修改相應同步端點支持的包大小)。
10.IF1(microphone)定義了endpoint 1 作爲其同步傳輸輸入端點。端點支持的最大傳輸字節爲0x84(132byte),這裏的0x44是由 32*2*2=128=0x80(採樣率*通道數*每個樣點的字節數)得到的(0x80byte/ms),之所以會多出4字節是因爲在之前的應用中如果支持44.1K時的一個考慮,當然這裏可以將其定義爲0x80。
11.在IF1和IF2的AS endpoint Descriptor中的字段attributes可以用來定義是否支持採樣率相關事務處理。也就是說如果在同一個IF的同一setting中支持多個採樣率,那麼在進行同步傳輸前,主機可以發起一個告知設備即將使用的採樣率的事務處理,而這裏的attributes用來定義設備是否支持這類事務處理。
12.支持多個採樣率不僅可以通過在第9點所敘述的方式來實現(同一setting下支持多個採樣率),也可以通過增加新的setting來支持(每個setting支持一個單獨的採樣率)。兩種方式各有優缺點,第一種方式比較簡潔不過需要由主機告知設備即將使用的採樣率。第二種方式會較大幅度的增加整個描述符長度,但是不需要主機告知設備使用的採樣率。也就是說在第一種方式下主機先告訴即將使用的接口setting,然後再發起一個採樣率的SET CUR事務處理。而第二種方式只需要告訴即將使用的接口就可以了(因爲一個setting只支持一個採樣率,不同採樣率將採用不同setting)。
13.同步問題,因爲usb傳輸都是主機發起的,而設備只是被動的響應來進行數據傳輸。對於同步傳輸,usb在每一個 frame中啓動一次同步傳輸,該同步傳輸的啓動是基於主機USB主控制器所使用的時鐘來進行的,而設備得到數據是依據自己的時鐘源,所以這裏就存在兩個時鐘源差異的問題。我採用的方式是利用設備響應主機的SOF中斷來調整自身時鐘來與主機同步。