xref: /dragonfly/sys/bus/u4b/gadget/g_audio.c (revision 2b3f93ea)
1 /*-
2  * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD: head/sys/dev/usb/gadget/g_audio.c 269664 2014-08-07 12:47:25Z hselasky $
26  */
27 
28 /*
29  * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
30  *		    http://www.usb.org/developers/devclass_docs/frmts10.pdf
31  *		    http://www.usb.org/developers/devclass_docs/termt10.pdf
32  */
33 
34 #include <sys/stdint.h>
35 #include <sys/queue.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/bus.h>
39 #include <sys/linker_set.h>
40 #include <sys/module.h>
41 #include <sys/lock.h>
42 #include <sys/condvar.h>
43 #include <sys/sysctl.h>
44 #include <sys/unistd.h>
45 #include <sys/callout.h>
46 #include <sys/malloc.h>
47 #include <sys/caps.h>
48 
49 #include <bus/u4b/usb.h>
50 #include <bus/u4b/usb_cdc.h>
51 #include <bus/u4b/usbdi.h>
52 #include <bus/u4b/usbdi_util.h>
53 #include <bus/u4b/usbhid.h>
54 #include "usb_if.h"
55 
56 #define	USB_DEBUG_VAR g_audio_debug
57 #include <bus/u4b/usb_debug.h>
58 
59 #include <bus/u4b/gadget/g_audio.h>
60 
61 enum {
62 	G_AUDIO_ISOC0_RD,
63 	G_AUDIO_ISOC1_RD,
64 	G_AUDIO_ISOC0_WR,
65 	G_AUDIO_ISOC1_WR,
66 	G_AUDIO_N_TRANSFER,
67 };
68 
69 struct g_audio_softc {
70 	struct lock sc_lock;
71 	struct usb_callout sc_callout;
72 	struct usb_callout sc_watchdog;
73 	struct usb_xfer *sc_xfer[G_AUDIO_N_TRANSFER];
74 
75 	int	sc_mode;
76 	int	sc_pattern_len;
77 	int	sc_throughput;
78 	int	sc_tx_interval;
79 	int	sc_state;
80 	int	sc_noise_rem;
81 
82 	int8_t	sc_pattern[G_AUDIO_MAX_STRLEN];
83 
84 	uint16_t sc_data_len[2][G_AUDIO_FRAMES];
85 
86 	int16_t	sc_data_buf[2][G_AUDIO_BUFSIZE / 2];
87 
88 	uint8_t	sc_volume_setting[32];
89 	uint8_t	sc_volume_limit[32];
90 	uint8_t	sc_sample_rate[32];
91 };
92 
93 static SYSCTL_NODE(_hw_usb, OID_AUTO, g_audio, CTLFLAG_RW, 0, "USB audio gadget");
94 
95 #ifdef USB_DEBUG
96 static int g_audio_debug = 0;
97 
98 SYSCTL_INT(_hw_usb_g_audio, OID_AUTO, debug, CTLFLAG_RW,
99     &g_audio_debug, 0, "Debug level");
100 #endif
101 
102 static int g_audio_mode = 0;
103 
104 SYSCTL_INT(_hw_usb_g_audio, OID_AUTO, mode, CTLFLAG_RW,
105     &g_audio_mode, 0, "Mode selection");
106 
107 static int g_audio_pattern_interval = 1000;
108 
109 SYSCTL_INT(_hw_usb_g_audio, OID_AUTO, pattern_interval, CTLFLAG_RW,
110     &g_audio_pattern_interval, 0, "Pattern interval in milliseconds");
111 
112 static char g_audio_pattern_data[G_AUDIO_MAX_STRLEN];
113 
114 SYSCTL_STRING(_hw_usb_g_audio, OID_AUTO, pattern, CTLFLAG_RW,
115     &g_audio_pattern_data, sizeof(g_audio_pattern_data), "Data pattern");
116 
117 static int g_audio_throughput;
118 
119 SYSCTL_INT(_hw_usb_g_audio, OID_AUTO, throughput, CTLFLAG_RD,
120     &g_audio_throughput, sizeof(g_audio_throughput), "Throughput in bytes per second");
121 
122 static device_probe_t g_audio_probe;
123 static device_attach_t g_audio_attach;
124 static device_detach_t g_audio_detach;
125 static usb_handle_request_t g_audio_handle_request;
126 
127 static usb_callback_t g_audio_isoc_read_callback;
128 static usb_callback_t g_audio_isoc_write_callback;
129 
130 static devclass_t g_audio_devclass;
131 
132 static void g_audio_watchdog(void *arg);
133 static void g_audio_timeout(void *arg);
134 
135 static device_method_t g_audio_methods[] = {
136 	/* USB interface */
137 	DEVMETHOD(usb_handle_request, g_audio_handle_request),
138 
139 	/* Device interface */
140 	DEVMETHOD(device_probe, g_audio_probe),
141 	DEVMETHOD(device_attach, g_audio_attach),
142 	DEVMETHOD(device_detach, g_audio_detach),
143 
144 	DEVMETHOD_END
145 };
146 
147 static driver_t g_audio_driver = {
148 	.name = "g_audio",
149 	.methods = g_audio_methods,
150 	.size = sizeof(struct g_audio_softc),
151 };
152 
153 DRIVER_MODULE(g_audio, uhub, g_audio_driver, g_audio_devclass, NULL, NULL);
154 MODULE_DEPEND(g_audio, usb, 1, 1, 1);
155 
156 static const struct usb_config g_audio_config[G_AUDIO_N_TRANSFER] = {
157 
158 	[G_AUDIO_ISOC0_RD] = {
159 		.type = UE_ISOCHRONOUS,
160 		.endpoint = UE_ADDR_ANY,
161 		.direction = UE_DIR_RX,
162 		.flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
163 		.bufsize = G_AUDIO_BUFSIZE,
164 		.callback = &g_audio_isoc_read_callback,
165 		.frames = G_AUDIO_FRAMES,
166 		.usb_mode = USB_MODE_DEVICE,
167 		.if_index = 1,
168 	},
169 
170 	[G_AUDIO_ISOC1_RD] = {
171 		.type = UE_ISOCHRONOUS,
172 		.endpoint = UE_ADDR_ANY,
173 		.direction = UE_DIR_RX,
174 		.flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
175 		.bufsize = G_AUDIO_BUFSIZE,
176 		.callback = &g_audio_isoc_read_callback,
177 		.frames = G_AUDIO_FRAMES,
178 		.usb_mode = USB_MODE_DEVICE,
179 		.if_index = 1,
180 	},
181 
182 	[G_AUDIO_ISOC0_WR] = {
183 		.type = UE_ISOCHRONOUS,
184 		.endpoint = UE_ADDR_ANY,
185 		.direction = UE_DIR_TX,
186 		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
187 		.bufsize = G_AUDIO_BUFSIZE,
188 		.callback = &g_audio_isoc_write_callback,
189 		.frames = G_AUDIO_FRAMES,
190 		.usb_mode = USB_MODE_DEVICE,
191 		.if_index = 2,
192 	},
193 
194 	[G_AUDIO_ISOC1_WR] = {
195 		.type = UE_ISOCHRONOUS,
196 		.endpoint = UE_ADDR_ANY,
197 		.direction = UE_DIR_TX,
198 		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
199 		.bufsize = G_AUDIO_BUFSIZE,
200 		.callback = &g_audio_isoc_write_callback,
201 		.frames = G_AUDIO_FRAMES,
202 		.usb_mode = USB_MODE_DEVICE,
203 		.if_index = 2,
204 	},
205 };
206 
207 static void
g_audio_timeout_reset(struct g_audio_softc * sc)208 g_audio_timeout_reset(struct g_audio_softc *sc)
209 {
210 	int i = g_audio_pattern_interval;
211 
212 	sc->sc_tx_interval = i;
213 
214 	if (i <= 0)
215 		i = 1;
216 	else if (i > 1023)
217 		i = 1023;
218 
219 	i = USB_MS_TO_TICKS(i);
220 
221 	usb_callout_reset(&sc->sc_callout, i, &g_audio_timeout, sc);
222 }
223 
224 static void
g_audio_timeout(void * arg)225 g_audio_timeout(void *arg)
226 {
227 	struct g_audio_softc *sc = arg;
228 
229 	sc->sc_mode = g_audio_mode;
230 
231 	memcpy(sc->sc_pattern, g_audio_pattern_data, sizeof(sc->sc_pattern));
232 
233 	sc->sc_pattern[G_AUDIO_MAX_STRLEN - 1] = 0;
234 
235 	sc->sc_pattern_len = strlen(sc->sc_pattern);
236 
237 	if (sc->sc_mode != G_AUDIO_MODE_LOOP) {
238 		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_WR]);
239 		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_WR]);
240 	}
241 	g_audio_timeout_reset(sc);
242 }
243 
244 static void
g_audio_watchdog_reset(struct g_audio_softc * sc)245 g_audio_watchdog_reset(struct g_audio_softc *sc)
246 {
247 	usb_callout_reset(&sc->sc_watchdog, hz, &g_audio_watchdog, sc);
248 }
249 
250 static void
g_audio_watchdog(void * arg)251 g_audio_watchdog(void *arg)
252 {
253 	struct g_audio_softc *sc = arg;
254 	int i;
255 
256 	i = sc->sc_throughput;
257 
258 	sc->sc_throughput = 0;
259 
260 	g_audio_throughput = i;
261 
262 	g_audio_watchdog_reset(sc);
263 }
264 
265 static int
g_audio_probe(device_t dev)266 g_audio_probe(device_t dev)
267 {
268 	struct usb_attach_arg *uaa = device_get_ivars(dev);
269 
270 	DPRINTFN(11, "\n");
271 
272 	if (uaa->usb_mode != USB_MODE_DEVICE)
273 		return (ENXIO);
274 
275 	if ((uaa->info.bInterfaceClass == UICLASS_AUDIO) &&
276 	    (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL))
277 		return (0);
278 
279 	return (ENXIO);
280 }
281 
282 static int
g_audio_attach(device_t dev)283 g_audio_attach(device_t dev)
284 {
285 	struct g_audio_softc *sc = device_get_softc(dev);
286 	struct usb_attach_arg *uaa = device_get_ivars(dev);
287 	int error;
288 	int i;
289 	uint8_t iface_index[3];
290 
291 	DPRINTFN(11, "\n");
292 
293 	device_set_usb_desc(dev);
294 
295 	lockinit(&sc->sc_lock, "g_audio", 0, 0);
296 
297 	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_lock, 0);
298 	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_lock, 0);
299 
300 	sc->sc_mode = G_AUDIO_MODE_SILENT;
301 
302 	sc->sc_noise_rem = 1;
303 
304 	for (i = 0; i != G_AUDIO_FRAMES; i++) {
305 		sc->sc_data_len[0][i] = G_AUDIO_BUFSIZE / G_AUDIO_FRAMES;
306 		sc->sc_data_len[1][i] = G_AUDIO_BUFSIZE / G_AUDIO_FRAMES;
307 	}
308 
309 	iface_index[0] = uaa->info.bIfaceIndex;
310 	iface_index[1] = uaa->info.bIfaceIndex + 1;
311 	iface_index[2] = uaa->info.bIfaceIndex + 2;
312 
313 	error = usbd_set_alt_interface_index(uaa->device, iface_index[1], 1);
314 	if (error) {
315 		DPRINTF("alt iface setting error=%s\n", usbd_errstr(error));
316 		goto detach;
317 	}
318 	error = usbd_set_alt_interface_index(uaa->device, iface_index[2], 1);
319 	if (error) {
320 		DPRINTF("alt iface setting error=%s\n", usbd_errstr(error));
321 		goto detach;
322 	}
323 	error = usbd_transfer_setup(uaa->device,
324 	    iface_index, sc->sc_xfer, g_audio_config,
325 	    G_AUDIO_N_TRANSFER, sc, &sc->sc_lock);
326 
327 	if (error) {
328 		DPRINTF("error=%s\n", usbd_errstr(error));
329 		goto detach;
330 	}
331 	usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
332 	usbd_set_parent_iface(uaa->device, iface_index[2], iface_index[0]);
333 
334 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
335 
336 	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_RD]);
337 	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_RD]);
338 
339 	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_WR]);
340 	usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_WR]);
341 
342 	g_audio_timeout_reset(sc);
343 
344 	g_audio_watchdog_reset(sc);
345 
346 	lockmgr(&sc->sc_lock, LK_RELEASE);
347 
348 	return (0);			/* success */
349 
350 detach:
351 	g_audio_detach(dev);
352 
353 	return (ENXIO);			/* error */
354 }
355 
356 static int
g_audio_detach(device_t dev)357 g_audio_detach(device_t dev)
358 {
359 	struct g_audio_softc *sc = device_get_softc(dev);
360 
361 	DPRINTF("\n");
362 
363 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
364 	usb_callout_stop(&sc->sc_callout);
365 	usb_callout_stop(&sc->sc_watchdog);
366 	lockmgr(&sc->sc_lock, LK_RELEASE);
367 
368 	usbd_transfer_unsetup(sc->sc_xfer, G_AUDIO_N_TRANSFER);
369 
370 	usb_callout_drain(&sc->sc_callout);
371 	usb_callout_drain(&sc->sc_watchdog);
372 
373 	lockuninit(&sc->sc_lock);
374 
375 	return (0);
376 }
377 
378 
379 static int32_t
g_noise(struct g_audio_softc * sc)380 g_noise(struct g_audio_softc *sc)
381 {
382 	uint32_t temp;
383 	const uint32_t prime = 0xFFFF1D;
384 
385 	if (sc->sc_noise_rem & 1) {
386 		sc->sc_noise_rem += prime;
387 	}
388 	sc->sc_noise_rem /= 2;
389 
390 	temp = sc->sc_noise_rem;
391 
392 	/* unsigned to signed conversion */
393 
394 	temp ^= 0x800000;
395 	if (temp & 0x800000) {
396 		temp |= (-0x800000);
397 	}
398 	return temp;
399 }
400 
401 static void
g_audio_make_samples(struct g_audio_softc * sc,int16_t * ptr,int samples)402 g_audio_make_samples(struct g_audio_softc *sc, int16_t *ptr, int samples)
403 {
404 	int i;
405 	int j;
406 
407 	for (i = 0; i != samples; i++) {
408 
409 		j = g_noise(sc);
410 
411 		if ((sc->sc_state < 0) || (sc->sc_state >= sc->sc_pattern_len))
412 			sc->sc_state = 0;
413 
414 		if (sc->sc_pattern_len != 0) {
415 			j = (j * sc->sc_pattern[sc->sc_state]) >> 16;
416 			sc->sc_state++;
417 		}
418 		*ptr++ = j / 256;
419 		*ptr++ = j / 256;
420 	}
421 }
422 
423 static void
g_audio_isoc_write_callback(struct usb_xfer * xfer,usb_error_t error)424 g_audio_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
425 {
426 	struct g_audio_softc *sc = usbd_xfer_softc(xfer);
427 	int actlen;
428 	int aframes;
429 	int nr = (xfer == sc->sc_xfer[G_AUDIO_ISOC0_WR]) ? 0 : 1;
430 	int16_t *ptr;
431 	int i;
432 
433 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
434 
435 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
436 	    USB_GET_STATE(xfer), aframes, actlen);
437 
438 	switch (USB_GET_STATE(xfer)) {
439 	case USB_ST_TRANSFERRED:
440 
441 		sc->sc_throughput += actlen;
442 
443 		if (sc->sc_mode == G_AUDIO_MODE_LOOP)
444 			break;		/* sync with RX */
445 
446 	case USB_ST_SETUP:
447 tr_setup:
448 
449 		ptr = sc->sc_data_buf[nr];
450 
451 		if (sc->sc_mode == G_AUDIO_MODE_PATTERN) {
452 
453 			for (i = 0; i != G_AUDIO_FRAMES; i++) {
454 
455 				usbd_xfer_set_frame_data(xfer, i, ptr, sc->sc_data_len[nr][i]);
456 
457 				g_audio_make_samples(sc, ptr, (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2);
458 
459 				ptr += (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2;
460 			}
461 		} else if (sc->sc_mode == G_AUDIO_MODE_LOOP) {
462 
463 			for (i = 0; i != G_AUDIO_FRAMES; i++) {
464 
465 				usbd_xfer_set_frame_data(xfer, i, ptr, sc->sc_data_len[nr][i] & ~3);
466 
467 				g_audio_make_samples(sc, ptr, sc->sc_data_len[nr][i] / 4);
468 
469 				ptr += (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2;
470 			}
471 		}
472 		break;
473 
474 	default:			/* Error */
475 		DPRINTF("error=%s\n", usbd_errstr(error));
476 
477 		if (error != USB_ERR_CANCELLED) {
478 			/* try to clear stall first */
479 			usbd_xfer_set_stall(xfer);
480 			goto tr_setup;
481 		}
482 		break;
483 	}
484 }
485 
486 static void
g_audio_isoc_read_callback(struct usb_xfer * xfer,usb_error_t error)487 g_audio_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
488 {
489 	struct g_audio_softc *sc = usbd_xfer_softc(xfer);
490 	int actlen;
491 	int aframes;
492 	int nr = (xfer == sc->sc_xfer[G_AUDIO_ISOC0_RD]) ? 0 : 1;
493 	int16_t *ptr;
494 	int i;
495 
496 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
497 
498 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
499 	    USB_GET_STATE(xfer), aframes, actlen);
500 
501 	switch (USB_GET_STATE(xfer)) {
502 	case USB_ST_TRANSFERRED:
503 
504 		sc->sc_throughput += actlen;
505 
506 		for (i = 0; i != G_AUDIO_FRAMES; i++) {
507 			sc->sc_data_len[nr][i] = usbd_xfer_frame_len(xfer, i);
508 		}
509 
510 		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC0_WR]);
511 		usbd_transfer_start(sc->sc_xfer[G_AUDIO_ISOC1_WR]);
512 
513 		break;
514 
515 	case USB_ST_SETUP:
516 tr_setup:
517 		ptr = sc->sc_data_buf[nr];
518 
519 		for (i = 0; i != G_AUDIO_FRAMES; i++) {
520 
521 			usbd_xfer_set_frame_data(xfer, i, ptr,
522 			    G_AUDIO_BUFSIZE / G_AUDIO_FRAMES);
523 
524 			ptr += (G_AUDIO_BUFSIZE / G_AUDIO_FRAMES) / 2;
525 		}
526 
527 		usbd_transfer_submit(xfer);
528 		break;
529 
530 	default:			/* Error */
531 		DPRINTF("error=%s\n", usbd_errstr(error));
532 
533 		if (error != USB_ERR_CANCELLED) {
534 			/* try to clear stall first */
535 			usbd_xfer_set_stall(xfer);
536 			goto tr_setup;
537 		}
538 		break;
539 	}
540 }
541 
542 
543 static int
g_audio_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)544 g_audio_handle_request(device_t dev,
545     const void *preq, void **pptr, uint16_t *plen,
546     uint16_t offset, uint8_t *pstate)
547 {
548 	struct g_audio_softc *sc = device_get_softc(dev);
549 	const struct usb_device_request *req = preq;
550 	uint8_t is_complete = *pstate;
551 
552 	if (!is_complete) {
553 		if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
554 		    (req->bRequest == 0x82 /* get min */ )) {
555 
556 			if (offset == 0) {
557 				USETW(sc->sc_volume_limit, 0);
558 				*plen = 2;
559 				*pptr = &sc->sc_volume_limit;
560 			} else {
561 				*plen = 0;
562 			}
563 			return (0);
564 		} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
565 		    (req->bRequest == 0x83 /* get max */ )) {
566 
567 			if (offset == 0) {
568 				USETW(sc->sc_volume_limit, 0x2000);
569 				*plen = 2;
570 				*pptr = &sc->sc_volume_limit;
571 			} else {
572 				*plen = 0;
573 			}
574 			return (0);
575 		} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
576 		    (req->bRequest == 0x84 /* get residue */ )) {
577 
578 			if (offset == 0) {
579 				USETW(sc->sc_volume_limit, 1);
580 				*plen = 2;
581 				*pptr = &sc->sc_volume_limit;
582 			} else {
583 				*plen = 0;
584 			}
585 			return (0);
586 		} else if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
587 		    (req->bRequest == 0x81 /* get value */ )) {
588 
589 			if (offset == 0) {
590 				USETW(sc->sc_volume_setting, 0x2000);
591 				*plen = sizeof(sc->sc_volume_setting);
592 				*pptr = &sc->sc_volume_setting;
593 			} else {
594 				*plen = 0;
595 			}
596 			return (0);
597 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
598 		    (req->bRequest == 0x01 /* set value */ )) {
599 
600 			if (offset == 0) {
601 				*plen = sizeof(sc->sc_volume_setting);
602 				*pptr = &sc->sc_volume_setting;
603 			} else {
604 				*plen = 0;
605 			}
606 			return (0);
607 		} else if ((req->bmRequestType == UT_WRITE_CLASS_ENDPOINT) &&
608 		    (req->bRequest == 0x01 /* set value */ )) {
609 
610 			if (offset == 0) {
611 				*plen = sizeof(sc->sc_sample_rate);
612 				*pptr = &sc->sc_sample_rate;
613 			} else {
614 				*plen = 0;
615 			}
616 			return (0);
617 		}
618 	}
619 	return (ENXIO);			/* use builtin handler */
620 }
621