root / hw / usb / dev-audio.c @ 0b7ade1d
History | View | Annotate | Download (22.9 kB)
1 | b870472d | H. Peter Anvin | /*
|
---|---|---|---|
2 | b870472d | H. Peter Anvin | * QEMU USB audio device
|
3 | b870472d | H. Peter Anvin | *
|
4 | b870472d | H. Peter Anvin | * written by:
|
5 | b870472d | H. Peter Anvin | * H. Peter Anvin <hpa@linux.intel.com>
|
6 | b870472d | H. Peter Anvin | * Gerd Hoffmann <kraxel@redhat.com>
|
7 | b870472d | H. Peter Anvin | *
|
8 | b870472d | H. Peter Anvin | * lousely based on usb net device code which is:
|
9 | b870472d | H. Peter Anvin | *
|
10 | b870472d | H. Peter Anvin | * Copyright (c) 2006 Thomas Sailer
|
11 | b870472d | H. Peter Anvin | * Copyright (c) 2008 Andrzej Zaborowski
|
12 | b870472d | H. Peter Anvin | *
|
13 | b870472d | H. Peter Anvin | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
14 | b870472d | H. Peter Anvin | * of this software and associated documentation files (the "Software"), to deal
|
15 | b870472d | H. Peter Anvin | * in the Software without restriction, including without limitation the rights
|
16 | b870472d | H. Peter Anvin | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
17 | b870472d | H. Peter Anvin | * copies of the Software, and to permit persons to whom the Software is
|
18 | b870472d | H. Peter Anvin | * furnished to do so, subject to the following conditions:
|
19 | b870472d | H. Peter Anvin | *
|
20 | b870472d | H. Peter Anvin | * The above copyright notice and this permission notice shall be included in
|
21 | b870472d | H. Peter Anvin | * all copies or substantial portions of the Software.
|
22 | b870472d | H. Peter Anvin | *
|
23 | b870472d | H. Peter Anvin | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
24 | b870472d | H. Peter Anvin | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
25 | b870472d | H. Peter Anvin | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
26 | b870472d | H. Peter Anvin | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
27 | b870472d | H. Peter Anvin | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
28 | b870472d | H. Peter Anvin | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
29 | b870472d | H. Peter Anvin | * THE SOFTWARE.
|
30 | b870472d | H. Peter Anvin | */
|
31 | b870472d | H. Peter Anvin | |
32 | b870472d | H. Peter Anvin | #include "qemu-common.h" |
33 | f1ae32a1 | Gerd Hoffmann | #include "hw/usb.h" |
34 | f1ae32a1 | Gerd Hoffmann | #include "hw/usb/desc.h" |
35 | f1ae32a1 | Gerd Hoffmann | #include "hw/hw.h" |
36 | f1ae32a1 | Gerd Hoffmann | #include "hw/audiodev.h" |
37 | b870472d | H. Peter Anvin | #include "audio/audio.h" |
38 | b870472d | H. Peter Anvin | |
39 | b870472d | H. Peter Anvin | #define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ |
40 | b870472d | H. Peter Anvin | #define USBAUDIO_PRODUCT_NUM 0x0002 |
41 | b870472d | H. Peter Anvin | |
42 | b870472d | H. Peter Anvin | #define DEV_CONFIG_VALUE 1 /* The one and only */ |
43 | b870472d | H. Peter Anvin | |
44 | b870472d | H. Peter Anvin | /* Descriptor subtypes for AC interfaces */
|
45 | b870472d | H. Peter Anvin | #define DST_AC_HEADER 1 |
46 | b870472d | H. Peter Anvin | #define DST_AC_INPUT_TERMINAL 2 |
47 | b870472d | H. Peter Anvin | #define DST_AC_OUTPUT_TERMINAL 3 |
48 | b870472d | H. Peter Anvin | #define DST_AC_FEATURE_UNIT 6 |
49 | b870472d | H. Peter Anvin | /* Descriptor subtypes for AS interfaces */
|
50 | b870472d | H. Peter Anvin | #define DST_AS_GENERAL 1 |
51 | b870472d | H. Peter Anvin | #define DST_AS_FORMAT_TYPE 2 |
52 | b870472d | H. Peter Anvin | /* Descriptor subtypes for endpoints */
|
53 | b870472d | H. Peter Anvin | #define DST_EP_GENERAL 1 |
54 | b870472d | H. Peter Anvin | |
55 | b870472d | H. Peter Anvin | enum usb_audio_strings {
|
56 | b870472d | H. Peter Anvin | STRING_NULL, |
57 | b870472d | H. Peter Anvin | STRING_MANUFACTURER, |
58 | b870472d | H. Peter Anvin | STRING_PRODUCT, |
59 | b870472d | H. Peter Anvin | STRING_SERIALNUMBER, |
60 | b870472d | H. Peter Anvin | STRING_CONFIG, |
61 | b870472d | H. Peter Anvin | STRING_USBAUDIO_CONTROL, |
62 | b870472d | H. Peter Anvin | STRING_INPUT_TERMINAL, |
63 | b870472d | H. Peter Anvin | STRING_FEATURE_UNIT, |
64 | b870472d | H. Peter Anvin | STRING_OUTPUT_TERMINAL, |
65 | b870472d | H. Peter Anvin | STRING_NULL_STREAM, |
66 | b870472d | H. Peter Anvin | STRING_REAL_STREAM, |
67 | b870472d | H. Peter Anvin | }; |
68 | b870472d | H. Peter Anvin | |
69 | b870472d | H. Peter Anvin | static const USBDescStrings usb_audio_stringtable = { |
70 | b870472d | H. Peter Anvin | [STRING_MANUFACTURER] = "QEMU",
|
71 | b870472d | H. Peter Anvin | [STRING_PRODUCT] = "QEMU USB Audio",
|
72 | b870472d | H. Peter Anvin | [STRING_SERIALNUMBER] = "1",
|
73 | b870472d | H. Peter Anvin | [STRING_CONFIG] = "Audio Configuration",
|
74 | b870472d | H. Peter Anvin | [STRING_USBAUDIO_CONTROL] = "Audio Device",
|
75 | b870472d | H. Peter Anvin | [STRING_INPUT_TERMINAL] = "Audio Output Pipe",
|
76 | b870472d | H. Peter Anvin | [STRING_FEATURE_UNIT] = "Audio Output Volume Control",
|
77 | b870472d | H. Peter Anvin | [STRING_OUTPUT_TERMINAL] = "Audio Output Terminal",
|
78 | b870472d | H. Peter Anvin | [STRING_NULL_STREAM] = "Audio Output - Disabled",
|
79 | b870472d | H. Peter Anvin | [STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo",
|
80 | b870472d | H. Peter Anvin | }; |
81 | b870472d | H. Peter Anvin | |
82 | b870472d | H. Peter Anvin | #define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff) |
83 | b870472d | H. Peter Anvin | #define U24(x) U16(x), (((x) >> 16) & 0xff) |
84 | b870472d | H. Peter Anvin | #define U32(x) U24(x), (((x) >> 24) & 0xff) |
85 | b870472d | H. Peter Anvin | |
86 | b870472d | H. Peter Anvin | /*
|
87 | b870472d | H. Peter Anvin | * A Basic Audio Device uses these specific values
|
88 | b870472d | H. Peter Anvin | */
|
89 | b870472d | H. Peter Anvin | #define USBAUDIO_PACKET_SIZE 192 |
90 | b870472d | H. Peter Anvin | #define USBAUDIO_SAMPLE_RATE 48000 |
91 | b870472d | H. Peter Anvin | #define USBAUDIO_PACKET_INTERVAL 1 |
92 | b870472d | H. Peter Anvin | |
93 | b870472d | H. Peter Anvin | static const USBDescIface desc_iface[] = { |
94 | b870472d | H. Peter Anvin | { |
95 | b870472d | H. Peter Anvin | .bInterfaceNumber = 0,
|
96 | b870472d | H. Peter Anvin | .bNumEndpoints = 0,
|
97 | b870472d | H. Peter Anvin | .bInterfaceClass = USB_CLASS_AUDIO, |
98 | b870472d | H. Peter Anvin | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, |
99 | b870472d | H. Peter Anvin | .bInterfaceProtocol = 0x04,
|
100 | b870472d | H. Peter Anvin | .iInterface = STRING_USBAUDIO_CONTROL, |
101 | b870472d | H. Peter Anvin | .ndesc = 4,
|
102 | b870472d | H. Peter Anvin | .descs = (USBDescOther[]) { |
103 | b870472d | H. Peter Anvin | { |
104 | b870472d | H. Peter Anvin | /* Headphone Class-Specific AC Interface Header Descriptor */
|
105 | b870472d | H. Peter Anvin | .data = (uint8_t[]) { |
106 | b870472d | H. Peter Anvin | 0x09, /* u8 bLength */ |
107 | b870472d | H. Peter Anvin | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
108 | b870472d | H. Peter Anvin | DST_AC_HEADER, /* u8 bDescriptorSubtype */
|
109 | b870472d | H. Peter Anvin | U16(0x0100), /* u16 bcdADC */ |
110 | b870472d | H. Peter Anvin | U16(0x2b), /* u16 wTotalLength */ |
111 | b870472d | H. Peter Anvin | 0x01, /* u8 bInCollection */ |
112 | b870472d | H. Peter Anvin | 0x01, /* u8 baInterfaceNr */ |
113 | b870472d | H. Peter Anvin | } |
114 | b870472d | H. Peter Anvin | },{ |
115 | b870472d | H. Peter Anvin | /* Generic Stereo Input Terminal ID1 Descriptor */
|
116 | b870472d | H. Peter Anvin | .data = (uint8_t[]) { |
117 | b870472d | H. Peter Anvin | 0x0c, /* u8 bLength */ |
118 | b870472d | H. Peter Anvin | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
119 | b870472d | H. Peter Anvin | DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */
|
120 | b870472d | H. Peter Anvin | 0x01, /* u8 bTerminalID */ |
121 | b870472d | H. Peter Anvin | U16(0x0101), /* u16 wTerminalType */ |
122 | b870472d | H. Peter Anvin | 0x00, /* u8 bAssocTerminal */ |
123 | b870472d | H. Peter Anvin | 0x02, /* u16 bNrChannels */ |
124 | b870472d | H. Peter Anvin | U16(0x0003), /* u16 wChannelConfig */ |
125 | b870472d | H. Peter Anvin | 0x00, /* u8 iChannelNames */ |
126 | b870472d | H. Peter Anvin | STRING_INPUT_TERMINAL, /* u8 iTerminal */
|
127 | b870472d | H. Peter Anvin | } |
128 | b870472d | H. Peter Anvin | },{ |
129 | b870472d | H. Peter Anvin | /* Generic Stereo Feature Unit ID2 Descriptor */
|
130 | b870472d | H. Peter Anvin | .data = (uint8_t[]) { |
131 | b870472d | H. Peter Anvin | 0x0d, /* u8 bLength */ |
132 | b870472d | H. Peter Anvin | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
133 | b870472d | H. Peter Anvin | DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */
|
134 | b870472d | H. Peter Anvin | 0x02, /* u8 bUnitID */ |
135 | b870472d | H. Peter Anvin | 0x01, /* u8 bSourceID */ |
136 | b870472d | H. Peter Anvin | 0x02, /* u8 bControlSize */ |
137 | b870472d | H. Peter Anvin | U16(0x0001), /* u16 bmaControls(0) */ |
138 | b870472d | H. Peter Anvin | U16(0x0002), /* u16 bmaControls(1) */ |
139 | b870472d | H. Peter Anvin | U16(0x0002), /* u16 bmaControls(2) */ |
140 | b870472d | H. Peter Anvin | STRING_FEATURE_UNIT, /* u8 iFeature */
|
141 | b870472d | H. Peter Anvin | } |
142 | b870472d | H. Peter Anvin | },{ |
143 | b870472d | H. Peter Anvin | /* Headphone Ouptut Terminal ID3 Descriptor */
|
144 | b870472d | H. Peter Anvin | .data = (uint8_t[]) { |
145 | b870472d | H. Peter Anvin | 0x09, /* u8 bLength */ |
146 | b870472d | H. Peter Anvin | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
147 | b870472d | H. Peter Anvin | DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */
|
148 | b870472d | H. Peter Anvin | 0x03, /* u8 bUnitID */ |
149 | b870472d | H. Peter Anvin | U16(0x0301), /* u16 wTerminalType (SPK) */ |
150 | b870472d | H. Peter Anvin | 0x00, /* u8 bAssocTerminal */ |
151 | b870472d | H. Peter Anvin | 0x02, /* u8 bSourceID */ |
152 | b870472d | H. Peter Anvin | STRING_OUTPUT_TERMINAL, /* u8 iTerminal */
|
153 | b870472d | H. Peter Anvin | } |
154 | b870472d | H. Peter Anvin | } |
155 | b870472d | H. Peter Anvin | }, |
156 | b870472d | H. Peter Anvin | },{ |
157 | b870472d | H. Peter Anvin | .bInterfaceNumber = 1,
|
158 | b870472d | H. Peter Anvin | .bAlternateSetting = 0,
|
159 | b870472d | H. Peter Anvin | .bNumEndpoints = 0,
|
160 | b870472d | H. Peter Anvin | .bInterfaceClass = USB_CLASS_AUDIO, |
161 | b870472d | H. Peter Anvin | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, |
162 | b870472d | H. Peter Anvin | .iInterface = STRING_NULL_STREAM, |
163 | b870472d | H. Peter Anvin | },{ |
164 | b870472d | H. Peter Anvin | .bInterfaceNumber = 1,
|
165 | b870472d | H. Peter Anvin | .bAlternateSetting = 1,
|
166 | b870472d | H. Peter Anvin | .bNumEndpoints = 1,
|
167 | b870472d | H. Peter Anvin | .bInterfaceClass = USB_CLASS_AUDIO, |
168 | b870472d | H. Peter Anvin | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, |
169 | b870472d | H. Peter Anvin | .iInterface = STRING_REAL_STREAM, |
170 | b870472d | H. Peter Anvin | .ndesc = 2,
|
171 | b870472d | H. Peter Anvin | .descs = (USBDescOther[]) { |
172 | b870472d | H. Peter Anvin | { |
173 | b870472d | H. Peter Anvin | /* Headphone Class-specific AS General Interface Descriptor */
|
174 | b870472d | H. Peter Anvin | .data = (uint8_t[]) { |
175 | b870472d | H. Peter Anvin | 0x07, /* u8 bLength */ |
176 | b870472d | H. Peter Anvin | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
177 | b870472d | H. Peter Anvin | DST_AS_GENERAL, /* u8 bDescriptorSubtype */
|
178 | b870472d | H. Peter Anvin | 0x01, /* u8 bTerminalLink */ |
179 | b870472d | H. Peter Anvin | 0x00, /* u8 bDelay */ |
180 | b870472d | H. Peter Anvin | 0x01, 0x00, /* u16 wFormatTag */ |
181 | b870472d | H. Peter Anvin | } |
182 | b870472d | H. Peter Anvin | },{ |
183 | b870472d | H. Peter Anvin | /* Headphone Type I Format Type Descriptor */
|
184 | b870472d | H. Peter Anvin | .data = (uint8_t[]) { |
185 | b870472d | H. Peter Anvin | 0x0b, /* u8 bLength */ |
186 | b870472d | H. Peter Anvin | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
187 | b870472d | H. Peter Anvin | DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */
|
188 | b870472d | H. Peter Anvin | 0x01, /* u8 bFormatType */ |
189 | b870472d | H. Peter Anvin | 0x02, /* u8 bNrChannels */ |
190 | b870472d | H. Peter Anvin | 0x02, /* u8 bSubFrameSize */ |
191 | b870472d | H. Peter Anvin | 0x10, /* u8 bBitResolution */ |
192 | b870472d | H. Peter Anvin | 0x01, /* u8 bSamFreqType */ |
193 | b870472d | H. Peter Anvin | U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */
|
194 | b870472d | H. Peter Anvin | } |
195 | b870472d | H. Peter Anvin | } |
196 | b870472d | H. Peter Anvin | }, |
197 | b870472d | H. Peter Anvin | .eps = (USBDescEndpoint[]) { |
198 | b870472d | H. Peter Anvin | { |
199 | b870472d | H. Peter Anvin | .bEndpointAddress = USB_DIR_OUT | 0x01,
|
200 | b870472d | H. Peter Anvin | .bmAttributes = 0x0d,
|
201 | b870472d | H. Peter Anvin | .wMaxPacketSize = USBAUDIO_PACKET_SIZE, |
202 | b870472d | H. Peter Anvin | .bInterval = 1,
|
203 | b870472d | H. Peter Anvin | .is_audio = 1,
|
204 | b870472d | H. Peter Anvin | /* Stereo Headphone Class-specific
|
205 | b870472d | H. Peter Anvin | AS Audio Data Endpoint Descriptor */
|
206 | b870472d | H. Peter Anvin | .extra = (uint8_t[]) { |
207 | b870472d | H. Peter Anvin | 0x07, /* u8 bLength */ |
208 | b870472d | H. Peter Anvin | USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */
|
209 | b870472d | H. Peter Anvin | DST_EP_GENERAL, /* u8 bDescriptorSubtype */
|
210 | b870472d | H. Peter Anvin | 0x00, /* u8 bmAttributes */ |
211 | b870472d | H. Peter Anvin | 0x00, /* u8 bLockDelayUnits */ |
212 | b870472d | H. Peter Anvin | U16(0x0000), /* u16 wLockDelay */ |
213 | b870472d | H. Peter Anvin | }, |
214 | b870472d | H. Peter Anvin | }, |
215 | b870472d | H. Peter Anvin | } |
216 | b870472d | H. Peter Anvin | } |
217 | b870472d | H. Peter Anvin | }; |
218 | b870472d | H. Peter Anvin | |
219 | b870472d | H. Peter Anvin | static const USBDescDevice desc_device = { |
220 | b870472d | H. Peter Anvin | .bcdUSB = 0x0200,
|
221 | b870472d | H. Peter Anvin | .bMaxPacketSize0 = 64,
|
222 | b870472d | H. Peter Anvin | .bNumConfigurations = 1,
|
223 | b870472d | H. Peter Anvin | .confs = (USBDescConfig[]) { |
224 | b870472d | H. Peter Anvin | { |
225 | b870472d | H. Peter Anvin | .bNumInterfaces = 2,
|
226 | b870472d | H. Peter Anvin | .bConfigurationValue = DEV_CONFIG_VALUE, |
227 | b870472d | H. Peter Anvin | .iConfiguration = STRING_CONFIG, |
228 | b870472d | H. Peter Anvin | .bmAttributes = 0xc0,
|
229 | b870472d | H. Peter Anvin | .bMaxPower = 0x32,
|
230 | b870472d | H. Peter Anvin | .nif = ARRAY_SIZE(desc_iface), |
231 | b870472d | H. Peter Anvin | .ifs = desc_iface, |
232 | b870472d | H. Peter Anvin | }, |
233 | b870472d | H. Peter Anvin | }, |
234 | b870472d | H. Peter Anvin | }; |
235 | b870472d | H. Peter Anvin | |
236 | b870472d | H. Peter Anvin | static const USBDesc desc_audio = { |
237 | b870472d | H. Peter Anvin | .id = { |
238 | b870472d | H. Peter Anvin | .idVendor = USBAUDIO_VENDOR_NUM, |
239 | b870472d | H. Peter Anvin | .idProduct = USBAUDIO_PRODUCT_NUM, |
240 | b870472d | H. Peter Anvin | .bcdDevice = 0,
|
241 | b870472d | H. Peter Anvin | .iManufacturer = STRING_MANUFACTURER, |
242 | b870472d | H. Peter Anvin | .iProduct = STRING_PRODUCT, |
243 | b870472d | H. Peter Anvin | .iSerialNumber = STRING_SERIALNUMBER, |
244 | b870472d | H. Peter Anvin | }, |
245 | b870472d | H. Peter Anvin | .full = &desc_device, |
246 | b870472d | H. Peter Anvin | .str = usb_audio_stringtable, |
247 | b870472d | H. Peter Anvin | }; |
248 | b870472d | H. Peter Anvin | |
249 | b870472d | H. Peter Anvin | /*
|
250 | b870472d | H. Peter Anvin | * A USB audio device supports an arbitrary number of alternate
|
251 | b870472d | H. Peter Anvin | * interface settings for each interface. Each corresponds to a block
|
252 | b870472d | H. Peter Anvin | * diagram of parameterized blocks. This can thus refer to things like
|
253 | b870472d | H. Peter Anvin | * number of channels, data rates, or in fact completely different
|
254 | b870472d | H. Peter Anvin | * block diagrams. Alternative setting 0 is always the null block diagram,
|
255 | b870472d | H. Peter Anvin | * which is used by a disabled device.
|
256 | b870472d | H. Peter Anvin | */
|
257 | b870472d | H. Peter Anvin | enum usb_audio_altset {
|
258 | b870472d | H. Peter Anvin | ALTSET_OFF = 0x00, /* No endpoint */ |
259 | b870472d | H. Peter Anvin | ALTSET_ON = 0x01, /* Single endpoint */ |
260 | b870472d | H. Peter Anvin | }; |
261 | b870472d | H. Peter Anvin | |
262 | b870472d | H. Peter Anvin | /*
|
263 | b870472d | H. Peter Anvin | * Class-specific control requests
|
264 | b870472d | H. Peter Anvin | */
|
265 | b870472d | H. Peter Anvin | #define CR_SET_CUR 0x01 |
266 | b870472d | H. Peter Anvin | #define CR_GET_CUR 0x81 |
267 | b870472d | H. Peter Anvin | #define CR_SET_MIN 0x02 |
268 | b870472d | H. Peter Anvin | #define CR_GET_MIN 0x82 |
269 | b870472d | H. Peter Anvin | #define CR_SET_MAX 0x03 |
270 | b870472d | H. Peter Anvin | #define CR_GET_MAX 0x83 |
271 | b870472d | H. Peter Anvin | #define CR_SET_RES 0x04 |
272 | b870472d | H. Peter Anvin | #define CR_GET_RES 0x84 |
273 | b870472d | H. Peter Anvin | #define CR_SET_MEM 0x05 |
274 | b870472d | H. Peter Anvin | #define CR_GET_MEM 0x85 |
275 | b870472d | H. Peter Anvin | #define CR_GET_STAT 0xff |
276 | b870472d | H. Peter Anvin | |
277 | b870472d | H. Peter Anvin | /*
|
278 | b870472d | H. Peter Anvin | * Feature Unit Control Selectors
|
279 | b870472d | H. Peter Anvin | */
|
280 | b870472d | H. Peter Anvin | #define MUTE_CONTROL 0x01 |
281 | b870472d | H. Peter Anvin | #define VOLUME_CONTROL 0x02 |
282 | b870472d | H. Peter Anvin | #define BASS_CONTROL 0x03 |
283 | b870472d | H. Peter Anvin | #define MID_CONTROL 0x04 |
284 | b870472d | H. Peter Anvin | #define TREBLE_CONTROL 0x05 |
285 | b870472d | H. Peter Anvin | #define GRAPHIC_EQUALIZER_CONTROL 0x06 |
286 | b870472d | H. Peter Anvin | #define AUTOMATIC_GAIN_CONTROL 0x07 |
287 | b870472d | H. Peter Anvin | #define DELAY_CONTROL 0x08 |
288 | b870472d | H. Peter Anvin | #define BASS_BOOST_CONTROL 0x09 |
289 | b870472d | H. Peter Anvin | #define LOUDNESS_CONTROL 0x0a |
290 | b870472d | H. Peter Anvin | |
291 | b870472d | H. Peter Anvin | /*
|
292 | b870472d | H. Peter Anvin | * buffering
|
293 | b870472d | H. Peter Anvin | */
|
294 | b870472d | H. Peter Anvin | |
295 | b870472d | H. Peter Anvin | struct streambuf {
|
296 | b870472d | H. Peter Anvin | uint8_t *data; |
297 | b870472d | H. Peter Anvin | uint32_t size; |
298 | b870472d | H. Peter Anvin | uint32_t prod; |
299 | b870472d | H. Peter Anvin | uint32_t cons; |
300 | b870472d | H. Peter Anvin | }; |
301 | b870472d | H. Peter Anvin | |
302 | b870472d | H. Peter Anvin | static void streambuf_init(struct streambuf *buf, uint32_t size) |
303 | b870472d | H. Peter Anvin | { |
304 | b870472d | H. Peter Anvin | g_free(buf->data); |
305 | b870472d | H. Peter Anvin | buf->size = size - (size % USBAUDIO_PACKET_SIZE); |
306 | b870472d | H. Peter Anvin | buf->data = g_malloc(buf->size); |
307 | b870472d | H. Peter Anvin | buf->prod = 0;
|
308 | b870472d | H. Peter Anvin | buf->cons = 0;
|
309 | b870472d | H. Peter Anvin | } |
310 | b870472d | H. Peter Anvin | |
311 | b870472d | H. Peter Anvin | static void streambuf_fini(struct streambuf *buf) |
312 | b870472d | H. Peter Anvin | { |
313 | b870472d | H. Peter Anvin | g_free(buf->data); |
314 | b870472d | H. Peter Anvin | buf->data = NULL;
|
315 | b870472d | H. Peter Anvin | } |
316 | b870472d | H. Peter Anvin | |
317 | b870472d | H. Peter Anvin | static int streambuf_put(struct streambuf *buf, USBPacket *p) |
318 | b870472d | H. Peter Anvin | { |
319 | b870472d | H. Peter Anvin | uint32_t free = buf->size - (buf->prod - buf->cons); |
320 | b870472d | H. Peter Anvin | |
321 | b870472d | H. Peter Anvin | if (!free) {
|
322 | b870472d | H. Peter Anvin | return 0; |
323 | b870472d | H. Peter Anvin | } |
324 | b870472d | H. Peter Anvin | assert(free >= USBAUDIO_PACKET_SIZE); |
325 | b870472d | H. Peter Anvin | usb_packet_copy(p, buf->data + (buf->prod % buf->size), |
326 | b870472d | H. Peter Anvin | USBAUDIO_PACKET_SIZE); |
327 | b870472d | H. Peter Anvin | buf->prod += USBAUDIO_PACKET_SIZE; |
328 | b870472d | H. Peter Anvin | return USBAUDIO_PACKET_SIZE;
|
329 | b870472d | H. Peter Anvin | } |
330 | b870472d | H. Peter Anvin | |
331 | b870472d | H. Peter Anvin | static uint8_t *streambuf_get(struct streambuf *buf) |
332 | b870472d | H. Peter Anvin | { |
333 | b870472d | H. Peter Anvin | uint32_t used = buf->prod - buf->cons; |
334 | b870472d | H. Peter Anvin | uint8_t *data; |
335 | b870472d | H. Peter Anvin | |
336 | b870472d | H. Peter Anvin | if (!used) {
|
337 | b870472d | H. Peter Anvin | return NULL; |
338 | b870472d | H. Peter Anvin | } |
339 | b870472d | H. Peter Anvin | assert(used >= USBAUDIO_PACKET_SIZE); |
340 | b870472d | H. Peter Anvin | data = buf->data + (buf->cons % buf->size); |
341 | b870472d | H. Peter Anvin | buf->cons += USBAUDIO_PACKET_SIZE; |
342 | b870472d | H. Peter Anvin | return data;
|
343 | b870472d | H. Peter Anvin | } |
344 | b870472d | H. Peter Anvin | |
345 | b870472d | H. Peter Anvin | typedef struct USBAudioState { |
346 | b870472d | H. Peter Anvin | /* qemu interfaces */
|
347 | b870472d | H. Peter Anvin | USBDevice dev; |
348 | b870472d | H. Peter Anvin | QEMUSoundCard card; |
349 | b870472d | H. Peter Anvin | |
350 | b870472d | H. Peter Anvin | /* state */
|
351 | b870472d | H. Peter Anvin | struct {
|
352 | b870472d | H. Peter Anvin | enum usb_audio_altset altset;
|
353 | b870472d | H. Peter Anvin | struct audsettings as;
|
354 | b870472d | H. Peter Anvin | SWVoiceOut *voice; |
355 | b870472d | H. Peter Anvin | bool mute;
|
356 | b870472d | H. Peter Anvin | uint8_t vol[2];
|
357 | b870472d | H. Peter Anvin | struct streambuf buf;
|
358 | b870472d | H. Peter Anvin | } out; |
359 | b870472d | H. Peter Anvin | |
360 | b870472d | H. Peter Anvin | /* properties */
|
361 | b870472d | H. Peter Anvin | uint32_t debug; |
362 | b870472d | H. Peter Anvin | uint32_t buffer; |
363 | b870472d | H. Peter Anvin | } USBAudioState; |
364 | b870472d | H. Peter Anvin | |
365 | b870472d | H. Peter Anvin | static void output_callback(void *opaque, int avail) |
366 | b870472d | H. Peter Anvin | { |
367 | b870472d | H. Peter Anvin | USBAudioState *s = opaque; |
368 | b870472d | H. Peter Anvin | uint8_t *data; |
369 | b870472d | H. Peter Anvin | |
370 | b870472d | H. Peter Anvin | for (;;) {
|
371 | b870472d | H. Peter Anvin | if (avail < USBAUDIO_PACKET_SIZE) {
|
372 | b870472d | H. Peter Anvin | return;
|
373 | b870472d | H. Peter Anvin | } |
374 | b870472d | H. Peter Anvin | data = streambuf_get(&s->out.buf); |
375 | b870472d | H. Peter Anvin | if (NULL == data) { |
376 | b870472d | H. Peter Anvin | return;
|
377 | b870472d | H. Peter Anvin | } |
378 | b870472d | H. Peter Anvin | AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE); |
379 | b870472d | H. Peter Anvin | avail -= USBAUDIO_PACKET_SIZE; |
380 | b870472d | H. Peter Anvin | } |
381 | b870472d | H. Peter Anvin | } |
382 | b870472d | H. Peter Anvin | |
383 | b870472d | H. Peter Anvin | static int usb_audio_set_output_altset(USBAudioState *s, int altset) |
384 | b870472d | H. Peter Anvin | { |
385 | b870472d | H. Peter Anvin | switch (altset) {
|
386 | b870472d | H. Peter Anvin | case ALTSET_OFF:
|
387 | b870472d | H. Peter Anvin | streambuf_init(&s->out.buf, s->buffer); |
388 | b870472d | H. Peter Anvin | AUD_set_active_out(s->out.voice, false);
|
389 | b870472d | H. Peter Anvin | break;
|
390 | b870472d | H. Peter Anvin | case ALTSET_ON:
|
391 | b870472d | H. Peter Anvin | AUD_set_active_out(s->out.voice, true);
|
392 | b870472d | H. Peter Anvin | break;
|
393 | b870472d | H. Peter Anvin | default:
|
394 | b870472d | H. Peter Anvin | return -1; |
395 | b870472d | H. Peter Anvin | } |
396 | b870472d | H. Peter Anvin | |
397 | b870472d | H. Peter Anvin | if (s->debug) {
|
398 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: set interface %d\n", altset);
|
399 | b870472d | H. Peter Anvin | } |
400 | b870472d | H. Peter Anvin | s->out.altset = altset; |
401 | b870472d | H. Peter Anvin | return 0; |
402 | b870472d | H. Peter Anvin | } |
403 | b870472d | H. Peter Anvin | |
404 | b870472d | H. Peter Anvin | /*
|
405 | b870472d | H. Peter Anvin | * Note: we arbitrarily map the volume control range onto -inf..+8 dB
|
406 | b870472d | H. Peter Anvin | */
|
407 | b870472d | H. Peter Anvin | #define ATTRIB_ID(cs, attrib, idif) \
|
408 | b870472d | H. Peter Anvin | (((cs) << 24) | ((attrib) << 16) | (idif)) |
409 | b870472d | H. Peter Anvin | |
410 | b870472d | H. Peter Anvin | static int usb_audio_get_control(USBAudioState *s, uint8_t attrib, |
411 | b870472d | H. Peter Anvin | uint16_t cscn, uint16_t idif, |
412 | b870472d | H. Peter Anvin | int length, uint8_t *data)
|
413 | b870472d | H. Peter Anvin | { |
414 | b870472d | H. Peter Anvin | uint8_t cs = cscn >> 8;
|
415 | b870472d | H. Peter Anvin | uint8_t cn = cscn - 1; /* -1 for the non-present master control */ |
416 | b870472d | H. Peter Anvin | uint32_t aid = ATTRIB_ID(cs, attrib, idif); |
417 | b870472d | H. Peter Anvin | int ret = USB_RET_STALL;
|
418 | b870472d | H. Peter Anvin | |
419 | b870472d | H. Peter Anvin | switch (aid) {
|
420 | b870472d | H. Peter Anvin | case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200): |
421 | b870472d | H. Peter Anvin | data[0] = s->out.mute;
|
422 | b870472d | H. Peter Anvin | ret = 1;
|
423 | b870472d | H. Peter Anvin | break;
|
424 | b870472d | H. Peter Anvin | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200): |
425 | b870472d | H. Peter Anvin | if (cn < 2) { |
426 | b870472d | H. Peter Anvin | uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000; |
427 | b870472d | H. Peter Anvin | data[0] = vol;
|
428 | b870472d | H. Peter Anvin | data[1] = vol >> 8; |
429 | b870472d | H. Peter Anvin | ret = 2;
|
430 | b870472d | H. Peter Anvin | } |
431 | b870472d | H. Peter Anvin | break;
|
432 | b870472d | H. Peter Anvin | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200): |
433 | b870472d | H. Peter Anvin | if (cn < 2) { |
434 | b870472d | H. Peter Anvin | data[0] = 0x01; |
435 | b870472d | H. Peter Anvin | data[1] = 0x80; |
436 | b870472d | H. Peter Anvin | ret = 2;
|
437 | b870472d | H. Peter Anvin | } |
438 | b870472d | H. Peter Anvin | break;
|
439 | b870472d | H. Peter Anvin | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200): |
440 | b870472d | H. Peter Anvin | if (cn < 2) { |
441 | b870472d | H. Peter Anvin | data[0] = 0x00; |
442 | b870472d | H. Peter Anvin | data[1] = 0x08; |
443 | b870472d | H. Peter Anvin | ret = 2;
|
444 | b870472d | H. Peter Anvin | } |
445 | b870472d | H. Peter Anvin | break;
|
446 | b870472d | H. Peter Anvin | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200): |
447 | b870472d | H. Peter Anvin | if (cn < 2) { |
448 | b870472d | H. Peter Anvin | data[0] = 0x88; |
449 | b870472d | H. Peter Anvin | data[1] = 0x00; |
450 | b870472d | H. Peter Anvin | ret = 2;
|
451 | b870472d | H. Peter Anvin | } |
452 | b870472d | H. Peter Anvin | break;
|
453 | b870472d | H. Peter Anvin | } |
454 | b870472d | H. Peter Anvin | |
455 | b870472d | H. Peter Anvin | return ret;
|
456 | b870472d | H. Peter Anvin | } |
457 | b870472d | H. Peter Anvin | static int usb_audio_set_control(USBAudioState *s, uint8_t attrib, |
458 | b870472d | H. Peter Anvin | uint16_t cscn, uint16_t idif, |
459 | b870472d | H. Peter Anvin | int length, uint8_t *data)
|
460 | b870472d | H. Peter Anvin | { |
461 | b870472d | H. Peter Anvin | uint8_t cs = cscn >> 8;
|
462 | b870472d | H. Peter Anvin | uint8_t cn = cscn - 1; /* -1 for the non-present master control */ |
463 | b870472d | H. Peter Anvin | uint32_t aid = ATTRIB_ID(cs, attrib, idif); |
464 | b870472d | H. Peter Anvin | int ret = USB_RET_STALL;
|
465 | b870472d | H. Peter Anvin | bool set_vol = false; |
466 | b870472d | H. Peter Anvin | |
467 | b870472d | H. Peter Anvin | switch (aid) {
|
468 | b870472d | H. Peter Anvin | case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200): |
469 | b870472d | H. Peter Anvin | s->out.mute = data[0] & 1; |
470 | b870472d | H. Peter Anvin | set_vol = true;
|
471 | b870472d | H. Peter Anvin | ret = 0;
|
472 | b870472d | H. Peter Anvin | break;
|
473 | b870472d | H. Peter Anvin | case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200): |
474 | b870472d | H. Peter Anvin | if (cn < 2) { |
475 | b870472d | H. Peter Anvin | uint16_t vol = data[0] + (data[1] << 8); |
476 | b870472d | H. Peter Anvin | |
477 | b870472d | H. Peter Anvin | if (s->debug) {
|
478 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
|
479 | b870472d | H. Peter Anvin | } |
480 | b870472d | H. Peter Anvin | |
481 | b870472d | H. Peter Anvin | vol -= 0x8000;
|
482 | b870472d | H. Peter Anvin | vol = (vol * 255 + 0x4400) / 0x8800; |
483 | b870472d | H. Peter Anvin | if (vol > 255) { |
484 | b870472d | H. Peter Anvin | vol = 255;
|
485 | b870472d | H. Peter Anvin | } |
486 | b870472d | H. Peter Anvin | |
487 | b870472d | H. Peter Anvin | s->out.vol[cn] = vol; |
488 | b870472d | H. Peter Anvin | set_vol = true;
|
489 | b870472d | H. Peter Anvin | ret = 0;
|
490 | b870472d | H. Peter Anvin | } |
491 | b870472d | H. Peter Anvin | break;
|
492 | b870472d | H. Peter Anvin | } |
493 | b870472d | H. Peter Anvin | |
494 | b870472d | H. Peter Anvin | if (set_vol) {
|
495 | b870472d | H. Peter Anvin | if (s->debug) {
|
496 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
|
497 | b870472d | H. Peter Anvin | s->out.mute, s->out.vol[0], s->out.vol[1]); |
498 | b870472d | H. Peter Anvin | } |
499 | b870472d | H. Peter Anvin | AUD_set_volume_out(s->out.voice, s->out.mute, |
500 | b870472d | H. Peter Anvin | s->out.vol[0], s->out.vol[1]); |
501 | b870472d | H. Peter Anvin | } |
502 | b870472d | H. Peter Anvin | |
503 | b870472d | H. Peter Anvin | return ret;
|
504 | b870472d | H. Peter Anvin | } |
505 | b870472d | H. Peter Anvin | |
506 | b870472d | H. Peter Anvin | static int usb_audio_handle_control(USBDevice *dev, USBPacket *p, |
507 | b870472d | H. Peter Anvin | int request, int value, int index, |
508 | b870472d | H. Peter Anvin | int length, uint8_t *data)
|
509 | b870472d | H. Peter Anvin | { |
510 | b870472d | H. Peter Anvin | USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); |
511 | b870472d | H. Peter Anvin | int ret = 0; |
512 | b870472d | H. Peter Anvin | |
513 | b870472d | H. Peter Anvin | if (s->debug) {
|
514 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: control transaction: "
|
515 | b870472d | H. Peter Anvin | "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
|
516 | b870472d | H. Peter Anvin | request, value, index, length); |
517 | b870472d | H. Peter Anvin | } |
518 | b870472d | H. Peter Anvin | |
519 | b870472d | H. Peter Anvin | ret = usb_desc_handle_control(dev, p, request, value, index, length, data); |
520 | b870472d | H. Peter Anvin | if (ret >= 0) { |
521 | b870472d | H. Peter Anvin | return ret;
|
522 | b870472d | H. Peter Anvin | } |
523 | b870472d | H. Peter Anvin | |
524 | b870472d | H. Peter Anvin | switch (request) {
|
525 | b870472d | H. Peter Anvin | case ClassInterfaceRequest | CR_GET_CUR:
|
526 | b870472d | H. Peter Anvin | case ClassInterfaceRequest | CR_GET_MIN:
|
527 | b870472d | H. Peter Anvin | case ClassInterfaceRequest | CR_GET_MAX:
|
528 | b870472d | H. Peter Anvin | case ClassInterfaceRequest | CR_GET_RES:
|
529 | b870472d | H. Peter Anvin | ret = usb_audio_get_control(s, request & 0xff, value, index,
|
530 | b870472d | H. Peter Anvin | length, data); |
531 | b870472d | H. Peter Anvin | if (ret < 0) { |
532 | b870472d | H. Peter Anvin | if (s->debug) {
|
533 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: fail: get control\n");
|
534 | b870472d | H. Peter Anvin | } |
535 | b870472d | H. Peter Anvin | goto fail;
|
536 | b870472d | H. Peter Anvin | } |
537 | b870472d | H. Peter Anvin | break;
|
538 | b870472d | H. Peter Anvin | |
539 | b870472d | H. Peter Anvin | case ClassInterfaceOutRequest | CR_SET_CUR:
|
540 | b870472d | H. Peter Anvin | case ClassInterfaceOutRequest | CR_SET_MIN:
|
541 | b870472d | H. Peter Anvin | case ClassInterfaceOutRequest | CR_SET_MAX:
|
542 | b870472d | H. Peter Anvin | case ClassInterfaceOutRequest | CR_SET_RES:
|
543 | b870472d | H. Peter Anvin | ret = usb_audio_set_control(s, request & 0xff, value, index,
|
544 | b870472d | H. Peter Anvin | length, data); |
545 | b870472d | H. Peter Anvin | if (ret < 0) { |
546 | b870472d | H. Peter Anvin | if (s->debug) {
|
547 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: fail: set control\n");
|
548 | b870472d | H. Peter Anvin | } |
549 | b870472d | H. Peter Anvin | goto fail;
|
550 | b870472d | H. Peter Anvin | } |
551 | b870472d | H. Peter Anvin | break;
|
552 | b870472d | H. Peter Anvin | |
553 | b870472d | H. Peter Anvin | default:
|
554 | b870472d | H. Peter Anvin | fail:
|
555 | b870472d | H. Peter Anvin | if (s->debug) {
|
556 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: failed control transaction: "
|
557 | b870472d | H. Peter Anvin | "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
|
558 | b870472d | H. Peter Anvin | request, value, index, length); |
559 | b870472d | H. Peter Anvin | } |
560 | b870472d | H. Peter Anvin | ret = USB_RET_STALL; |
561 | b870472d | H. Peter Anvin | break;
|
562 | b870472d | H. Peter Anvin | } |
563 | b870472d | H. Peter Anvin | return ret;
|
564 | b870472d | H. Peter Anvin | } |
565 | b870472d | H. Peter Anvin | |
566 | b870472d | H. Peter Anvin | static void usb_audio_set_interface(USBDevice *dev, int iface, |
567 | b870472d | H. Peter Anvin | int old, int value) |
568 | b870472d | H. Peter Anvin | { |
569 | b870472d | H. Peter Anvin | USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); |
570 | b870472d | H. Peter Anvin | |
571 | b870472d | H. Peter Anvin | if (iface == 1) { |
572 | b870472d | H. Peter Anvin | usb_audio_set_output_altset(s, value); |
573 | b870472d | H. Peter Anvin | } |
574 | b870472d | H. Peter Anvin | } |
575 | b870472d | H. Peter Anvin | |
576 | b870472d | H. Peter Anvin | static void usb_audio_handle_reset(USBDevice *dev) |
577 | b870472d | H. Peter Anvin | { |
578 | b870472d | H. Peter Anvin | USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); |
579 | b870472d | H. Peter Anvin | |
580 | b870472d | H. Peter Anvin | if (s->debug) {
|
581 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: reset\n");
|
582 | b870472d | H. Peter Anvin | } |
583 | b870472d | H. Peter Anvin | usb_audio_set_output_altset(s, ALTSET_OFF); |
584 | b870472d | H. Peter Anvin | } |
585 | b870472d | H. Peter Anvin | |
586 | b870472d | H. Peter Anvin | static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) |
587 | b870472d | H. Peter Anvin | { |
588 | b870472d | H. Peter Anvin | int rc;
|
589 | b870472d | H. Peter Anvin | |
590 | b870472d | H. Peter Anvin | if (s->out.altset == ALTSET_OFF) {
|
591 | b870472d | H. Peter Anvin | return USB_RET_STALL;
|
592 | b870472d | H. Peter Anvin | } |
593 | b870472d | H. Peter Anvin | |
594 | b870472d | H. Peter Anvin | rc = streambuf_put(&s->out.buf, p); |
595 | b870472d | H. Peter Anvin | if (rc < p->iov.size && s->debug > 1) { |
596 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
|
597 | b870472d | H. Peter Anvin | p->iov.size - rc); |
598 | b870472d | H. Peter Anvin | } |
599 | b870472d | H. Peter Anvin | |
600 | b870472d | H. Peter Anvin | return 0; |
601 | b870472d | H. Peter Anvin | } |
602 | b870472d | H. Peter Anvin | |
603 | b870472d | H. Peter Anvin | static int usb_audio_handle_data(USBDevice *dev, USBPacket *p) |
604 | b870472d | H. Peter Anvin | { |
605 | b870472d | H. Peter Anvin | USBAudioState *s = (USBAudioState *) dev; |
606 | b870472d | H. Peter Anvin | int ret = 0; |
607 | b870472d | H. Peter Anvin | |
608 | b870472d | H. Peter Anvin | switch (p->pid) {
|
609 | b870472d | H. Peter Anvin | case USB_TOKEN_OUT:
|
610 | 079d0b7f | Gerd Hoffmann | switch (p->ep->nr) {
|
611 | b870472d | H. Peter Anvin | case 1: |
612 | b870472d | H. Peter Anvin | ret = usb_audio_handle_dataout(s, p); |
613 | b870472d | H. Peter Anvin | break;
|
614 | b870472d | H. Peter Anvin | default:
|
615 | b870472d | H. Peter Anvin | goto fail;
|
616 | b870472d | H. Peter Anvin | } |
617 | b870472d | H. Peter Anvin | break;
|
618 | b870472d | H. Peter Anvin | |
619 | b870472d | H. Peter Anvin | default:
|
620 | b870472d | H. Peter Anvin | fail:
|
621 | b870472d | H. Peter Anvin | ret = USB_RET_STALL; |
622 | b870472d | H. Peter Anvin | break;
|
623 | b870472d | H. Peter Anvin | } |
624 | b870472d | H. Peter Anvin | if (ret == USB_RET_STALL && s->debug) {
|
625 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: failed data transaction: "
|
626 | b870472d | H. Peter Anvin | "pid 0x%x ep 0x%x len 0x%zx\n",
|
627 | 079d0b7f | Gerd Hoffmann | p->pid, p->ep->nr, p->iov.size); |
628 | b870472d | H. Peter Anvin | } |
629 | b870472d | H. Peter Anvin | return ret;
|
630 | b870472d | H. Peter Anvin | } |
631 | b870472d | H. Peter Anvin | |
632 | b870472d | H. Peter Anvin | static void usb_audio_handle_destroy(USBDevice *dev) |
633 | b870472d | H. Peter Anvin | { |
634 | b870472d | H. Peter Anvin | USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); |
635 | b870472d | H. Peter Anvin | |
636 | b870472d | H. Peter Anvin | if (s->debug) {
|
637 | b870472d | H. Peter Anvin | fprintf(stderr, "usb-audio: destroy\n");
|
638 | b870472d | H. Peter Anvin | } |
639 | b870472d | H. Peter Anvin | |
640 | b870472d | H. Peter Anvin | usb_audio_set_output_altset(s, ALTSET_OFF); |
641 | b870472d | H. Peter Anvin | AUD_close_out(&s->card, s->out.voice); |
642 | b870472d | H. Peter Anvin | AUD_remove_card(&s->card); |
643 | b870472d | H. Peter Anvin | |
644 | b870472d | H. Peter Anvin | streambuf_fini(&s->out.buf); |
645 | b870472d | H. Peter Anvin | } |
646 | b870472d | H. Peter Anvin | |
647 | b870472d | H. Peter Anvin | static int usb_audio_initfn(USBDevice *dev) |
648 | b870472d | H. Peter Anvin | { |
649 | b870472d | H. Peter Anvin | USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); |
650 | b870472d | H. Peter Anvin | |
651 | b870472d | H. Peter Anvin | usb_desc_init(dev); |
652 | b870472d | H. Peter Anvin | s->dev.opaque = s; |
653 | b870472d | H. Peter Anvin | AUD_register_card("usb-audio", &s->card);
|
654 | b870472d | H. Peter Anvin | |
655 | b870472d | H. Peter Anvin | s->out.altset = ALTSET_OFF; |
656 | b870472d | H. Peter Anvin | s->out.mute = false;
|
657 | b870472d | H. Peter Anvin | s->out.vol[0] = 240; /* 0 dB */ |
658 | b870472d | H. Peter Anvin | s->out.vol[1] = 240; /* 0 dB */ |
659 | b870472d | H. Peter Anvin | s->out.as.freq = USBAUDIO_SAMPLE_RATE; |
660 | b870472d | H. Peter Anvin | s->out.as.nchannels = 2;
|
661 | b870472d | H. Peter Anvin | s->out.as.fmt = AUD_FMT_S16; |
662 | b870472d | H. Peter Anvin | s->out.as.endianness = 0;
|
663 | b870472d | H. Peter Anvin | streambuf_init(&s->out.buf, s->buffer); |
664 | b870472d | H. Peter Anvin | |
665 | b870472d | H. Peter Anvin | s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio",
|
666 | b870472d | H. Peter Anvin | s, output_callback, &s->out.as); |
667 | b870472d | H. Peter Anvin | AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]); |
668 | b870472d | H. Peter Anvin | AUD_set_active_out(s->out.voice, 0);
|
669 | b870472d | H. Peter Anvin | return 0; |
670 | b870472d | H. Peter Anvin | } |
671 | b870472d | H. Peter Anvin | |
672 | b870472d | H. Peter Anvin | static const VMStateDescription vmstate_usb_audio = { |
673 | b870472d | H. Peter Anvin | .name = "usb-audio",
|
674 | b870472d | H. Peter Anvin | .unmigratable = 1,
|
675 | b870472d | H. Peter Anvin | }; |
676 | b870472d | H. Peter Anvin | |
677 | 39bffca2 | Anthony Liguori | static Property usb_audio_properties[] = {
|
678 | 39bffca2 | Anthony Liguori | DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), |
679 | 39bffca2 | Anthony Liguori | DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
|
680 | 39bffca2 | Anthony Liguori | 8 * USBAUDIO_PACKET_SIZE),
|
681 | 39bffca2 | Anthony Liguori | DEFINE_PROP_END_OF_LIST(), |
682 | 39bffca2 | Anthony Liguori | }; |
683 | 39bffca2 | Anthony Liguori | |
684 | 62aed765 | Anthony Liguori | static void usb_audio_class_init(ObjectClass *klass, void *data) |
685 | 62aed765 | Anthony Liguori | { |
686 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
687 | 62aed765 | Anthony Liguori | USBDeviceClass *k = USB_DEVICE_CLASS(klass); |
688 | 62aed765 | Anthony Liguori | |
689 | 39bffca2 | Anthony Liguori | dc->vmsd = &vmstate_usb_audio; |
690 | 39bffca2 | Anthony Liguori | dc->props = usb_audio_properties; |
691 | 62aed765 | Anthony Liguori | k->product_desc = "QEMU USB Audio Interface";
|
692 | 62aed765 | Anthony Liguori | k->usb_desc = &desc_audio; |
693 | 62aed765 | Anthony Liguori | k->init = usb_audio_initfn; |
694 | 62aed765 | Anthony Liguori | k->handle_reset = usb_audio_handle_reset; |
695 | 62aed765 | Anthony Liguori | k->handle_control = usb_audio_handle_control; |
696 | 62aed765 | Anthony Liguori | k->handle_data = usb_audio_handle_data; |
697 | 62aed765 | Anthony Liguori | k->handle_destroy = usb_audio_handle_destroy; |
698 | 62aed765 | Anthony Liguori | k->set_interface = usb_audio_set_interface; |
699 | 62aed765 | Anthony Liguori | } |
700 | 62aed765 | Anthony Liguori | |
701 | 39bffca2 | Anthony Liguori | static TypeInfo usb_audio_info = {
|
702 | 39bffca2 | Anthony Liguori | .name = "usb-audio",
|
703 | 39bffca2 | Anthony Liguori | .parent = TYPE_USB_DEVICE, |
704 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(USBAudioState),
|
705 | 39bffca2 | Anthony Liguori | .class_init = usb_audio_class_init, |
706 | b870472d | H. Peter Anvin | }; |
707 | b870472d | H. Peter Anvin | |
708 | 83f7d43a | Andreas Färber | static void usb_audio_register_types(void) |
709 | b870472d | H. Peter Anvin | { |
710 | 39bffca2 | Anthony Liguori | type_register_static(&usb_audio_info); |
711 | ba02430f | Anthony Liguori | usb_legacy_register("usb-audio", "audio", NULL); |
712 | b870472d | H. Peter Anvin | } |
713 | b870472d | H. Peter Anvin | |
714 | 83f7d43a | Andreas Färber | type_init(usb_audio_register_types) |