写在前面:
前两天拿到一个产品,也就是一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中断来调整自身时钟来与主机同步。