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