1253544Shselasky/*-
2253544Shselasky * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3253544Shselasky *
4253544Shselasky * Redistribution and use in source and binary forms, with or without
5253544Shselasky * modification, are permitted provided that the following conditions
6253544Shselasky * are met:
7253544Shselasky * 1. Redistributions of source code must retain the above copyright
8253544Shselasky *    notice, this list of conditions and the following disclaimer.
9253544Shselasky * 2. Redistributions in binary form must reproduce the above copyright
10253544Shselasky *    notice, this list of conditions and the following disclaimer in the
11253544Shselasky *    documentation and/or other materials provided with the distribution.
12253544Shselasky *
13253544Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14253544Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15253544Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16253544Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17253544Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18253544Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19253544Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20253544Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21253544Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22253544Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23253544Shselasky * SUCH DAMAGE.
24253544Shselasky */
25253544Shselasky
26253544Shselasky/*
27253544Shselasky * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
28253544Shselasky *		    http://www.usb.org/developers/devclass_docs/frmts10.pdf
29253544Shselasky *		    http://www.usb.org/developers/devclass_docs/termt10.pdf
30253544Shselasky */
31253544Shselasky
32253618Sobrien#include <sys/param.h>
33253618Sobrien__FBSDID("$FreeBSD$");
34253618Sobrien
35253544Shselasky#include <sys/stdint.h>
36253544Shselasky#include <sys/stddef.h>
37253544Shselasky#include <sys/queue.h>
38253544Shselasky#include <sys/systm.h>
39253544Shselasky#include <sys/kernel.h>
40253544Shselasky#include <sys/bus.h>
41253544Shselasky#include <sys/linker_set.h>
42253544Shselasky#include <sys/module.h>
43253544Shselasky#include <sys/lock.h>
44253544Shselasky#include <sys/mutex.h>
45253544Shselasky#include <sys/condvar.h>
46253544Shselasky#include <sys/sysctl.h>
47253544Shselasky#include <sys/sx.h>
48253544Shselasky#include <sys/unistd.h>
49253544Shselasky#include <sys/callout.h>
50253544Shselasky#include <sys/malloc.h>
51253544Shselasky#include <sys/priv.h>
52253544Shselasky
53253544Shselasky#include <dev/usb/usb.h>
54253544Shselasky#include <dev/usb/usb_cdc.h>
55253544Shselasky#include <dev/usb/usbdi.h>
56253544Shselasky#include <dev/usb/usbdi_util.h>
57253544Shselasky#include <dev/usb/usbhid.h>
58253544Shselasky#include "usb_if.h"
59253544Shselasky
60253544Shselasky#define	USB_DEBUG_VAR g_audio_debug
61253544Shselasky#include <dev/usb/usb_debug.h>
62253544Shselasky
63253544Shselasky#include <dev/usb/gadget/g_audio.h>
64253544Shselasky
65253544Shselaskyenum {
66253544Shselasky	G_AUDIO_ISOC0_RD,
67253544Shselasky	G_AUDIO_ISOC1_RD,
68253544Shselasky	G_AUDIO_ISOC0_WR,
69253544Shselasky	G_AUDIO_ISOC1_WR,
70253544Shselasky	G_AUDIO_N_TRANSFER,
71253544Shselasky};
72253544Shselasky
73253544Shselaskystruct g_audio_softc {
74253544Shselasky	struct mtx sc_mtx;
75253544Shselasky	struct usb_callout sc_callout;
76253544Shselasky	struct usb_callout sc_watchdog;
77253544Shselasky	struct usb_xfer *sc_xfer[G_AUDIO_N_TRANSFER];
78253544Shselasky
79253544Shselasky	int	sc_mode;
80253544Shselasky	int	sc_pattern_len;
81253544Shselasky	int	sc_throughput;
82253544Shselasky	int	sc_tx_interval;
83253544Shselasky	int	sc_state;
84253544Shselasky	int	sc_noise_rem;
85253544Shselasky
86253544Shselasky	int8_t	sc_pattern[G_AUDIO_MAX_STRLEN];
87253544Shselasky
88253544Shselasky	uint16_t sc_data_len[2][G_AUDIO_FRAMES];
89253544Shselasky
90253544Shselasky	int16_t	sc_data_buf[2][G_AUDIO_BUFSIZE / 2];
91253544Shselasky
92253544Shselasky	uint8_t	sc_volume_setting[32];
93253544Shselasky	uint8_t	sc_volume_limit[32];
94253544Shselasky	uint8_t	sc_sample_rate[32];
95253544Shselasky};
96253544Shselasky
97253544Shselaskystatic SYSCTL_NODE(_hw_usb, OID_AUTO, g_audio, CTLFLAG_RW, 0, "USB audio gadget");
98253544Shselasky
99253544Shselasky#ifdef USB_DEBUG
100253544Shselaskystatic int g_audio_debug = 0;
101253544Shselasky
102276701ShselaskySYSCTL_INT(_hw_usb_g_audio, OID_AUTO, debug, CTLFLAG_RWTUN,
103253544Shselasky    &g_audio_debug, 0, "Debug level");
104253544Shselasky#endif
105253544Shselasky
106253544Shselaskystatic int g_audio_mode = 0;
107253544Shselasky
108276701ShselaskySYSCTL_INT(_hw_usb_g_audio, OID_AUTO, mode, CTLFLAG_RWTUN,
109253544Shselasky    &g_audio_mode, 0, "Mode selection");
110253544Shselasky
111253544Shselaskystatic int g_audio_pattern_interval = 1000;
112253544Shselasky
113276701ShselaskySYSCTL_INT(_hw_usb_g_audio, OID_AUTO, pattern_interval, CTLFLAG_RWTUN,
114253544Shselasky    &g_audio_pattern_interval, 0, "Pattern interval in milliseconds");
115253544Shselasky
116253544Shselaskystatic char g_audio_pattern_data[G_AUDIO_MAX_STRLEN];
117253544Shselasky
118253544ShselaskySYSCTL_STRING(_hw_usb_g_audio, OID_AUTO, pattern, CTLFLAG_RW,
119253544Shselasky    &g_audio_pattern_data, sizeof(g_audio_pattern_data), "Data pattern");
120253544Shselasky
121253544Shselaskystatic int g_audio_throughput;
122253544Shselasky
123253544ShselaskySYSCTL_INT(_hw_usb_g_audio, OID_AUTO, throughput, CTLFLAG_RD,
124253544Shselasky    &g_audio_throughput, sizeof(g_audio_throughput), "Throughput in bytes per second");
125253544Shselasky
126253544Shselaskystatic device_probe_t g_audio_probe;
127253544Shselaskystatic device_attach_t g_audio_attach;
128253544Shselaskystatic device_detach_t g_audio_detach;
129253544Shselaskystatic usb_handle_request_t g_audio_handle_request;
130253544Shselasky
131253544Shselaskystatic usb_callback_t g_audio_isoc_read_callback;
132253544Shselaskystatic usb_callback_t g_audio_isoc_write_callback;
133253544Shselasky
134253544Shselaskystatic devclass_t g_audio_devclass;
135253544Shselasky
136253544Shselaskystatic void g_audio_watchdog(void *arg);
137253544Shselaskystatic void g_audio_timeout(void *arg);
138253544Shselasky
139253544Shselaskystatic device_method_t g_audio_methods[] = {
140253544Shselasky	/* USB interface */
141253544Shselasky	DEVMETHOD(usb_handle_request, g_audio_handle_request),
142253544Shselasky
143253544Shselasky	/* Device interface */
144253544Shselasky	DEVMETHOD(device_probe, g_audio_probe),
145253544Shselasky	DEVMETHOD(device_attach, g_audio_attach),
146253544Shselasky	DEVMETHOD(device_detach, g_audio_detach),
147253544Shselasky
148253544Shselasky	DEVMETHOD_END
149253544Shselasky};
150253544Shselasky
151253544Shselaskystatic driver_t g_audio_driver = {
152253544Shselasky	.name = "g_audio",
153253544Shselasky	.methods = g_audio_methods,
154253544Shselasky	.size = sizeof(struct g_audio_softc),
155253544Shselasky};
156253544Shselasky
157253544ShselaskyDRIVER_MODULE(g_audio, uhub, g_audio_driver, g_audio_devclass, 0, 0);
158253544ShselaskyMODULE_DEPEND(g_audio, usb, 1, 1, 1);
159253544Shselasky
160253544Shselaskystatic const struct usb_config g_audio_config[G_AUDIO_N_TRANSFER] = {
161253544Shselasky
162253544Shselasky	[G_AUDIO_ISOC0_RD] = {
163253544Shselasky		.type = UE_ISOCHRONOUS,
164253544Shselasky		.endpoint = UE_ADDR_ANY,
165253544Shselasky		.direction = UE_DIR_RX,
166253544Shselasky		.flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
167253544Shselasky		.bufsize = G_AUDIO_BUFSIZE,
168253544Shselasky		.callback = &g_audio_isoc_read_callback,
169253544Shselasky		.frames = G_AUDIO_FRAMES,
170253544Shselasky		.usb_mode = USB_MODE_DEVICE,
171253544Shselasky		.if_index = 1,
172253544Shselasky	},
173253544Shselasky
174253544Shselasky	[G_AUDIO_ISOC1_RD] = {
175253544Shselasky		.type = UE_ISOCHRONOUS,
176253544Shselasky		.endpoint = UE_ADDR_ANY,
177253544Shselasky		.direction = UE_DIR_RX,
178253544Shselasky		.flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
179253544Shselasky		.bufsize = G_AUDIO_BUFSIZE,
180253544Shselasky		.callback = &g_audio_isoc_read_callback,
181253544Shselasky		.frames = G_AUDIO_FRAMES,
182253544Shselasky		.usb_mode = USB_MODE_DEVICE,
183253544Shselasky		.if_index = 1,
184253544Shselasky	},
185253544Shselasky
186253544Shselasky	[G_AUDIO_ISOC0_WR] = {
187253544Shselasky		.type = UE_ISOCHRONOUS,
188253544Shselasky		.endpoint = UE_ADDR_ANY,
189253544Shselasky		.direction = UE_DIR_TX,
190253544Shselasky		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
191253544Shselasky		.bufsize = G_AUDIO_BUFSIZE,
192253544Shselasky		.callback = &g_audio_isoc_write_callback,
193253544Shselasky		.frames = G_AUDIO_FRAMES,
194253544Shselasky		.usb_mode = USB_MODE_DEVICE,
195253544Shselasky		.if_index = 2,
196253544Shselasky	},
197253544Shselasky
198253544Shselasky	[G_AUDIO_ISOC1_WR] = {
199253544Shselasky		.type = UE_ISOCHRONOUS,
200253544Shselasky		.endpoint = UE_ADDR_ANY,
201253544Shselasky		.direction = UE_DIR_TX,
202253544Shselasky		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
203253544Shselasky		.bufsize = G_AUDIO_BUFSIZE,
204253544Shselasky		.callback = &g_audio_isoc_write_callback,
205253544Shselasky		.frames = G_AUDIO_FRAMES,
206253544Shselasky		.usb_mode = USB_MODE_DEVICE,
207253544Shselasky		.if_index = 2,
208253544Shselasky	},
209253544Shselasky};
210253544Shselasky
211253544Shselaskystatic void
212253544Shselaskyg_audio_timeout_reset(struct g_audio_softc *sc)
213253544Shselasky{
214253544Shselasky	int i = g_audio_pattern_interval;
215253544Shselasky
216253544Shselasky	sc->sc_tx_interval = i;
217253544Shselasky
218253544Shselasky	if (i <= 0)
219253544Shselasky		i = 1;
220253544Shselasky	else if (i > 1023)
221253544Shselasky		i = 1023;
222253544Shselasky
223253544Shselasky	i = USB_MS_TO_TICKS(i);
224253544Shselasky
225253544Shselasky	usb_callout_reset(&sc->sc_callout, i, &g_audio_timeout, sc);
226253544Shselasky}
227253544Shselasky
228253544Shselaskystatic void
229253544Shselaskyg_audio_timeout(void *arg)
230253544Shselasky{
231253544Shselasky	struct g_audio_softc *sc = arg;
232253544Shselasky
233253544Shselasky	sc->sc_mode = g_audio_mode;
234253544Shselasky
235253544Shselasky	memcpy(sc->sc_pattern, g_audio_pattern_data, sizeof(sc->sc_pattern));
236253544Shselasky
237253544Shselasky	sc->sc_pattern[G_AUDIO_MAX_STRLEN - 1] = 0;
238253544Shselasky
239253544Shselasky	sc->sc_pattern_len = strlen(sc->sc_pattern);
240253544Shselasky
241253544Shselasky	if (sc->sc_mode != G_AUDIO_MODE_LOOP) {
242253544Shselasky		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_WR]);
243253544Shselasky		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_WR]);
244253544Shselasky	}
245253544Shselasky	g_audio_timeout_reset(sc);
246253544Shselasky}
247253544Shselasky
248253544Shselaskystatic void
249253544Shselaskyg_audio_watchdog_reset(struct g_audio_softc *sc)
250253544Shselasky{
251253544Shselasky	usb_callout_reset(&sc->sc_watchdog, hz, &g_audio_watchdog, sc);
252253544Shselasky}
253253544Shselasky
254253544Shselaskystatic void
255253544Shselaskyg_audio_watchdog(void *arg)
256253544Shselasky{
257253544Shselasky	struct g_audio_softc *sc = arg;
258253544Shselasky	int i;
259253544Shselasky
260253544Shselasky	i = sc->sc_throughput;
261253544Shselasky
262253544Shselasky	sc->sc_throughput = 0;
263253544Shselasky
264253544Shselasky	g_audio_throughput = i;
265253544Shselasky
266253544Shselasky	g_audio_watchdog_reset(sc);
267253544Shselasky}
268253544Shselasky
269253544Shselaskystatic int
270253544Shselaskyg_audio_probe(device_t dev)
271253544Shselasky{
272253544Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
273253544Shselasky
274253544Shselasky	DPRINTFN(11, "\n");
275253544Shselasky
276253544Shselasky	if (uaa->usb_mode != USB_MODE_DEVICE)
277253544Shselasky		return (ENXIO);
278253544Shselasky
279253544Shselasky	if ((uaa->info.bInterfaceClass == UICLASS_AUDIO) &&
280253544Shselasky	    (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL))
281253544Shselasky		return (0);
282253544Shselasky
283253544Shselasky	return (ENXIO);
284253544Shselasky}
285253544Shselasky
286253544Shselaskystatic int
287253544Shselaskyg_audio_attach(device_t dev)
288253544Shselasky{
289253544Shselasky	struct g_audio_softc *sc = device_get_softc(dev);
290253544Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
291253544Shselasky	int error;
292253544Shselasky	int i;
293253544Shselasky	uint8_t iface_index[3];
294253544Shselasky
295253544Shselasky	DPRINTFN(11, "\n");
296253544Shselasky
297253544Shselasky	device_set_usb_desc(dev);
298253544Shselasky
299253544Shselasky	mtx_init(&sc->sc_mtx, "g_audio", NULL, MTX_DEF);
300253544Shselasky
301253544Shselasky	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
302253544Shselasky	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
303253544Shselasky
304253544Shselasky	sc->sc_mode = G_AUDIO_MODE_SILENT;
305253544Shselasky
306253544Shselasky	sc->sc_noise_rem = 1;
307253544Shselasky
308253544Shselasky	for (i = 0; i != G_AUDIO_FRAMES; i++) {
309253544Shselasky		sc->sc_data_len[0][i] = G_AUDIO_BUFSIZE / G_AUDIO_FRAMES;
310253544Shselasky		sc->sc_data_len[1][i] = G_AUDIO_BUFSIZE / G_AUDIO_FRAMES;
311253544Shselasky	}
312253544Shselasky
313253544Shselasky	iface_index[0] = uaa->info.bIfaceIndex;
314253544Shselasky	iface_index[1] = uaa->info.bIfaceIndex + 1;
315253544Shselasky	iface_index[2] = uaa->info.bIfaceIndex + 2;
316253544Shselasky
317253544Shselasky	error = usbd_set_alt_interface_index(uaa->device, iface_index[1], 1);
318253544Shselasky	if (error) {
319253544Shselasky		DPRINTF("alt iface setting error=%s\n", usbd_errstr(error));
320253544Shselasky		goto detach;
321253544Shselasky	}
322253544Shselasky	error = usbd_set_alt_interface_index(uaa->device, iface_index[2], 1);
323253544Shselasky	if (error) {
324253544Shselasky		DPRINTF("alt iface setting error=%s\n", usbd_errstr(error));
325253544Shselasky		goto detach;
326253544Shselasky	}
327253544Shselasky	error = usbd_transfer_setup(uaa->device,
328253544Shselasky	    iface_index, sc->sc_xfer, g_audio_config,
329253544Shselasky	    G_AUDIO_N_TRANSFER, sc, &sc->sc_mtx);
330253544Shselasky
331253544Shselasky	if (error) {
332253544Shselasky		DPRINTF("error=%s\n", usbd_errstr(error));
333253544Shselasky		goto detach;
334253544Shselasky	}
335253544Shselasky	usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
336253544Shselasky	usbd_set_parent_iface(uaa->device, iface_index[2], iface_index[0]);
337253544Shselasky
338253544Shselasky	mtx_lock(&sc->sc_mtx);
339253544Shselasky
340253544Shselasky	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_RD]);
341253544Shselasky	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_RD]);
342253544Shselasky
343253544Shselasky	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_WR]);
344253544Shselasky	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_WR]);
345253544Shselasky
346253544Shselasky	g_audio_timeout_reset(sc);
347253544Shselasky
348253544Shselasky	g_audio_watchdog_reset(sc);
349253544Shselasky
350253544Shselasky	mtx_unlock(&sc->sc_mtx);
351253544Shselasky
352253544Shselasky	return (0);			/* success */
353253544Shselasky
354253544Shselaskydetach:
355253544Shselasky	g_audio_detach(dev);
356253544Shselasky
357253544Shselasky	return (ENXIO);			/* error */
358253544Shselasky}
359253544Shselasky
360253544Shselaskystatic int
361253544Shselaskyg_audio_detach(device_t dev)
362253544Shselasky{
363253544Shselasky	struct g_audio_softc *sc = device_get_softc(dev);
364253544Shselasky
365253544Shselasky	DPRINTF("\n");
366253544Shselasky
367253544Shselasky	mtx_lock(&sc->sc_mtx);
368253544Shselasky	usb_callout_stop(&sc->sc_callout);
369253544Shselasky	usb_callout_stop(&sc->sc_watchdog);
370253544Shselasky	mtx_unlock(&sc->sc_mtx);
371253544Shselasky
372253544Shselasky	usbd_transfer_unsetup(sc->sc_xfer, G_AUDIO_N_TRANSFER);
373253544Shselasky
374253544Shselasky	usb_callout_drain(&sc->sc_callout);
375253544Shselasky	usb_callout_drain(&sc->sc_watchdog);
376253544Shselasky
377253544Shselasky	mtx_destroy(&sc->sc_mtx);
378253544Shselasky
379253544Shselasky	return (0);
380253544Shselasky}
381253544Shselasky
382253544Shselasky
383253544Shselaskystatic int32_t
384253544Shselaskyg_noise(struct g_audio_softc *sc)
385253544Shselasky{
386253544Shselasky	uint32_t temp;
387253544Shselasky	const uint32_t prime = 0xFFFF1D;
388253544Shselasky
389253544Shselasky	if (sc->sc_noise_rem & 1) {
390253544Shselasky		sc->sc_noise_rem += prime;
391253544Shselasky	}
392253544Shselasky	sc->sc_noise_rem /= 2;
393253544Shselasky
394253544Shselasky	temp = sc->sc_noise_rem;
395253544Shselasky
396253544Shselasky	/* unsigned to signed conversion */
397253544Shselasky
398253544Shselasky	temp ^= 0x800000;
399253544Shselasky	if (temp & 0x800000) {
400253544Shselasky		temp |= (-0x800000);
401253544Shselasky	}
402253544Shselasky	return temp;
403253544Shselasky}
404253544Shselasky
405253544Shselaskystatic void
406253544Shselaskyg_audio_make_samples(struct g_audio_softc *sc, int16_t *ptr, int samples)
407253544Shselasky{
408253544Shselasky	int i;
409253544Shselasky	int j;
410253544Shselasky
411253544Shselasky	for (i = 0; i != samples; i++) {
412253544Shselasky
413253544Shselasky		j = g_noise(sc);
414253544Shselasky
415253544Shselasky		if ((sc->sc_state < 0) || (sc->sc_state >= sc->sc_pattern_len))
416253544Shselasky			sc->sc_state = 0;
417253544Shselasky
418253544Shselasky		if (sc->sc_pattern_len != 0) {
419253544Shselasky			j = (j * sc->sc_pattern[sc->sc_state]) >> 16;
420253544Shselasky			sc->sc_state++;
421253544Shselasky		}
422253544Shselasky		*ptr++ = j / 256;
423253544Shselasky		*ptr++ = j / 256;
424253544Shselasky	}
425253544Shselasky}
426253544Shselasky
427253544Shselaskystatic void
428253544Shselaskyg_audio_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
429253544Shselasky{
430253544Shselasky	struct g_audio_softc *sc = usbd_xfer_softc(xfer);
431253544Shselasky	int actlen;
432253544Shselasky	int aframes;
433253544Shselasky	int nr = (xfer == sc->sc_xfer[G_AUDIO_ISOC0_WR]) ? 0 : 1;
434253544Shselasky	int16_t *ptr;
435253544Shselasky	int i;
436253544Shselasky
437253544Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
438253544Shselasky
439253544Shselasky	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
440253544Shselasky	    USB_GET_STATE(xfer), aframes, actlen);
441253544Shselasky
442253544Shselasky	switch (USB_GET_STATE(xfer)) {
443253544Shselasky	case USB_ST_TRANSFERRED:
444253544Shselasky
445253544Shselasky		sc->sc_throughput += actlen;
446253544Shselasky
447253544Shselasky		if (sc->sc_mode == G_AUDIO_MODE_LOOP)
448253544Shselasky			break;		/* sync with RX */
449253544Shselasky
450253544Shselasky	case USB_ST_SETUP:
451253544Shselaskytr_setup:
452253544Shselasky
453253544Shselasky		ptr = sc->sc_data_buf[nr];
454253544Shselasky
455253544Shselasky		if (sc->sc_mode == G_AUDIO_MODE_PATTERN) {
456253544Shselasky
457253544Shselasky			for (i = 0; i != G_AUDIO_FRAMES; i++) {
458253544Shselasky
459253544Shselasky				usbd_xfer_set_frame_data(xfer, i, ptr, sc->sc_data_len[nr][i]);
460253544Shselasky
461253544Shselasky				g_audio_make_samples(sc, ptr, (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2);
462253544Shselasky
463253544Shselasky				ptr += (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2;
464253544Shselasky			}
465253544Shselasky		} else if (sc->sc_mode == G_AUDIO_MODE_LOOP) {
466253544Shselasky
467253544Shselasky			for (i = 0; i != G_AUDIO_FRAMES; i++) {
468253544Shselasky
469253544Shselasky				usbd_xfer_set_frame_data(xfer, i, ptr, sc->sc_data_len[nr][i] & ~3);
470253544Shselasky
471253544Shselasky				g_audio_make_samples(sc, ptr, sc->sc_data_len[nr][i] / 4);
472253544Shselasky
473253544Shselasky				ptr += (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2;
474253544Shselasky			}
475253544Shselasky		}
476253544Shselasky		break;
477253544Shselasky
478253544Shselasky	default:			/* Error */
479253544Shselasky		DPRINTF("error=%s\n", usbd_errstr(error));
480253544Shselasky
481253544Shselasky		if (error != USB_ERR_CANCELLED) {
482253544Shselasky			/* try to clear stall first */
483253544Shselasky			usbd_xfer_set_stall(xfer);
484253544Shselasky			goto tr_setup;
485253544Shselasky		}
486253544Shselasky		break;
487253544Shselasky	}
488253544Shselasky}
489253544Shselasky
490253544Shselaskystatic void
491253544Shselaskyg_audio_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
492253544Shselasky{
493253544Shselasky	struct g_audio_softc *sc = usbd_xfer_softc(xfer);
494253544Shselasky	int actlen;
495253544Shselasky	int aframes;
496253544Shselasky	int nr = (xfer == sc->sc_xfer[G_AUDIO_ISOC0_RD]) ? 0 : 1;
497253544Shselasky	int16_t *ptr;
498253544Shselasky	int i;
499253544Shselasky
500253544Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
501253544Shselasky
502253544Shselasky	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
503253544Shselasky	    USB_GET_STATE(xfer), aframes, actlen);
504253544Shselasky
505253544Shselasky	switch (USB_GET_STATE(xfer)) {
506253544Shselasky	case USB_ST_TRANSFERRED:
507253544Shselasky
508253544Shselasky		sc->sc_throughput += actlen;
509253544Shselasky
510253544Shselasky		for (i = 0; i != G_AUDIO_FRAMES; i++) {
511253544Shselasky			sc->sc_data_len[nr][i] = usbd_xfer_frame_len(xfer, i);
512253544Shselasky		}
513253544Shselasky
514253544Shselasky		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_WR]);
515253544Shselasky		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_WR]);
516253544Shselasky
517253544Shselasky		break;
518253544Shselasky
519253544Shselasky	case USB_ST_SETUP:
520253544Shselaskytr_setup:
521253544Shselasky		ptr = sc->sc_data_buf[nr];
522253544Shselasky
523253544Shselasky		for (i = 0; i != G_AUDIO_FRAMES; i++) {
524253544Shselasky
525253544Shselasky			usbd_xfer_set_frame_data(xfer, i, ptr,
526253544Shselasky			    G_AUDIO_BUFSIZE / G_AUDIO_FRAMES);
527253544Shselasky
528253544Shselasky			ptr += (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2;
529253544Shselasky		}
530253544Shselasky
531253544Shselasky		usbd_transfer_submit(xfer);
532253544Shselasky		break;
533253544Shselasky
534253544Shselasky	default:			/* Error */
535253544Shselasky		DPRINTF("error=%s\n", usbd_errstr(error));
536253544Shselasky
537253544Shselasky		if (error != USB_ERR_CANCELLED) {
538253544Shselasky			/* try to clear stall first */
539253544Shselasky			usbd_xfer_set_stall(xfer);
540253544Shselasky			goto tr_setup;
541253544Shselasky		}
542253544Shselasky		break;
543253544Shselasky	}
544253544Shselasky}
545253544Shselasky
546253544Shselasky
547253544Shselaskystatic int
548253544Shselaskyg_audio_handle_request(device_t dev,
549253544Shselasky    const void *preq, void **pptr, uint16_t *plen,
550253544Shselasky    uint16_t offset, uint8_t *pstate)
551253544Shselasky{
552253544Shselasky	struct g_audio_softc *sc = device_get_softc(dev);
553253544Shselasky	const struct usb_device_request *req = preq;
554253544Shselasky	uint8_t is_complete = *pstate;
555253544Shselasky
556253544Shselasky	if (!is_complete) {
557253544Shselasky		if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
558253544Shselasky		    (req->bRequest == 0x82 /* get min */ )) {
559253544Shselasky
560253544Shselasky			if (offset == 0) {
561253544Shselasky				USETW(sc->sc_volume_limit, 0);
562253544Shselasky				*plen = 2;
563253544Shselasky				*pptr = &sc->sc_volume_limit;
564253544Shselasky			} else {
565253544Shselasky				*plen = 0;
566253544Shselasky			}
567253544Shselasky			return (0);
568253544Shselasky		} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
569253544Shselasky		    (req->bRequest == 0x83 /* get max */ )) {
570253544Shselasky
571253544Shselasky			if (offset == 0) {
572253544Shselasky				USETW(sc->sc_volume_limit, 0x2000);
573253544Shselasky				*plen = 2;
574253544Shselasky				*pptr = &sc->sc_volume_limit;
575253544Shselasky			} else {
576253544Shselasky				*plen = 0;
577253544Shselasky			}
578253544Shselasky			return (0);
579253544Shselasky		} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
580253544Shselasky		    (req->bRequest == 0x84 /* get residue */ )) {
581253544Shselasky
582253544Shselasky			if (offset == 0) {
583269664Shselasky				USETW(sc->sc_volume_limit, 1);
584253544Shselasky				*plen = 2;
585253544Shselasky				*pptr = &sc->sc_volume_limit;
586253544Shselasky			} else {
587253544Shselasky				*plen = 0;
588253544Shselasky			}
589253544Shselasky			return (0);
590269602Shselasky		} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
591269602Shselasky		    (req->bRequest == 0x81 /* get value */ )) {
592269602Shselasky
593269602Shselasky			if (offset == 0) {
594269664Shselasky				USETW(sc->sc_volume_setting, 0x2000);
595269602Shselasky				*plen = sizeof(sc->sc_volume_setting);
596269602Shselasky				*pptr = &sc->sc_volume_setting;
597269602Shselasky			} else {
598269602Shselasky				*plen = 0;
599269602Shselasky			}
600269602Shselasky			return (0);
601253544Shselasky		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
602253544Shselasky		    (req->bRequest == 0x01 /* set value */ )) {
603253544Shselasky
604253544Shselasky			if (offset == 0) {
605253544Shselasky				*plen = sizeof(sc->sc_volume_setting);
606253544Shselasky				*pptr = &sc->sc_volume_setting;
607253544Shselasky			} else {
608253544Shselasky				*plen = 0;
609253544Shselasky			}
610253544Shselasky			return (0);
611253544Shselasky		} else if ((req->bmRequestType == UT_WRITE_CLASS_ENDPOINT) &&
612253544Shselasky		    (req->bRequest == 0x01 /* set value */ )) {
613253544Shselasky
614253544Shselasky			if (offset == 0) {
615253544Shselasky				*plen = sizeof(sc->sc_sample_rate);
616253544Shselasky				*pptr = &sc->sc_sample_rate;
617253544Shselasky			} else {
618253544Shselasky				*plen = 0;
619253544Shselasky			}
620253544Shselasky			return (0);
621253544Shselasky		}
622253544Shselasky	}
623253544Shselasky	return (ENXIO);			/* use builtin handler */
624253544Shselasky}
625