]>
Commit | Line | Data |
---|---|---|
b870472d PA |
1 | /* |
2 | * QEMU USB audio device | |
3 | * | |
4 | * written by: | |
5 | * H. Peter Anvin <hpa@linux.intel.com> | |
6 | * Gerd Hoffmann <kraxel@redhat.com> | |
7 | * | |
8 | * lousely based on usb net device code which is: | |
9 | * | |
10 | * Copyright (c) 2006 Thomas Sailer | |
11 | * Copyright (c) 2008 Andrzej Zaborowski | |
12 | * | |
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
14 | * of this software and associated documentation files (the "Software"), to deal | |
15 | * in the Software without restriction, including without limitation the rights | |
16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
17 | * copies of the Software, and to permit persons to whom the Software is | |
18 | * furnished to do so, subject to the following conditions: | |
19 | * | |
20 | * The above copyright notice and this permission notice shall be included in | |
21 | * all copies or substantial portions of the Software. | |
22 | * | |
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
26 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
29 | * THE SOFTWARE. | |
30 | */ | |
31 | ||
e532b2e0 | 32 | #include "qemu/osdep.h" |
0b8fa32f | 33 | #include "qemu/module.h" |
a27bd6c7 | 34 | #include "hw/qdev-properties.h" |
f1ae32a1 | 35 | #include "hw/usb.h" |
d6454270 | 36 | #include "migration/vmstate.h" |
463581a8 | 37 | #include "desc.h" |
b870472d PA |
38 | #include "audio/audio.h" |
39 | ||
3e44607e KZ |
40 | static void usb_audio_reinit(USBDevice *dev, unsigned channels); |
41 | ||
b870472d PA |
42 | #define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ |
43 | #define USBAUDIO_PRODUCT_NUM 0x0002 | |
44 | ||
45 | #define DEV_CONFIG_VALUE 1 /* The one and only */ | |
46 | ||
3e44607e KZ |
47 | #define USBAUDIO_MAX_CHANNELS(s) (s->multi ? 8 : 2) |
48 | ||
b870472d PA |
49 | /* Descriptor subtypes for AC interfaces */ |
50 | #define DST_AC_HEADER 1 | |
51 | #define DST_AC_INPUT_TERMINAL 2 | |
52 | #define DST_AC_OUTPUT_TERMINAL 3 | |
53 | #define DST_AC_FEATURE_UNIT 6 | |
54 | /* Descriptor subtypes for AS interfaces */ | |
55 | #define DST_AS_GENERAL 1 | |
56 | #define DST_AS_FORMAT_TYPE 2 | |
57 | /* Descriptor subtypes for endpoints */ | |
58 | #define DST_EP_GENERAL 1 | |
59 | ||
60 | enum usb_audio_strings { | |
61 | STRING_NULL, | |
62 | STRING_MANUFACTURER, | |
63 | STRING_PRODUCT, | |
64 | STRING_SERIALNUMBER, | |
65 | STRING_CONFIG, | |
66 | STRING_USBAUDIO_CONTROL, | |
67 | STRING_INPUT_TERMINAL, | |
68 | STRING_FEATURE_UNIT, | |
69 | STRING_OUTPUT_TERMINAL, | |
70 | STRING_NULL_STREAM, | |
71 | STRING_REAL_STREAM, | |
72 | }; | |
73 | ||
74 | static const USBDescStrings usb_audio_stringtable = { | |
75 | [STRING_MANUFACTURER] = "QEMU", | |
76 | [STRING_PRODUCT] = "QEMU USB Audio", | |
77 | [STRING_SERIALNUMBER] = "1", | |
78 | [STRING_CONFIG] = "Audio Configuration", | |
79 | [STRING_USBAUDIO_CONTROL] = "Audio Device", | |
80 | [STRING_INPUT_TERMINAL] = "Audio Output Pipe", | |
81 | [STRING_FEATURE_UNIT] = "Audio Output Volume Control", | |
82 | [STRING_OUTPUT_TERMINAL] = "Audio Output Terminal", | |
83 | [STRING_NULL_STREAM] = "Audio Output - Disabled", | |
84 | [STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo", | |
85 | }; | |
86 | ||
3e44607e KZ |
87 | /* |
88 | * A USB audio device supports an arbitrary number of alternate | |
89 | * interface settings for each interface. Each corresponds to a block | |
90 | * diagram of parameterized blocks. This can thus refer to things like | |
91 | * number of channels, data rates, or in fact completely different | |
92 | * block diagrams. Alternative setting 0 is always the null block diagram, | |
93 | * which is used by a disabled device. | |
94 | */ | |
95 | enum usb_audio_altset { | |
96 | ALTSET_OFF = 0x00, /* No endpoint */ | |
97 | ALTSET_STEREO = 0x01, /* Single endpoint */ | |
98 | ALTSET_51 = 0x02, | |
99 | ALTSET_71 = 0x03, | |
100 | }; | |
101 | ||
102 | static unsigned altset_channels[] = { | |
103 | [ALTSET_STEREO] = 2, | |
104 | [ALTSET_51] = 6, | |
105 | [ALTSET_71] = 8, | |
106 | }; | |
107 | ||
b870472d PA |
108 | #define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff) |
109 | #define U24(x) U16(x), (((x) >> 16) & 0xff) | |
110 | #define U32(x) U24(x), (((x) >> 24) & 0xff) | |
111 | ||
112 | /* | |
113 | * A Basic Audio Device uses these specific values | |
114 | */ | |
3e44607e KZ |
115 | #define USBAUDIO_PACKET_SIZE_BASE 96 |
116 | #define USBAUDIO_PACKET_SIZE(channels) (USBAUDIO_PACKET_SIZE_BASE * channels) | |
b870472d PA |
117 | #define USBAUDIO_SAMPLE_RATE 48000 |
118 | #define USBAUDIO_PACKET_INTERVAL 1 | |
119 | ||
120 | static const USBDescIface desc_iface[] = { | |
121 | { | |
122 | .bInterfaceNumber = 0, | |
123 | .bNumEndpoints = 0, | |
124 | .bInterfaceClass = USB_CLASS_AUDIO, | |
125 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, | |
126 | .bInterfaceProtocol = 0x04, | |
127 | .iInterface = STRING_USBAUDIO_CONTROL, | |
128 | .ndesc = 4, | |
129 | .descs = (USBDescOther[]) { | |
130 | { | |
131 | /* Headphone Class-Specific AC Interface Header Descriptor */ | |
132 | .data = (uint8_t[]) { | |
133 | 0x09, /* u8 bLength */ | |
134 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
135 | DST_AC_HEADER, /* u8 bDescriptorSubtype */ | |
136 | U16(0x0100), /* u16 bcdADC */ | |
137 | U16(0x2b), /* u16 wTotalLength */ | |
138 | 0x01, /* u8 bInCollection */ | |
139 | 0x01, /* u8 baInterfaceNr */ | |
140 | } | |
141 | },{ | |
142 | /* Generic Stereo Input Terminal ID1 Descriptor */ | |
143 | .data = (uint8_t[]) { | |
144 | 0x0c, /* u8 bLength */ | |
145 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
146 | DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ | |
147 | 0x01, /* u8 bTerminalID */ | |
148 | U16(0x0101), /* u16 wTerminalType */ | |
149 | 0x00, /* u8 bAssocTerminal */ | |
3e44607e | 150 | 0x02, /* u8 bNrChannels */ |
b870472d PA |
151 | U16(0x0003), /* u16 wChannelConfig */ |
152 | 0x00, /* u8 iChannelNames */ | |
153 | STRING_INPUT_TERMINAL, /* u8 iTerminal */ | |
154 | } | |
155 | },{ | |
156 | /* Generic Stereo Feature Unit ID2 Descriptor */ | |
157 | .data = (uint8_t[]) { | |
158 | 0x0d, /* u8 bLength */ | |
159 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
160 | DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ | |
161 | 0x02, /* u8 bUnitID */ | |
162 | 0x01, /* u8 bSourceID */ | |
163 | 0x02, /* u8 bControlSize */ | |
164 | U16(0x0001), /* u16 bmaControls(0) */ | |
165 | U16(0x0002), /* u16 bmaControls(1) */ | |
166 | U16(0x0002), /* u16 bmaControls(2) */ | |
167 | STRING_FEATURE_UNIT, /* u8 iFeature */ | |
168 | } | |
169 | },{ | |
170 | /* Headphone Ouptut Terminal ID3 Descriptor */ | |
171 | .data = (uint8_t[]) { | |
172 | 0x09, /* u8 bLength */ | |
173 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
174 | DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ | |
175 | 0x03, /* u8 bUnitID */ | |
176 | U16(0x0301), /* u16 wTerminalType (SPK) */ | |
177 | 0x00, /* u8 bAssocTerminal */ | |
178 | 0x02, /* u8 bSourceID */ | |
179 | STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ | |
180 | } | |
181 | } | |
182 | }, | |
183 | },{ | |
184 | .bInterfaceNumber = 1, | |
3e44607e | 185 | .bAlternateSetting = ALTSET_OFF, |
b870472d PA |
186 | .bNumEndpoints = 0, |
187 | .bInterfaceClass = USB_CLASS_AUDIO, | |
188 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, | |
189 | .iInterface = STRING_NULL_STREAM, | |
190 | },{ | |
191 | .bInterfaceNumber = 1, | |
3e44607e | 192 | .bAlternateSetting = ALTSET_STEREO, |
b870472d PA |
193 | .bNumEndpoints = 1, |
194 | .bInterfaceClass = USB_CLASS_AUDIO, | |
195 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, | |
196 | .iInterface = STRING_REAL_STREAM, | |
197 | .ndesc = 2, | |
198 | .descs = (USBDescOther[]) { | |
199 | { | |
200 | /* Headphone Class-specific AS General Interface Descriptor */ | |
201 | .data = (uint8_t[]) { | |
202 | 0x07, /* u8 bLength */ | |
203 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
204 | DST_AS_GENERAL, /* u8 bDescriptorSubtype */ | |
205 | 0x01, /* u8 bTerminalLink */ | |
206 | 0x00, /* u8 bDelay */ | |
207 | 0x01, 0x00, /* u16 wFormatTag */ | |
208 | } | |
209 | },{ | |
210 | /* Headphone Type I Format Type Descriptor */ | |
211 | .data = (uint8_t[]) { | |
212 | 0x0b, /* u8 bLength */ | |
213 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
214 | DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ | |
215 | 0x01, /* u8 bFormatType */ | |
216 | 0x02, /* u8 bNrChannels */ | |
217 | 0x02, /* u8 bSubFrameSize */ | |
218 | 0x10, /* u8 bBitResolution */ | |
219 | 0x01, /* u8 bSamFreqType */ | |
220 | U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ | |
221 | } | |
222 | } | |
223 | }, | |
224 | .eps = (USBDescEndpoint[]) { | |
225 | { | |
226 | .bEndpointAddress = USB_DIR_OUT | 0x01, | |
227 | .bmAttributes = 0x0d, | |
3e44607e | 228 | .wMaxPacketSize = USBAUDIO_PACKET_SIZE(2), |
b870472d PA |
229 | .bInterval = 1, |
230 | .is_audio = 1, | |
231 | /* Stereo Headphone Class-specific | |
232 | AS Audio Data Endpoint Descriptor */ | |
233 | .extra = (uint8_t[]) { | |
234 | 0x07, /* u8 bLength */ | |
235 | USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ | |
236 | DST_EP_GENERAL, /* u8 bDescriptorSubtype */ | |
237 | 0x00, /* u8 bmAttributes */ | |
238 | 0x00, /* u8 bLockDelayUnits */ | |
239 | U16(0x0000), /* u16 wLockDelay */ | |
240 | }, | |
241 | }, | |
242 | } | |
243 | } | |
244 | }; | |
245 | ||
246 | static const USBDescDevice desc_device = { | |
2bbd086c | 247 | .bcdUSB = 0x0100, |
b870472d PA |
248 | .bMaxPacketSize0 = 64, |
249 | .bNumConfigurations = 1, | |
250 | .confs = (USBDescConfig[]) { | |
251 | { | |
252 | .bNumInterfaces = 2, | |
253 | .bConfigurationValue = DEV_CONFIG_VALUE, | |
254 | .iConfiguration = STRING_CONFIG, | |
bd93976a | 255 | .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, |
b870472d PA |
256 | .bMaxPower = 0x32, |
257 | .nif = ARRAY_SIZE(desc_iface), | |
258 | .ifs = desc_iface, | |
259 | }, | |
260 | }, | |
261 | }; | |
262 | ||
263 | static const USBDesc desc_audio = { | |
264 | .id = { | |
265 | .idVendor = USBAUDIO_VENDOR_NUM, | |
266 | .idProduct = USBAUDIO_PRODUCT_NUM, | |
267 | .bcdDevice = 0, | |
268 | .iManufacturer = STRING_MANUFACTURER, | |
269 | .iProduct = STRING_PRODUCT, | |
270 | .iSerialNumber = STRING_SERIALNUMBER, | |
271 | }, | |
272 | .full = &desc_device, | |
273 | .str = usb_audio_stringtable, | |
274 | }; | |
275 | ||
3e44607e KZ |
276 | /* multi channel compatible desc */ |
277 | ||
278 | static const USBDescIface desc_iface_multi[] = { | |
279 | { | |
280 | .bInterfaceNumber = 0, | |
281 | .bNumEndpoints = 0, | |
282 | .bInterfaceClass = USB_CLASS_AUDIO, | |
283 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, | |
284 | .bInterfaceProtocol = 0x04, | |
285 | .iInterface = STRING_USBAUDIO_CONTROL, | |
286 | .ndesc = 4, | |
287 | .descs = (USBDescOther[]) { | |
288 | { | |
289 | /* Headphone Class-Specific AC Interface Header Descriptor */ | |
290 | .data = (uint8_t[]) { | |
291 | 0x09, /* u8 bLength */ | |
292 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
293 | DST_AC_HEADER, /* u8 bDescriptorSubtype */ | |
294 | U16(0x0100), /* u16 bcdADC */ | |
295 | U16(0x38), /* u16 wTotalLength */ | |
296 | 0x01, /* u8 bInCollection */ | |
297 | 0x01, /* u8 baInterfaceNr */ | |
298 | } | |
299 | },{ | |
300 | /* Generic Stereo Input Terminal ID1 Descriptor */ | |
301 | .data = (uint8_t[]) { | |
302 | 0x0c, /* u8 bLength */ | |
303 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
304 | DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ | |
305 | 0x01, /* u8 bTerminalID */ | |
306 | U16(0x0101), /* u16 wTerminalType */ | |
307 | 0x00, /* u8 bAssocTerminal */ | |
308 | 0x08, /* u8 bNrChannels */ | |
309 | U16(0x063f), /* u16 wChannelConfig */ | |
310 | 0x00, /* u8 iChannelNames */ | |
311 | STRING_INPUT_TERMINAL, /* u8 iTerminal */ | |
312 | } | |
313 | },{ | |
314 | /* Generic Stereo Feature Unit ID2 Descriptor */ | |
315 | .data = (uint8_t[]) { | |
316 | 0x19, /* u8 bLength */ | |
317 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
318 | DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ | |
319 | 0x02, /* u8 bUnitID */ | |
320 | 0x01, /* u8 bSourceID */ | |
321 | 0x02, /* u8 bControlSize */ | |
322 | U16(0x0001), /* u16 bmaControls(0) */ | |
323 | U16(0x0002), /* u16 bmaControls(1) */ | |
324 | U16(0x0002), /* u16 bmaControls(2) */ | |
325 | U16(0x0002), /* u16 bmaControls(3) */ | |
326 | U16(0x0002), /* u16 bmaControls(4) */ | |
327 | U16(0x0002), /* u16 bmaControls(5) */ | |
328 | U16(0x0002), /* u16 bmaControls(6) */ | |
329 | U16(0x0002), /* u16 bmaControls(7) */ | |
330 | U16(0x0002), /* u16 bmaControls(8) */ | |
331 | STRING_FEATURE_UNIT, /* u8 iFeature */ | |
332 | } | |
333 | },{ | |
334 | /* Headphone Ouptut Terminal ID3 Descriptor */ | |
335 | .data = (uint8_t[]) { | |
336 | 0x09, /* u8 bLength */ | |
337 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
338 | DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ | |
339 | 0x03, /* u8 bUnitID */ | |
340 | U16(0x0301), /* u16 wTerminalType (SPK) */ | |
341 | 0x00, /* u8 bAssocTerminal */ | |
342 | 0x02, /* u8 bSourceID */ | |
343 | STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ | |
344 | } | |
345 | } | |
346 | }, | |
347 | },{ | |
348 | .bInterfaceNumber = 1, | |
349 | .bAlternateSetting = ALTSET_OFF, | |
350 | .bNumEndpoints = 0, | |
351 | .bInterfaceClass = USB_CLASS_AUDIO, | |
352 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, | |
353 | .iInterface = STRING_NULL_STREAM, | |
354 | },{ | |
355 | .bInterfaceNumber = 1, | |
356 | .bAlternateSetting = ALTSET_STEREO, | |
357 | .bNumEndpoints = 1, | |
358 | .bInterfaceClass = USB_CLASS_AUDIO, | |
359 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, | |
360 | .iInterface = STRING_REAL_STREAM, | |
361 | .ndesc = 2, | |
362 | .descs = (USBDescOther[]) { | |
363 | { | |
364 | /* Headphone Class-specific AS General Interface Descriptor */ | |
365 | .data = (uint8_t[]) { | |
366 | 0x07, /* u8 bLength */ | |
367 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
368 | DST_AS_GENERAL, /* u8 bDescriptorSubtype */ | |
369 | 0x01, /* u8 bTerminalLink */ | |
370 | 0x00, /* u8 bDelay */ | |
371 | 0x01, 0x00, /* u16 wFormatTag */ | |
372 | } | |
373 | },{ | |
374 | /* Headphone Type I Format Type Descriptor */ | |
375 | .data = (uint8_t[]) { | |
376 | 0x0b, /* u8 bLength */ | |
377 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
378 | DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ | |
379 | 0x01, /* u8 bFormatType */ | |
380 | 0x02, /* u8 bNrChannels */ | |
381 | 0x02, /* u8 bSubFrameSize */ | |
382 | 0x10, /* u8 bBitResolution */ | |
383 | 0x01, /* u8 bSamFreqType */ | |
384 | U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ | |
385 | } | |
386 | } | |
387 | }, | |
388 | .eps = (USBDescEndpoint[]) { | |
389 | { | |
390 | .bEndpointAddress = USB_DIR_OUT | 0x01, | |
391 | .bmAttributes = 0x0d, | |
392 | .wMaxPacketSize = USBAUDIO_PACKET_SIZE(2), | |
393 | .bInterval = 1, | |
394 | .is_audio = 1, | |
395 | /* Stereo Headphone Class-specific | |
396 | AS Audio Data Endpoint Descriptor */ | |
397 | .extra = (uint8_t[]) { | |
398 | 0x07, /* u8 bLength */ | |
399 | USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ | |
400 | DST_EP_GENERAL, /* u8 bDescriptorSubtype */ | |
401 | 0x00, /* u8 bmAttributes */ | |
402 | 0x00, /* u8 bLockDelayUnits */ | |
403 | U16(0x0000), /* u16 wLockDelay */ | |
404 | }, | |
405 | }, | |
406 | } | |
407 | },{ | |
408 | .bInterfaceNumber = 1, | |
409 | .bAlternateSetting = ALTSET_51, | |
410 | .bNumEndpoints = 1, | |
411 | .bInterfaceClass = USB_CLASS_AUDIO, | |
412 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, | |
413 | .iInterface = STRING_REAL_STREAM, | |
414 | .ndesc = 2, | |
415 | .descs = (USBDescOther[]) { | |
416 | { | |
417 | /* Headphone Class-specific AS General Interface Descriptor */ | |
418 | .data = (uint8_t[]) { | |
419 | 0x07, /* u8 bLength */ | |
420 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
421 | DST_AS_GENERAL, /* u8 bDescriptorSubtype */ | |
422 | 0x01, /* u8 bTerminalLink */ | |
423 | 0x00, /* u8 bDelay */ | |
424 | 0x01, 0x00, /* u16 wFormatTag */ | |
425 | } | |
426 | },{ | |
427 | /* Headphone Type I Format Type Descriptor */ | |
428 | .data = (uint8_t[]) { | |
429 | 0x0b, /* u8 bLength */ | |
430 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
431 | DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ | |
432 | 0x01, /* u8 bFormatType */ | |
433 | 0x06, /* u8 bNrChannels */ | |
434 | 0x02, /* u8 bSubFrameSize */ | |
435 | 0x10, /* u8 bBitResolution */ | |
436 | 0x01, /* u8 bSamFreqType */ | |
437 | U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ | |
438 | } | |
439 | } | |
440 | }, | |
441 | .eps = (USBDescEndpoint[]) { | |
442 | { | |
443 | .bEndpointAddress = USB_DIR_OUT | 0x01, | |
444 | .bmAttributes = 0x0d, | |
445 | .wMaxPacketSize = USBAUDIO_PACKET_SIZE(6), | |
446 | .bInterval = 1, | |
447 | .is_audio = 1, | |
448 | /* Stereo Headphone Class-specific | |
449 | AS Audio Data Endpoint Descriptor */ | |
450 | .extra = (uint8_t[]) { | |
451 | 0x07, /* u8 bLength */ | |
452 | USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ | |
453 | DST_EP_GENERAL, /* u8 bDescriptorSubtype */ | |
454 | 0x00, /* u8 bmAttributes */ | |
455 | 0x00, /* u8 bLockDelayUnits */ | |
456 | U16(0x0000), /* u16 wLockDelay */ | |
457 | }, | |
458 | }, | |
459 | } | |
460 | },{ | |
461 | .bInterfaceNumber = 1, | |
462 | .bAlternateSetting = ALTSET_71, | |
463 | .bNumEndpoints = 1, | |
464 | .bInterfaceClass = USB_CLASS_AUDIO, | |
465 | .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, | |
466 | .iInterface = STRING_REAL_STREAM, | |
467 | .ndesc = 2, | |
468 | .descs = (USBDescOther[]) { | |
469 | { | |
470 | /* Headphone Class-specific AS General Interface Descriptor */ | |
471 | .data = (uint8_t[]) { | |
472 | 0x07, /* u8 bLength */ | |
473 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
474 | DST_AS_GENERAL, /* u8 bDescriptorSubtype */ | |
475 | 0x01, /* u8 bTerminalLink */ | |
476 | 0x00, /* u8 bDelay */ | |
477 | 0x01, 0x00, /* u16 wFormatTag */ | |
478 | } | |
479 | },{ | |
480 | /* Headphone Type I Format Type Descriptor */ | |
481 | .data = (uint8_t[]) { | |
482 | 0x0b, /* u8 bLength */ | |
483 | USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ | |
484 | DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ | |
485 | 0x01, /* u8 bFormatType */ | |
486 | 0x08, /* u8 bNrChannels */ | |
487 | 0x02, /* u8 bSubFrameSize */ | |
488 | 0x10, /* u8 bBitResolution */ | |
489 | 0x01, /* u8 bSamFreqType */ | |
490 | U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ | |
491 | } | |
492 | } | |
493 | }, | |
494 | .eps = (USBDescEndpoint[]) { | |
495 | { | |
496 | .bEndpointAddress = USB_DIR_OUT | 0x01, | |
497 | .bmAttributes = 0x0d, | |
498 | .wMaxPacketSize = USBAUDIO_PACKET_SIZE(8), | |
499 | .bInterval = 1, | |
500 | .is_audio = 1, | |
501 | /* Stereo Headphone Class-specific | |
502 | AS Audio Data Endpoint Descriptor */ | |
503 | .extra = (uint8_t[]) { | |
504 | 0x07, /* u8 bLength */ | |
505 | USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ | |
506 | DST_EP_GENERAL, /* u8 bDescriptorSubtype */ | |
507 | 0x00, /* u8 bmAttributes */ | |
508 | 0x00, /* u8 bLockDelayUnits */ | |
509 | U16(0x0000), /* u16 wLockDelay */ | |
510 | }, | |
511 | }, | |
512 | } | |
513 | } | |
514 | }; | |
515 | ||
516 | static const USBDescDevice desc_device_multi = { | |
517 | .bcdUSB = 0x0100, | |
518 | .bMaxPacketSize0 = 64, | |
519 | .bNumConfigurations = 1, | |
520 | .confs = (USBDescConfig[]) { | |
521 | { | |
522 | .bNumInterfaces = 2, | |
523 | .bConfigurationValue = DEV_CONFIG_VALUE, | |
524 | .iConfiguration = STRING_CONFIG, | |
525 | .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, | |
526 | .bMaxPower = 0x32, | |
527 | .nif = ARRAY_SIZE(desc_iface_multi), | |
528 | .ifs = desc_iface_multi, | |
529 | } | |
530 | }, | |
531 | }; | |
532 | ||
533 | static const USBDesc desc_audio_multi = { | |
534 | .id = { | |
535 | .idVendor = USBAUDIO_VENDOR_NUM, | |
536 | .idProduct = USBAUDIO_PRODUCT_NUM, | |
537 | .bcdDevice = 0, | |
538 | .iManufacturer = STRING_MANUFACTURER, | |
539 | .iProduct = STRING_PRODUCT, | |
540 | .iSerialNumber = STRING_SERIALNUMBER, | |
541 | }, | |
542 | .full = &desc_device_multi, | |
543 | .str = usb_audio_stringtable, | |
b870472d PA |
544 | }; |
545 | ||
546 | /* | |
547 | * Class-specific control requests | |
548 | */ | |
549 | #define CR_SET_CUR 0x01 | |
550 | #define CR_GET_CUR 0x81 | |
551 | #define CR_SET_MIN 0x02 | |
552 | #define CR_GET_MIN 0x82 | |
553 | #define CR_SET_MAX 0x03 | |
554 | #define CR_GET_MAX 0x83 | |
555 | #define CR_SET_RES 0x04 | |
556 | #define CR_GET_RES 0x84 | |
557 | #define CR_SET_MEM 0x05 | |
558 | #define CR_GET_MEM 0x85 | |
559 | #define CR_GET_STAT 0xff | |
560 | ||
561 | /* | |
562 | * Feature Unit Control Selectors | |
563 | */ | |
564 | #define MUTE_CONTROL 0x01 | |
565 | #define VOLUME_CONTROL 0x02 | |
566 | #define BASS_CONTROL 0x03 | |
567 | #define MID_CONTROL 0x04 | |
568 | #define TREBLE_CONTROL 0x05 | |
569 | #define GRAPHIC_EQUALIZER_CONTROL 0x06 | |
570 | #define AUTOMATIC_GAIN_CONTROL 0x07 | |
571 | #define DELAY_CONTROL 0x08 | |
572 | #define BASS_BOOST_CONTROL 0x09 | |
573 | #define LOUDNESS_CONTROL 0x0a | |
574 | ||
575 | /* | |
576 | * buffering | |
577 | */ | |
578 | ||
579 | struct streambuf { | |
580 | uint8_t *data; | |
670777a9 KZ |
581 | size_t size; |
582 | uint64_t prod; | |
583 | uint64_t cons; | |
b870472d PA |
584 | }; |
585 | ||
3e44607e KZ |
586 | static void streambuf_init(struct streambuf *buf, uint32_t size, |
587 | uint32_t channels) | |
b870472d PA |
588 | { |
589 | g_free(buf->data); | |
3e44607e | 590 | buf->size = size - (size % USBAUDIO_PACKET_SIZE(channels)); |
b870472d PA |
591 | buf->data = g_malloc(buf->size); |
592 | buf->prod = 0; | |
593 | buf->cons = 0; | |
594 | } | |
595 | ||
596 | static void streambuf_fini(struct streambuf *buf) | |
597 | { | |
598 | g_free(buf->data); | |
599 | buf->data = NULL; | |
600 | } | |
601 | ||
3e44607e | 602 | static int streambuf_put(struct streambuf *buf, USBPacket *p, uint32_t channels) |
b870472d | 603 | { |
670777a9 | 604 | int64_t free = buf->size - (buf->prod - buf->cons); |
b870472d | 605 | |
3e44607e | 606 | if (free < USBAUDIO_PACKET_SIZE(channels)) { |
b870472d PA |
607 | return 0; |
608 | } | |
3e44607e | 609 | if (p->iov.size != USBAUDIO_PACKET_SIZE(channels)) { |
a7fde1c1 GH |
610 | return 0; |
611 | } | |
2c6a740f | 612 | |
670777a9 KZ |
613 | /* can happen if prod overflows */ |
614 | assert(buf->prod % USBAUDIO_PACKET_SIZE(channels) == 0); | |
b870472d | 615 | usb_packet_copy(p, buf->data + (buf->prod % buf->size), |
3e44607e KZ |
616 | USBAUDIO_PACKET_SIZE(channels)); |
617 | buf->prod += USBAUDIO_PACKET_SIZE(channels); | |
618 | return USBAUDIO_PACKET_SIZE(channels); | |
b870472d PA |
619 | } |
620 | ||
2c6a740f | 621 | static uint8_t *streambuf_get(struct streambuf *buf, size_t *len) |
b870472d | 622 | { |
670777a9 | 623 | int64_t used = buf->prod - buf->cons; |
b870472d PA |
624 | uint8_t *data; |
625 | ||
670777a9 | 626 | if (used <= 0) { |
2c6a740f | 627 | *len = 0; |
b870472d PA |
628 | return NULL; |
629 | } | |
b870472d | 630 | data = buf->data + (buf->cons % buf->size); |
2c6a740f KZ |
631 | *len = MIN(buf->prod - buf->cons, |
632 | buf->size - (buf->cons % buf->size)); | |
b870472d PA |
633 | return data; |
634 | } | |
635 | ||
636 | typedef struct USBAudioState { | |
637 | /* qemu interfaces */ | |
638 | USBDevice dev; | |
639 | QEMUSoundCard card; | |
640 | ||
641 | /* state */ | |
642 | struct { | |
643 | enum usb_audio_altset altset; | |
644 | struct audsettings as; | |
645 | SWVoiceOut *voice; | |
3e44607e | 646 | Volume vol; |
b870472d | 647 | struct streambuf buf; |
3e44607e | 648 | uint32_t channels; |
b870472d PA |
649 | } out; |
650 | ||
651 | /* properties */ | |
652 | uint32_t debug; | |
3e44607e KZ |
653 | uint32_t buffer_user, buffer; |
654 | bool multi; | |
b870472d PA |
655 | } USBAudioState; |
656 | ||
0389a0b1 GA |
657 | #define TYPE_USB_AUDIO "usb-audio" |
658 | #define USB_AUDIO(obj) OBJECT_CHECK(USBAudioState, (obj), TYPE_USB_AUDIO) | |
659 | ||
b870472d PA |
660 | static void output_callback(void *opaque, int avail) |
661 | { | |
662 | USBAudioState *s = opaque; | |
663 | uint8_t *data; | |
664 | ||
2c6a740f KZ |
665 | while (avail) { |
666 | size_t written, len; | |
667 | ||
668 | data = streambuf_get(&s->out.buf, &len); | |
669 | if (!data) { | |
b870472d PA |
670 | return; |
671 | } | |
2c6a740f KZ |
672 | |
673 | written = AUD_write(s->out.voice, data, len); | |
674 | avail -= written; | |
675 | s->out.buf.cons += written; | |
676 | ||
677 | if (written < len) { | |
b870472d PA |
678 | return; |
679 | } | |
b870472d PA |
680 | } |
681 | } | |
682 | ||
683 | static int usb_audio_set_output_altset(USBAudioState *s, int altset) | |
684 | { | |
685 | switch (altset) { | |
686 | case ALTSET_OFF: | |
b870472d PA |
687 | AUD_set_active_out(s->out.voice, false); |
688 | break; | |
3e44607e KZ |
689 | case ALTSET_STEREO: |
690 | case ALTSET_51: | |
691 | case ALTSET_71: | |
692 | if (s->out.channels != altset_channels[altset]) { | |
693 | usb_audio_reinit(USB_DEVICE(s), altset_channels[altset]); | |
694 | } | |
695 | streambuf_init(&s->out.buf, s->buffer, s->out.channels); | |
b870472d PA |
696 | AUD_set_active_out(s->out.voice, true); |
697 | break; | |
698 | default: | |
699 | return -1; | |
700 | } | |
701 | ||
702 | if (s->debug) { | |
703 | fprintf(stderr, "usb-audio: set interface %d\n", altset); | |
704 | } | |
705 | s->out.altset = altset; | |
706 | return 0; | |
707 | } | |
708 | ||
709 | /* | |
710 | * Note: we arbitrarily map the volume control range onto -inf..+8 dB | |
711 | */ | |
712 | #define ATTRIB_ID(cs, attrib, idif) \ | |
713 | (((cs) << 24) | ((attrib) << 16) | (idif)) | |
714 | ||
715 | static int usb_audio_get_control(USBAudioState *s, uint8_t attrib, | |
716 | uint16_t cscn, uint16_t idif, | |
717 | int length, uint8_t *data) | |
718 | { | |
719 | uint8_t cs = cscn >> 8; | |
720 | uint8_t cn = cscn - 1; /* -1 for the non-present master control */ | |
721 | uint32_t aid = ATTRIB_ID(cs, attrib, idif); | |
722 | int ret = USB_RET_STALL; | |
723 | ||
724 | switch (aid) { | |
725 | case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200): | |
3e44607e | 726 | data[0] = s->out.vol.mute; |
b870472d PA |
727 | ret = 1; |
728 | break; | |
729 | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200): | |
3e44607e KZ |
730 | if (cn < USBAUDIO_MAX_CHANNELS(s)) { |
731 | uint16_t vol = (s->out.vol.vol[cn] * 0x8800 + 127) / 255 + 0x8000; | |
b870472d PA |
732 | data[0] = vol; |
733 | data[1] = vol >> 8; | |
734 | ret = 2; | |
735 | } | |
736 | break; | |
737 | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200): | |
3e44607e | 738 | if (cn < USBAUDIO_MAX_CHANNELS(s)) { |
b870472d PA |
739 | data[0] = 0x01; |
740 | data[1] = 0x80; | |
741 | ret = 2; | |
742 | } | |
743 | break; | |
744 | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200): | |
3e44607e | 745 | if (cn < USBAUDIO_MAX_CHANNELS(s)) { |
b870472d PA |
746 | data[0] = 0x00; |
747 | data[1] = 0x08; | |
748 | ret = 2; | |
749 | } | |
750 | break; | |
751 | case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200): | |
3e44607e | 752 | if (cn < USBAUDIO_MAX_CHANNELS(s)) { |
b870472d PA |
753 | data[0] = 0x88; |
754 | data[1] = 0x00; | |
755 | ret = 2; | |
756 | } | |
757 | break; | |
758 | } | |
759 | ||
760 | return ret; | |
761 | } | |
762 | static int usb_audio_set_control(USBAudioState *s, uint8_t attrib, | |
763 | uint16_t cscn, uint16_t idif, | |
764 | int length, uint8_t *data) | |
765 | { | |
766 | uint8_t cs = cscn >> 8; | |
767 | uint8_t cn = cscn - 1; /* -1 for the non-present master control */ | |
768 | uint32_t aid = ATTRIB_ID(cs, attrib, idif); | |
769 | int ret = USB_RET_STALL; | |
770 | bool set_vol = false; | |
771 | ||
772 | switch (aid) { | |
773 | case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200): | |
3e44607e | 774 | s->out.vol.mute = data[0] & 1; |
b870472d PA |
775 | set_vol = true; |
776 | ret = 0; | |
777 | break; | |
778 | case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200): | |
3e44607e | 779 | if (cn < USBAUDIO_MAX_CHANNELS(s)) { |
b870472d PA |
780 | uint16_t vol = data[0] + (data[1] << 8); |
781 | ||
782 | if (s->debug) { | |
3e44607e KZ |
783 | fprintf(stderr, "usb-audio: cn %d vol %04x\n", cn, |
784 | (uint16_t)vol); | |
b870472d PA |
785 | } |
786 | ||
787 | vol -= 0x8000; | |
788 | vol = (vol * 255 + 0x4400) / 0x8800; | |
789 | if (vol > 255) { | |
790 | vol = 255; | |
791 | } | |
792 | ||
3e44607e | 793 | s->out.vol.vol[cn] = vol; |
b870472d PA |
794 | set_vol = true; |
795 | ret = 0; | |
796 | } | |
797 | break; | |
798 | } | |
799 | ||
800 | if (set_vol) { | |
801 | if (s->debug) { | |
3e44607e KZ |
802 | int i; |
803 | fprintf(stderr, "usb-audio: mute %d", s->out.vol.mute); | |
804 | for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) { | |
805 | fprintf(stderr, ", vol[%d] %3d", i, s->out.vol.vol[i]); | |
806 | } | |
807 | fprintf(stderr, "\n"); | |
b870472d | 808 | } |
3e44607e | 809 | audio_set_volume_out(s->out.voice, &s->out.vol); |
b870472d PA |
810 | } |
811 | ||
812 | return ret; | |
813 | } | |
814 | ||
9a77a0f5 | 815 | static void usb_audio_handle_control(USBDevice *dev, USBPacket *p, |
b870472d PA |
816 | int request, int value, int index, |
817 | int length, uint8_t *data) | |
818 | { | |
0389a0b1 | 819 | USBAudioState *s = USB_AUDIO(dev); |
b870472d PA |
820 | int ret = 0; |
821 | ||
822 | if (s->debug) { | |
823 | fprintf(stderr, "usb-audio: control transaction: " | |
824 | "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", | |
825 | request, value, index, length); | |
826 | } | |
827 | ||
828 | ret = usb_desc_handle_control(dev, p, request, value, index, length, data); | |
829 | if (ret >= 0) { | |
9a77a0f5 | 830 | return; |
b870472d PA |
831 | } |
832 | ||
833 | switch (request) { | |
834 | case ClassInterfaceRequest | CR_GET_CUR: | |
835 | case ClassInterfaceRequest | CR_GET_MIN: | |
836 | case ClassInterfaceRequest | CR_GET_MAX: | |
837 | case ClassInterfaceRequest | CR_GET_RES: | |
838 | ret = usb_audio_get_control(s, request & 0xff, value, index, | |
839 | length, data); | |
840 | if (ret < 0) { | |
841 | if (s->debug) { | |
842 | fprintf(stderr, "usb-audio: fail: get control\n"); | |
843 | } | |
844 | goto fail; | |
845 | } | |
9a77a0f5 | 846 | p->actual_length = ret; |
b870472d PA |
847 | break; |
848 | ||
849 | case ClassInterfaceOutRequest | CR_SET_CUR: | |
850 | case ClassInterfaceOutRequest | CR_SET_MIN: | |
851 | case ClassInterfaceOutRequest | CR_SET_MAX: | |
852 | case ClassInterfaceOutRequest | CR_SET_RES: | |
853 | ret = usb_audio_set_control(s, request & 0xff, value, index, | |
854 | length, data); | |
855 | if (ret < 0) { | |
856 | if (s->debug) { | |
857 | fprintf(stderr, "usb-audio: fail: set control\n"); | |
858 | } | |
859 | goto fail; | |
860 | } | |
861 | break; | |
862 | ||
863 | default: | |
864 | fail: | |
865 | if (s->debug) { | |
866 | fprintf(stderr, "usb-audio: failed control transaction: " | |
867 | "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", | |
868 | request, value, index, length); | |
869 | } | |
9a77a0f5 | 870 | p->status = USB_RET_STALL; |
b870472d PA |
871 | break; |
872 | } | |
b870472d PA |
873 | } |
874 | ||
875 | static void usb_audio_set_interface(USBDevice *dev, int iface, | |
876 | int old, int value) | |
877 | { | |
0389a0b1 | 878 | USBAudioState *s = USB_AUDIO(dev); |
b870472d PA |
879 | |
880 | if (iface == 1) { | |
881 | usb_audio_set_output_altset(s, value); | |
882 | } | |
883 | } | |
884 | ||
885 | static void usb_audio_handle_reset(USBDevice *dev) | |
886 | { | |
0389a0b1 | 887 | USBAudioState *s = USB_AUDIO(dev); |
b870472d PA |
888 | |
889 | if (s->debug) { | |
890 | fprintf(stderr, "usb-audio: reset\n"); | |
891 | } | |
892 | usb_audio_set_output_altset(s, ALTSET_OFF); | |
893 | } | |
894 | ||
9a77a0f5 | 895 | static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) |
b870472d | 896 | { |
b870472d | 897 | if (s->out.altset == ALTSET_OFF) { |
9a77a0f5 HG |
898 | p->status = USB_RET_STALL; |
899 | return; | |
b870472d PA |
900 | } |
901 | ||
3e44607e | 902 | streambuf_put(&s->out.buf, p, s->out.channels); |
9a77a0f5 | 903 | if (p->actual_length < p->iov.size && s->debug > 1) { |
b870472d | 904 | fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n", |
9a77a0f5 | 905 | p->iov.size - p->actual_length); |
b870472d | 906 | } |
b870472d PA |
907 | } |
908 | ||
9a77a0f5 | 909 | static void usb_audio_handle_data(USBDevice *dev, USBPacket *p) |
b870472d PA |
910 | { |
911 | USBAudioState *s = (USBAudioState *) dev; | |
b870472d | 912 | |
9a77a0f5 HG |
913 | if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) { |
914 | usb_audio_handle_dataout(s, p); | |
915 | return; | |
b870472d | 916 | } |
9a77a0f5 HG |
917 | |
918 | p->status = USB_RET_STALL; | |
919 | if (s->debug) { | |
b870472d PA |
920 | fprintf(stderr, "usb-audio: failed data transaction: " |
921 | "pid 0x%x ep 0x%x len 0x%zx\n", | |
079d0b7f | 922 | p->pid, p->ep->nr, p->iov.size); |
b870472d | 923 | } |
b870472d PA |
924 | } |
925 | ||
b69c3c21 | 926 | static void usb_audio_unrealize(USBDevice *dev) |
b870472d | 927 | { |
0389a0b1 | 928 | USBAudioState *s = USB_AUDIO(dev); |
b870472d PA |
929 | |
930 | if (s->debug) { | |
931 | fprintf(stderr, "usb-audio: destroy\n"); | |
932 | } | |
933 | ||
934 | usb_audio_set_output_altset(s, ALTSET_OFF); | |
935 | AUD_close_out(&s->card, s->out.voice); | |
936 | AUD_remove_card(&s->card); | |
937 | ||
938 | streambuf_fini(&s->out.buf); | |
939 | } | |
940 | ||
5450eeaa | 941 | static void usb_audio_realize(USBDevice *dev, Error **errp) |
b870472d | 942 | { |
0389a0b1 | 943 | USBAudioState *s = USB_AUDIO(dev); |
3e44607e KZ |
944 | int i; |
945 | ||
946 | dev->usb_desc = s->multi ? &desc_audio_multi : &desc_audio; | |
b870472d | 947 | |
9d55d1ad | 948 | usb_desc_create_serial(dev); |
b870472d PA |
949 | usb_desc_init(dev); |
950 | s->dev.opaque = s; | |
0389a0b1 | 951 | AUD_register_card(TYPE_USB_AUDIO, &s->card); |
b870472d PA |
952 | |
953 | s->out.altset = ALTSET_OFF; | |
3e44607e KZ |
954 | s->out.vol.mute = false; |
955 | for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) { | |
956 | s->out.vol.vol[i] = 240; /* 0 dB */ | |
957 | } | |
958 | ||
959 | usb_audio_reinit(dev, 2); | |
960 | } | |
961 | ||
962 | static void usb_audio_reinit(USBDevice *dev, unsigned channels) | |
963 | { | |
964 | USBAudioState *s = USB_AUDIO(dev); | |
965 | ||
966 | s->out.channels = channels; | |
967 | if (!s->buffer_user) { | |
968 | s->buffer = 32 * USBAUDIO_PACKET_SIZE(s->out.channels); | |
969 | } else { | |
970 | s->buffer = s->buffer_user; | |
971 | } | |
972 | ||
973 | s->out.vol.channels = s->out.channels; | |
b870472d | 974 | s->out.as.freq = USBAUDIO_SAMPLE_RATE; |
3e44607e | 975 | s->out.as.nchannels = s->out.channels; |
85bc5852 | 976 | s->out.as.fmt = AUDIO_FORMAT_S16; |
b870472d | 977 | s->out.as.endianness = 0; |
3e44607e | 978 | streambuf_init(&s->out.buf, s->buffer, s->out.channels); |
b870472d | 979 | |
0389a0b1 | 980 | s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO, |
b870472d | 981 | s, output_callback, &s->out.as); |
3e44607e | 982 | audio_set_volume_out(s->out.voice, &s->out.vol); |
b870472d | 983 | AUD_set_active_out(s->out.voice, 0); |
b870472d PA |
984 | } |
985 | ||
986 | static const VMStateDescription vmstate_usb_audio = { | |
0389a0b1 | 987 | .name = TYPE_USB_AUDIO, |
b870472d PA |
988 | .unmigratable = 1, |
989 | }; | |
990 | ||
39bffca2 | 991 | static Property usb_audio_properties[] = { |
88e47b9a | 992 | DEFINE_AUDIO_PROPERTIES(USBAudioState, card), |
39bffca2 | 993 | DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), |
3e44607e KZ |
994 | DEFINE_PROP_UINT32("buffer", USBAudioState, buffer_user, 0), |
995 | DEFINE_PROP_BOOL("multi", USBAudioState, multi, false), | |
39bffca2 AL |
996 | DEFINE_PROP_END_OF_LIST(), |
997 | }; | |
998 | ||
62aed765 AL |
999 | static void usb_audio_class_init(ObjectClass *klass, void *data) |
1000 | { | |
39bffca2 | 1001 | DeviceClass *dc = DEVICE_CLASS(klass); |
62aed765 AL |
1002 | USBDeviceClass *k = USB_DEVICE_CLASS(klass); |
1003 | ||
39bffca2 | 1004 | dc->vmsd = &vmstate_usb_audio; |
4f67d30b | 1005 | device_class_set_props(dc, usb_audio_properties); |
125ee0ed | 1006 | set_bit(DEVICE_CATEGORY_SOUND, dc->categories); |
62aed765 | 1007 | k->product_desc = "QEMU USB Audio Interface"; |
5450eeaa | 1008 | k->realize = usb_audio_realize; |
62aed765 AL |
1009 | k->handle_reset = usb_audio_handle_reset; |
1010 | k->handle_control = usb_audio_handle_control; | |
1011 | k->handle_data = usb_audio_handle_data; | |
c4fe9700 | 1012 | k->unrealize = usb_audio_unrealize; |
62aed765 AL |
1013 | k->set_interface = usb_audio_set_interface; |
1014 | } | |
1015 | ||
8c43a6f0 | 1016 | static const TypeInfo usb_audio_info = { |
0389a0b1 | 1017 | .name = TYPE_USB_AUDIO, |
39bffca2 AL |
1018 | .parent = TYPE_USB_DEVICE, |
1019 | .instance_size = sizeof(USBAudioState), | |
1020 | .class_init = usb_audio_class_init, | |
b870472d PA |
1021 | }; |
1022 | ||
83f7d43a | 1023 | static void usb_audio_register_types(void) |
b870472d | 1024 | { |
39bffca2 | 1025 | type_register_static(&usb_audio_info); |
0389a0b1 | 1026 | usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL); |
b870472d PA |
1027 | } |
1028 | ||
83f7d43a | 1029 | type_init(usb_audio_register_types) |