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